Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@
[submodule "extlib/mimalloc"]
path = extlib/mimalloc
url = https://github.com/microsoft/mimalloc
[submodule "extlib/libspnavdev"]
path = extlib/libspnavdev
url = https://github.com/rpavlik/libspnavdev.git
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ if(ENABLE_GUI)
"${CMAKE_SOURCE_DIR}/extlib/si")
set(SPACEWARE_LIBRARIES
"${CMAKE_SOURCE_DIR}/extlib/si/siapp.lib")
else()
message(STATUS "Using libspnavdev")
add_vendored_subdirectory(extlib/libspnavdev)
set(HAVE_SPNAVDEV TRUE)
endif()
elseif(APPLE)
find_package(OpenGL REQUIRED)
Expand Down
1 change: 1 addition & 0 deletions extlib/libspnavdev
Submodule libspnavdev added at 69f892
9 changes: 7 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ endif()

set(platform_SOURCES
${gl_SOURCES}
platform/entrygui.cpp)
platform/entrygui.cpp
platform/spnavdevice.cpp)

if(WIN32)
list(APPEND platform_SOURCES
Expand Down Expand Up @@ -134,6 +135,9 @@ else()
list(APPEND platform_LIBRARIES ${${pkg_config_lib}_LIBRARIES})
endforeach()
endif()
if(HAVE_SPNAVDEV)
list(APPEND platform_LIBRARIES spnavdev)
endif()

set(every_platform_SOURCES
platform/guiwin.cpp
Expand Down Expand Up @@ -319,7 +323,7 @@ endif()
# solvespace graphical executable

if(ENABLE_GUI)
add_executable(solvespace WIN32 MACOSX_BUNDLE
add_executable(solvespace MACOSX_BUNDLE
${solvespace_core_gl_SOURCES}
${platform_SOURCES}
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
Expand All @@ -340,6 +344,7 @@ if(ENABLE_GUI)
set_target_properties(solvespace PROPERTIES
OUTPUT_NAME SolveSpace)
endif()

endif()

# solvespace headless library
Expand Down
3 changes: 3 additions & 0 deletions src/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
/* Do we have the si library on win32, or libspnav on *nix? */
#cmakedefine HAVE_SPACEWARE

/* Do we have libspnavdev? */
#cmakedefine HAVE_SPNAVDEV

/* What OpenGL version do we use? */
#define HAVE_OPENGL @OPENGL@

Expand Down
55 changes: 54 additions & 1 deletion src/platform/guiwin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
# undef uint32_t
#endif

#include "spnavdevice.h"

#if defined(__GNUC__)
// Disable bogus warning emitted by GCC on GetProcAddress, since there seems to be no way
// of restructuring the code to easily disable it just at the call site.
Expand Down Expand Up @@ -541,6 +543,8 @@ class WindowImplWin32 final : public Window {
SiHdl hSpaceWare = SI_NO_HANDLE;
#endif

std::unique_ptr<NavDeviceWrapper> navDev;

std::shared_ptr<MenuBarImplWin32> menuBar;
std::string tooltipText;
bool scrollbarVisible = false;
Expand Down Expand Up @@ -695,6 +699,11 @@ class WindowImplWin32 final : public Window {
// Make sure any of our child windows get destroyed before we call DestroyWindow, or their
// own destructors may fail.
menuBar.reset();

if (navDev != NULL) {
KillTimer(hWindow, (UINT_PTR)this);
}
navDev.reset();

sscheck(DestroyWindow(hWindow));
#if defined(HAVE_SPACEWARE)
Expand Down Expand Up @@ -725,7 +734,7 @@ class WindowImplWin32 final : public Window {
if(SiGetEvent(window->hSpaceWare, 0, &sged, &sse) == SI_IS_EVENT) {
SixDofEvent event = {};
event.shiftDown = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0);
event.controlDown = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0);
event.controlDown = ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0);
if(sse.type == SI_MOTION_EVENT) {
// The Z axis translation and rotation are both
// backwards in the default mapping.
Expand All @@ -736,6 +745,8 @@ class WindowImplWin32 final : public Window {
event.rotationX = sse.u.spwData.mData[SI_RX]*0.001,
event.rotationY = sse.u.spwData.mData[SI_RY]*0.001,
event.rotationZ = -sse.u.spwData.mData[SI_RZ]*0.001;
dbp("spacemouse %f, %f, %f, %f, %f, %f", event.translationX, event.translationY,
event.translationZ, event.rotationX, event.rotationY, event.rotationZ);
} else if(sse.type == SI_BUTTON_EVENT) {
if(SiButtonPressed(&sse) == SI_APP_FIT_BUTTON) {
event.type = SixDofEvent::Type::PRESS;
Expand Down Expand Up @@ -1064,6 +1075,28 @@ class WindowImplWin32 final : public Window {
break;
}

case WM_TIMER: {
//! @todo where to put this? We don't actually need to handle window messages,
//! just poll it periodically.
if(window->navDev != nullptr && wParam == (WPARAM)window) {

SixDofEvent event = {};
event.shiftDown = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0);
event.controlDown = ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0);
if(window->navDev->process(event)) {
if(event.type == SixDofEvent::Type::MOTION) {
dbp("spnavdev %f, %f, %f, %f, %f, %f", event.translationX,
event.translationY, event.translationZ, event.rotationX,
event.rotationY, event.rotationZ);
}
if(window->onSixDofEvent) {
window->onSixDofEvent(event);
}
}
}
break;
}

default:
return DefWindowProcW(h, msg, wParam, lParam);
}
Expand Down Expand Up @@ -1437,6 +1470,26 @@ void Request3DConnexionEventsForWindow(WindowRef window) {
SiSetUiMode(windowImpl->hSpaceWare, SI_UI_NO_CONTROLS);
}
}
#elif defined(HAVE_SPNAVDEV)
static std::unique_ptr<NavDeviceWrapper> navWrapper;
void Open3DConnexion() {
navWrapper = std::make_unique<NavDeviceWrapper>();
if(!navWrapper->active()) {
navWrapper.reset();
}
}
void Close3DConnexion() {
navWrapper.reset();
}
void Request3DConnexionEventsForWindow(WindowRef window) {
std::shared_ptr<WindowImplWin32> windowImpl = std::static_pointer_cast<WindowImplWin32>(window);
if(navWrapper) {
// Have the window adopt our object
windowImpl->navDev = std::move(navWrapper);
navWrapper.reset();
SetTimer(windowImpl->hWindow, (UINT_PTR)windowImpl.get(), USER_TIMER_MINIMUM, (TIMERPROC) NULL);
}
}
#else
void Open3DConnexion() {}
void Close3DConnexion() {}
Expand Down
146 changes: 146 additions & 0 deletions src/platform/spnavdevice.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//-----------------------------------------------------------------------------
// Cross-platform handling of spnavdev 6-dof input.
//
// Copyright 2021 Collabora, Ltd.
//-----------------------------------------------------------------------------

#include "spnavdevice.h"
#include "config.h"

#ifdef HAVE_SPNAVDEV
# include "spnavdev.h"
# include <string>
# include <solvespace.h>

NavDeviceWrapper::NavDeviceWrapper(const char* device) {
dev = spndev_open(device);
if(dev == nullptr) {
return;
}

int fd = spndev_fd(dev);
populateAxes();
populateButtons();
}
NavDeviceWrapper::~NavDeviceWrapper() {
if(dev != nullptr) {
spndev_close(dev);
dev = nullptr;
}
}

static double transformIndex(union spndev_event const& ev,
NavDeviceWrapper::AxisData const& axisData) {
ssassert(ev.type == SPNDEV_MOTION, "shouldn't be in here if not a motion event");
if(axisData.spnavdevIndex < 0) {
return 0;
}
return double(ev.mot.v[axisData.spnavdevIndex]);
}
bool NavDeviceWrapper::process(SolveSpace::Platform::SixDofEvent& event) {
using SolveSpace::Platform::SixDofEvent;
union spndev_event ev;
if(0 == spndev_process(dev, &ev)) {
return false;
}
if(ctrlPressed) {
event.controlDown = true;
}
if(shiftPressed) {
event.shiftDown = true;
}
switch(ev.type) {
case SPNDEV_MOTION:
event.type = SixDofEvent::Type::MOTION;
event.translationX = transformIndex(ev, axes[0]);
event.translationY = transformIndex(ev, axes[1]);
event.translationZ = transformIndex(ev, axes[2]);
event.rotationX = transformIndex(ev, axes[3]) * 0.001;
event.rotationY = transformIndex(ev, axes[4]) * 0.001;
event.rotationZ = transformIndex(ev, axes[5]) * 0.001;
return true;
case SPNDEV_BUTTON: {
if(ev.bn.num >= buttons.size()) {
return false;
}
const NavButton meaning = buttons[ev.bn.num];
auto type = ev.bn.press ? SixDofEvent::Type::PRESS : SixDofEvent::Type::RELEASE;
switch(meaning) {
case NavButton::UNUSED:
// we don't handle this button.
return false;

case NavButton::SHIFT:
// handled internally to this class
shiftPressed = type == SixDofEvent::Type::PRESS;
return false;

case NavButton::CTRL:
// handled internally to this class
ctrlPressed = type == SixDofEvent::Type::PRESS;
return false;

case NavButton::FIT:
event.button = SixDofEvent::Button::FIT;
event.type = type;
return true;
}
break;
}
default: return false;
}
return false;
}

void NavDeviceWrapper::populateAxes() {
using std::begin;
using std::end;
const std::string axis_names[] = {"Tx", "Ty", "Tz", "Rx", "Ry", "Rz"};
const auto b = begin(axis_names);
const auto e = end(axis_names);
const auto num_axes = spndev_num_axes(dev);
for(int axis_idx = 0; axis_idx < num_axes; ++axis_idx) {
auto axis_name = spndev_axis_name(dev, axis_idx);
auto it = std::find_if(b, e, [&](std::string const& name) { return name == axis_name; });
if(it != e) {
ptrdiff_t remapped_index = std::distance(b, it);
axes[remapped_index] = AxisData{axis_idx};
}
}
}

void NavDeviceWrapper::populateButtons() {
using std::begin;
using std::end;
using ButtonData = std::pair<std::string, NavButton>;
const ButtonData button_name_pairs[] = {
{"CTRL", NavButton::CTRL},
{"FIT", NavButton::FIT},
{"SHIFT", NavButton::SHIFT},
};

const auto b = begin(button_name_pairs);
const auto e = end(button_name_pairs);
const auto num_buttons = spndev_num_buttons(dev);
buttons.resize(num_buttons);
for(int button_idx = 0; button_idx < num_buttons; ++button_idx) {
auto button_name = spndev_button_name(dev, button_idx);
auto it =
std::find_if(b, e, [&](ButtonData const& data) { return data.first == button_name; });
if(it != e) {
buttons[button_idx] = it->second;
}
}
}

#else

NavDeviceWrapper::NavDeviceWrapper(const char* device) {
}
NavDeviceWrapper::~NavDeviceWrapper() {
}

bool NavDeviceWrapper::process(SolveSpace::Platform::SixDofEvent&) {
return false;
}
#endif
62 changes: 62 additions & 0 deletions src/platform/spnavdevice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//-----------------------------------------------------------------------------
// Cross-platform handling of spnavdev 6-dof input.
//
// Copyright 2021 Collabora, Ltd.
//-----------------------------------------------------------------------------

#ifndef SOLVESPACE_SPNAVDEVICE_H
#define SOLVESPACE_SPNAVDEVICE_H
#include <vector>
#include <array>

namespace SolveSpace {
namespace Platform {
struct SixDofEvent;
}
} // namespace SolveSpace
struct spndev;

class NavDeviceWrapper {
public:
explicit NavDeviceWrapper(const char* device = nullptr);
~NavDeviceWrapper();

// no copy, no move
NavDeviceWrapper(NavDeviceWrapper const&) = delete;
NavDeviceWrapper& operator=(NavDeviceWrapper const&) = delete;
NavDeviceWrapper(NavDeviceWrapper&&) = delete;
NavDeviceWrapper& operator=(NavDeviceWrapper&&) = delete;

bool active() const noexcept {
return dev != nullptr;
}

//! true when the event has data in it to deal with.
bool process(SolveSpace::Platform::SixDofEvent& event);

enum class NavButton {
UNUSED,
FIT,
CTRL,
SHIFT,
};
struct AxisData {
AxisData() = default;
AxisData(int spnavdevIndex_) : spnavdevIndex(spnavdevIndex_) {
}

int spnavdevIndex = -1;
};

private:
void populateAxes();
void populateButtons();
struct spndev* dev = nullptr;
//! indexed by the spnavdev index
std::vector<NavButton> buttons;
//! indexed by our internal axis index.
std::array<AxisData, 6> axes;
bool ctrlPressed = false;
bool shiftPressed = false;
};
#endif // !SOLVESPACE_SPNAVDEVICE_H