123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- //
- // HIDRemote.m
- // HIDRemote V1.6 (27th September 2017)
- //
- // Created by Felix Schwarz on 06.04.07.
- // Copyright 2007-2017 IOSPIRIT GmbH. All rights reserved.
- //
- // The latest version of this class is available at
- // http://www.iospirit.com/developers/hidremote/
- //
- // ** LICENSE *************************************************************************
- //
- // Copyright (c) 2007-2017 IOSPIRIT GmbH (http://www.iospirit.com/)
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without modification,
- // are permitted provided that the following conditions are met:
- //
- // * Redistributions of source code must retain the above copyright notice, this list
- // of conditions and the following disclaimer.
- //
- // * 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.
- //
- // * Neither the name of IOSPIRIT GmbH nor the names of its contributors may be used to
- // endorse or promote products derived from this software without specific prior
- // written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
- //
- // ************************************************************************************
- // ************************************************************************************
- // ********************************** DOCUMENTATION ***********************************
- // ************************************************************************************
- //
- // - a reference is available at http://www.iospirit.com/developers/hidremote/reference/
- // - for a guide, please see http://www.iospirit.com/developers/hidremote/guide/
- //
- // ************************************************************************************
- #import <Cocoa/Cocoa.h>
- // For legacy SDKs
- #ifndef MAC_OS_X_VERSION_10_9
- #define MAC_OS_X_VERSION_10_9 1090
- #endif /* MAC_OS_X_VERSION_10_9 */
- #ifndef MAC_OS_X_VERSION_10_10
- #define MAC_OS_X_VERSION_10_10 101000
- #endif /* MAC_OS_X_VERSION_10_10 */
- #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
- // Carbon is only required on OS X versions prior to 10.10 (for getting the OS version via Gestalt() -
- // replaced by [[NSProcessInfo processInfo] operatingSystemVersion] in 10.10)
- #include <Carbon/Carbon.h>
- #endif
- #ifndef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING
- #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
- // Enable thread-safe notification handling by default if deploying to OS X >= 10.5
- #define HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING 1
- #else
- #define HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING 0
- #endif
- #endif
- #include <unistd.h>
- #include <mach/mach.h>
- #include <sys/types.h>
- #include <IOKit/IOKitLib.h>
- #include <IOKit/IOCFPlugIn.h>
- #include <IOKit/IOMessage.h>
- #include <IOKit/hid/IOHIDKeys.h>
- #include <IOKit/hid/IOHIDLib.h>
- #include <IOKit/hid/IOHIDUsageTables.h>
- #include <IOKit/hidsystem/IOHIDLib.h>
- #include <IOKit/hidsystem/IOHIDParameter.h>
- #include <IOKit/hidsystem/IOHIDShared.h>
- #pragma mark - Enums / Codes
- #ifndef HID_REMOTE_MODE_ENUM
- #define HID_REMOTE_MODE_ENUM 1
- typedef enum
- {
- kHIDRemoteModeNone = 0L,
- kHIDRemoteModeShared, // Share the remote with others - let's you listen to the remote control events as long as noone has an exclusive lock on it
- // (RECOMMENDED ONLY FOR SPECIAL PURPOSES)
- kHIDRemoteModeExclusive, // Try to acquire an exclusive lock on the remote (NOT RECOMMENDED)
- kHIDRemoteModeExclusiveAuto // Try to acquire an exclusive lock on the remote whenever the application has focus. Temporarily release control over the
- // remote when another application has focus (RECOMMENDED)
- } HIDRemoteMode;
- #endif /* HID_REMOTE_MODE_ENUM */
- typedef enum
- {
- /* A code reserved for "no button" (needed for tracking) */
- kHIDRemoteButtonCodeNone = 0L,
- /* Standard codes - available for white plastic and aluminum remote */
- kHIDRemoteButtonCodeUp,
- kHIDRemoteButtonCodeDown,
- kHIDRemoteButtonCodeLeft,
- kHIDRemoteButtonCodeRight,
- kHIDRemoteButtonCodeCenter,
- kHIDRemoteButtonCodeMenu,
- /* Extra codes - Only available for the new aluminum version of the remote */
- kHIDRemoteButtonCodePlay,
- /* Masks */
- kHIDRemoteButtonCodeCodeMask = 0xFFL,
- kHIDRemoteButtonCodeHoldMask = (1L << 16L),
- kHIDRemoteButtonCodeSpecialMask = (1L << 17L),
- kHIDRemoteButtonCodeAluminumMask = (1L << 21L), // PRIVATE - only used internally
- /* Hold button standard codes - available for white plastic and aluminum remote */
- kHIDRemoteButtonCodeUpHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeUp),
- kHIDRemoteButtonCodeDownHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeDown),
- kHIDRemoteButtonCodeLeftHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeLeft),
- kHIDRemoteButtonCodeRightHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeRight),
- kHIDRemoteButtonCodeCenterHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeCenter),
- kHIDRemoteButtonCodeMenuHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeMenu),
- /* Hold button extra codes - Only available for aluminum version of the remote */
- kHIDRemoteButtonCodePlayHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodePlay),
- /* DEPRECATED codes - compatibility with HIDRemote 1.0 */
- kHIDRemoteButtonCodePlus = kHIDRemoteButtonCodeUp,
- kHIDRemoteButtonCodePlusHold = kHIDRemoteButtonCodeUpHold,
- kHIDRemoteButtonCodeMinus = kHIDRemoteButtonCodeDown,
- kHIDRemoteButtonCodeMinusHold = kHIDRemoteButtonCodeDownHold,
- kHIDRemoteButtonCodePlayPause = kHIDRemoteButtonCodeCenter,
- kHIDRemoteButtonCodePlayPauseHold = kHIDRemoteButtonCodeCenterHold,
- /* Special purpose codes */
- kHIDRemoteButtonCodeIDChanged = (kHIDRemoteButtonCodeSpecialMask|(1L << 18L)), // (the ID of the connected remote has changed, you can safely ignore this)
- #ifdef _HIDREMOTE_EXTENSIONS
- #define _HIDREMOTE_EXTENSIONS_SECTION 1
- #include "HIDRemoteAdditions.h"
- #undef _HIDREMOTE_EXTENSIONS_SECTION
- #endif /* _HIDREMOTE_EXTENSIONS */
- } HIDRemoteButtonCode;
- typedef enum
- {
- kHIDRemoteModelUndetermined = 0L, // Assume a white plastic remote
- kHIDRemoteModelWhitePlastic, // Signal *likely* to be coming from a white plastic remote
- kHIDRemoteModelAluminum // Signal *definitely* coming from an aluminum remote
- } HIDRemoteModel;
- typedef enum
- {
- kHIDRemoteAluminumRemoteSupportLevelNone = 0L, // This system has no support for the Aluminum Remote at all
- kHIDRemoteAluminumRemoteSupportLevelEmulation, // This system possibly has support for the Aluminum Remote (via emulation)
- kHIDRemoteAluminumRemoteSupportLevelNative // This system has native support for the Aluminum Remote
- } HIDRemoteAluminumRemoteSupportLevel;
- @class HIDRemote;
- #pragma mark - Delegate protocol (mandatory)
- @protocol HIDRemoteDelegate
- // Notification of button events
- - (void)hidRemote:(HIDRemote *)hidRemote // The instance of HIDRemote sending this
- eventWithButton:(HIDRemoteButtonCode)buttonCode // Event for the button specified by code
- isPressed:(BOOL)isPressed // The button was pressed (YES) / released (NO)
- fromHardwareWithAttributes:(NSMutableDictionary *)attributes; // Information on the device this event comes from
- @optional
- // Notification of ID changes
- - (void)hidRemote:(HIDRemote *)hidRemote // Invoked when the user switched to a remote control with a different ID
- remoteIDChangedOldID:(SInt32)old
- newID:(SInt32)newID
- forHardwareWithAttributes:(NSMutableDictionary *)attributes;
- // Notification about hardware additions/removals
- - (void)hidRemote:(HIDRemote *)hidRemote // Invoked when new hardware was found / added to HIDRemote's pool
- foundNewHardwareWithAttributes:(NSMutableDictionary *)attributes;
- - (void)hidRemote:(HIDRemote *)hidRemote // Invoked when initialization of new hardware as requested failed
- failedNewHardwareWithError:(NSError *)error;
- - (void)hidRemote:(HIDRemote *)hidRemote // Invoked when hardware was removed from HIDRemote's pool
- releasedHardwareWithAttributes:(NSMutableDictionary *)attributes;
- // ### WARNING: Unless you know VERY PRECISELY what you are doing, do not implement any of the delegate methods below. ###
- // Matching of newly found receiver hardware
- - (BOOL)hidRemote:(HIDRemote *)hidRemote // Invoked when new hardware is inspected
- inspectNewHardwareWithService:(io_service_t)service //
- prematchResult:(BOOL)prematchResult; // Return YES if HIDRemote should go on with this hardware and try
- // to use it, or NO if it should not be persued further.
- // Exlusive lock lending
- - (BOOL)hidRemote:(HIDRemote *)hidRemote
- lendExclusiveLockToApplicationWithInfo:(NSDictionary *)applicationInfo;
- - (void)hidRemote:(HIDRemote *)hidRemote
- exclusiveLockReleasedByApplicationWithInfo:(NSDictionary *)applicationInfo;
- - (BOOL)hidRemote:(HIDRemote *)hidRemote
- shouldRetryExclusiveLockWithInfo:(NSDictionary *)applicationInfo;
- @end
- #pragma mark - Actual header file for class
- @interface HIDRemote : NSObject
- {
- // IOMasterPort
- mach_port_t _masterPort;
- // Notification ports
- IONotificationPortRef _notifyPort;
- CFRunLoopSourceRef _notifyRLSource;
- // Matching iterator
- io_iterator_t _matchingServicesIterator;
- // SecureInput notification
- io_object_t _secureInputNotification;
- // Service attributes
- NSMutableDictionary *_serviceAttribMap;
- // Mode
- HIDRemoteMode _mode;
- BOOL _autoRecover;
- NSTimer *_autoRecoveryTimer;
- // Delegate
- NSObject <HIDRemoteDelegate> *_delegate;
- // Last seen ID and remote model
- SInt32 _lastSeenRemoteID;
- HIDRemoteModel _lastSeenModel;
- SInt32 _lastSeenModelRemoteID;
- // Unused button codes
- NSArray *_unusedButtonCodes;
- // Simulate Plus/Minus Hold
- BOOL _simulateHoldEvents;
- // SecureEventInput workaround
- BOOL _secureEventInputWorkAround;
- UInt64 _lastSecureEventInputPIDSum;
- uid_t _lastFrontUserSession;
- BOOL _lastScreenIsLocked;
- // Exclusive lock lending
- BOOL _exclusiveLockLending;
- BOOL _sendExclusiveResourceReuseNotification;
- NSNumber *_waitForReturnByPID;
- NSNumber *_returnToPID;
- BOOL _isRestarting;
- // Status notifications
- BOOL _sendStatusNotifications;
- NSString *_pidString;
- // Status
- BOOL _applicationIsTerminating;
- BOOL _isStopping;
- // Thread safety
- #if HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING /* #define HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING if you're running your HIDRemote instance on a background thread (requires OS X 10.5 or later) */
- NSThread *_runOnThread;
- #endif
- }
- #pragma mark - PUBLIC: Shared HID Remote
- + (HIDRemote *)sharedHIDRemote;
- #pragma mark - PUBLIC: System Information
- + (BOOL)isCandelairInstalled;
- + (BOOL)isCandelairInstallationRequiredForRemoteMode:(HIDRemoteMode)remoteMode;
- + (SInt32)OSXVersion;
- - (HIDRemoteAluminumRemoteSupportLevel)aluminiumRemoteSystemSupportLevel;
- #pragma mark - PUBLIC: Interface / API
- - (BOOL)startRemoteControl:(HIDRemoteMode)hidRemoteMode;
- - (void)stopRemoteControl;
- - (BOOL)isStarted;
- - (HIDRemoteMode)startedInMode;
- - (unsigned)activeRemoteControlCount;
- - (SInt32)lastSeenRemoteControlID;
- - (void)setLastSeenModel:(HIDRemoteModel)aModel;
- - (HIDRemoteModel)lastSeenModel;
- - (void)setDelegate:(NSObject <HIDRemoteDelegate> *)newDelegate;
- - (NSObject <HIDRemoteDelegate> *)delegate;
- - (void)setSimulateHoldEvents:(BOOL)newSimulateHoldEvents;
- - (BOOL)simulateHoldEvents;
- - (void)setUnusedButtonCodes:(NSArray *)newArrayWithUnusedButtonCodesAsNSNumbers;
- - (NSArray *)unusedButtonCodes;
- #pragma mark - PUBLIC: Expert APIs
- - (void)setEnableSecureEventInputWorkaround:(BOOL)newEnableSecureEventInputWorkaround;
- - (BOOL)enableSecureEventInputWorkaround;
- - (void)setExclusiveLockLendingEnabled:(BOOL)newExclusiveLockLendingEnabled;
- - (BOOL)exclusiveLockLendingEnabled;
- - (BOOL)isApplicationTerminating;
- - (BOOL)isStopping;
- #pragma mark - PRIVATE: HID Event handling
- - (void)_handleButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict;
- - (void)_sendButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict;
- - (void)_hidEventFor:(io_service_t)hidDevice from:(IOHIDQueueInterface **)interface withResult:(IOReturn)result;
- #pragma mark - PRIVATE: Service setup and destruction
- - (BOOL)_prematchService:(io_object_t)service;
- - (HIDRemoteButtonCode)buttonCodeForUsage:(unsigned int)usage usagePage:(unsigned int)usagePage;
- - (BOOL)_setupService:(io_object_t)service;
- - (void)_destructService:(io_object_t)service;
- #pragma mark - PRIVATE: Distributed notifiations handling
- - (void)_postStatusWithAction:(NSString *)action;
- - (void)_handleNotifications:(NSNotification *)notification;
- - (void)_setSendStatusNotifications:(BOOL)doSend;
- - (BOOL)_sendStatusNotifications;
- #pragma mark - PRIVATE: Application becomes active / inactive handling for kHIDRemoteModeExclusiveAuto
- - (void)_appStatusChanged:(NSNotification *)notification;
- - (void)_delayedAutoRecovery:(NSTimer *)aTimer;
- #pragma mark - PRIVATE: Notification handling
- - (void)_serviceMatching:(io_iterator_t)iterator;
- - (void)_serviceNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument;
- - (void)_updateSessionInformation;
- - (void)_secureInputNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument;
- @end
- #pragma mark - Information attribute keys
- extern NSString *kHIDRemoteManufacturer;
- extern NSString *kHIDRemoteProduct;
- extern NSString *kHIDRemoteTransport;
- #pragma mark - Internal/Expert attribute keys (AKA: don't touch these unless you really, really, REALLY know what you do)
- extern NSString *kHIDRemoteCFPluginInterface;
- extern NSString *kHIDRemoteHIDDeviceInterface;
- extern NSString *kHIDRemoteCookieButtonCodeLUT;
- extern NSString *kHIDRemoteHIDQueueInterface;
- extern NSString *kHIDRemoteServiceNotification;
- extern NSString *kHIDRemoteCFRunLoopSource;
- extern NSString *kHIDRemoteLastButtonPressed;
- extern NSString *kHIDRemoteService;
- extern NSString *kHIDRemoteSimulateHoldEventsTimer;
- extern NSString *kHIDRemoteSimulateHoldEventsOriginButtonCode;
- extern NSString *kHIDRemoteAluminumRemoteSupportLevel;
- extern NSString *kHIDRemoteAluminumRemoteSupportOnDemand;
- #pragma mark - Distributed notifications
- extern NSString *kHIDRemoteDNHIDRemotePing;
- extern NSString *kHIDRemoteDNHIDRemoteRetry;
- extern NSString *kHIDRemoteDNHIDRemoteStatus;
- extern NSString *kHIDRemoteDNHIDRemoteRetryGlobalObject;
- #pragma mark - Distributed notifications userInfo keys and values
- extern NSString *kHIDRemoteDNStatusHIDRemoteVersionKey;
- extern NSString *kHIDRemoteDNStatusPIDKey;
- extern NSString *kHIDRemoteDNStatusModeKey;
- extern NSString *kHIDRemoteDNStatusUnusedButtonCodesKey;
- extern NSString *kHIDRemoteDNStatusRemoteControlCountKey;
- extern NSString *kHIDRemoteDNStatusReturnToPIDKey;
- extern NSString *kHIDRemoteDNStatusActionKey;
- extern NSString *kHIDRemoteDNStatusActionStart;
- extern NSString *kHIDRemoteDNStatusActionStop;
- extern NSString *kHIDRemoteDNStatusActionUpdate;
- extern NSString *kHIDRemoteDNStatusActionNoNeed;
- #pragma mark - Driver compatibility flags
- #ifndef HID_REMOTE_COMPATIBILITY_FLAGS_ENUM
- #define HID_REMOTE_COMPATIBILITY_FLAGS_ENUM 1
- typedef enum
- {
- kHIDRemoteCompatibilityFlagsStandardHIDRemoteDevice = 1L,
- } HIDRemoteCompatibilityFlags;
- #endif /* HID_REMOTE_COMPATIBILITY_FLAGS_ENUM */
|