Fixing up HID event input on Mac/Windows
Removing old macOS separate input - the HID version is better in every way.
This commit is contained in:
parent
2904321959
commit
96be636240
6 changed files with 6 additions and 740 deletions
6
3rdparty/hidapi/CMakeLists.txt
vendored
6
3rdparty/hidapi/CMakeLists.txt
vendored
|
@ -23,11 +23,7 @@ add_library(hidapi STATIC
|
|||
)
|
||||
|
||||
if(WIN32)
|
||||
find_library(SETUP_API_LIB Setupapi)
|
||||
if (NOT SETUP_API_LIB)
|
||||
message(FATAL_ERROR "Failed to find Setupapi.lib")
|
||||
endif()
|
||||
target_link_libraries(hidapi ${SETUP_API_LIB})
|
||||
target_link_libraries(hidapi PUBLIC SetupApi)
|
||||
elseif(APPLE)
|
||||
find_library(IOKIT_FRAMEWORK IOKit)
|
||||
target_link_libraries(hidapi ${IOKIT_FRAMEWORK})
|
||||
|
|
|
@ -123,11 +123,7 @@ IF(APPLE)
|
|||
find_library(COCOA_LIBRARY Cocoa)
|
||||
list(APPEND PLATFORM_LIBS ${COCOA_LIBRARY} ${CORESERVICES_LIBRARY})
|
||||
elseif(WIN32)
|
||||
find_library(SETUP_API_LIB Setupapi)
|
||||
if (SETUP_API_LIB)
|
||||
set(EVENT_INPUT_DEFAULT 1)
|
||||
endif()
|
||||
|
||||
set(EVENT_INPUT_DEFAULT 1)
|
||||
list(APPEND PLATFORM_LIBS "Shlwapi.lib")
|
||||
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION "bin")
|
||||
|
@ -265,14 +261,7 @@ if(EVENT_INPUT)
|
|||
message(STATUS "event-based input enabled. Using ${UDEV_LIBRARIES}")
|
||||
endif()
|
||||
else()
|
||||
# Windows
|
||||
if (NOT SETUP_API_LIB)
|
||||
message(WARNING "Setupapi.lib not found, HID/event input is disabled")
|
||||
set(ENABLE_HID_INPUT 0)
|
||||
set(EVENT_INPUT 0)
|
||||
else()
|
||||
add_definitions(-DWITH_EVENTINPUT)
|
||||
endif()
|
||||
add_definitions(-DWITH_EVENTINPUT)
|
||||
endif()
|
||||
|
||||
if (ENABLE_HID_INPUT)
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
include(FlightGearComponent)
|
||||
|
||||
IF(APPLE)
|
||||
set(EVENT_INPUT_SOURCES FGMacOSXEventInput.cxx)
|
||||
set(EVENT_INPUT_HEADERS FGMacOSXEventInput.hxx)
|
||||
# no Mac implemention, use HID
|
||||
elseif(WIN32)
|
||||
# no Win32 specific implementation, at least for now
|
||||
else()
|
||||
|
|
|
@ -1,650 +0,0 @@
|
|||
// FGMacOSXEventInput.cxx -- handle event driven input devices for Mac OS X
|
||||
//
|
||||
// Written by Tatsuhiro Nishioka, started Aug. 2009.
|
||||
//
|
||||
// Copyright (C) 2009 Tasuhiro Nishioka, tat <dot> fgmacosx <at> gmail <dot> com
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#include "FGMacOSXEventInput.hxx"
|
||||
|
||||
#include <simgear/sg_inlines.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/hid/IOHIDLib.h>
|
||||
#include <IOKit/HID/IOHIDKeys.h>
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
static std::string nameForUsage(uint32_t usagePage, uint32_t usage)
|
||||
{
|
||||
if (usagePage == kHIDPage_GenericDesktop) {
|
||||
switch (usage) {
|
||||
case kHIDUsage_GD_Joystick: return "joystick";
|
||||
case kHIDUsage_GD_Wheel: return "wheel";
|
||||
case kHIDUsage_GD_Dial: return "dial";
|
||||
case kHIDUsage_GD_Hatswitch: return "hat";
|
||||
case kHIDUsage_GD_Slider: return "slider";
|
||||
case kHIDUsage_GD_Rx: return "x-rotate";
|
||||
case kHIDUsage_GD_Ry: return "y-rotate";
|
||||
case kHIDUsage_GD_Rz: return "z-rotate";
|
||||
case kHIDUsage_GD_X: return "x-translate";
|
||||
case kHIDUsage_GD_Y: return "y-translate";
|
||||
case kHIDUsage_GD_Z: return "z-translate";
|
||||
default:
|
||||
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID generic desktop usage:" << usage);
|
||||
}
|
||||
} else if (usagePage == kHIDPage_Simulation) {
|
||||
switch (usage) {
|
||||
default:
|
||||
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID simulation usage:" << usage);
|
||||
}
|
||||
} else if (usagePage == kHIDPage_Consumer) {
|
||||
switch (usage) {
|
||||
default:
|
||||
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID consumer usage:" << usage);
|
||||
}
|
||||
} else if (usagePage == kHIDPage_AlphanumericDisplay) {
|
||||
switch (usage) {
|
||||
case kHIDUsage_AD_AlphanumericDisplay: return "alphanumeric";
|
||||
case kHIDUsage_AD_CharacterReport: return "character-report";
|
||||
case kHIDUsage_AD_DisplayData: return "display-data";
|
||||
case 0x46: return "display-brightness";
|
||||
|
||||
default:
|
||||
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID alphanumeric usage:" << usage);
|
||||
}
|
||||
} else if (usagePage == kHIDPage_LEDs) {
|
||||
switch (usage) {
|
||||
case kHIDUsage_LED_GenericIndicator: return "led-misc";
|
||||
case kHIDUsage_LED_Pause: return "led-pause";
|
||||
default:
|
||||
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID LED usage:" << usage);
|
||||
|
||||
}
|
||||
} else if (usagePage == kHIDPage_Button) {
|
||||
std::stringstream os;
|
||||
os << "button-" << usage;
|
||||
return os.str();
|
||||
} else if (usagePage >= kHIDPage_VendorDefinedStart) {
|
||||
return "vendor";
|
||||
} else {
|
||||
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID usage page:" << usagePage);
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
class FGMacOSXEventInputPrivate
|
||||
{
|
||||
public:
|
||||
IOHIDManagerRef hidManager = nullptr;
|
||||
FGMacOSXEventInput* p = nullptr;
|
||||
double currentDt = 0.0;
|
||||
int currentModifiers = 0;
|
||||
|
||||
void matchedDevice(IOHIDDeviceRef device);
|
||||
void removedDevice(IOHIDDeviceRef device);
|
||||
|
||||
void iterateDevices(CFSetRef matchingSet);
|
||||
|
||||
std::string getDeviceStringProperty(IOHIDDeviceRef device, CFStringRef hidProp);
|
||||
bool getDeviceIntProperty(IOHIDDeviceRef device, CFStringRef hidProp, int& value);
|
||||
};
|
||||
|
||||
|
||||
static void deviceMatchingCallback(
|
||||
void * inContext, // context from IOHIDManagerRegisterDeviceMatchingCallback
|
||||
IOReturn inResult, // the result of the matching operation
|
||||
void * inSender, // the IOHIDManagerRef for the new device
|
||||
IOHIDDeviceRef inIOHIDDeviceRef // the new HID device
|
||||
)
|
||||
{
|
||||
// printf("%s(context: %p, result: %p, sender: %p, device: %p).\n",
|
||||
// __PRETTY_FUNCTION__, inContext, (void *) inResult, inSender, (void*) inIOHIDDeviceRef);
|
||||
|
||||
FGMacOSXEventInputPrivate* p = static_cast<FGMacOSXEventInputPrivate*>(inContext);
|
||||
p->matchedDevice(inIOHIDDeviceRef);
|
||||
} // Handle_DeviceMatchingCallback
|
||||
|
||||
|
||||
static void deviceRemovalCallback(
|
||||
void * inContext, // context from IOHIDManagerRegisterDeviceMatchingCallback
|
||||
IOReturn inResult, // the result of the matching operation
|
||||
void * inSender, // the IOHIDManagerRef for the new device
|
||||
IOHIDDeviceRef inIOHIDDeviceRef // the new HID device
|
||||
)
|
||||
{
|
||||
// printf("%s(context: %p, result: %p, sender: %p, device: %p).\n",
|
||||
// __PRETTY_FUNCTION__, inContext, (void *) inResult, inSender, (void*) inIOHIDDeviceRef);
|
||||
FGMacOSXEventInputPrivate* p = static_cast<FGMacOSXEventInputPrivate*>(inContext);
|
||||
p->removedDevice(inIOHIDDeviceRef);
|
||||
} // Handle_DeviceMatchingCallback
|
||||
|
||||
//
|
||||
// FGMacOSXInputDevice implementation
|
||||
//
|
||||
|
||||
//
|
||||
// FGMacOSXInputDevice
|
||||
// Mac OS X specific FGInputDevice
|
||||
//
|
||||
class FGMacOSXInputDevice : public FGInputDevice {
|
||||
public:
|
||||
|
||||
FGMacOSXInputDevice(IOHIDDeviceRef hidRef,
|
||||
FGMacOSXEventInputPrivate* subsys);
|
||||
|
||||
virtual ~FGMacOSXInputDevice();
|
||||
|
||||
bool Open() override;
|
||||
void Close() override;
|
||||
|
||||
virtual void update(double dt);
|
||||
virtual const char *TranslateEventName(FGEventData &eventData);
|
||||
virtual void Send( const char * eventName, double value );
|
||||
virtual void SendFeatureReport(unsigned int reportId, const std::string& data);
|
||||
virtual void AddHandledEvent( FGInputEvent_ptr handledEvent );
|
||||
|
||||
void drainQueue();
|
||||
void handleValue(IOHIDValueRef value);
|
||||
|
||||
private:
|
||||
void buildElementNameDictionary();
|
||||
|
||||
std::string nameForHIDElement(IOHIDElementRef element) const;
|
||||
|
||||
IOHIDDeviceRef _hid = nullptr;
|
||||
IOHIDQueueRef _queue = nullptr;
|
||||
FGMacOSXEventInputPrivate* _subsystem;
|
||||
|
||||
typedef std::map<std::string, IOHIDElementRef> NameElementDict;
|
||||
NameElementDict namedElements;
|
||||
};
|
||||
|
||||
#if 0
|
||||
static void valueAvailableCallback(void * inContext, // context from IOHIDQueueRegisterValueAvailableCallback
|
||||
IOReturn inResult, // the inResult
|
||||
void * inSender // IOHIDQueueRef of the queue
|
||||
) {
|
||||
FGMacOSXInputDevice* dev = static_cast<FGMacOSXInputDevice*>(inContext);
|
||||
dev->drainQueue();
|
||||
} // Handle_ValueAvailableCallback
|
||||
#endif
|
||||
|
||||
//
|
||||
// FGMacOSXEventInput implementation
|
||||
//
|
||||
|
||||
|
||||
FGMacOSXEventInput::FGMacOSXEventInput() :
|
||||
FGEventInput(),
|
||||
d(new FGMacOSXEventInputPrivate)
|
||||
{
|
||||
d->p = this; // store back pointer to outer object on pimpl
|
||||
SG_LOG(SG_INPUT, SG_DEBUG, "FGMacOSXEventInput created");
|
||||
}
|
||||
|
||||
FGMacOSXEventInput::~FGMacOSXEventInput()
|
||||
{
|
||||
}
|
||||
|
||||
void FGMacOSXEventInput::init()
|
||||
{
|
||||
// everything is deffered until postinit since we need Nasal
|
||||
}
|
||||
|
||||
void FGMacOSXEventInput::postinit()
|
||||
{
|
||||
d->hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
||||
|
||||
// set the HID device matching dictionary
|
||||
IOHIDManagerSetDeviceMatching( d->hidManager, NULL /* all devices */);
|
||||
|
||||
IOHIDManagerRegisterDeviceMatchingCallback(d->hidManager, deviceMatchingCallback, d.get());
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(d->hidManager, deviceRemovalCallback, d.get());
|
||||
|
||||
IOHIDManagerOpen(d->hidManager, kIOHIDOptionsTypeNone);
|
||||
|
||||
IOHIDManagerScheduleWithRunLoop(d->hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
}
|
||||
|
||||
void FGMacOSXEventInput::shutdown()
|
||||
{
|
||||
FGEventInput::shutdown();
|
||||
|
||||
if (d->hidManager) {
|
||||
IOHIDManagerClose(d->hidManager, kIOHIDOptionsTypeNone);
|
||||
IOHIDManagerUnscheduleFromRunLoop(d->hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
CFRelease(d->hidManager);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// read all elements in each input device
|
||||
//
|
||||
void FGMacOSXEventInput::update(double dt)
|
||||
{
|
||||
d->currentDt = dt;
|
||||
d->currentModifiers = fgGetKeyModifiers();
|
||||
FGEventInput::update(dt);
|
||||
}
|
||||
|
||||
void FGMacOSXEventInputPrivate::matchedDevice(IOHIDDeviceRef device)
|
||||
{
|
||||
std::string productName = getDeviceStringProperty(device, CFSTR(kIOHIDProductKey));
|
||||
std::string manufacturer = getDeviceStringProperty(device, CFSTR(kIOHIDManufacturerKey));
|
||||
std::string serial = getDeviceStringProperty(device, CFSTR(kIOHIDSerialNumberKey));
|
||||
|
||||
// filter out keyboard and mouse devices : this is especially important for
|
||||
// Catalina TCC hardening to avoid secuirty alerts
|
||||
int usagePage, usage;
|
||||
getDeviceIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey), usagePage);
|
||||
getDeviceIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey), usage);
|
||||
|
||||
if (usagePage == kHIDPage_GenericDesktop) {
|
||||
if ((usage == kHIDUsage_GD_Keyboard) || (usage == kHIDUsage_GD_Mouse)) {
|
||||
SG_LOG(SG_INPUT, SG_INFO, "MacOSX-EventInput: skipping device:" << productName << "( from " << manufacturer << ") becuase it is a keyboard or mouse");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SG_LOG(SG_INPUT, SG_DEBUG, "MacOSX-EventInput: matched device:" << productName << "( from " << manufacturer << ")");
|
||||
|
||||
// allocate a Mac input device, and add to the base class to see if we have
|
||||
// a config
|
||||
|
||||
FGMacOSXInputDevice* macInputDevice = new FGMacOSXInputDevice(device, this);
|
||||
macInputDevice->SetName(manufacturer + " " + productName);
|
||||
macInputDevice->SetSerialNumber(serial);
|
||||
|
||||
p->AddDevice(macInputDevice);
|
||||
}
|
||||
|
||||
void FGMacOSXEventInputPrivate::removedDevice(IOHIDDeviceRef device)
|
||||
{
|
||||
std::string productName = getDeviceStringProperty(device, CFSTR(kIOHIDProductKey));
|
||||
std::string manufacturer = getDeviceStringProperty(device, CFSTR(kIOHIDManufacturerKey));
|
||||
// see if we have an entry for the device
|
||||
}
|
||||
|
||||
std::string FGMacOSXEventInputPrivate::getDeviceStringProperty(IOHIDDeviceRef device, CFStringRef hidProp)
|
||||
{
|
||||
CFStringRef prop = (CFStringRef) IOHIDDeviceGetProperty(device, hidProp);
|
||||
if ((prop == nullptr)|| (CFGetTypeID(prop) != CFStringGetTypeID())) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
size_t len = CFStringGetLength(prop);
|
||||
if (len == 0) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
char* buffer = static_cast<char*>(malloc(len + 1)); // + 1 for the null byte
|
||||
Boolean ok = CFStringGetCString(prop, buffer, len + 1, kCFStringEncodingUTF8);
|
||||
if (!ok) {
|
||||
SG_LOG(SG_INPUT, SG_WARN, "string conversion failed");
|
||||
}
|
||||
|
||||
std::string result(buffer, len);
|
||||
free(buffer);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
bool FGMacOSXEventInputPrivate::getDeviceIntProperty(IOHIDDeviceRef device, CFStringRef hidProp, int& value)
|
||||
{
|
||||
CFTypeRef prop = IOHIDDeviceGetProperty(device, hidProp);
|
||||
if (CFGetTypeID(prop) != CFNumberGetTypeID()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t v;
|
||||
Boolean result = CFNumberGetValue((CFNumberRef) prop, kCFNumberSInt32Type, &v);
|
||||
value = v;
|
||||
return result;
|
||||
}
|
||||
|
||||
void FGMacOSXEventInputPrivate::iterateDevices(CFSetRef matchingSet)
|
||||
{
|
||||
size_t numDevices = CFSetGetCount(matchingSet);
|
||||
IOHIDDeviceRef* devs = static_cast<IOHIDDeviceRef*>(::malloc(numDevices * sizeof(IOHIDDeviceRef)));
|
||||
CFSetGetValues(matchingSet, (const void **) devs);
|
||||
|
||||
for (size_t i=0; i < numDevices; ++i) {
|
||||
matchedDevice(devs[i]);
|
||||
}
|
||||
|
||||
free(devs);
|
||||
}
|
||||
|
||||
|
||||
FGMacOSXInputDevice::FGMacOSXInputDevice(IOHIDDeviceRef hidRef,
|
||||
FGMacOSXEventInputPrivate* subsys)
|
||||
{
|
||||
_hid = hidRef;
|
||||
CFRetain(_hid);
|
||||
_subsystem = subsys;
|
||||
}
|
||||
|
||||
FGMacOSXInputDevice::~FGMacOSXInputDevice()
|
||||
{
|
||||
if (_queue) {
|
||||
CFRelease(_queue);
|
||||
}
|
||||
|
||||
CFRelease(_hid);
|
||||
}
|
||||
|
||||
bool FGMacOSXInputDevice::Open()
|
||||
{
|
||||
IOReturn result = IOHIDDeviceOpen(_hid, kIOHIDOptionsTypeNone);
|
||||
if (result != kIOReturnSuccess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IOHIDDeviceScheduleWithRunLoop(_hid, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
// IOHIDQueueRegisterValueAvailableCallback(_queue, valueAvailableCallback, this);
|
||||
|
||||
CFIndex maxDepth = 128;
|
||||
_queue = IOHIDQueueCreate(kCFAllocatorDefault, _hid, maxDepth, kIOHIDOptionsTypeNone);
|
||||
|
||||
IOHIDQueueScheduleWithRunLoop(_queue, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
IOHIDQueueStart(_queue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGMacOSXInputDevice::buildElementNameDictionary()
|
||||
{
|
||||
// copy all elements from the device
|
||||
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(_hid, NULL,
|
||||
kIOHIDOptionsTypeNone);
|
||||
|
||||
CFIndex count = CFArrayGetCount(elements);
|
||||
typedef std::map<std::string, unsigned int> NameCountMap;
|
||||
NameCountMap nameCounts;
|
||||
|
||||
std::set<IOHIDElementCookie> seenCookies;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
IOHIDElementRef element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i);
|
||||
IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
|
||||
if (seenCookies.find(cookie) != seenCookies.end()) {
|
||||
// skip duplicate match of this element;
|
||||
continue;
|
||||
}
|
||||
|
||||
seenCookies.insert(cookie);
|
||||
|
||||
|
||||
// bool isWrapping = IOHIDElementIsWrapping(element);
|
||||
IOHIDElementType ty = IOHIDElementGetType(element);
|
||||
if (ty == kIOHIDElementTypeCollection)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t page = IOHIDElementGetUsagePage(element);
|
||||
uint32_t usage = IOHIDElementGetUsage(element);
|
||||
bool isRelative = IOHIDElementIsRelative(element);
|
||||
|
||||
// compute the name for the element
|
||||
std::string name = nameForUsage(page, usage);
|
||||
if (isRelative) {
|
||||
name = "rel-" + name; // prefix relative elements
|
||||
}
|
||||
|
||||
NameCountMap::iterator it = nameCounts.find(name);
|
||||
unsigned int nameCount;
|
||||
std::string finalName = name;
|
||||
|
||||
if (it == nameCounts.end()) {
|
||||
nameCounts[name] = nameCount = 1;
|
||||
} else {
|
||||
// check if we have a collison but different element types, eg
|
||||
// input & feature. In which case, prefix the feature one since
|
||||
// we assume it's the input one which is more interesting.
|
||||
|
||||
IOHIDElementRef other = namedElements[name];
|
||||
IOHIDElementType otherTy = IOHIDElementGetType(other);
|
||||
if (otherTy != ty) {
|
||||
// types mismatch
|
||||
if (otherTy == kIOHIDElementTypeFeature) {
|
||||
namedElements[name] = element;
|
||||
element = other;
|
||||
finalName = "feature-" + name;
|
||||
} else if (ty == kIOHIDElementTypeFeature) {
|
||||
finalName = "feature-" + name;
|
||||
}
|
||||
nameCount = 1;
|
||||
} else {
|
||||
// duplicate element, append ordinal suffix
|
||||
std::stringstream os;
|
||||
os << name << "-" << it->second;
|
||||
finalName = os.str();
|
||||
nameCount = ++(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
CFRetain(element);
|
||||
namedElements[finalName] = element;
|
||||
|
||||
if (nameCount == 2) {
|
||||
// we have more than one entry for this name, so ensures
|
||||
// the first item is availabe with the -0 suffix
|
||||
std::stringstream os;
|
||||
os << name << "-0";
|
||||
namedElements[os.str()] = namedElements[name];
|
||||
CFRetain(namedElements[os.str()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// HID debugging code
|
||||
#if 0
|
||||
NameElementDict::const_iterator it;
|
||||
for (it = namedElements.begin(); it != namedElements.end(); ++it) {
|
||||
int report = IOHIDElementGetReportID(it->second);
|
||||
int reportCount = IOHIDElementGetReportCount(it->second);
|
||||
int reportSize = IOHIDElementGetReportSize(it->second);
|
||||
CFTypeRef sizeProp = IOHIDElementGetProperty(it->second, CFSTR(kIOHIDElementDuplicateIndexKey));
|
||||
if (sizeProp) {
|
||||
CFShow(sizeProp);
|
||||
}
|
||||
|
||||
bool isArray = IOHIDElementIsArray(it->second);
|
||||
if (isArray) {
|
||||
SG_LOG(SG_INPUT, SG_INFO, "YYYYYYYYYYYYYYYYYY");
|
||||
}
|
||||
SG_LOG(SG_INPUT, SG_INFO, "\t" << it->first << " report ID " << report << " /count=" << reportCount
|
||||
<< " ;sz=" << reportSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
CFRelease(elements);
|
||||
}
|
||||
|
||||
void FGMacOSXInputDevice::Close()
|
||||
{
|
||||
// leaking these otherwise we get a crash shutting down the HID-manager
|
||||
// object. Don't understand why that should be the case
|
||||
#if 0
|
||||
for (auto it : namedElements) {
|
||||
CFRelease(it.second);
|
||||
}
|
||||
#endif
|
||||
namedElements.clear();
|
||||
|
||||
IOHIDQueueStop(_queue);
|
||||
IOHIDQueueUnscheduleFromRunLoop(_queue, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
|
||||
IOHIDDeviceUnscheduleFromRunLoop(_hid, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
IOHIDDeviceClose(_hid, kIOHIDOptionsTypeNone);
|
||||
}
|
||||
|
||||
void FGMacOSXInputDevice::AddHandledEvent( FGInputEvent_ptr handledEvent )
|
||||
{
|
||||
SG_LOG(SG_INPUT, SG_DEBUG, "adding event:" << handledEvent->GetName());
|
||||
if (namedElements.empty()) {
|
||||
buildElementNameDictionary();
|
||||
}
|
||||
|
||||
NameElementDict::iterator it = namedElements.find(handledEvent->GetName());
|
||||
if (it == namedElements.end()) {
|
||||
SG_LOG(SG_INPUT, SG_WARN, "device does not have any element with name:" << handledEvent->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
IOHIDQueueAddElement(_queue, it->second);
|
||||
FGInputDevice::AddHandledEvent(handledEvent);
|
||||
}
|
||||
|
||||
void FGMacOSXInputDevice::update(double dt)
|
||||
{
|
||||
SG_UNUSED(dt);
|
||||
drainQueue();
|
||||
|
||||
FGInputDevice::update(dt);
|
||||
}
|
||||
|
||||
void FGMacOSXInputDevice::drainQueue()
|
||||
{
|
||||
do {
|
||||
IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout( _queue, 0. );
|
||||
if ( !valueRef ) break;
|
||||
handleValue(valueRef);
|
||||
CFRelease( valueRef );
|
||||
} while ( 1 ) ;
|
||||
}
|
||||
|
||||
void FGMacOSXInputDevice::handleValue(IOHIDValueRef value)
|
||||
{
|
||||
IOHIDElementRef element = IOHIDValueGetElement(value);
|
||||
CFIndex val;
|
||||
|
||||
// need report count to know if we have to handle this specially
|
||||
int reportCount = IOHIDElementGetReportCount(element);
|
||||
if (reportCount > 1) {
|
||||
// for a repeated element, we need to read the final value of
|
||||
// the data bytes (even though this will be the lowest numbered name
|
||||
// for this element.
|
||||
int bitSize = IOHIDElementGetReportSize(element);
|
||||
const uint8_t* bytes= IOHIDValueGetBytePtr(value);
|
||||
size_t byteLen = IOHIDValueGetLength(value);
|
||||
uint8_t finalByte = bytes[byteLen - 1];
|
||||
|
||||
if (bitSize == 8) {
|
||||
val = (int8_t) finalByte; // force sign extension
|
||||
} else if (bitSize == 4) {
|
||||
int8_t b = finalByte >> 4; // high nibble
|
||||
if (b & 0x08) {
|
||||
// manually sign extend
|
||||
b |= 0xf0; // set all bits except the low nibble
|
||||
}
|
||||
val = b;
|
||||
} else {
|
||||
throw sg_io_exception("Unhandled bit size in MacoSXHID");
|
||||
}
|
||||
} else {
|
||||
val = IOHIDValueGetIntegerValue(value);
|
||||
}
|
||||
|
||||
// supress spurious 0-valued relative events
|
||||
std::string name = nameForHIDElement(element);
|
||||
if ((name.find("rel-") == 0) && (val == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FGMacOSXEventData eventData(name, val,
|
||||
_subsystem->currentDt,
|
||||
_subsystem->currentModifiers);
|
||||
HandleEvent(eventData);
|
||||
|
||||
}
|
||||
|
||||
std::string FGMacOSXInputDevice::nameForHIDElement(IOHIDElementRef element) const
|
||||
{
|
||||
NameElementDict::const_iterator it;
|
||||
for (it = namedElements.begin(); it != namedElements.end(); ++it) {
|
||||
if (it->second == element) {
|
||||
return it->first;
|
||||
}
|
||||
}
|
||||
|
||||
throw sg_exception("Unknown HID element");
|
||||
}
|
||||
|
||||
const char *FGMacOSXInputDevice::TranslateEventName(FGEventData &eventData)
|
||||
{
|
||||
FGMacOSXEventData &macEvent = (FGMacOSXEventData &)eventData;
|
||||
return macEvent.name.c_str();
|
||||
}
|
||||
|
||||
//
|
||||
// Outputs value to an writable element (like LEDElement)
|
||||
//
|
||||
void FGMacOSXInputDevice::Send(const char *eventName, double value)
|
||||
{
|
||||
NameElementDict::const_iterator it = namedElements.find(eventName);
|
||||
if (it == namedElements.end()) {
|
||||
SG_LOG(SG_INPUT, SG_WARN, "FGMacOSXInputDevice::Send: unknown element:" << eventName);
|
||||
return;
|
||||
}
|
||||
|
||||
CFIndex cfVal = value;
|
||||
uint64_t timestamp = 0;
|
||||
IOHIDValueRef valueRef = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault,
|
||||
it->second, timestamp, cfVal);
|
||||
IOHIDDeviceSetValue(_hid, it->second, valueRef);
|
||||
CFRelease(valueRef);
|
||||
}
|
||||
|
||||
void FGMacOSXInputDevice::SendFeatureReport(unsigned int reportId, const std::string& data)
|
||||
{
|
||||
|
||||
string d(data);
|
||||
if (reportId > 0) {
|
||||
// prefix with report number
|
||||
d.insert(d.begin(), static_cast<char>(reportId));
|
||||
}
|
||||
|
||||
size_t len = d.size();
|
||||
const uint8_t* bytes = (const uint8_t*) d.data();
|
||||
|
||||
std::stringstream ss;
|
||||
for (int i=0; i<len; ++i) {
|
||||
ss << static_cast<int>(bytes[i]);
|
||||
ss << ":";
|
||||
}
|
||||
|
||||
SG_LOG(SG_INPUT, SG_INFO, "report " << reportId << " sending " << ss.str());
|
||||
|
||||
IOReturn res = IOHIDDeviceSetReport(_hid,
|
||||
kIOHIDReportTypeFeature,
|
||||
reportId, /* Report ID*/
|
||||
bytes, len);
|
||||
|
||||
if (res != kIOReturnSuccess) {
|
||||
SG_LOG(SG_INPUT, SG_WARN, "failed to send feature report" << reportId);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
// FGMacOSXEventInput.hxx -- handle event driven input devices for Mac OS X
|
||||
//
|
||||
// Written by Tatsuhiro Nishioka, started Aug. 2009.
|
||||
//
|
||||
// Copyright (C) 2009 Tasuhiro Nishioka, tat <dot> fgmacosx <at> gmail <dot> com
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifndef __FGMACOSXEVENTINPUT_HXX_
|
||||
#define __FGMACOSXEVENTINPUT_HXX_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "FGEventInput.hxx"
|
||||
|
||||
class FGMacOSXEventInputPrivate;
|
||||
|
||||
struct FGMacOSXEventData : public FGEventData {
|
||||
FGMacOSXEventData(std::string name, double value, double dt, int modifiers) :
|
||||
FGEventData(value, dt, modifiers), name(name) {}
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Mac OS X specific FGEventInput
|
||||
//
|
||||
class FGMacOSXEventInput : public FGEventInput
|
||||
{
|
||||
public:
|
||||
FGMacOSXEventInput();
|
||||
|
||||
virtual ~FGMacOSXEventInput();
|
||||
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void postinit() override;
|
||||
void shutdown() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "input-event"; }
|
||||
|
||||
private:
|
||||
friend class FGMacOSXEventInputPrivate;
|
||||
|
||||
std::unique_ptr<FGMacOSXEventInputPrivate> d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -38,10 +38,9 @@
|
|||
|
||||
#ifdef WITH_EVENTINPUT
|
||||
#if defined ( SG_MAC )
|
||||
#include "FGMacOSXEventInput.hxx"
|
||||
#define INPUTEVENT_CLASS FGMacOSXEventInput
|
||||
// we use HID
|
||||
#elif defined(SG_WINDOWS)
|
||||
|
||||
// we use HID
|
||||
#else
|
||||
#include "FGLinuxEventInput.hxx"
|
||||
#define INPUTEVENT_CLASS FGLinuxEventInput
|
||||
|
|
Loading…
Add table
Reference in a new issue