diff --git a/CMakeLists.txt b/CMakeLists.txt index 43c8a00..d059463 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,11 +158,8 @@ if(WIN32 OR APPLE) else() # On Linux and BSDs we're a good citizen and link to system libraries. - find_package(PkgConfig REQUIRED) - find_package(Backtrace) - find_package(SpaceWare) - + find_package(PkgConfig REQUIRED) find_package(ZLIB REQUIRED) find_package(PNG REQUIRED) find_package(Freetype REQUIRED) @@ -202,6 +199,7 @@ if(ENABLE_GUI) find_library(APPKIT_LIBRARY AppKit REQUIRED) else() find_package(OpenGL REQUIRED) + find_package(SpaceWare) pkg_check_modules(FONTCONFIG REQUIRED fontconfig) pkg_check_modules(JSONC REQUIRED json-c) pkg_check_modules(GTKMM REQUIRED gtkmm-3.0>=3.18 pangomm-1.4 x11) diff --git a/cmake/FindSpaceWare.cmake b/cmake/FindSpaceWare.cmake index 135384a..fb6073c 100644 --- a/cmake/FindSpaceWare.cmake +++ b/cmake/FindSpaceWare.cmake @@ -4,7 +4,7 @@ # # SPACEWARE_INCLUDE_DIR - header location # SPACEWARE_LIBRARIES - library to link against -# SPACEWARE_FOUND - true if pugixml was found. +# SPACEWARE_FOUND - true if libspnav was found. if(UNIX) diff --git a/src/graphicswin.cpp b/src/graphicswin.cpp index b3e261e..9c85f35 100644 --- a/src/graphicswin.cpp +++ b/src/graphicswin.cpp @@ -405,6 +405,7 @@ void GraphicsWindow::Init() { window->onRender = std::bind(&GraphicsWindow::Paint, this); window->onKeyboardEvent = std::bind(&GraphicsWindow::KeyboardEvent, this, _1); window->onMouseEvent = std::bind(&GraphicsWindow::MouseEvent, this, _1); + window->onSixDofEvent = std::bind(&GraphicsWindow::SixDofEvent, this, _1); window->onEditingDone = std::bind(&GraphicsWindow::EditControlDone, this, _1); window->SetMinContentSize(720, 670); PopulateMainMenu(); diff --git a/src/mouse.cpp b/src/mouse.cpp index f823be8..a9b1f03 100644 --- a/src/mouse.cpp +++ b/src/mouse.cpp @@ -1495,15 +1495,18 @@ void GraphicsWindow::MouseLeave() { SS.extraLine.draw = false; } -void GraphicsWindow::SpaceNavigatorMoved(double tx, double ty, double tz, - double rx, double ry, double rz, - bool shiftDown) -{ +void GraphicsWindow::SixDofEvent(Platform::SixDofEvent event) { + if(event.type == Platform::SixDofEvent::Type::RELEASE) { + ZoomToFit(/*includingInvisibles=*/false, /*useSelection=*/true); + Invalidate(); + return; + } + if(!havePainted) return; Vector out = projRight.Cross(projUp); // rotation vector is axis of rotation, and its magnitude is angle - Vector aa = Vector::From(rx, ry, rz); + Vector aa = Vector::From(event.rotationX, event.rotationY, event.rotationZ); // but it's given with respect to screen projection frame aa = aa.ScaleOutOfCsys(projRight, projUp, out); double aam = aa.Magnitude(); @@ -1516,38 +1519,38 @@ void GraphicsWindow::SpaceNavigatorMoved(double tx, double ty, double tz, if(gs.points == 1 && gs.n == 1) e = SK.GetEntity(gs.point [0]); if(gs.entities == 1 && gs.n == 1) e = SK.GetEntity(gs.entity[0]); if(e) g = SK.GetGroup(e->group); - if(g && g->type == Group::Type::LINKED && !shiftDown) { + if(g && g->type == Group::Type::LINKED && !event.shiftDown) { // Apply the transformation to a linked part. Gain down the Z // axis, since it's hard to see what you're doing on that one since // it's normal to the screen. - Vector t = projRight.ScaledBy(tx/scale).Plus( - projUp .ScaledBy(ty/scale).Plus( - out .ScaledBy(0.1*tz/scale))); + Vector t = projRight.ScaledBy(event.translationX/scale).Plus( + projUp .ScaledBy(event.translationY/scale).Plus( + out .ScaledBy(0.1*event.translationZ/scale))); Quaternion q = Quaternion::From(aa, aam); // If we go five seconds without SpaceNavigator input, or if we've // switched groups, then consider that a new action and save an undo // point. int64_t now = GetMilliseconds(); - if(now - lastSpaceNavigatorTime > 5000 || - lastSpaceNavigatorGroup.v != g->h.v) + if(now - last6DofTime > 5000 || + last6DofGroup.v != g->h.v) { SS.UndoRemember(); } g->TransformImportedBy(t, q); - lastSpaceNavigatorTime = now; - lastSpaceNavigatorGroup = g->h; + last6DofTime = now; + last6DofGroup = g->h; SS.MarkGroupDirty(g->h); } else { // Apply the transformation to the view of the everything. The // x and y components are translation; but z component is scale, // not translation, or else it would do nothing in a parallel // projection - offset = offset.Plus(projRight.ScaledBy(tx/scale)); - offset = offset.Plus(projUp.ScaledBy(ty/scale)); - scale *= exp(0.001*tz); + offset = offset.Plus(projRight.ScaledBy(event.translationX/scale)); + offset = offset.Plus(projUp.ScaledBy(event.translationY/scale)); + scale *= exp(0.001*event.translationZ); if(aam > 0.0) { projRight = projRight.RotatedAbout(aa, -aam); @@ -1559,9 +1562,3 @@ void GraphicsWindow::SpaceNavigatorMoved(double tx, double ty, double tz, havePainted = false; Invalidate(); } - -void GraphicsWindow::SpaceNavigatorButtonUp() { - ZoomToFit(/*includingInvisibles=*/false, /*useSelection=*/true); - Invalidate(); -} - diff --git a/src/platform/cocoamain.mm b/src/platform/cocoamain.mm index 65be7eb..161ed6a 100644 --- a/src/platform/cocoamain.mm +++ b/src/platform/cocoamain.mm @@ -63,154 +63,6 @@ std::vector SolveSpace::GetFontFiles() { } @end -/* - * Normally we would just link to the 3DconnexionClient framework. - * We don't want to (are not allowed to) distribute the official - * framework, so we're trying to use the one installed on the users - * computer. There are some different versions of the framework, - * the official one and re-implementations using an open source driver - * for older devices (spacenav-plus). So weak-linking isn't an option, - * either. The only remaining way is using CFBundle to dynamically - * load the library at runtime, and also detect its availability. - * - * We're also defining everything needed from the 3DconnexionClientAPI, - * so we're not depending on the API headers. - */ - -#pragma pack(push,2) - -enum { - kConnexionClientModeTakeOver = 1, - kConnexionClientModePlugin = 2 -}; - -#define kConnexionMsgDeviceState '3dSR' -#define kConnexionMaskButtons 0x00FF -#define kConnexionMaskAxis 0x3F00 - -typedef struct { - uint16_t version; - uint16_t client; - uint16_t command; - int16_t param; - int32_t value; - UInt64 time; - uint8_t report[8]; - uint16_t buttons8; - int16_t axis[6]; - uint16_t address; - uint32_t buttons; -} ConnexionDeviceState, *ConnexionDeviceStatePtr; - -#pragma pack(pop) - -typedef void (*ConnexionAddedHandlerProc)(io_connect_t); -typedef void (*ConnexionRemovedHandlerProc)(io_connect_t); -typedef void (*ConnexionMessageHandlerProc)(io_connect_t, natural_t, void *); - -typedef OSErr (*InstallConnexionHandlersProc)(ConnexionMessageHandlerProc, ConnexionAddedHandlerProc, ConnexionRemovedHandlerProc); -typedef void (*CleanupConnexionHandlersProc)(void); -typedef UInt16 (*RegisterConnexionClientProc)(UInt32, UInt8 *, UInt16, UInt32); -typedef void (*UnregisterConnexionClientProc)(UInt16); - -static BOOL connexionShiftIsDown = NO; -static UInt16 connexionClient = 0; -static UInt32 connexionSignature = 'SoSp'; -static UInt8 *connexionName = (UInt8 *)"\x10SolveSpace"; -static CFBundleRef spaceBundle = NULL; -static InstallConnexionHandlersProc installConnexionHandlers = NULL; -static CleanupConnexionHandlersProc cleanupConnexionHandlers = NULL; -static RegisterConnexionClientProc registerConnexionClient = NULL; -static UnregisterConnexionClientProc unregisterConnexionClient = NULL; - -static void connexionAdded(io_connect_t con) {} -static void connexionRemoved(io_connect_t con) {} -static void connexionMessage(io_connect_t con, natural_t type, void *arg) { - if (type != kConnexionMsgDeviceState) { - return; - } - - ConnexionDeviceState *device = (ConnexionDeviceState *)arg; - - dispatch_async(dispatch_get_main_queue(), ^(void){ - SolveSpace::SS.GW.SpaceNavigatorMoved( - (double)device->axis[0] * -0.25, - (double)device->axis[1] * -0.25, - (double)device->axis[2] * 0.25, - (double)device->axis[3] * -0.0005, - (double)device->axis[4] * -0.0005, - (double)device->axis[5] * -0.0005, - (connexionShiftIsDown == YES) ? 1 : 0 - ); - }); -} - -static void connexionInit() { - NSString *bundlePath = @"/Library/Frameworks/3DconnexionClient.framework"; - NSURL *bundleURL = [NSURL fileURLWithPath:bundlePath]; - spaceBundle = CFBundleCreate(kCFAllocatorDefault, (__bridge CFURLRef)bundleURL); - - // Don't continue if no Spacemouse driver is installed on this machine - if (spaceBundle == NULL) { - return; - } - - installConnexionHandlers = (InstallConnexionHandlersProc) - CFBundleGetFunctionPointerForName(spaceBundle, - CFSTR("InstallConnexionHandlers")); - - cleanupConnexionHandlers = (CleanupConnexionHandlersProc) - CFBundleGetFunctionPointerForName(spaceBundle, - CFSTR("CleanupConnexionHandlers")); - - registerConnexionClient = (RegisterConnexionClientProc) - CFBundleGetFunctionPointerForName(spaceBundle, - CFSTR("RegisterConnexionClient")); - - unregisterConnexionClient = (UnregisterConnexionClientProc) - CFBundleGetFunctionPointerForName(spaceBundle, - CFSTR("UnregisterConnexionClient")); - - // Only continue if all required symbols have been loaded - if ((installConnexionHandlers == NULL) || (cleanupConnexionHandlers == NULL) - || (registerConnexionClient == NULL) || (unregisterConnexionClient == NULL)) { - CFRelease(spaceBundle); - spaceBundle = NULL; - return; - } - - installConnexionHandlers(&connexionMessage, &connexionAdded, &connexionRemoved); - connexionClient = registerConnexionClient(connexionSignature, connexionName, - kConnexionClientModeTakeOver, kConnexionMaskButtons | kConnexionMaskAxis); - - // Monitor modifier flags to detect Shift button state changes - [NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyDownMask | NSFlagsChangedMask) - handler:^(NSEvent *event) { - if (event.modifierFlags & NSShiftKeyMask) { - connexionShiftIsDown = YES; - } - return event; - }]; - - [NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyUpMask | NSFlagsChangedMask) - handler:^(NSEvent *event) { - if (!(event.modifierFlags & NSShiftKeyMask)) { - connexionShiftIsDown = NO; - } - return event; - }]; -} - -static void connexionClose() { - if (spaceBundle == NULL) { - return; - } - - unregisterConnexionClient(connexionClient); - cleanupConnexionHandlers(); - - CFRelease(spaceBundle); -} int main(int argc, const char *argv[]) { ApplicationDelegate *delegate = [[ApplicationDelegate alloc] init]; @@ -226,12 +78,12 @@ int main(int argc, const char *argv[]) { SolveSpace::SetLocale("en_US"); } - connexionInit(); + SolveSpace::Platform::Open3DConnexion(); SolveSpace::SS.Init(); [NSApp run]; - connexionClose(); + SolveSpace::Platform::Close3DConnexion(); SolveSpace::SK.Clear(); SolveSpace::SS.Clear(); diff --git a/src/platform/gtkmain.cpp b/src/platform/gtkmain.cpp index bf43340..60e290c 100644 --- a/src/platform/gtkmain.cpp +++ b/src/platform/gtkmain.cpp @@ -47,10 +47,6 @@ #include "solvespace.h" #include "config.h" -#ifdef HAVE_SPACEWARE -#include -#endif - namespace SolveSpace { void OpenWebsite(const char *url) { @@ -78,40 +74,6 @@ std::vector GetFontFiles() { return fonts; } - -/* Space Navigator support */ - -#ifdef HAVE_SPACEWARE -static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gxevent, GdkEvent *, gpointer) { - XEvent *xevent = (XEvent*) gxevent; - - spnav_event sev; - if(!spnav_x11_event(xevent, &sev)) - return GDK_FILTER_CONTINUE; - - switch(sev.type) { - case SPNAV_EVENT_MOTION: - SS.GW.SpaceNavigatorMoved( - (double)sev.motion.x, - (double)sev.motion.y, - (double)sev.motion.z * -1.0, - (double)sev.motion.rx * 0.001, - (double)sev.motion.ry * 0.001, - (double)sev.motion.rz * -0.001, - xevent->xmotion.state & ShiftMask); - break; - - case SPNAV_EVENT_BUTTON: - if(!sev.button.press && sev.button.bnum == 0) { - SS.GW.SpaceNavigatorButtonUp(); - } - break; - } - - return GDK_FILTER_REMOVE; -} -#endif - }; int main(int argc, char** argv) { @@ -149,10 +111,6 @@ int main(int argc, char** argv) { style_provider, 600 /*Gtk::STYLE_PROVIDER_PRIORITY_APPLICATION*/); -#ifdef HAVE_SPACEWARE - gdk_window_add_filter(NULL, GdkSpnavFilter, NULL); -#endif - const char* const* langNames = g_get_language_names(); while(*langNames) { if(SetLocale(*langNames++)) break; @@ -163,15 +121,6 @@ int main(int argc, char** argv) { SS.Init(); -#if defined(HAVE_SPACEWARE) && defined(GDK_WINDOWING_X11) - if(GDK_IS_X11_DISPLAY(Gdk::Display::get_default()->gobj())) { - // We don't care if it can't be opened; just continue without. - spnav_x11_open(gdk_x11_get_default_xdisplay(), - gdk_x11_window_get_xid(((Gtk::Window *)SS.GW.window->NativePtr()) - ->get_window()->gobj())); - } -#endif - if(argc >= 2) { if(argc > 2) { dbp("Only the first file passed on command line will be opened."); diff --git a/src/platform/gui.h b/src/platform/gui.h index 0796d3e..100be5a 100644 --- a/src/platform/gui.h +++ b/src/platform/gui.h @@ -43,6 +43,26 @@ public: }; }; +// A 3-DOF input event. +struct SixDofEvent { + enum class Type { + MOTION, + PRESS, + RELEASE, + }; + + enum class Button { + FIT, + }; + + Type type; + bool shiftDown; + bool controlDown; + double translationX, translationY, translationZ; // for Type::MOTION + double rotationX, rotationY, rotationZ; // for Type::MOTION + Button button; // for Type::{PRESS,RELEASE} +}; + // A keyboard input event. struct KeyboardEvent { enum class Type { @@ -191,6 +211,7 @@ public: std::function onClose; std::function onFullScreen; std::function onMouseEvent; + std::function onSixDofEvent; std::function onKeyboardEvent; std::function onEditingDone; std::function onScrollbarAdjusted; @@ -250,6 +271,11 @@ typedef std::shared_ptr WindowRef; WindowRef CreateWindow(Window::Kind kind = Window::Kind::TOPLEVEL, WindowRef parentWindow = NULL); +// 3DConnexion support. +void Open3DConnexion(); +void Close3DConnexion(); +void Request3DConnexionEventsForWindow(WindowRef window); + // A native dialog that asks for one choice out of several. class MessageDialog { public: diff --git a/src/platform/guigtk.cpp b/src/platform/guigtk.cpp index 6bfbd32..92d4a48 100644 --- a/src/platform/guigtk.cpp +++ b/src/platform/guigtk.cpp @@ -3,7 +3,6 @@ // // Copyright 2018 whitequark //----------------------------------------------------------------------------- -#include "solvespace.h" #include #include #include @@ -24,6 +23,15 @@ #include #include +#include "config.h" + +#if defined(HAVE_SPACEWARE) +# include +# include +#endif + +#include "solvespace.h" + namespace SolveSpace { namespace Platform { @@ -992,6 +1000,78 @@ WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) { return window; } +//----------------------------------------------------------------------------- +// 3DConnexion support +//----------------------------------------------------------------------------- + +void Open3DConnexion() {} +void Close3DConnexion() {} + +#if defined(HAVE_SPACEWARE) && defined(GDK_WINDOWING_X11) +static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gdkXEvent, GdkEvent *gdkEvent, gpointer data) { + XEvent *xEvent = (XEvent *)gdkXEvent; + WindowImplGtk *window = (WindowImplGtk *)data; + + spnav_event spnavEvent; + if(!spnav_x11_event(xEvent, &spnavEvent)) { + return GDK_FILTER_CONTINUE; + } + + switch(spnavEvent.type) { + case SPNAV_EVENT_MOTION: { + SixDofEvent event = {}; + event.type = SixDofEvent::Type::MOTION; + event.translationX = (double)spnavEvent.motion.x; + event.translationY = (double)spnavEvent.motion.y; + event.translationZ = (double)spnavEvent.motion.z * -1.0; + event.rotationX = (double)spnavEvent.motion.rx * 0.001; + event.rotationY = (double)spnavEvent.motion.ry * 0.001; + event.rotationZ = (double)spnavEvent.motion.rz * -0.001; + event.shiftDown = xEvent->xmotion.state & ShiftMask; + event.controlDown = xEvent->xmotion.state & ControlMask; + if(window->onSixDofEvent) { + window->onSixDofEvent(event); + } + break; + } + + case SPNAV_EVENT_BUTTON: + SixDofEvent event = {}; + if(spnavEvent.button.press) { + event.type = SixDofEvent::Type::PRESS; + } else { + event.type = SixDofEvent::Type::RELEASE; + } + switch(spnavEvent.button.bnum) { + case 0: event.button = SixDofEvent::Button::FIT; break; + default: return GDK_FILTER_REMOVE; + } + event.shiftDown = xEvent->xmotion.state & ShiftMask; + event.controlDown = xEvent->xmotion.state & ControlMask; + if(window->onSixDofEvent) { + window->onSixDofEvent(event); + } + break; + } + + return GDK_FILTER_REMOVE; +} + +void Request3DConnexionEventsForWindow(WindowRef window) { + std::shared_ptr windowImpl = + std::static_pointer_cast(window); + + Glib::RefPtr gdkWindow = windowImpl->gtkWindow.get_window(); + if(GDK_IS_X11_DISPLAY(gdkWindow->get_display()->gobj())) { + gdkWindow->add_filter(GdkSpnavFilter, windowImpl.get()); + spnav_x11_open(gdk_x11_get_default_xdisplay(), + gdk_x11_window_get_xid(gdkWindow->gobj())); + } +} +#else +void Request3DConnexionEventsForWindow(WindowRef window) {} +#endif + //----------------------------------------------------------------------------- // Message dialogs //----------------------------------------------------------------------------- diff --git a/src/platform/guimac.mm b/src/platform/guimac.mm index 6b5f34c..607eeb1 100644 --- a/src/platform/guimac.mm +++ b/src/platform/guimac.mm @@ -1000,6 +1000,170 @@ WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) { std::static_pointer_cast(parentWindow)); } +//----------------------------------------------------------------------------- +// 3DConnexion support +//----------------------------------------------------------------------------- + +// Normally we would just link to the 3DconnexionClient framework. +// +// We don't want to (are not allowed to) distribute the official framework, so we're trying +// to use the one installed on the users' computer. There are some different versions of +// the framework, the official one and re-implementations using an open source driver for +// older devices (spacenav-plus). So weak-linking isn't an option, either. The only remaining +// way is using CFBundle to dynamically load the library at runtime, and also detect its +// availability. +// +// We're also defining everything needed from the 3DconnexionClientAPI, so we're not depending +// on the API headers. + +#pragma pack(push,2) + +enum { + kConnexionClientModeTakeOver = 1, + kConnexionClientModePlugin = 2 +}; + +#define kConnexionMsgDeviceState '3dSR' +#define kConnexionMaskButtons 0x00FF +#define kConnexionMaskAxis 0x3F00 + +typedef struct { + uint16_t version; + uint16_t client; + uint16_t command; + int16_t param; + int32_t value; + UInt64 time; + uint8_t report[8]; + uint16_t buttons8; + int16_t axis[6]; + uint16_t address; + uint32_t buttons; +} ConnexionDeviceState, *ConnexionDeviceStatePtr; + +#pragma pack(pop) + +typedef void (*ConnexionAddedHandlerProc)(io_connect_t); +typedef void (*ConnexionRemovedHandlerProc)(io_connect_t); +typedef void (*ConnexionMessageHandlerProc)(io_connect_t, natural_t, void *); + +typedef OSErr (*InstallConnexionHandlersProc)(ConnexionMessageHandlerProc, ConnexionAddedHandlerProc, ConnexionRemovedHandlerProc); +typedef void (*CleanupConnexionHandlersProc)(void); +typedef UInt16 (*RegisterConnexionClientProc)(UInt32, UInt8 *, UInt16, UInt32); +typedef void (*UnregisterConnexionClientProc)(UInt16); + +static CFBundleRef spaceBundle = nil; +static InstallConnexionHandlersProc installConnexionHandlers = NULL; +static CleanupConnexionHandlersProc cleanupConnexionHandlers = NULL; +static RegisterConnexionClientProc registerConnexionClient = NULL; +static UnregisterConnexionClientProc unregisterConnexionClient = NULL; +static UInt32 connexionSignature = 'SoSp'; +static UInt8 *connexionName = (UInt8 *)"\x10SolveSpace"; +static UInt16 connexionClient = 0; + +static std::vector> connexionWindows; +static bool connexionShiftIsDown = false; +static bool connexionCommandIsDown = false; + +static void ConnexionAdded(io_connect_t con) {} +static void ConnexionRemoved(io_connect_t con) {} +static void ConnexionMessage(io_connect_t con, natural_t type, void *arg) { + if (type != kConnexionMsgDeviceState) { + return; + } + + ConnexionDeviceState *device = (ConnexionDeviceState *)arg; + + dispatch_async(dispatch_get_main_queue(), ^(void){ + SixDofEvent event = {}; + event.type = SixDofEvent::Type::MOTION; + event.translationX = (double)device->axis[0] * -0.25; + event.translationY = (double)device->axis[1] * -0.25; + event.translationZ = (double)device->axis[2] * 0.25; + event.rotationX = (double)device->axis[3] * -0.0005; + event.rotationY = (double)device->axis[4] * -0.0005; + event.rotationZ = (double)device->axis[5] * -0.0005; + event.shiftDown = connexionShiftIsDown; + event.controlDown = connexionCommandIsDown; + + for(auto window : connexionWindows) { + if(auto windowStrong = window.lock()) { + if(windowStrong->onSixDofEvent) { + windowStrong->onSixDofEvent(event); + } + } + } + }); +} + +void Open3DConnexion() { + NSString *bundlePath = @"/Library/Frameworks/3DconnexionClient.framework"; + NSURL *bundleURL = [NSURL fileURLWithPath:bundlePath]; + spaceBundle = CFBundleCreate(kCFAllocatorDefault, (__bridge CFURLRef)bundleURL); + + // Don't continue if no driver is installed on this machine + if(spaceBundle == nil) { + return; + } + + installConnexionHandlers = (InstallConnexionHandlersProc) + CFBundleGetFunctionPointerForName(spaceBundle, + CFSTR("InstallConnexionHandlers")); + + cleanupConnexionHandlers = (CleanupConnexionHandlersProc) + CFBundleGetFunctionPointerForName(spaceBundle, + CFSTR("CleanupConnexionHandlers")); + + registerConnexionClient = (RegisterConnexionClientProc) + CFBundleGetFunctionPointerForName(spaceBundle, + CFSTR("RegisterConnexionClient")); + + unregisterConnexionClient = (UnregisterConnexionClientProc) + CFBundleGetFunctionPointerForName(spaceBundle, + CFSTR("UnregisterConnexionClient")); + + // Only continue if all required symbols have been loaded + if((installConnexionHandlers == NULL) || (cleanupConnexionHandlers == NULL) + || (registerConnexionClient == NULL) || (unregisterConnexionClient == NULL)) { + CFRelease(spaceBundle); + spaceBundle = nil; + return; + } + + installConnexionHandlers(&ConnexionMessage, &ConnexionAdded, &ConnexionRemoved); + connexionClient = registerConnexionClient(connexionSignature, connexionName, + kConnexionClientModeTakeOver, kConnexionMaskButtons | kConnexionMaskAxis); + + [NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyDownMask | NSFlagsChangedMask) + handler:^(NSEvent *event) { + connexionShiftIsDown = (event.modifierFlags & NSShiftKeyMask); + connexionCommandIsDown = (event.modifierFlags & NSCommandKeyMask); + return event; + }]; + + [NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyUpMask | NSFlagsChangedMask) + handler:^(NSEvent *event) { + connexionShiftIsDown = (event.modifierFlags & NSShiftKeyMask); + connexionCommandIsDown = (event.modifierFlags & NSCommandKeyMask); + return event; + }]; +} + +void Close3DConnexion() { + if(spaceBundle == nil) { + return; + } + + unregisterConnexionClient(connexionClient); + cleanupConnexionHandlers(); + + CFRelease(spaceBundle); +} + +void Request3DConnexionEventsForWindow(WindowRef window) { + connexionWindows.push_back(window); +} + //----------------------------------------------------------------------------- // Message dialogs //----------------------------------------------------------------------------- diff --git a/src/platform/guinone.cpp b/src/platform/guinone.cpp index b6f26d5..8c656d0 100644 --- a/src/platform/guinone.cpp +++ b/src/platform/guinone.cpp @@ -113,6 +113,8 @@ WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) { return std::shared_ptr(); } +void Request3DConnexionEventsForWindow(WindowRef window) {} + //----------------------------------------------------------------------------- // Message dialogs //----------------------------------------------------------------------------- diff --git a/src/platform/guiwin.cpp b/src/platform/guiwin.cpp index 25b6082..a77b26f 100644 --- a/src/platform/guiwin.cpp +++ b/src/platform/guiwin.cpp @@ -12,8 +12,8 @@ #include #include -#ifndef WM_DPICHANGED -#define WM_DPICHANGED 0x02E0 +#if !defined(WM_DPICHANGED) +# define WM_DPICHANGED 0x02E0 #endif // These interfere with our identifiers. @@ -21,8 +21,14 @@ #undef ERROR #if HAVE_OPENGL == 3 -#define EGLAPI /*static linkage*/ -#include +# define EGLAPI /*static linkage*/ +# include +#endif + +#if defined(HAVE_SPACEWARE) +# include +# include +# undef uint32_t #endif namespace SolveSpace { @@ -483,6 +489,11 @@ public: WINDOWPLACEMENT placement = {}; int minWidth = 0, minHeight = 0; +#if defined(HAVE_SPACEWARE) + SiOpenData sod = {}; + SiHdl hSpaceWare = SI_NO_HANDLE; +#endif + std::shared_ptr menuBar; std::string tooltipText; bool scrollbarVisible = false; @@ -616,6 +627,15 @@ public: sscheck(ReleaseDC(hWindow, hDc)); } + ~WindowImplWin32() { + sscheck(DestroyWindow(hWindow)); +#if defined(HAVE_SPACEWARE) + if(hSpaceWare != SI_NO_HANDLE) { + SiClose(hSpaceWare); + } +#endif + } + static LRESULT CALLBACK WndProc(HWND h, UINT msg, WPARAM wParam, LPARAM lParam) { if(handlingFatalError) return TRUE; @@ -628,6 +648,41 @@ public: return DefWindowProc(h, msg, wParam, lParam); } +#if defined(HAVE_SPACEWARE) + if(window->hSpaceWare != SI_NO_HANDLE) { + SiGetEventData sged; + SiGetEventWinInit(&sged, msg, wParam, lParam); + + SiSpwEvent sse; + 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); + if(sse.type == SI_MOTION_EVENT) { + // The Z axis translation and rotation are both + // backwards in the default mapping. + event.type = SixDofEvent::Type::MOTION; + event.translationX = sse.u.spwData.mData[SI_TX]*1.0, + event.translationY = sse.u.spwData.mData[SI_TY]*1.0, + event.translationZ = -sse.u.spwData.mData[SI_TZ]*1.0, + 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; + } else if(sse.type == SI_BUTTON_EVENT) { + if(SiButtonPressed(&sse) == SI_APP_FIT_BUTTON) { + event.type = SixDofEvent::Type::PRESS; + event.button = SixDofEvent::Button::FIT; + } + if(SiButtonReleased(&sse) == SI_APP_FIT_BUTTON) { + event.type = SixDofEvent::Type::RELEASE; + event.button = SixDofEvent::Button::FIT; + } + } + return 0; + } + } +#endif + switch (msg) { case WM_ERASEBKGND: break; @@ -1272,6 +1327,42 @@ WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) { std::static_pointer_cast(parentWindow)); } +//----------------------------------------------------------------------------- +// 3DConnexion support +//----------------------------------------------------------------------------- + +#if defined(HAVE_SPACEWARE) +static HWND hSpaceWareDriverClass; + +void Open3DConnexion() { + HWND hSpaceWareDriverClass = FindWindowW(L"SpaceWare Driver Class", NULL); + if(hSpaceWareDriverClass != NULL) { + SiInitialize(); + } +} + +void Close3DConnexion() { + if(hSpaceWareDriverClass != NULL) { + SiTerminate(); + } +} + +void Request3DConnexionEventsForWindow(WindowRef window) { + std::shared_ptr windowImpl = + std::static_pointer_cast(window); + if(hSpaceWareDriverClass != NULL) { + SiOpenWinInit(&windowImpl->sod, windowImpl->hWindow); + windowImpl->hSpaceWare = SiOpen("SolveSpace", SI_ANY_DEVICE, SI_NO_MASK, SI_EVENT, + &windowImpl->sod); + SiSetUiMode(windowImpl->hSpaceWare, SI_UI_NO_CONTROLS); + } +} +#else +void Open3DConnexion() {} +void Close3DConnexion() {} +void Request3DConnexionEventsForWindow(WindowRef window) {} +#endif + //----------------------------------------------------------------------------- // Message dialogs //----------------------------------------------------------------------------- diff --git a/src/platform/w32main.cpp b/src/platform/w32main.cpp index b063254..0262410 100644 --- a/src/platform/w32main.cpp +++ b/src/platform/w32main.cpp @@ -16,20 +16,9 @@ #include #include -#ifdef HAVE_SPACEWARE -# include -# include -# undef uint32_t // thanks but no thanks -#endif - using Platform::Narrow; using Platform::Widen; -#ifdef HAVE_SPACEWARE -// The 6-DOF input device. -SiHdl SpaceNavigator = SI_NO_HANDLE; -#endif - //----------------------------------------------------------------------------- // Utility routines //----------------------------------------------------------------------------- @@ -56,44 +45,6 @@ std::vector SolveSpace::GetFontFiles() { return fonts; } -#ifdef HAVE_SPACEWARE -//----------------------------------------------------------------------------- -// Test if a message comes from the SpaceNavigator device. If yes, dispatch -// it appropriately and return true. Otherwise, do nothing and return false. -//----------------------------------------------------------------------------- -static bool ProcessSpaceNavigatorMsg(MSG *msg) { - if(SpaceNavigator == SI_NO_HANDLE) return false; - - SiGetEventData sged; - SiSpwEvent sse; - - SiGetEventWinInit(&sged, msg->message, msg->wParam, msg->lParam); - int ret = SiGetEvent(SpaceNavigator, 0, &sged, &sse); - if(ret == SI_NOT_EVENT) return false; - // So the device is a SpaceNavigator event, or a SpaceNavigator error. - - if(ret == SI_IS_EVENT) { - if(sse.type == SI_MOTION_EVENT) { - // The Z axis translation and rotation are both - // backwards in the default mapping. - double tx = sse.u.spwData.mData[SI_TX]*1.0, - ty = sse.u.spwData.mData[SI_TY]*1.0, - tz = -sse.u.spwData.mData[SI_TZ]*1.0, - rx = sse.u.spwData.mData[SI_RX]*0.001, - ry = sse.u.spwData.mData[SI_RY]*0.001, - rz = -sse.u.spwData.mData[SI_RZ]*0.001; - SS.GW.SpaceNavigatorMoved(tx, ty, tz, rx, ry, rz, - !!(GetAsyncKeyState(VK_SHIFT) & 0x8000)); - } else if(sse.type == SI_BUTTON_EVENT) { - int button; - button = SiButtonReleased(&sse); - if(button == SI_APP_FIT_BUTTON) SS.GW.SpaceNavigatorButtonUp(); - } - } - return true; -} -#endif // HAVE_SPACEWARE - //----------------------------------------------------------------------------- // Entry point into the program. //----------------------------------------------------------------------------- @@ -107,19 +58,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, std::vector args = InitPlatform(0, NULL); -#ifdef HAVE_SPACEWARE - // Initialize the SpaceBall, if present. Test if the driver is running - // first, to avoid a long timeout if it's not. - HWND swdc = FindWindowW(L"SpaceWare Driver Class", NULL); - if(swdc != NULL) { - SiOpenData sod; - SiInitialize(); - SiOpenWinInit(&sod, (HWND)SS.GW.window->NativePtr()); - SpaceNavigator = SiOpen("GraphicsWnd", SI_ANY_DEVICE, SI_NO_MASK, SI_EVENT, &sod); - SiSetUiMode(SpaceNavigator, SI_UI_NO_CONTROLS); - } -#endif - // Use the user default locale, then fall back to English. if(!SetLocale((uint16_t)GetUserDefaultLCID())) { SetLocale("en_US"); @@ -136,25 +74,11 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, // And now it's the message loop. All calls in to the rest of the code // will be from the wndprocs. MSG msg; - DWORD ret; - while((ret = GetMessage(&msg, NULL, 0, 0)) != 0) { -#ifdef HAVE_SPACEWARE - // Is it a message from the six degree of freedom input device? - if(ProcessSpaceNavigatorMsg(&msg)) continue; -#endif - - // None of the above; so just a normal message to process. + while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } -#ifdef HAVE_SPACEWARE - if(swdc != NULL) { - if(SpaceNavigator != SI_NO_HANDLE) SiClose(SpaceNavigator); - SiTerminate(); - } -#endif - // Free the memory we've used; anything that remains is a leak. SK.Clear(); SS.Clear(); diff --git a/src/solvespace.cpp b/src/solvespace.cpp index cc26d92..0da238c 100644 --- a/src/solvespace.cpp +++ b/src/solvespace.cpp @@ -136,6 +136,9 @@ void SolveSpaceUI::Init() { TW.window->SetVisible(true); GW.window->SetVisible(true); GW.window->Focus(); + + // Do this once the window is created. + Request3DConnexionEventsForWindow(GW.window); } } diff --git a/src/ui.h b/src/ui.h index 35218c0..4dbe607 100644 --- a/src/ui.h +++ b/src/ui.h @@ -795,11 +795,9 @@ public: bool KeyboardEvent(Platform::KeyboardEvent event); void EditControlDone(const std::string &s); - int64_t lastSpaceNavigatorTime; - hGroup lastSpaceNavigatorGroup; - void SpaceNavigatorMoved(double tx, double ty, double tz, - double rx, double ry, double rz, bool shiftDown); - void SpaceNavigatorButtonUp(); + int64_t last6DofTime; + hGroup last6DofGroup; + void SixDofEvent(Platform::SixDofEvent event); };