SL-13610 [MAC] WIP List HID available devices in joystick selection

Doesn't filter the list yet, just shows full list of usb devices
Selecting visible devices doesn't work yet
master
Andrey Kleshchev 2022-12-14 19:41:07 +02:00 committed by akleshchev
parent 702e4c7dc1
commit 9b27b6e509
14 changed files with 384 additions and 25 deletions

View File

@ -44,8 +44,8 @@ bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set<s
bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root, LLDir::ALL_SKINS);
if (!success)
{
gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
LL_ERRS() << "Couldn't load string table " << xml_filename << ". Please reinstall viewer from https://secondlife.com/support/downloads/ and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL;
//gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
LL_ERRS() << "Couldn't load string table " << xml_filename << " " << errno << ". Please reinstall viewer from https://secondlife.com/support/downloads/ and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL;
return false;
}

View File

@ -196,7 +196,13 @@ public:
// windows only DirectInput8 for joysticks
virtual void* getDirectInput8() { return NULL; };
virtual bool getInputDevices(U32 device_type_filter, void * devices_callback, void* userdata) { return false; };
virtual bool getInputDevices(U32 device_type_filter,
std::function<void(std::string&, LLSD::Binary&, void*)> osx_callback,
void* win_callback,
void* userdata)
{
return false;
};
virtual S32 getRefreshRate() { return mRefreshRate; }
protected:

View File

@ -27,6 +27,7 @@
#include <AppKit/AppKit.h>
#include <Cocoa/Cocoa.h>
#include <errno.h>
#include "llopenglview-objc.h"
#include "llwindowmacosx-objc.h"
#include "llappdelegate-objc.h"

View File

@ -43,6 +43,13 @@
#include <CoreServices/CoreServices.h>
#include <CoreGraphics/CGDisplayConfiguration.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/hid/IOHIDUsageTables.h>
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/usb/IOUSBLib.h>
extern BOOL gDebugWindowProc;
BOOL gHiDPISupport = TRUE;
@ -212,13 +219,16 @@ bool callKeyUp(NSKeyEventRef event, unsigned short key, unsigned int mask)
bool callKeyDown(NSKeyEventRef event, unsigned short key, unsigned int mask, wchar_t character)
{
if((key == gKeyboard->inverseTranslateKey('Z')) && (character == 'y'))
//if (mask!=MASK_NONE)
{
key = gKeyboard->inverseTranslateKey('Y');
}
else if ((key == gKeyboard->inverseTranslateKey('Y')) && (character == 'z'))
{
key = gKeyboard->inverseTranslateKey('Z');
if((key == gKeyboard->inverseTranslateKey('Z')) && (character == 'y'))
{
key = gKeyboard->inverseTranslateKey('Y');
}
else if ((key == gKeyboard->inverseTranslateKey('Y')) && (character == 'z'))
{
key = gKeyboard->inverseTranslateKey('Z');
}
}
mRawKeyEvent = event;
@ -1803,6 +1813,299 @@ void LLWindowMacOSX::spawnWebBrowser(const std::string& escaped_url, bool async)
}
}
// Device and Element Interfaces
typedef enum HIDElementTypeMask
{
kHIDElementTypeInput = 1 << 1,
kHIDElementTypeOutput = 1 << 2,
kHIDElementTypeFeature = 1 << 3,
kHIDElementTypeCollection = 1 << 4,
kHIDElementTypeIO = kHIDElementTypeInput | kHIDElementTypeOutput | kHIDElementTypeFeature,
kHIDElementTypeAll = kHIDElementTypeIO | kHIDElementTypeCollection
}HIDElementTypeMask;
struct hu_element_t
{
unsigned long type; // the type defined by IOHIDElementType in IOHIDKeys.h
long usage; // usage within above page from IOUSBHIDParser.h which defines specific usage
long usagePage; // usage page from IOUSBHIDParser.h which defines general usage
void* cookie; // unique value( within device of specific vendorID and productID ) which identifies element, will NOT change
long min; // reported min value possible
long max; // reported max value possible
long scaledMin; // reported scaled min value possible
long scaledMax; // reported scaled max value possible
long size; // size in bits of data return from element
unsigned char relative; // are reports relative to last report( deltas )
unsigned char wrapping; // does element wrap around( one value higher than max is min )
unsigned char nonLinear; // are the values reported non-linear relative to element movement
unsigned char preferredState; // does element have a preferred state( such as a button )
unsigned char nullState; // does element have null state
long units; // units value is reported in( not used very often )
long unitExp; // exponent for units( also not used very often )
char name[256]; // name of element( c string )
// runtime variables
long initialCenter; // center value at start up
unsigned char hasCenter; // whether or not to use center for calibration
long minReport; // min returned value
long maxReport; // max returned value( calibrate call )
long userMin; // user set value to scale to( scale call )
long userMax;
struct hu_element_t* pPrevious; // previous element( NULL at list head )
struct hu_element_t* pChild; // next child( only of collections )
struct hu_element_t* pSibling; // next sibling( for elements and collections )
long depth;
};
struct HidDevice
{ // interface to device, NULL = no interface
char mProduct[256]; // name of product
long mlocalID; // long representing location in USB( or other I/O ) chain which device is pluged into, can identify specific device on machine
long mUsage; // usage page from IOUSBHID Parser.h which defines general usage
long mUsagePage; // usage within above page from IOUSBHID Parser.h which defines specific usage
};
/*************************************************************************
*
* hu_BuildDevice( inHIDDevice )
*
* Purpose: given a IO device object build a flat device record including device info and all elements
*
* Notes: handles NULL lists properly
*
* Inputs: inHIDDevice - the I/O device object
*
* Returns: hu_device_t* - the address of the new device record
*/
static void populate_device_info( io_object_t io_obj_p, CFDictionaryRef device_dic, HidDevice* devicep )
{
CFMutableDictionaryRef io_properties = nil;
io_registry_entry_t entry1;
io_registry_entry_t entry2;
kern_return_t rc;
// Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
// get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
// try to get parent1
rc = IORegistryEntryGetParentEntry( io_obj_p, kIOServicePlane, &entry1 );
if ( KERN_SUCCESS == rc )
{
rc = IORegistryEntryGetParentEntry( entry1, kIOServicePlane, &entry2 );
IOObjectRelease( entry1 );
if ( KERN_SUCCESS == rc )
{
rc = IORegistryEntryCreateCFProperties( entry2, &io_properties, kCFAllocatorDefault, kNilOptions );
// either way, release parent2
IOObjectRelease( entry2 );
}
}
if ( KERN_SUCCESS == rc )
{
// IORegistryEntryCreateCFProperties() succeeded
if ( io_properties != nil )
{
CFTypeRef dict_element = 0;
// get device info
// try hid dictionary first, if fail then go to usb dictionary
dict_element = CFDictionaryGetValue( device_dic, CFSTR(kIOHIDProductKey) );
if ( !dict_element )
{
dict_element = CFDictionaryGetValue( io_properties, CFSTR( "USB Product Name" ) );
}
if ( dict_element )
{
bool res = CFStringGetCString((CFStringRef)dict_element, devicep->mProduct, 256, kCFStringEncodingUTF8);
if ( !res )
{
LL_WARNS("Joystick") << "Failed to populate mProduct" << LL_ENDL;
}
}
dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDLocationIDKey ) );
if ( !dict_element )
{
dict_element = CFDictionaryGetValue( io_properties, CFSTR( "locationID" ) );
}
if ( dict_element )
{
bool res = CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mlocalID );
if ( !res )
{
LL_WARNS("Joystick") << "Failed to populate mLocalID" << LL_ENDL;
}
}
dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDPrimaryUsagePageKey ) );
if ( dict_element )
{
bool res = CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mUsagePage );
if ( !res )
{
LL_WARNS("Joystick") << "Failed to populate mUsagePage" << LL_ENDL;
}
dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDPrimaryUsageKey ) );
if ( dict_element )
{
if ( !CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mUsage ) )
{
LL_WARNS("Joystick") << "Failed to populate mUsage" << LL_ENDL;
}
}
}
CFRelease(io_properties);
}
else
{
LL_WARNS("Joystick") << "Failed to populate fields" << LL_ENDL;
}
}
}
HidDevice populate_device( io_object_t io_obj )
{
void* interfacep = nullptr;
HidDevice device;
memset( &device, 0, sizeof( HidDevice ) );
CFMutableDictionaryRef device_dic = 0;
kern_return_t result = IORegistryEntryCreateCFProperties( io_obj, &device_dic, kCFAllocatorDefault, kNilOptions );
if ( KERN_SUCCESS == result
&& device_dic )
{
IOReturn io_result = kIOReturnSuccess;
HRESULT query_result = S_OK;
SInt32 the_score = 0;
IOCFPlugInInterface **the_interface = NULL;
io_result = IOCreatePlugInInterfaceForService( io_obj, kIOHIDDeviceUserClientTypeID,
kIOCFPlugInInterfaceID, &the_interface, &the_score );
if ( io_result == kIOReturnSuccess )
{
query_result = ( *the_interface )->QueryInterface( the_interface, CFUUIDGetUUIDBytes( kIOHIDDeviceInterfaceID ), ( LPVOID * ) & ( interfacep ) );
if ( query_result != S_OK )
{
LL_WARNS("Joystick") << "QueryInterface failed" << LL_ENDL;
}
IODestroyPlugInInterface( the_interface );
}
else
{
LL_WARNS("Joystick") << "IOCreatePlugInInterfaceForService failed" << LL_ENDL;
}
if ( interfacep )
{
result = ( *( IOHIDDeviceInterface** )interfacep )->open( interfacep, 0 );
if ( result != kIOReturnSuccess)
{
LL_WARNS("Joystick") << "open failed" << LL_ENDL;
}
}
// extract needed fields
populate_device_info( io_obj, device_dic, &device );
// Release interface
if ( interfacep )
{
( *( IOHIDDeviceInterface** ) interfacep )->close( interfacep );
( *( IOHIDDeviceInterface** ) interfacep )->Release( interfacep );
interfacep = NULL;
}
CFRelease( device_dic );
}
else
{
LL_WARNS("Joystick") << "populate_device failed" << LL_ENDL;
}
return device;
}
static void get_devices(std::list<HidDevice> &list_of_devices,
io_iterator_t inIODeviceIterator)
{
IOReturn result = kIOReturnSuccess; // assume success( optimist! )
io_object_t io_obj = 0;
while ( 0 != (io_obj = IOIteratorNext( inIODeviceIterator ) ) )
{
HidDevice device = populate_device( io_obj );
list_of_devices.push_back(device);
// release the device object, it is no longer needed
result = IOObjectRelease( io_obj );
if ( KERN_SUCCESS != result )
{
LL_WARNS("Joystick") << "IOObjectRelease failed" << LL_ENDL;
}
}
}
bool LLWindowMacOSX::getInputDevices(U32 device_type_filter,
std::function<void(std::string&, LLSD::Binary&, void*)> osx_callback,
void* win_callback,
void* userdata)
{
CFMutableDictionaryRef device_dict_ref;
IOReturn result = kIOReturnSuccess; // assume success( optimist! )
// Set up matching dictionary to search the I/O Registry for HID devices we are interested in. Dictionary reference is NULL if error.
// A dictionary to match devices to?
device_dict_ref = IOServiceMatching( kIOHIDDeviceKey );
// BUG FIX! one reference is consumed by IOServiceGetMatchingServices
CFRetain( device_dict_ref );
io_iterator_t io_iter = 0;
// create an IO object iterator
result = IOServiceGetMatchingServices( kIOMasterPortDefault, device_dict_ref, &io_iter );
if ( kIOReturnSuccess != result )
{
LL_WARNS("Joystick") << "IOServiceGetMatchingServices failed" << LL_ENDL;
}
if ( io_iter )
{
// add all existing devices
std::list<HidDevice> device_list;
get_devices(device_list, io_iter);
std::list<HidDevice>::iterator iter;
for (iter = device_list.begin(); iter != device_list.end(); ++iter)
{
S32 size = sizeof(long);
LLSD::Binary data; //just an std::vector
data.resize(size);
memcpy(&data[0], &iter->mlocalID, size);
std::string label(iter->mProduct);
osx_callback(label, data, userdata);
}
}
CFRelease( device_dict_ref );
return false; // todo: should be true once UI part gets done
}
LLSD LLWindowMacOSX::getNativeKeyData()
{
LLSD result = LLSD::emptyMap();

View File

@ -112,6 +112,11 @@ public:
void interruptLanguageTextInput() override;
void spawnWebBrowser(const std::string& escaped_url, bool async) override;
F32 getSystemUISize() override;
bool getInputDevices(U32 device_type_filter,
std::function<void(std::string&, LLSD::Binary&, void*)> osx_callback,
void* win_callback,
void* userdata) override;
static std::vector<std::string> getDisplaysResolutionList();

View File

@ -4495,7 +4495,10 @@ void* LLWindowWin32::getDirectInput8()
return &gDirectInput8;
}
bool LLWindowWin32::getInputDevices(U32 device_type_filter, void * di8_devices_callback, void* userdata)
bool LLWindowWin32::getInputDevices(U32 device_type_filter
std::function<void(std::string&, LLSD::Binary&, void*)> osx_callback,
void * di8_devices_callback,
void* userdata)
{
if (gDirectInput8 != NULL)
{

View File

@ -129,7 +129,10 @@ public:
static void setDPIAwareness();
/*virtual*/ void* getDirectInput8();
/*virtual*/ bool getInputDevices(U32 device_type_filter, void * di8_devices_callback, void* userdata);
/*virtual*/ bool getInputDevices(U32 device_type_filter,
std::function<void(std::string&, LLSD::Binary&, void*)> osx_callback,
void* win_callback,
void* userdata);
U32 getRawWParam() { return mRawWParam; }

View File

@ -837,7 +837,7 @@ bool LLXMLNode::getLayeredXMLNode(LLXMLNodePtr& root,
if (!LLXMLNode::parseFile(filename, root, NULL))
{
LL_WARNS() << "Problem reading UI description file: " << filename << LL_ENDL;
LL_WARNS() << "Problem reading UI description file: " << filename << " " << errno << LL_ENDL;
return false;
}

View File

@ -599,6 +599,9 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
switch(filter)
{
case FFLOAD_ALL:
case FFLOAD_EXE:
allowedv->push_back("app");
allowedv->push_back("exe");
allowedv->push_back("wav");
allowedv->push_back("bvh");
allowedv->push_back("anim");
@ -617,9 +620,6 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
allowedv->push_back("tpic");
allowedv->push_back("png");
break;
case FFLOAD_EXE:
allowedv->push_back("app");
allowedv->push_back("exe");
break;
case FFLOAD_WAV:
allowedv->push_back("wav");

View File

@ -250,6 +250,12 @@ void LLFloaterJoystick::refresh()
initFromSettings();
}
void LLFloaterJoystick::addDeviceCallback(std::string &name, LLSD::Binary& value, void* userdata)
{
LLFloaterJoystick * floater = (LLFloaterJoystick*)userdata;
floater->mJoysticksCombo->add(name, value, ADD_BOTTOM, 1);
}
void LLFloaterJoystick::addDevice(std::string &name, LLSD& value)
{
mJoysticksCombo->add(name, value, ADD_BOTTOM, 1);
@ -264,19 +270,21 @@ void LLFloaterJoystick::refreshListOfDevices()
mHasDeviceList = false;
void* win_calback = nullptr;
// di8_devices_callback callback is immediate and happens in scope of getInputDevices()
#if LL_WINDOWS && !LL_MESA_HEADLESS
// space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
U32 device_type = DI8DEVCLASS_GAMECTRL;
void* callback = &di8_list_devices_callback;
win_calback = di8_list_devices_callback;
#elif LL_DARWIN
U32 device_type = 0;
#else
// MAC doesn't support device search yet
// On MAC there is an ndof_idsearch and it is possible to specify product
// and manufacturer in NDOF_Device for ndof_init_first to pick specific one
U32 device_type = 0;
void* callback = NULL;
#endif
if (gViewerWindow->getWindow()->getInputDevices(device_type, callback, this))
if (gViewerWindow->getWindow()->getInputDevices(device_type, addDeviceCallback, win_calback, this))
{
mHasDeviceList = true;
}

View File

@ -46,6 +46,7 @@ public:
virtual void draw();
static void setSNDefaults();
static void addDeviceCallback(std::string &name, LLSD::Binary& value, void* userdata);
void addDevice(std::string &name, LLSD& value);
protected:

View File

@ -227,8 +227,17 @@ std::string string_from_guid(const GUID &guid)
return res;
}
#elif LL_DARWIN
bool macos_devices_callback(std::string &product, LLSD::Binary &data, void* userdata)
{
//LLViewerJoystick::getInstance()->initDevice(&device, product_name, data);
return false;
}
#endif
// -----------------------------------------------------------------------------
void LLViewerJoystick::updateEnabled(bool autoenable)
{
@ -365,19 +374,21 @@ void LLViewerJoystick::init(bool autoenable)
{
if (mNdofDev)
{
void* win_callback = nullptr;
std::function<void(std::string&, LLSD::Binary&, void*)> osx_callback;
// di8_devices_callback callback is immediate and happens in scope of getInputDevices()
#if LL_WINDOWS && !LL_MESA_HEADLESS
// space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
U32 device_type = DI8DEVCLASS_GAMECTRL;
void* callback = &di8_devices_callback;
win_callback = &di8_devices_callback;
#else
// MAC doesn't support device search yet
// On MAC there is an ndof_idsearch and it is possible to specify product
// and manufacturer in NDOF_Device for ndof_init_first to pick specific one
U32 device_type = 0;
void* callback = NULL;
osx_callback = macos_devices_callback;
#endif
if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback, NULL))
if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL))
{
LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL;
// Failed to gather devices from windows, init first suitable one
@ -438,21 +449,23 @@ void LLViewerJoystick::initDevice(LLSD &guid)
{
#if LIB_NDOF
mLastDeviceUUID = guid;
void* win_callback = nullptr;
std::function<void(std::string&, LLSD::Binary&, void*)> osx_callback;
#if LL_WINDOWS && !LL_MESA_HEADLESS
// space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
U32 device_type = DI8DEVCLASS_GAMECTRL;
void* callback = &di8_devices_callback;
win_callback = &di8_devices_callback;
#else
// MAC doesn't support device search yet
// On MAC there is an ndof_idsearch and it is possible to specify product
// and manufacturer in NDOF_Device for ndof_init_first to pick specific one
U32 device_type = 0;
void* callback = NULL;
osx_callback = macos_devices_callback;
#endif
mDriverState = JDS_INITIALIZING;
if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback, NULL))
if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL))
{
LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL;
// Failed to gather devices from windows, init first suitable one

View File

@ -103,7 +103,11 @@ private:
bool mCameraUpdated;
bool mOverrideCamera;
U32 mJoystickRun;
LLSD mLastDeviceUUID; // _GUID as U8 binary map, integer 1 for no device/ndof's device
// Windows: _GUID as U8 binary map
// MacOS: long as an U8 binary map
// Else: integer 1 for no device/ndof's default device
LLSD mLastDeviceUUID;
static F32 sLastDelta[7];
static F32 sDelta[7];

View File

@ -435,6 +435,18 @@
left_pad="5"
top_pad="7"
width="180" />
<text
type="string"
length="1"
follows="left|top"
height="16"
layout="topleft"
left="10"
name="Claimed:"
top="247"
width="100">
Place Page:
</text>
<text
type="string"
length="1"