diff options
Diffstat (limited to 'src/slic3r/GUI/Mouse3DController.cpp')
-rw-r--r-- | src/slic3r/GUI/Mouse3DController.cpp | 464 |
1 files changed, 315 insertions, 149 deletions
diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 75ec9c3bc..692ae7f1b 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -1,11 +1,12 @@ #include "libslic3r/libslic3r.h" +#include "libslic3r/PresetBundle.hpp" #include "Mouse3DController.hpp" #include "Camera.hpp" #include "GUI_App.hpp" -#include "PresetBundle.hpp" -#include "AppConfig.hpp" #include "GLCanvas3D.hpp" +#include "Plater.hpp" +#include "NotificationManager.hpp" #include <wx/glcanvas.h> @@ -98,7 +99,166 @@ void Mouse3DController::State::append_button(unsigned int id, size_t /* input_qu #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT } -#ifdef WIN32 +#ifdef _WIN32 +#if ENABLE_CTRL_M_ON_WINDOWS +static std::string format_device_string(int vid, int pid) +{ + std::string ret; + + switch (vid) + { + case 0x046d: { ret = "LOGITECH"; break; } + case 0x256F: { ret = "3DCONNECTION"; break; } + default: { ret = "UNKNOWN"; break; } + } + + ret += "::"; + + switch (pid) + { + case 0xc603: { ret += "spacemouse plus XT"; break; } + case 0xc605: { ret += "cadman"; break; } + case 0xc606: { ret += "spacemouse classic"; break; } + case 0xc621: { ret += "spaceball 5000"; break; } + case 0xc623: { ret += "space traveller"; break; } + case 0xc625: { ret += "space pilot"; break; } + case 0xc626: { ret += "space navigator"; break; } + case 0xc627: { ret += "space explorer"; break; } + case 0xc628: { ret += "space navigator for notebooks"; break; } + case 0xc629: { ret += "space pilot pro"; break; } + case 0xc62b: { ret += "space mouse pro"; break; } + case 0xc62e: { ret += "spacemouse wireless (USB cable)"; break; } + case 0xc62f: { ret += "spacemouse wireless receiver"; break; } + case 0xc631: { ret += "spacemouse pro wireless"; break; } + case 0xc632: { ret += "spacemouse pro wireless receiver"; break; } + case 0xc633: { ret += "spacemouse enterprise"; break; } + case 0xc635: { ret += "spacemouse compact"; break; } + case 0xc636: { ret += "spacemouse module"; break; } + case 0xc640: { ret += "nulooq"; break; } + case 0xc652: { ret += "3Dconnexion universal receiver"; break; } + default: { ret += "UNKNOWN"; break; } + } + + return ret; +} + +static std::string detect_attached_device() +{ + std::string ret; + + // Initialize the hidapi library + int res = hid_init(); + if (res != 0) + BOOST_LOG_TRIVIAL(error) << "Unable to initialize hidapi library"; + else { + // Enumerates devices + hid_device_info* devices = hid_enumerate(0, 0); + if (devices == nullptr) + BOOST_LOG_TRIVIAL(trace) << "detect_attached_device() - no HID device enumerated."; + else { + // Searches for 1st connected 3Dconnexion device + struct DeviceData + { + unsigned short usage_page{ 0 }; + unsigned short usage{ 0 }; + + DeviceData(unsigned short usage_page, unsigned short usage) + : usage_page(usage_page), usage(usage) + {} + + // https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf + // Usage page 1 - Generic Desktop Controls + // Usage page 1, usage 8 - Multi-axis Controller + bool has_valid_usage() const { return usage_page == 1 && usage == 8; } + }; + + // 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: usage_page == 1 and usage == 8 + // 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, + + hid_device_info* current = devices; + typedef std::pair<unsigned short, unsigned short> DeviceIds; + typedef std::vector<DeviceData> DeviceDataList; + typedef std::map<DeviceIds, DeviceDataList> DetectedDevices; + DetectedDevices detected_devices; + while (current != nullptr) { + unsigned short vendor_id = 0; + unsigned short product_id = 0; + + 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) { + if (_3DCONNEXION_DEVICES[i] == current->product_id) { + product_id = current->product_id; + 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; + + it->second.emplace_back(current->usage_page, current->usage); + } + } + } + + current = current->next; + } + + // Free enumerated devices + hid_free_enumeration(devices); + + unsigned short vendor_id = 0; + unsigned short product_id = 0; + if (!detected_devices.empty()) { + // 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) { + if (device.second.front().has_valid_usage()) { + vendor_id = device.first.first; + product_id = device.first.second; + break; + } + } + else { + bool found = false; + for (const DeviceData& data : device.second) { + if (data.has_valid_usage()) { + vendor_id = device.first.first; + product_id = device.first.second; + found = true; + break; + } + } + + if (found) + break; + } + } + } + + if (vendor_id != 0 && product_id != 0) { + ret = format_device_string(static_cast<int>(vendor_id), static_cast<int>(product_id)); + BOOST_LOG_TRIVIAL(trace) << "Detected device: " << std::hex << vendor_id << std::dec << "::" << std::hex << product_id << std::dec << " " << ret; + } + else + BOOST_LOG_TRIVIAL(trace) << "No 3DConnexion device detected"; + } + + // Finalize the hidapi library + hid_exit(); + } + + return ret; +} +#endif // ENABLE_CTRL_M_ON_WINDOWS + // Called by Win32 HID enumeration callback. void Mouse3DController::device_attached(const std::string &device) { @@ -114,10 +274,36 @@ void Mouse3DController::device_attached(const std::string &device) // Never mind, enumeration will be performed until connected. m_wakeup = true; m_stop_condition.notify_all(); - } +#if ENABLE_CTRL_M_ON_WINDOWS + m_device_str = format_device_string(vid, pid); + 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; + } + else + m_params_by_device[format_device_string(vid, pid)] = Params(); + m_connected = true; +#endif // ENABLE_CTRL_M_ON_WINDOWS + } } } +#if ENABLE_CTRL_M_ON_WINDOWS +void Mouse3DController::device_detached(const std::string& device) +{ + int vid = 0; + int pid = 0; + if (sscanf(device.c_str(), "\\\\?\\HID#VID_%x&PID_%x&", &vid, &pid) == 2) { + if (std::find(_3DCONNEXION_VENDORS.begin(), _3DCONNEXION_VENDORS.end(), vid) != _3DCONNEXION_VENDORS.end()) { + tbb::mutex::scoped_lock lock(m_params_ui_mutex); + m_params_by_device[format_device_string(vid, pid)] = m_params_ui; + } + } + m_device_str = ""; + m_connected = false; +} +#endif // ENABLE_CTRL_M_ON_WINDOWS + // Filter out mouse scroll events produced by the 3DConnexion driver. bool Mouse3DController::State::process_mouse_wheel() { @@ -133,7 +319,7 @@ bool Mouse3DController::State::process_mouse_wheel() m_mouse_wheel_counter = 0; return true; } -#endif // WIN32 +#endif // _WIN32 bool Mouse3DController::State::apply(const Mouse3DController::Params ¶ms, Camera& camera) { @@ -160,7 +346,6 @@ bool Mouse3DController::State::apply(const Mouse3DController::Params ¶ms, Ca if (params.swap_yz) rot = Vec3d(rot.x(), -rot.z(), rot.y()); camera.rotate_local_around_target(Vec3d(rot.x(), - rot.z(), rot.y())); - break; } else { assert(input_queue_item.is_buttons()); switch (input_queue_item.type_or_buttons) { @@ -211,6 +396,7 @@ void Mouse3DController::save_config(AppConfig &appconfig) const { // 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 ¶ms = key_value_pair.second; @@ -228,6 +414,19 @@ bool Mouse3DController::apply(Camera& camera) m_show_settings_dialog = false; m_settings_dialog_closed_by_user = false; } + +#if ENABLE_CTRL_M_ON_WINDOWS +#ifdef _WIN32 + { + tbb::mutex::scoped_lock lock(m_params_ui_mutex); + if (m_params_ui_changed) { + m_params = m_params_ui; + m_params_ui_changed = false; + } + } +#endif // _WIN32 +#endif // ENABLE_CTRL_M_ON_WINDOWS + return m_state.apply(m_params, camera); } @@ -238,8 +437,7 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const // 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) - { + if (m_settings_dialog_closed_by_user) { m_show_settings_dialog = false; m_settings_dialog_closed_by_user = false; canvas.request_extra_frame(); @@ -260,13 +458,10 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const 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) - { + 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)) - { + 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; @@ -274,59 +469,51 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const } const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Device:"))); - ImGui::PopStyleColor(); + imgui.text_colored(color, _L("Device:")); ImGui::SameLine(); imgui.text(m_device_str); ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Speed:"))); - ImGui::PopStyleColor(); + imgui.text_colored(color, _L("Speed:")); float translation_scale = (float)params_copy.translation.scale / Params::DefaultTranslationScale; - if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.1f, 10.0f, "%.1f")) { + 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; } float rotation_scale = params_copy.rotation.scale / Params::DefaultRotationScale; - if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.1f, 10.0f, "%.1f")) { + 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; } float zoom_scale = params_copy.zoom.scale / Params::DefaultZoomScale; - if (imgui.slider_float(_(L("Zoom")), &zoom_scale, 0.1f, 10.0f, "%.1f")) { + if (imgui.slider_float(_L("Zoom"), &zoom_scale, 0.1f, 10.0f, "%.1f")) { params_copy.zoom.scale = Params::DefaultZoomScale * zoom_scale; params_changed = true; } ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Deadzone:"))); - ImGui::PopStyleColor(); + imgui.text_colored(color, _L("Deadzone:")); float translation_deadzone = (float)params_copy.translation.deadzone; - if (imgui.slider_float(_(L("Translation")) + "/" + _(L("Zoom")), &translation_deadzone, 0.0f, (float)Params::MaxTranslationDeadzone, "%.2f")) { + 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; } float rotation_deadzone = params_copy.rotation.deadzone; - if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, Params::MaxRotationDeadzone, "%.2f")) { + if (imgui.slider_float(_L("Rotation") + "##2", &rotation_deadzone, 0.0f, Params::MaxRotationDeadzone, "%.2f")) { params_copy.rotation.deadzone = rotation_deadzone; params_changed = true; } ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Options:"))); - ImGui::PopStyleColor(); + imgui.text_colored(color, _L("Options:")); bool swap_yz = params_copy.swap_yz; - if (imgui.checkbox("Swap Y/Z axes", swap_yz)) { + if (imgui.checkbox(_L("Swap Y/Z axes"), swap_yz)) { params_copy.swap_yz = swap_yz; params_changed = true; } @@ -334,25 +521,20 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT ImGui::Separator(); ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text("DEBUG:"); - imgui.text("Vectors:"); - ImGui::PopStyleColor(); + imgui.text_colored(color, "DEBUG:"); + imgui.text_colored(color, "Vectors:"); 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>(); 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(); + imgui.text_colored(color, "Queue size:"); 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); 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)) - { + if (ImGui::InputInt("Max size", &input_queue_size_param, 1, 1, ImGuiInputTextFlags_ReadOnly)) { if (input_queue_size_param > 0) { params_copy.input_queue_max_size = input_queue_size_param; params_changed = true; @@ -360,23 +542,19 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const } ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text("Camera:"); - ImGui::PopStyleColor(); + imgui.text_colored(color, "Camera:"); Vec3f target = wxGetApp().plater()->get_camera().get_target().cast<float>(); ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT ImGui::Separator(); - if (imgui.button(_(L("Close")))) - { + if (imgui.button(_L("Close"))) { // the user clicked on the [Close] button m_settings_dialog_closed_by_user = true; canvas.set_as_dirty(); } } - else - { + else { // the user clicked on the [X] button m_settings_dialog_closed_by_user = true; canvas.set_as_dirty(); @@ -386,7 +564,7 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const imgui.end(); if (params_changed) { - // Synchronize front end parameters to back end. + // 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 @@ -423,6 +601,8 @@ void Mouse3DController::disconnected() m_params_by_device[m_device_str] = m_params_ui; m_device_str.clear(); m_connected = false; + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::Mouse3dDisconnected); + wxGetApp().plater()->CallAfter([]() { Plater *plater = wxGetApp().plater(); if (plater != nullptr) { @@ -481,10 +661,24 @@ bool Mouse3DController::handle_input(const DataPacketAxis& packet) // Initialize the application. void Mouse3DController::init() { +#if ENABLE_CTRL_M_ON_WINDOWS +#ifdef _WIN32 + m_device_str = detect_attached_device(); + if (!m_device_str.empty()) { + m_connected = true; + if (auto it_params = m_params_by_device.find(m_device_str); it_params != m_params_by_device.end()) + m_params = m_params_ui = it_params->second; + } +#endif // _WIN32 +#endif // ENABLE_CTRL_M_ON_WINDOWS + assert(! m_thread.joinable()); if (! m_thread.joinable()) { m_stop = false; +#ifndef _WIN32 + // Don't start the background thread on Windows, as the HID messages are sent as Windows messages. m_thread = std::thread(&Mouse3DController::run, this); +#endif // _WIN32 } } @@ -503,6 +697,13 @@ void Mouse3DController::shutdown() m_thread.join(); m_stop = false; } + +#if ENABLE_CTRL_M_ON_WINDOWS +#ifdef _WIN32 + if (!m_device_str.empty()) + m_params_by_device[m_device_str] = m_params_ui; +#endif // _WIN32 +#endif // ENABLE_CTRL_M_ON_WINDOWS } // Main routine of the worker thread. @@ -536,7 +737,7 @@ void Mouse3DController::run() if (m_stop) break; if (m_params_ui_changed) { - m_params = m_params_ui; + m_params = m_params_ui; m_params_ui_changed = false; } } @@ -576,8 +777,7 @@ bool Mouse3DController::connect_device() // Enumerates devices hid_device_info* devices = hid_enumerate(0, 0); - if (devices == nullptr) - { + if (devices == nullptr) { BOOST_LOG_TRIVIAL(trace) << "Mouse3DController::connect_device() - no HID device enumerated."; return false; } @@ -600,15 +800,17 @@ bool Mouse3DController::connect_device() : path(path), usage_page(usage_page), usage(usage) {} - bool has_valid_usage() const { return (usage_page == 1) && (usage == 8); } + // https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf + // Usage page 1 - Generic Desktop Controls + // Usage page 1, usage 8 - Multi-axis Controller + bool has_valid_usage() const { return usage_page == 1 && usage == 8; } }; #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) - { + while (cur != nullptr) { std::cout << "\""; std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); std::cout << "/"; @@ -638,26 +840,20 @@ bool Mouse3DController::connect_device() #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << std::endl << "Detected 3D connexion devices:" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - while (current != nullptr) - { + while (current != nullptr) { unsigned short vendor_id = 0; unsigned short product_id = 0; - for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) - { - if (_3DCONNEXION_VENDORS[i] == current->vendor_id) - { + 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) - { - if (_3DCONNEXION_DEVICES[i] == current->product_id) - { + if (vendor_id != 0) { + for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i) { + if (_3DCONNEXION_DEVICES[i] == current->product_id) { product_id = current->product_id; DeviceIds detected_device(vendor_id, product_id); DetectedDevices::iterator it = detected_devices.find(detected_device); @@ -688,46 +884,42 @@ bool Mouse3DController::connect_device() if (detected_devices.empty()) return false; - std::string path = ""; + 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) - { + for (const DetectedDevices::value_type& device : detected_devices) { + if (device.second.size() == 1) { #if defined(__linux__) hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr); - if (test_device != nullptr) - { + if (test_device == nullptr) { + BOOST_LOG_TRIVIAL(error) << "3DConnexion device cannot be opened: " << device.second.front().path << + " You may need to update /etc/udev/rules.d"; + } else { hid_close(test_device); #else - if (device.second.front().has_valid_usage()) - { + if (device.second.front().has_valid_usage()) { #endif // __linux__ vendor_id = device.first.first; product_id = device.first.second; break; } } - else - { + else { bool found = false; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - for (const DeviceData& data : device.second) - { + for (const DeviceData& data : device.second) { #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) - { + if (test_device != nullptr) { path = data.path; vendor_id = device.first.first; product_id = device.first.second; @@ -739,8 +931,7 @@ bool Mouse3DController::connect_device() break; } #else // !__linux__ - if (data.has_valid_usage()) - { + if (data.has_valid_usage()) { path = data.path; vendor_id = device.first.first; product_id = device.first.second; @@ -751,10 +942,13 @@ bool Mouse3DController::connect_device() break; } #endif // __linux__ + else { + BOOST_LOG_TRIVIAL(error) << "3DConnexion device cannot be opened: " << data.path << + " You may need to update /etc/udev/rules.d"; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - else std::cout << "-> NOT PASSED" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + } } if (found) @@ -762,10 +956,8 @@ bool Mouse3DController::connect_device() } } - if (path.empty()) - { - if ((vendor_id != 0) && (product_id != 0)) - { + if (path.empty()) { + if ((vendor_id != 0) && (product_id != 0)) { // Open the 3Dconnexion device using vendor_id and product_id #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; @@ -775,8 +967,7 @@ bool Mouse3DController::connect_device() else return false; } - else - { + else { // Open the 3Dconnexion device using the device path #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; @@ -784,8 +975,7 @@ bool Mouse3DController::connect_device() m_device = hid_open_path(path.c_str()); } - if (m_device != nullptr) - { + if (m_device != nullptr) { wchar_t buffer[1024]; hid_get_manufacturer_string(m_device, buffer, 1024); m_device_str = boost::nowide::narrow(buffer); @@ -813,8 +1003,7 @@ bool Mouse3DController::connect_device() } } #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - else - { + 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; @@ -865,45 +1054,63 @@ void Mouse3DController::collect_input() this->handle_input(packet, res, m_params, m_state); } +#ifdef _WIN32 +bool Mouse3DController::handle_raw_input_win32(const unsigned char *data, const int packet_length) +{ + if (! wxGetApp().IsActive()) + return false; + + if (packet_length == 7 || packet_length == 13) { + DataPacketRaw packet; + memcpy(packet.data(), data, packet_length); + handle_packet(packet, packet_length, m_params, m_state); +#if ENABLE_CTRL_M_ON_WINDOWS + m_connected = true; +#endif // ENABLE_CTRL_M_ON_WINDOWS + } + + return true; +} +#endif /* _WIN32 */ + // 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 ¶ms, State &state_in_out) +bool Mouse3DController::handle_input(const DataPacketRaw& packet, const int packet_length, const Params ¶ms, State &state_in_out) { if (! wxGetApp().IsActive()) return false; - int res = packet_lenght; + int res = packet_length; bool updated = false; - if (res == 7) - updated = handle_packet(packet, params, state_in_out); - else if (res == 13) - updated = handle_wireless_packet(packet, params, state_in_out); - else if ((res == 3) && (packet[0] == 3)) + if (res == 7 || res == 13 || // On Mac button packets can be 3 bytes long - updated = handle_packet(packet, params, state_in_out); + ((res == 3) && (packet[0] == 3))) + updated = handle_packet(packet, res, params, state_in_out); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT else if (res > 0) std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -#if 1 if (updated) { wxGetApp().plater()->set_current_canvas_as_dirty(); // ask for an idle event to update 3D scene wxWakeUpIdle(); } -#endif return updated; } // 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 ¶ms, State &state_in_out) +bool Mouse3DController::handle_packet(const DataPacketRaw& packet, const int packet_length, const Params ¶ms, State &state_in_out) { switch (packet[0]) { - case 1: // Translation + case 1: // Translation + Rotation { - if (handle_packet_translation(packet, params, state_in_out)) + bool updated = handle_packet_translation(packet, params, state_in_out); + if (packet_length == 13) + updated |= handle_packet_rotation(packet, 7, params, state_in_out); + + if (updated) return true; break; @@ -941,47 +1148,6 @@ bool Mouse3DController::handle_packet(const DataPacketRaw& packet, const Params return false; } -// 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 ¶ms, State &state_in_out) -{ - switch (packet[0]) - { - case 1: // Translation + Rotation - { - bool updated = handle_packet_translation(packet, params, state_in_out); - updated |= handle_packet_rotation(packet, 7, params, state_in_out); - - if (updated) - return true; - - break; - } - case 3: // Button - { - if (params.buttons_enabled && handle_packet_button(packet, 12, params, state_in_out)) - return true; - - break; - } - case 23: // Battery charge - { -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - std::cout << "3DConnexion - battery level: " << (int)packet[1] << " percent" << std::endl; -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - break; - } - default: - { -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - std::cout << "3DConnexion - Got unknown data packet of code: " << (int)packet[0] << std::endl; -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - break; - } - } - - return false; -} - // 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) { |