diff options
Diffstat (limited to 'intern/ghost/intern/GHOST_NDOFManagerCocoa.mm')
-rw-r--r-- | intern/ghost/intern/GHOST_NDOFManagerCocoa.mm | 240 |
1 files changed, 174 insertions, 66 deletions
diff --git a/intern/ghost/intern/GHOST_NDOFManagerCocoa.mm b/intern/ghost/intern/GHOST_NDOFManagerCocoa.mm index 6eedaafb2d1..6fee39dcb82 100644 --- a/intern/ghost/intern/GHOST_NDOFManagerCocoa.mm +++ b/intern/ghost/intern/GHOST_NDOFManagerCocoa.mm @@ -23,59 +23,178 @@ #ifdef WITH_INPUT_NDOF +#define DEBUG_NDOF_DRIVER false + #include "GHOST_NDOFManagerCocoa.h" -#include "GHOST_NDOFManager3Dconnexion.h" #include "GHOST_SystemCocoa.h" -extern "C" { - #include <ConnexionClientAPI.h> - #include <stdio.h> -} +#include <stdint.h> +#include <dlfcn.h> +#if DEBUG_NDOF_DRIVER + #include <cstdio> +#endif -// static functions need to talk to these objects: +// static callback functions need to talk to these objects: static GHOST_SystemCocoa* ghost_system = NULL; static GHOST_NDOFManager* ndof_manager = NULL; -// 3Dconnexion drivers before 10.x are "old" -// not all buttons will work -static bool has_old_driver = true; +static uint16_t clientID = 0; + +static bool driver_loaded = false; +static bool has_old_driver = false; // 3Dconnexion drivers before 10 beta 4 are "old", not all buttons will work +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 kConnexionMaskAll 0x3fff +#define kConnexionMaskAllButtons 0xffffffff +#define kConnexionCmdHandleButtons 2 +#define kConnexionCmdHandleAxis 3 +#define kConnexionCmdAppSpecific 10 +#define kConnexionMsgDeviceState '3dSR' +#define kConnexionCtlGetDeviceID '3did' + +#pragma pack(push,2) // just this struct +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); -static void NDOF_DeviceAdded(io_connect_t connection) +// 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) { - printf("ndof: device added\n"); // change these: printf --> informational reports + void* func = dlsym(module, func_name); -#if 0 // device preferences will be useful some day - ConnexionDevicePrefs p; - ConnexionGetCurrentDevicePrefs(kDevID_AnyDevice, &p); +#if DEBUG_NDOF_DRIVER + if (func) { + printf("'%s' loaded :D\n", func_name); + } + else { + printf("<!> %s\n", dlerror()); + } +#endif + + return func; +} + +#define LOAD_FUNC(name) name = (name##_ptr) load_func(module, #name) + +static bool load_driver_functions() +{ + if (driver_loaded) { + return true; + } + + void* module = dlopen("3DconnexionClient.framework/3DconnexionClient", RTLD_LAZY | RTLD_LOCAL); + + if (module) { + LOAD_FUNC(SetConnexionHandlers); + + if (SetConnexionHandlers != NULL) { + driver_loaded = true; + has_new_driver = true; + } + else { + 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); + + has_old_driver = (SetConnexionClientButtonMask == NULL); + } + + dlclose(module); // functions will remain loaded + } +#if DEBUG_NDOF_DRIVER + else { + printf("<!> %s\n", dlerror()); + } + + printf("loaded: %s\n", driver_loaded ? "YES" : "NO"); + printf("old: %s\n", has_old_driver ? "YES" : "NO"); + printf("new: %s\n", has_new_driver ? "YES" : "NO"); +#endif + + return driver_loaded; + } + + +static void DeviceAdded(uint32_t unused) +{ +#if DEBUG_NDOF_DRIVER + printf("ndof: device added\n"); #endif // determine exactly which device is plugged in - SInt32 result = 0; - GHOST_NDOFManager3Dconnexion_ConnexionControl(kConnexionCtlGetDeviceID, 0, &result); - unsigned short vendorID = result >> 16; - unsigned short productID = result & 0xffff; + int32_t result; + ConnexionClientControl(clientID, kConnexionCtlGetDeviceID, 0, &result); + int16_t vendorID = result >> 16; + int16_t productID = result & 0xffff; ndof_manager->setDevice(vendorID, productID); } -static void NDOF_DeviceRemoved(io_connect_t connection) +static void DeviceRemoved(uint32_t unused) { +#if DEBUG_NDOF_DRIVER printf("ndof: device removed\n"); +#endif } -static void NDOF_DeviceEvent(io_connect_t connection, natural_t messageType, void* messageArgument) +static void DeviceEvent(uint32_t unused, uint32_t msg_type, void* msg_arg) { - switch (messageType) - { - case kConnexionMsgDeviceState: - { - ConnexionDeviceState* s = (ConnexionDeviceState*)messageArgument; + if (msg_type == kConnexionMsgDeviceState) { + ConnexionDeviceState* s = (ConnexionDeviceState*)msg_arg; + // device state is broadcast to all clients; only react if sent to us + if (s->client == clientID) { + // TODO: is s->time compatible with GHOST timestamps? if so use that instead. GHOST_TUns64 now = ghost_system->getMilliSeconds(); - switch (s->command) - { + switch (s->command) { case kConnexionCmdHandleAxis: { // convert to blender view coordinates @@ -91,82 +210,71 @@ static void NDOF_DeviceEvent(io_connect_t connection, natural_t messageType, voi case kConnexionCmdHandleButtons: { int button_bits = has_old_driver ? s->buttons8 : s->buttons; + printf("button bits: 0x%08x\n", button_bits); ndof_manager->updateButtons(button_bits, now); ghost_system->notifyExternalEventProcessed(); break; } +#if DEBUG_NDOF_DRIVER case kConnexionCmdAppSpecific: printf("ndof: app-specific command, param = %hd, value = %d\n", s->param, s->value); break; default: printf("ndof: mystery device command %d\n", s->command); +#endif } - break; } - case kConnexionMsgPrefsChanged: - // printf("ndof: prefs changed\n"); // this includes app switches - // TODO: look through updated prefs for things blender cares about - break; - case kConnexionMsgCalibrateDevice: - printf("ndof: calibrate\n"); // but what should blender do? - break; - case kConnexionMsgDoMapping: - // printf("ndof: driver did something\n"); - // sent when the driver itself consumes an NDOF event - // and performs whatever action is set in user prefs - // 3Dx header file says to ignore these - break; - default: - printf("ndof: mystery event %d\n", messageType); } } GHOST_NDOFManagerCocoa::GHOST_NDOFManagerCocoa(GHOST_System& sys) : GHOST_NDOFManager(sys) { - if (GHOST_NDOFManager3Dconnexion_available()) - { + if (load_driver_functions()) { // give static functions something to talk to: ghost_system = dynamic_cast<GHOST_SystemCocoa*>(&sys); ndof_manager = this; - OSErr error = GHOST_NDOFManager3Dconnexion_InstallConnexionHandlers(NDOF_DeviceEvent, NDOF_DeviceAdded, NDOF_DeviceRemoved); + uint16_t error; + if (has_new_driver) { + const bool separate_thread = false; // TODO: rework Mac event handler to allow this + error = SetConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved, separate_thread); + } + else { + error = InstallConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved); + } + if (error) { - printf("ndof: error %d while installing handlers\n", error); +#if DEBUG_NDOF_DRIVER + printf("ndof: error %d while setting up handlers\n", error); +#endif return; } // Pascal string *and* a four-letter constant. How old-skool. - m_clientID = GHOST_NDOFManager3Dconnexion_RegisterConnexionClient('blnd', (UInt8*) "\007blender", - kConnexionClientModeTakeOver, kConnexionMaskAll); - - // printf("ndof: client id = %d\n", m_clientID); + clientID = RegisterConnexionClient('blnd', "\007blender", kConnexionClientModeTakeOver, kConnexionMaskAll); - if (GHOST_NDOFManager3Dconnexion_oldDRV()) { - has_old_driver = false; - GHOST_NDOFManager3Dconnexion_SetConnexionClientButtonMask(m_clientID, kConnexionMaskAllButtons); - } - else { - printf("ndof: old 3Dx driver installed, some buttons may not work\n"); + if (!has_old_driver) { + SetConnexionClientButtonMask(clientID, kConnexionMaskAllButtons); } } - else { - printf("ndof: 3Dx driver not found\n"); - // This isn't a hard error, just means the user doesn't have a 3D mouse. - } } GHOST_NDOFManagerCocoa::~GHOST_NDOFManagerCocoa() { - if (GHOST_NDOFManager3Dconnexion_available()) - { - GHOST_NDOFManager3Dconnexion_UnregisterConnexionClient(m_clientID); - GHOST_NDOFManager3Dconnexion_UnregisterConnexionClient(m_clientID); + if (driver_loaded) { + UnregisterConnexionClient(clientID); + CleanupConnexionHandlers(); - GHOST_NDOFManager3Dconnexion_CleanupConnexionHandlers(); ghost_system = NULL; ndof_manager = NULL; } } + +bool GHOST_NDOFManagerCocoa::available() +{ + return driver_loaded; +} + #endif // WITH_INPUT_NDOF |