diff --git a/src/posix/cocoa/i_common.h b/src/posix/cocoa/i_common.h index ea51e1c88..081466e87 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() @@ -115,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_input.mm b/src/posix/cocoa/i_input.mm index 46f5e4450..5e0c5f1c8 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_joystick.cpp b/src/posix/cocoa/i_joystick.cpp index b102cf5a0..9b9487cf6 100644 --- a/src/posix/cocoa/i_joystick.cpp +++ b/src/posix/cocoa/i_joystick.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -45,6 +46,9 @@ #include "v_text.h" +EXTERN_CVAR(Bool, joy_axespolling) + + namespace { @@ -73,6 +77,9 @@ FString ToFString(const CFStringRef string) } +// --------------------------------------------------------------------------- + + class IOKitJoystick : public IJoystickConfig { public: @@ -105,6 +112,10 @@ public: void Update(); + void UseAxesPolling(bool axesPolling); + + io_object_t* GetNotificationPtr(); + private: IOHIDDeviceInterface** m_interface; IOHIDQueueInterface** m_queue; @@ -154,6 +165,10 @@ private: TArray m_buttons; TArray m_POVs; + bool m_useAxesPolling; + + io_object_t m_notification; + static const float DEFAULT_DEADZONE; static const float DEFAULT_SENSITIVITY; @@ -171,7 +186,9 @@ private: void AddAxis(CFDictionaryRef element); void AddButton(CFDictionaryRef element); void AddPOV(CFDictionaryRef element); + void AddToQueue(IOHIDElementCookie cookie); + void RemoveFromQueue(IOHIDElementCookie cookie); }; @@ -212,7 +229,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; } } @@ -261,6 +279,8 @@ IOKitJoystick::IOKitJoystick(const io_object_t device) : m_interface(CreateDeviceInterface(device)) , m_queue(CreateDeviceQueue(m_interface)) , m_sensitivity(DEFAULT_SENSITIVITY) +, m_useAxesPolling(true) +, m_notification(0) { if (NULL == m_interface || NULL == m_queue) { @@ -282,6 +302,8 @@ IOKitJoystick::IOKitJoystick(const io_object_t device) CFRelease(properties); + UseAxesPolling(joy_axespolling); + (*m_queue)->start(m_queue); SetDefaultConfig(); @@ -291,6 +313,11 @@ IOKitJoystick::~IOKitJoystick() { M_SaveJoystickConfig(this); + if (0 != m_notification) + { + IOObjectRelease(m_notification); + } + if (NULL != m_queue) { (*m_queue)->stop(m_queue); @@ -486,6 +513,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 +556,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 +595,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 +881,6 @@ void IOKitJoystick::AddAxis(const CFDictionaryRef element) } m_axes.Push(axis); - - AddToQueue(axis.cookie); } void IOKitJoystick::AddButton(CFDictionaryRef element) @@ -849,9 +901,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 +915,25 @@ 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); + } +} + + +io_object_t* IOKitJoystick::GetNotificationPtr() +{ + return &m_notification; +} + // --------------------------------------------------------------------------- @@ -876,26 +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; + } + } } @@ -931,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); @@ -957,44 +1089,85 @@ 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::OnDeviceRemoved(void* const refcon, io_service_t, const natural_t messageType, void*) +{ + if (messageType != kIOMessageServiceIsTerminated) + { + return; + } -IOKitJoystickManager* s_joystickManager; + 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(); +} } // unnamed namespace @@ -1036,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; } @@ -1068,3 +1238,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); + } +} diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index b76ec0984..05a8081f6 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -31,10 +31,10 @@ ** */ -#include +#include "i_common.h" -#import -#import +#include +#include // Avoid collision between DObject class and Objective-C #define Class ObjectClass @@ -49,9 +49,6 @@ #include "s_sound.h" #include "version.h" -#include "i_common.h" -#include "i_osversion.h" - #undef Class @@ -492,27 +489,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 +504,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 5e6ac6d20..000000000 --- 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 40a9ff17a..000000000 --- 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_timer.cpp b/src/posix/cocoa/i_timer.cpp index c63561077..319cdd2b8 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 diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index cb988002c..b5f54c256 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