WIP: Windows specific 3Dconnexion using WM_INPUT.

This implementation works with the 3DConnexion driver (sic!)
if PrusaSlicer.xml is stored into c:\Program Files\3Dconnexion\3DxWare\3DxWinCore64\Cfg\

The implementation is inspired with Blender, see code inside WITH_INPUT_NDOF blocks.
This commit is contained in:
bubnikv 2020-03-31 09:01:48 +02:00
parent 70baa0d246
commit 25d58faaad
4 changed files with 66 additions and 60 deletions

View file

@ -47,6 +47,7 @@
#include "SysInfoDialog.hpp"
#include "KBShortcutsDialog.hpp"
#include "UpdateDialogs.hpp"
#include "Mouse3DController.hpp"
#include "RemovableDriveManager.hpp"
#ifdef __WXMSW__
@ -193,6 +194,20 @@ static void register_win32_device_notification_event()
}
return true;
});
wxWindow::MSWRegisterMessageHandler(WM_INPUT, [](wxWindow *win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
auto main_frame = dynamic_cast<MainFrame*>(Slic3r::GUI::find_toplevel_parent(win));
auto plater = (main_frame == nullptr) ? nullptr : main_frame->plater();
// if (wParam == RIM_INPUTSINK && plater != nullptr && main_frame->IsActive()) {
if (wParam == RIM_INPUT && plater != nullptr && main_frame->IsActive()) {
RAWINPUT raw;
UINT rawSize = sizeof(RAWINPUT);
::GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &raw, &rawSize, sizeof(RAWINPUTHEADER));
if (raw.header.dwType == RIM_TYPEHID && plater->get_mouse3d_controller().handle_raw_input_win32(raw.data.hid.bRawData, raw.data.hid.dwSizeHid))
return true;
}
return false;
});
}
#endif // WIN32

View file

@ -152,6 +152,16 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
// Failed to get desktop location
assert(false);
}
{
static constexpr int device_count = 1;
RAWINPUTDEVICE devices[device_count] = { 0 };
// multi-axis mouse (SpaceNavigator, etc.)
devices[0].usUsagePage = 0x01;
devices[0].usUsage = 0x08;
if (! RegisterRawInputDevices(devices, device_count, sizeof(RAWINPUTDEVICE)))
BOOST_LOG_TRIVIAL(error) << "RegisterRawInputDevices failed";
}
#endif // _WIN32
// propagate event

View file

@ -484,7 +484,10 @@ void Mouse3DController::init()
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
}
}
@ -600,7 +603,10 @@ 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
@ -688,7 +694,7 @@ 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;
@ -865,45 +871,60 @@ void Mouse3DController::collect_input()
this->handle_input(packet, res, m_params, m_state);
}
// 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)
#ifdef _WIN32
bool Mouse3DController::handle_raw_input_win32(const unsigned char *data, const int packet_length)
{
if (! wxGetApp().IsActive())
return false;
int res = packet_lenght;
if (packet_length == 7 || packet_length == 13) {
DataPacketRaw packet;
memcpy(packet.data(), data, packet_length);
handle_packet(packet, packet_length, m_params, m_state);
}
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_length, const Params &params, State &state_in_out)
{
if (! wxGetApp().IsActive())
return false;
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 &params, State &state_in_out)
bool Mouse3DController::handle_packet(const DataPacketRaw& packet, const int packet_length, const Params &params, 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 +962,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 &params, 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)
{

View file

@ -190,6 +190,8 @@ public:
#endif // __APPLE__
#ifdef WIN32
bool handle_raw_input_win32(const unsigned char *data, const int packet_lenght);
// Called by Win32 HID enumeration callback.
void device_attached(const std::string &device);
@ -218,10 +220,9 @@ private:
typedef std::array<unsigned char, 13> DataPacketRaw;
// Unpack raw 3DConnexion HID packet of a wired 3D mouse into m_state. Called by the worker thread.
static bool handle_input(const DataPacketRaw& packet, const int packet_lenght, const Params &params, State &state_in_out);
static bool handle_input(const DataPacketRaw& packet, const int packet_length, const Params &params, State &state_in_out);
// The following is called by handle_input() from the worker thread.
static bool handle_packet(const DataPacketRaw& packet, const Params &params, State &state_in_out);
static bool handle_wireless_packet(const DataPacketRaw& packet, const Params &params, State &state_in_out);
static bool handle_packet(const DataPacketRaw& packet, const int packet_length, const Params &params, State &state_in_out);
static bool handle_packet_translation(const DataPacketRaw& packet, const Params &params, State &state_in_out);
static bool handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte, const Params &params, State &state_in_out);
static bool handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size, const Params &params, State &state_in_out);