From 31d232e886a3739b4f21140f0a35fe114bab712b Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 5 Jan 2015 13:22:53 +0200 Subject: [PATCH 1/6] Added polling of analog axes to IOKit gaming controllers handling This feature helps a lot with buggy gamepads that constantly generate events from "sticky" hats/sticks Polling is enabled by default, use joy_axespolling CVAR to turn it on/off --- src/posix/cocoa/i_joystick.cpp | 85 ++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/src/posix/cocoa/i_joystick.cpp b/src/posix/cocoa/i_joystick.cpp index b102cf5a09..0471b43cdd 100644 --- a/src/posix/cocoa/i_joystick.cpp +++ b/src/posix/cocoa/i_joystick.cpp @@ -45,6 +45,9 @@ #include "v_text.h" +EXTERN_CVAR(Bool, joy_axespolling) + + namespace { @@ -105,6 +108,8 @@ public: void Update(); + void UseAxesPolling(bool axesPolling); + private: IOHIDDeviceInterface** m_interface; IOHIDQueueInterface** m_queue; @@ -154,6 +159,8 @@ private: TArray m_buttons; TArray m_POVs; + bool m_useAxesPolling; + static const float DEFAULT_DEADZONE; static const float DEFAULT_SENSITIVITY; @@ -171,7 +178,9 @@ private: void AddAxis(CFDictionaryRef element); void AddButton(CFDictionaryRef element); void AddPOV(CFDictionaryRef element); + void AddToQueue(IOHIDElementCookie cookie); + void RemoveFromQueue(IOHIDElementCookie cookie); }; @@ -261,6 +270,7 @@ IOKitJoystick::IOKitJoystick(const io_object_t device) : m_interface(CreateDeviceInterface(device)) , m_queue(CreateDeviceQueue(m_interface)) , m_sensitivity(DEFAULT_SENSITIVITY) +, m_useAxesPolling(true) { if (NULL == m_interface || NULL == m_queue) { @@ -282,6 +292,8 @@ IOKitJoystick::IOKitJoystick(const io_object_t device) CFRelease(properties); + UseAxesPolling(joy_axespolling); + (*m_queue)->start(m_queue); SetDefaultConfig(); @@ -486,6 +498,26 @@ void IOKitJoystick::AddAxes(float axes[NUM_JOYAXIS]) const } +void IOKitJoystick::UseAxesPolling(const bool axesPolling) +{ + m_useAxesPolling = axesPolling; + + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) + { + AnalogAxis& axis = m_axes[i]; + + if (m_useAxesPolling) + { + RemoveFromQueue(axis.cookie); + } + else + { + AddToQueue(axis.cookie); + } + } +} + + void IOKitJoystick::Update() { if (NULL == m_queue) @@ -509,12 +541,14 @@ void IOKitJoystick::Update() { Printf(TEXTCOLOR_RED "IOHIDQueueInterface::getNextEvent() failed with code 0x%08X\n", eventResult); } + + ProcessAxes(); } void IOKitJoystick::ProcessAxes() { - if (NULL == m_interface) + if (NULL == m_interface || !m_useAxesPolling) { return; } @@ -546,6 +580,11 @@ void IOKitJoystick::ProcessAxes() bool IOKitJoystick::ProcessAxis(const IOHIDEventStruct& event) { + if (m_useAxesPolling) + { + return false; + } + for (size_t i = 0, count = m_axes.Size(); i < count; ++i) { if (event.elementCookie != m_axes[i].cookie) @@ -827,8 +866,6 @@ void IOKitJoystick::AddAxis(const CFDictionaryRef element) } m_axes.Push(axis); - - AddToQueue(axis.cookie); } void IOKitJoystick::AddButton(CFDictionaryRef element) @@ -849,9 +886,13 @@ void IOKitJoystick::AddPOV(CFDictionaryRef element) AddToQueue(pov.cookie); } + void IOKitJoystick::AddToQueue(const IOHIDElementCookie cookie) { - assert(NULL != m_queue); + if (NULL == m_queue) + { + return; + } if (!(*m_queue)->hasElement(m_queue, cookie)) { @@ -859,6 +900,19 @@ void IOKitJoystick::AddToQueue(const IOHIDElementCookie cookie) } } +void IOKitJoystick::RemoveFromQueue(const IOHIDElementCookie cookie) +{ + if (NULL == m_queue) + { + return; + } + + if ((*m_queue)->hasElement(m_queue, cookie)) + { + (*m_queue)->removeElement(m_queue, cookie); + } +} + // --------------------------------------------------------------------------- @@ -879,6 +933,8 @@ public: // Rebuilds device list void Rescan(); + void UseAxesPolling(bool axesPolling); + private: TArray m_joysticks; @@ -993,6 +1049,15 @@ void IOKitJoystickManager::ReleaseJoysticks() } +void IOKitJoystickManager::UseAxesPolling(const bool axesPolling) +{ + for (size_t i = 0, count = m_joysticks.Size(); i UseAxesPolling(axesPolling); + } +} + + IOKitJoystickManager* s_joystickManager; @@ -1068,3 +1133,15 @@ void I_ProcessJoysticks() s_joystickManager->Update(); } } + + +// --------------------------------------------------------------------------- + + +CUSTOM_CVAR(Bool, joy_axespolling, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (NULL != s_joystickManager) + { + s_joystickManager->UseAxesPolling(self); + } +} From d418648e5902c27884f71822ac5e7b6c27d70464 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 5 Jan 2015 15:46:57 +0200 Subject: [PATCH 2/6] Fixed compiler warning with format string parameter --- src/posix/cocoa/i_joystick.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/posix/cocoa/i_joystick.cpp b/src/posix/cocoa/i_joystick.cpp index 0471b43cdd..3e3b0e5276 100644 --- a/src/posix/cocoa/i_joystick.cpp +++ b/src/posix/cocoa/i_joystick.cpp @@ -221,7 +221,8 @@ IOHIDDeviceInterface** CreateDeviceInterface(const io_object_t device) } else { - Printf(TEXTCOLOR_RED "IOCFPlugInInterface::QueryInterface() failed with code 0x%08X\n", queryResult); + Printf(TEXTCOLOR_RED "IOCFPlugInInterface::QueryInterface() failed with code 0x%08X\n", + static_cast(queryResult)); return NULL; } } From cb681aad2d1cb8e1220074da2e1e438a8038012f Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 5 Jan 2015 17:24:54 +0200 Subject: [PATCH 3/6] Rearranged header files and #include's Removed unused OS version check Reduced number of headers Fixed build with SDK 10.4 --- src/posix/cocoa/i_common.h | 21 +++++++++++--- src/posix/cocoa/i_input.mm | 9 ++---- src/posix/cocoa/i_main.mm | 32 ++------------------- src/posix/cocoa/i_osversion.h | 43 ----------------------------- src/posix/cocoa/i_rbopts.h | 52 ----------------------------------- src/posix/cocoa/i_video.mm | 12 ++------ 6 files changed, 25 insertions(+), 144 deletions(-) delete mode 100644 src/posix/cocoa/i_osversion.h delete mode 100644 src/posix/cocoa/i_rbopts.h diff --git a/src/posix/cocoa/i_common.h b/src/posix/cocoa/i_common.h index ea51e1c886..3f26e7940e 100644 --- a/src/posix/cocoa/i_common.h +++ b/src/posix/cocoa/i_common.h @@ -31,10 +31,23 @@ ** */ -#import -#import -#import -#import +#import + + +struct RenderBufferOptions +{ + float pixelScale; + + float shiftX; + float shiftY; + + float width; + float height; + + bool dirty; +}; + +extern RenderBufferOptions rbOpts; inline bool I_IsHiDPISupported() diff --git a/src/posix/cocoa/i_input.mm b/src/posix/cocoa/i_input.mm index 46f5e4450d..5e0c5f1c85 100644 --- a/src/posix/cocoa/i_input.mm +++ b/src/posix/cocoa/i_input.mm @@ -2,7 +2,7 @@ ** i_input.mm ** **--------------------------------------------------------------------------- - ** Copyright 2012-2014 Alexey Lysiuk + ** Copyright 2012-2015 Alexey Lysiuk ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -31,8 +31,8 @@ ** */ -#import -#import +#include "i_common.h" + #import // Avoid collision between DObject class and Objective-C @@ -48,9 +48,6 @@ #include "doomstat.h" #include "v_video.h" -#include "i_common.h" -#include "i_rbopts.h" - #undef Class diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index b76ec09841..bb13091095 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -31,10 +31,9 @@ ** */ -#include +#include "i_common.h" -#import -#import +#include // Avoid collision between DObject class and Objective-C #define Class ObjectClass @@ -49,9 +48,6 @@ #include "s_sound.h" #include "version.h" -#include "i_common.h" -#include "i_osversion.h" - #undef Class @@ -492,27 +488,6 @@ void CreateMenu() [NSApp setMainMenu:menuBar]; } -DarwinVersion GetDarwinVersion() -{ - DarwinVersion result = {}; - - int mib[2] = { CTL_KERN, KERN_OSRELEASE }; - size_t size = 0; - - if (0 == sysctl(mib, 2, NULL, &size, NULL, 0)) - { - char* version = static_cast(alloca(size)); - - if (0 == sysctl(mib, 2, version, &size, NULL, 0)) - { - sscanf(version, "%hu.%hu.%hu", - &result.major, &result.minor, &result.bugfix); - } - } - - return result; -} - void ReleaseApplicationController() { if (NULL != appCtrl) @@ -528,9 +503,6 @@ void ReleaseApplicationController() } // unnamed namespace -const DarwinVersion darwinVersion = GetDarwinVersion(); - - int main(int argc, char** argv) { for (int i = 0; i <= argc; ++i) diff --git a/src/posix/cocoa/i_osversion.h b/src/posix/cocoa/i_osversion.h deleted file mode 100644 index 5e6ac6d205..0000000000 --- a/src/posix/cocoa/i_osversion.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - ** i_osversion.h - ** - **--------------------------------------------------------------------------- - ** Copyright 2012-2014 Alexey Lysiuk - ** All rights reserved. - ** - ** Redistribution and use in source and binary forms, with or without - ** modification, are permitted provided that the following conditions - ** are met: - ** - ** 1. Redistributions of source code must retain the above copyright - ** notice, this list of conditions and the following disclaimer. - ** 2. Redistributions in binary form must reproduce the above copyright - ** notice, this list of conditions and the following disclaimer in the - ** documentation and/or other materials provided with the distribution. - ** 3. The name of the author may not be used to endorse or promote products - ** derived from this software without specific prior written permission. - ** - ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **--------------------------------------------------------------------------- - ** - */ - -#include - -struct DarwinVersion -{ - uint16_t major; - uint16_t minor; - uint16_t bugfix; -}; - -extern const DarwinVersion darwinVersion; diff --git a/src/posix/cocoa/i_rbopts.h b/src/posix/cocoa/i_rbopts.h deleted file mode 100644 index 40a9ff17ae..0000000000 --- a/src/posix/cocoa/i_rbopts.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - ** i_rbopts.h - ** - **--------------------------------------------------------------------------- - ** Copyright 2014 Alexey Lysiuk - ** All rights reserved. - ** - ** Redistribution and use in source and binary forms, with or without - ** modification, are permitted provided that the following conditions - ** are met: - ** - ** 1. Redistributions of source code must retain the above copyright - ** notice, this list of conditions and the following disclaimer. - ** 2. Redistributions in binary form must reproduce the above copyright - ** notice, this list of conditions and the following disclaimer in the - ** documentation and/or other materials provided with the distribution. - ** 3. The name of the author may not be used to endorse or promote products - ** derived from this software without specific prior written permission. - ** - ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **--------------------------------------------------------------------------- - ** - */ - -#ifndef SRC_COCOA_I_RBOPTS_H_INCLUDED -#define SRC_COCOA_I_RBOPTS_H_INCLUDED - -struct RenderBufferOptions -{ - float pixelScale; - - float shiftX; - float shiftY; - - float width; - float height; - - bool dirty; -}; - -extern RenderBufferOptions rbOpts; - -#endif // SRC_COCOA_I_RBOPTS_H_INCLUDED diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index cb988002ca..b5f54c2563 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -2,7 +2,7 @@ ** i_video.mm ** **--------------------------------------------------------------------------- - ** Copyright 2012-2014 Alexey Lysiuk + ** Copyright 2012-2015 Alexey Lysiuk ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -31,11 +31,8 @@ ** */ -#import -#import -#import -#import -#import +#include "i_common.h" + #import #import @@ -58,9 +55,6 @@ #include "v_video.h" #include "version.h" -#include "i_common.h" -#include "i_rbopts.h" - #undef Class From b59fc595393e00f437937a39f12ec0b2fe8d5d66 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 5 Jan 2015 17:26:06 +0200 Subject: [PATCH 4/6] Added missing header comment --- src/posix/cocoa/i_timer.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/posix/cocoa/i_timer.cpp b/src/posix/cocoa/i_timer.cpp index c635610774..319cdd2b83 100644 --- a/src/posix/cocoa/i_timer.cpp +++ b/src/posix/cocoa/i_timer.cpp @@ -1,3 +1,35 @@ +/* + ** i_timer.cpp + ** + **--------------------------------------------------------------------------- + ** Copyright 2012-2015 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ #include #include From 04d38029605c1d5b5554207975e55eccf34438a1 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 5 Jan 2015 18:12:07 +0200 Subject: [PATCH 5/6] Fixed build issue with OS X SDK 10.4 --- src/posix/cocoa/i_common.h | 11 ----------- src/posix/cocoa/i_main.mm | 1 + 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/posix/cocoa/i_common.h b/src/posix/cocoa/i_common.h index 3f26e7940e..081466e87d 100644 --- a/src/posix/cocoa/i_common.h +++ b/src/posix/cocoa/i_common.h @@ -128,17 +128,6 @@ enum kVK_UpArrow = 0x7E }; -@interface NSView(SupportOutdatedOSX) -- (NSPoint)convertPointFromBase:(NSPoint)aPoint; -@end - -@implementation NSView(SupportOutdatedOSX) -- (NSPoint)convertPointFromBase:(NSPoint)aPoint -{ - return [self convertPoint:aPoint fromView:nil]; -} -@end - #endif // prior to 10.5 diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index bb13091095..05a8081f6c 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -34,6 +34,7 @@ #include "i_common.h" #include +#include // Avoid collision between DObject class and Objective-C #define Class ObjectClass From 324a1a7b77522139eeb7796de5a7c75c1e28ad04 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 6 Jan 2015 11:21:11 +0200 Subject: [PATCH 6/6] Added support for dynamic device attachment and removal to IOKit gaming controllers handling --- src/posix/cocoa/i_joystick.cpp | 186 +++++++++++++++++++++++++-------- 1 file changed, 145 insertions(+), 41 deletions(-) diff --git a/src/posix/cocoa/i_joystick.cpp b/src/posix/cocoa/i_joystick.cpp index 3e3b0e5276..9b9487cf68 100644 --- a/src/posix/cocoa/i_joystick.cpp +++ b/src/posix/cocoa/i_joystick.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -76,6 +77,9 @@ FString ToFString(const CFStringRef string) } +// --------------------------------------------------------------------------- + + class IOKitJoystick : public IJoystickConfig { public: @@ -110,6 +114,8 @@ public: void UseAxesPolling(bool axesPolling); + io_object_t* GetNotificationPtr(); + private: IOHIDDeviceInterface** m_interface; IOHIDQueueInterface** m_queue; @@ -161,6 +167,8 @@ private: bool m_useAxesPolling; + io_object_t m_notification; + static const float DEFAULT_DEADZONE; static const float DEFAULT_SENSITIVITY; @@ -272,6 +280,7 @@ IOKitJoystick::IOKitJoystick(const io_object_t device) , m_queue(CreateDeviceQueue(m_interface)) , m_sensitivity(DEFAULT_SENSITIVITY) , m_useAxesPolling(true) +, m_notification(0) { if (NULL == m_interface || NULL == m_queue) { @@ -304,6 +313,11 @@ IOKitJoystick::~IOKitJoystick() { M_SaveJoystickConfig(this); + if (0 != m_notification) + { + IOObjectRelease(m_notification); + } + if (NULL != m_queue) { (*m_queue)->stop(m_queue); @@ -915,6 +929,12 @@ void IOKitJoystick::RemoveFromQueue(const IOHIDElementCookie cookie) } +io_object_t* IOKitJoystick::GetNotificationPtr() +{ + return &m_notification; +} + + // --------------------------------------------------------------------------- @@ -931,28 +951,75 @@ public: // Updates axes/buttons states void Update(); - // Rebuilds device list - void Rescan(); - void UseAxesPolling(bool axesPolling); private: - TArray m_joysticks; + typedef TDeletingArray JoystickList; + JoystickList m_joysticks; - void Rescan(int usagePage, int usage); + static const size_t NOTIFICATION_PORT_COUNT = 2; - void ReleaseJoysticks(); + IONotificationPortRef m_notificationPorts[NOTIFICATION_PORT_COUNT]; + io_iterator_t m_notifications [NOTIFICATION_PORT_COUNT]; + + // Rebuilds device list + void Rescan(int usagePage, int usage, size_t notificationPortIndex); + void AddDevices(IONotificationPortRef notificationPort, const io_iterator_t iterator); + + static void OnDeviceAttached(void* refcon, io_iterator_t iterator); + static void OnDeviceRemoved(void* refcon, io_service_t service, + natural_t messageType, void* messageArgument); }; +IOKitJoystickManager* s_joystickManager; + + IOKitJoystickManager::IOKitJoystickManager() { - Rescan(); + memset(m_notifications, 0, sizeof m_notifications); + + for (size_t i = 0; i < NOTIFICATION_PORT_COUNT; ++i) + { + m_notificationPorts[i] = IONotificationPortCreate(kIOMasterPortDefault); + + if (NULL == m_notificationPorts[i]) + { + Printf(TEXTCOLOR_RED "IONotificationPortCreate(%zu) failed\n", i); + return; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), + IONotificationPortGetRunLoopSource(m_notificationPorts[i]), kCFRunLoopDefaultMode); + } + + Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, 0); + Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, 1); } IOKitJoystickManager::~IOKitJoystickManager() { - ReleaseJoysticks(); + for (size_t i = 0; i < NOTIFICATION_PORT_COUNT; ++i) + { + IONotificationPortRef& port = m_notificationPorts[i]; + + if (NULL != port) + { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), + IONotificationPortGetRunLoopSource(port), kCFRunLoopDefaultMode); + + IONotificationPortDestroy(port); + port = NULL; + } + + io_iterator_t& notification = m_notifications[i]; + + if (0 != notification) + { + IOObjectRelease(notification); + notification = NULL; + } + } } @@ -988,15 +1055,23 @@ void IOKitJoystickManager::Update() } -void IOKitJoystickManager::Rescan() +void IOKitJoystickManager::UseAxesPolling(const bool axesPolling) { - ReleaseJoysticks(); - - Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick); - Rescan(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); + for (size_t i = 0, count = m_joysticks.Size(); i < count; ++i) + { + m_joysticks[i]->UseAxesPolling(axesPolling); + } } -void IOKitJoystickManager::Rescan(const int usagePage, const int usage) + +void PostDeviceChangeEvent() +{ + const event_t event = { EV_DeviceChange }; + D_PostEvent(&event); +} + + +void IOKitJoystickManager::Rescan(const int usagePage, const int usage, const size_t notificationPortIndex) { CFMutableDictionaryRef deviceMatching = IOServiceMatching(kIOHIDDeviceKey); @@ -1014,54 +1089,86 @@ void IOKitJoystickManager::Rescan(const int usagePage, const int usage) CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); CFDictionarySetValue(deviceMatching, CFSTR(kIOHIDPrimaryUsageKey), usageRef); - io_iterator_t iterator = 0; - const kern_return_t matchResult = - IOServiceGetMatchingServices(kIOMasterPortDefault, deviceMatching, &iterator); + assert(notificationPortIndex < NOTIFICATION_PORT_COUNT); + io_iterator_t* iteratorPtr = &m_notifications[notificationPortIndex]; + + const IONotificationPortRef notificationPort = m_notificationPorts[notificationPortIndex]; + assert(NULL != notificationPort); + + const kern_return_t notificationResult = IOServiceAddMatchingNotification(notificationPort, + kIOFirstMatchNotification, deviceMatching, OnDeviceAttached, notificationPort, iteratorPtr); + + // IOServiceAddMatchingNotification() consumes one reference of matching dictionary + // Thus CFRelease(deviceMatching) is not needed CFRelease(usageRef); CFRelease(usagePageRef); - if (KERN_SUCCESS != matchResult) + if (KERN_SUCCESS != notificationResult) { - Printf(TEXTCOLOR_RED "IOServiceGetMatchingServices() failed with code %i\n", matchResult); - return; + Printf(TEXTCOLOR_RED "IOServiceAddMatchingNotification() failed with code %i\n", notificationResult); } + AddDevices(notificationPort, *iteratorPtr); +} + +void IOKitJoystickManager::AddDevices(const IONotificationPortRef notificationPort, const io_iterator_t iterator) +{ while (io_object_t device = IOIteratorNext(iterator)) { IOKitJoystick* joystick = new IOKitJoystick(device); m_joysticks.Push(joystick); + const kern_return_t notificationResult = IOServiceAddInterestNotification(notificationPort, + device, kIOGeneralInterest, OnDeviceRemoved, joystick, joystick->GetNotificationPtr()); + if (KERN_SUCCESS != notificationResult) + { + Printf(TEXTCOLOR_RED "IOServiceAddInterestNotification() failed with code %i\n", notificationResult); + } + IOObjectRelease(device); + + PostDeviceChangeEvent(); } - - IOObjectRelease(iterator); } -void IOKitJoystickManager::ReleaseJoysticks() +void IOKitJoystickManager::OnDeviceAttached(void* const refcon, const io_iterator_t iterator) { - for (size_t i = 0, count = m_joysticks.Size(); i (refcon); - m_joysticks.Clear(); + assert(NULL != s_joystickManager); + s_joystickManager->AddDevices(notificationPort, iterator); } - -void IOKitJoystickManager::UseAxesPolling(const bool axesPolling) +void IOKitJoystickManager::OnDeviceRemoved(void* const refcon, io_service_t, const natural_t messageType, void*) { - for (size_t i = 0, count = m_joysticks.Size(); i UseAxesPolling(axesPolling); + return; } + + assert(NULL != refcon); + IOKitJoystick* const joystick = static_cast(refcon); + + assert(NULL != s_joystickManager); + JoystickList& joysticks = s_joystickManager->m_joysticks; + + for (unsigned int i = 0, count = joysticks.Size(); i < count; ++i) + { + if (joystick == joysticks[i]) + { + joysticks.Delete(i); + break; + } + } + + delete joystick; + + PostDeviceChangeEvent(); } - -IOKitJoystickManager* s_joystickManager; - - } // unnamed namespace @@ -1102,7 +1209,7 @@ void I_GetJoysticks(TArray& sticks) void I_GetAxes(float axes[NUM_JOYAXIS]) { - for (size_t i = 0; i Rescan(); - } + // Does nothing, device list is always kept up-to-date return NULL; }