ndof: rework Mac driver glue

Load driver dynamically at runtime instead of weak-linking the
3Dconnexion framework. Driver no longer needed at build time!

Works with really old drivers (as in PowerMac old), more recent
versions, and the latest which allows us to process events on a
separate thread.
This commit is contained in:
Mike Erwin 2015-10-24 19:58:06 +02:00
parent 4f767e37e8
commit c3cec828e8
Notes: blender-bot 2023-02-14 08:24:06 +01:00
Referenced by issue #46897, AMD OpenCl kernel compile failure
8 changed files with 182 additions and 250 deletions

View File

@ -166,11 +166,6 @@ elseif(APPLE AND NOT WITH_X11)
intern/GHOST_NDOFManagerCocoa.h
)
list(APPEND SRC_NDOF3DCONNEXION
intern/GHOST_NDOFManager3Dconnexion.c
intern/GHOST_NDOFManager3Dconnexion.h
)
endif()
if(WITH_CODEC_QUICKTIME)
@ -338,8 +333,3 @@ endif()
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_intern_ghost "${SRC}" "${INC}" "${INC_SYS}")
# workaround for apple clang mangling extern "C" symbols
if(WITH_INPUT_NDOF AND APPLE)
blender_add_lib(bf_intern_ghostndof3dconnexion "${SRC_NDOF3DCONNEXION}" "${INC}" "${INC_SYS}")
endif()

View File

@ -33,7 +33,6 @@ Import ('env')
window_system = env['OURPLATFORM']
sources = env.Glob('intern/*.cpp')
sources2 = env.Glob('intern/GHOST_NDOFManager3Dconnexion.c')
if window_system == 'darwin':
sources += env.Glob('intern/*.mm')
#remove, will be readded below if needed.
@ -193,7 +192,3 @@ elif window_system == 'darwin' and env['C_COMPILER_ID'] == 'gcc' and env['CCVER
else:
env.BlenderLib ('bf_intern_ghost', sources, Split(incs), defines=defs, libtype=['intern','player'], priority = [40,15] )
if window_system == 'darwin' and env['WITH_BF_3DMOUSE']: # build seperate to circumvent extern "C" linkage issues
env.BlenderLib ('bf_intern_ghostndof3dconnexion', sources2, Split(incs), defines=defs, libtype=['intern','player'], priority = [40,15] )

View File

@ -107,10 +107,13 @@ typedef enum {
class GHOST_NDOFManager
{
public:
GHOST_NDOFManager(GHOST_System &);
GHOST_NDOFManager(GHOST_System&);
virtual ~GHOST_NDOFManager() {}
// whether multi-axis functionality is available (via the OS or driver)
// does not imply that a device is plugged in or being used
virtual bool available() = 0;
// each platform's device detection should call this
// use standard USB/HID identifiers
bool setDevice(unsigned short vendor_id, unsigned short product_id);

View File

@ -1,94 +0,0 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s):
* Jake Kauth on 9/12/13.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifdef WITH_INPUT_NDOF
#include <ConnexionClientAPI.h>
#include <stdio.h>
#include "GHOST_NDOFManager3Dconnexion.h"
/* It is to be noted that these implementations are linked in as
* 'extern "C"' calls from GHOST_NDOFManagerCocoa.
*
* This is done in order to
* preserve weak linking capability (which as of clang-3.3 and xcode5
* breaks weak linking when there is name mangling of c++ libraries.)
*
* We need to have the weak linked file as pure C. Therefore we build a
* compiled bridge from the real weak linked calls and the calls within C++
*
*/
OSErr GHOST_NDOFManager3Dconnexion_available(void)
{
// extern unsigned int InstallConnexionHandlers() __attribute__((weak_import));
// Make the linker happy for the framework check (see link below for more info)
// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html
return InstallConnexionHandlers != 0;
// this means that the driver is installed and dynamically linked to blender
}
OSErr GHOST_NDOFManager3Dconnexion_oldDRV()
{
//extern unsigned int SetConnexionClientButtonMask() __attribute__((weak_import));
// Make the linker happy for the framework check (see link below for more info)
// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html
return SetConnexionClientButtonMask != 0;
// this means that the driver has this symbol
}
UInt16 GHOST_NDOFManager3Dconnexion_RegisterConnexionClient(UInt32 signature, UInt8 *name, UInt16 mode, UInt32 mask)
{
return RegisterConnexionClient(signature, name, mode, mask);
}
void GHOST_NDOFManager3Dconnexion_SetConnexionClientButtonMask(UInt16 clientID, UInt32 buttonMask)
{
return SetConnexionClientButtonMask( clientID, buttonMask);
}
void GHOST_NDOFManager3Dconnexion_UnregisterConnexionClient(UInt16 clientID)
{
return UnregisterConnexionClient( clientID);
}
OSErr GHOST_NDOFManager3Dconnexion_InstallConnexionHandlers(
ConnexionMessageHandlerProc messageHandler,
ConnexionAddedHandlerProc addedHandler,
ConnexionRemovedHandlerProc removedHandler)
{
return InstallConnexionHandlers( messageHandler, addedHandler, removedHandler);
}
void GHOST_NDOFManager3Dconnexion_CleanupConnexionHandlers(void)
{
return CleanupConnexionHandlers();
}
OSErr GHOST_NDOFManager3Dconnexion_ConnexionControl(UInt32 message, SInt32 param, SInt32 *result)
{
return ConnexionControl( message, param, result);
}
#endif // WITH_INPUT_NDOF

View File

@ -1,50 +0,0 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s):
* Jake Kauth on 9/12/13.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __GHOST_NDOFMANAGER3DCONNEXION_H__
#define __GHOST_NDOFMANAGER3DCONNEXION_H__
#ifdef WITH_INPUT_NDOF
#include <ConnexionClientAPI.h>
OSErr GHOST_NDOFManager3Dconnexion_available(void);
OSErr GHOST_NDOFManager3Dconnexion_oldDRV(void);
OSErr GHOST_NDOFManager3Dconnexion_InstallConnexionHandlers(ConnexionMessageHandlerProc messageHandler, ConnexionAddedHandlerProc addedHandler, ConnexionRemovedHandlerProc removedHandler);
void GHOST_NDOFManager3Dconnexion_CleanupConnexionHandlers(void);
UInt16 GHOST_NDOFManager3Dconnexion_RegisterConnexionClient(UInt32 signature, UInt8 *name, UInt16 mode, UInt32 mask);
void GHOST_NDOFManager3Dconnexion_SetConnexionClientButtonMask(UInt16 clientID, UInt32 buttonMask);
void GHOST_NDOFManager3Dconnexion_UnregisterConnexionClient(UInt16 clientID);
OSErr GHOST_NDOFManager3Dconnexion_ConnexionControl(UInt32 message, SInt32 param, SInt32 *result);
extern OSErr InstallConnexionHandlers(ConnexionMessageHandlerProc messageHandler, ConnexionAddedHandlerProc addedHandler, ConnexionRemovedHandlerProc removedHandler) __attribute__((weak_import));
extern void CleanupConnexionHandlers(void) __attribute__((weak_import));
extern UInt16 RegisterConnexionClient(UInt32 signature, UInt8 *name, UInt16 mode, UInt32 mask) __attribute__((weak_import));
extern void SetConnexionClientButtonMask(UInt16 clientID, UInt32 buttonMask) __attribute__((weak_import));
extern void UnregisterConnexionClient(UInt16 clientID) __attribute__((weak_import));
extern OSErr ConnexionControl(UInt32 message, SInt32 param, SInt32 *result) __attribute__((weak_import));
#endif // WITH_INPUT_NDOF
#endif // #include guard

View File

@ -26,22 +26,7 @@
#ifdef WITH_INPUT_NDOF
extern "C" {
#include <ConnexionClientAPI.h>
#include <stdio.h>
}
#include "GHOST_NDOFManager.h"
extern "C" OSErr GHOST_NDOFManager3Dconnexion_available(void);
extern "C" OSErr GHOST_NDOFManager3Dconnexion_oldDRV(void);
extern "C" OSErr GHOST_NDOFManager3Dconnexion_InstallConnexionHandlers(ConnexionMessageHandlerProc messageHandler, ConnexionAddedHandlerProc addedHandler, ConnexionRemovedHandlerProc removedHandler);
extern "C" void GHOST_NDOFManager3Dconnexion_CleanupConnexionHandlers(void);
extern "C" UInt16 GHOST_NDOFManager3Dconnexion_RegisterConnexionClient(UInt32 signature, UInt8 *name, UInt16 mode, UInt32 mask);
extern "C" void GHOST_NDOFManager3Dconnexion_SetConnexionClientButtonMask(UInt16 clientID, UInt32 buttonMask);
extern "C" void GHOST_NDOFManager3Dconnexion_UnregisterConnexionClient(UInt16 clientID);
extern "C" OSErr GHOST_NDOFManager3Dconnexion_ConnexionControl(UInt32 message, SInt32 param, SInt32 *result);
// Event capture is handled within the NDOF manager on Macintosh,
// so there's no need for SystemCocoa to look for them.
@ -50,14 +35,9 @@ class GHOST_NDOFManagerCocoa : public GHOST_NDOFManager
{
public:
GHOST_NDOFManagerCocoa(GHOST_System&);
~GHOST_NDOFManagerCocoa();
// whether multi-axis functionality is available (via the OS or driver)
// does not imply that a device is plugged in or being used
private:
unsigned short m_clientID;
bool available();
};

View File

@ -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 void NDOF_DeviceAdded(io_connect_t connection)
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);
// 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);
clientID = RegisterConnexionClient('blnd', "\007blender", kConnexionClientModeTakeOver, kConnexionMaskAll);
// printf("ndof: client id = %d\n", m_clientID);
if (GHOST_NDOFManager3Dconnexion_oldDRV()) {
has_old_driver = false;
GHOST_NDOFManager3Dconnexion_SetConnexionClientButtonMask(m_clientID, kConnexionMaskAllButtons);
if (!has_old_driver) {
SetConnexionClientButtonMask(clientID, kConnexionMaskAllButtons);
}
else {
printf("ndof: old 3Dx driver installed, some buttons may not work\n");
}
}
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

View File

@ -50,9 +50,9 @@
#include "GHOST_TimerTask.h"
#include "GHOST_WindowManager.h"
#include "GHOST_WindowCocoa.h"
#ifdef WITH_INPUT_NDOF
#include "GHOST_NDOFManagerCocoa.h"
#include "GHOST_NDOFManager3Dconnexion.h"
#include "GHOST_NDOFManagerCocoa.h"
#endif
#include "AssertMacros.h"