diff options
-rw-r--r-- | DOCS/man/en/options.rst | 3 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | core/defaultopts.c | 3 | ||||
-rw-r--r-- | core/input/input.c | 30 | ||||
-rw-r--r-- | core/input/input.h | 2 | ||||
-rw-r--r-- | core/input/keycodes.h | 17 | ||||
-rw-r--r-- | core/mplayer.c | 2 | ||||
-rw-r--r-- | core/options.h | 3 | ||||
-rw-r--r-- | etc/input.conf | 17 | ||||
-rw-r--r-- | osdep/ar/HIDRemote.h | 378 | ||||
-rw-r--r-- | osdep/ar/HIDRemote.m | 2068 | ||||
-rw-r--r-- | osdep/macosx_application_objc.h | 6 | ||||
-rw-r--r-- | osdep/macosx_events.h | 2 | ||||
-rw-r--r-- | osdep/macosx_events.m | 61 |
14 files changed, 2587 insertions, 8 deletions
diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst index d98fe960e6..f1aea822e7 100644 --- a/DOCS/man/en/options.rst +++ b/DOCS/man/en/options.rst @@ -80,6 +80,9 @@ configuration files specifying a list of fallbacks may make sense. See `audio_outputs` for details and descriptions of available drivers. +--ar, --no-ar + Enable/disable AppleIR remote support. Enabled by default. + --aspect=<ratio> Override movie aspect ratio, in case aspect information is incorrect or missing in the file being played. See also ``--no-aspect``. @@ -48,7 +48,8 @@ SOURCES-$(LIBSMBCLIENT) += stream/stream_smb.c SOURCES-$(MACOSX_BUNDLE) += osdep/macosx_bundle.m SOURCES-$(COCOA) += video/out/cocoa_common.m \ osdep/macosx_application.m \ - osdep/macosx_events.m + osdep/macosx_events.m \ + osdep/ar/HIDRemote.m SOURCES-$(MNG) += demux/demux_mng.c SOURCES-$(MPG123) += audio/decode/ad_mpg123.c diff --git a/core/defaultopts.c b/core/defaultopts.c index c4e2a0fa98..5f6521ebfb 100644 --- a/core/defaultopts.c +++ b/core/defaultopts.c @@ -106,6 +106,9 @@ void set_default_mplayer_options(struct MPOpts *opts) .use_joystick = 1, .use_lirc = 1, .use_lircc = 1, +#ifdef CONFIG_COCOA + .use_ar = 1, +#endif .default_bindings = 1, } }; diff --git a/core/input/input.c b/core/input/input.c index 0154fa11aa..62d75bb3c0 100644 --- a/core/input/input.c +++ b/core/input/input.c @@ -394,6 +394,21 @@ static const struct key_name key_names[] = { { MP_JOY_BTN8, "JOY_BTN8" }, { MP_JOY_BTN9, "JOY_BTN9" }, + { MP_AR_PLAY, "AR_PLAY" }, + { MP_AR_PLAY_HOLD, "AR_PLAY_HOLD" }, + { MP_AR_CENTER, "AR_CENTER" }, + { MP_AR_CENTER_HOLD, "AR_CENTER_HOLD" }, + { MP_AR_NEXT, "AR_NEXT" }, + { MP_AR_NEXT_HOLD, "AR_NEXT_HOLD" }, + { MP_AR_PREV, "AR_PREV" }, + { MP_AR_PREV_HOLD, "AR_PREV_HOLD" }, + { MP_AR_MENU, "AR_MENU" }, + { MP_AR_MENU_HOLD, "AR_MENU_HOLD" }, + { MP_AR_VUP, "AR_VUP" }, + { MP_AR_VUP_HOLD, "AR_VUP_HOLD" }, + { MP_AR_VDOWN, "AR_VDOWN" }, + { MP_AR_VDOWN_HOLD, "AR_VDOWN_HOLD" }, + { MP_KEY_POWER, "POWER" }, { MP_KEY_MENU, "MENU" }, { MP_KEY_PLAY, "PLAY" }, @@ -536,6 +551,7 @@ static const m_option_t mp_input_opts[] = { OPT_FLAG("joystick", input.use_joystick, CONF_GLOBAL), OPT_FLAG("lirc", input.use_lirc, CONF_GLOBAL), OPT_FLAG("lircc", input.use_lircc, CONF_GLOBAL), + OPT_FLAG("ar", input.use_ar, CONF_GLOBAL), { NULL, NULL, 0, 0, 0, 0, NULL} }; @@ -1810,6 +1826,12 @@ struct input_ctx *mp_input_init(struct input_conf *input_conf, } #endif +#ifdef CONFIG_COCOA + if (input_conf->use_ar) { + cocoa_start_apple_remote(); + } +#endif + if (input_conf->in_file) { int mode = O_RDONLY; #ifndef __MINGW32__ @@ -1841,11 +1863,17 @@ static void clear_queue(struct cmd_queue *queue) } } -void mp_input_uninit(struct input_ctx *ictx) +void mp_input_uninit(struct input_ctx *ictx, struct input_conf *input_conf) { if (!ictx) return; +#ifdef CONFIG_COCOA + if (input_conf->use_ar) { + cocoa_stop_apple_remote(); + } +#endif + for (int i = 0; i < ictx->num_key_fd; i++) { if (ictx->key_fds[i].close_func) ictx->key_fds[i].close_func(ictx->key_fds[i].fd); diff --git a/core/input/input.h b/core/input/input.h index 6c33e47fdc..944debd847 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -216,7 +216,7 @@ struct input_conf; struct input_ctx *mp_input_init(struct input_conf *input_conf, bool load_default_conf); -void mp_input_uninit(struct input_ctx *ictx); +void mp_input_uninit(struct input_ctx *ictx, struct input_conf *input_conf); struct m_config; void mp_input_register_options(struct m_config *cfg); diff --git a/core/input/keycodes.h b/core/input/keycodes.h index 2e0e5fd33f..b9d2da23b7 100644 --- a/core/input/keycodes.h +++ b/core/input/keycodes.h @@ -172,6 +172,23 @@ #define MP_MOUSE_BTN19_DBL (MP_MOUSE_BASE_DBL+19) #define MP_MOUSE_BTN_DBL_END (MP_MOUSE_BASE_DBL+20) +// Apple Remote input module +#define MP_AR_BASE (MP_KEY_BASE+0xE0) +#define MP_AR_PLAY (MP_AR_BASE + 0) +#define MP_AR_PLAY_HOLD (MP_AR_BASE + 1) +#define MP_AR_CENTER (MP_AR_BASE + 2) +#define MP_AR_CENTER_HOLD (MP_AR_BASE + 3) +#define MP_AR_NEXT (MP_AR_BASE + 4) +#define MP_AR_NEXT_HOLD (MP_AR_BASE + 5) +#define MP_AR_PREV (MP_AR_BASE + 6) +#define MP_AR_PREV_HOLD (MP_AR_BASE + 7) +#define MP_AR_MENU (MP_AR_BASE + 8) +#define MP_AR_MENU_HOLD (MP_AR_BASE + 9) +#define MP_AR_VUP (MP_AR_BASE + 10) +#define MP_AR_VUP_HOLD (MP_AR_BASE + 11) +#define MP_AR_VDOWN (MP_AR_BASE + 12) +#define MP_AR_VDOWN_HOLD (MP_AR_BASE + 13) + /* Special keys */ #define MP_KEY_INTERN (MP_KEY_BASE+0x1000) #define MP_KEY_CLOSE_WIN (MP_KEY_INTERN+0) diff --git a/core/mplayer.c b/core/mplayer.c index 89e79ef044..b5123a3d41 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -601,7 +601,7 @@ static MP_NORETURN void exit_player(struct MPContext *mpctx, timeEndPeriod(1); #endif - mp_input_uninit(mpctx->input); + mp_input_uninit(mpctx->input, &mpctx->opts.input); osd_free(mpctx->osd); diff --git a/core/options.h b/core/options.h index d870ab9677..a2e91d417b 100644 --- a/core/options.h +++ b/core/options.h @@ -231,6 +231,9 @@ typedef struct MPOpts { int use_joystick; int use_lirc; int use_lircc; +#ifdef CONFIG_COCOA + int use_ar; +#endif int default_bindings; int test; } input; diff --git a/etc/input.conf b/etc/input.conf index 3a24f8e19c..1cbd2446ba 100644 --- a/etc/input.conf +++ b/etc/input.conf @@ -136,7 +136,22 @@ k tv_step_channel -1 n tv_step_norm u tv_step_chanlist -# +# Apple Remote section +AR_PLAY cycle pause +AR_PLAY_HOLD quit +AR_CENTER cycle pause +AR_CENTER_HOLD quit +AR_NEXT seek 10 +AR_NEXT_HOLD seek 120 +AR_PREV seek -10 +AR_PREV_HOLD seek -120 +AR_MENU show_progress +AR_MENU_HOLD cycle mute +AR_VUP add volume 1 +AR_VUP_HOLD add chapter 1 +AR_VDOWN add volume -1 +AR_VDOWN_HOLD add chapter -1 + # Joystick section # WARNING: joystick support has to be explicitly enabled at # compiletime with --enable-joystick diff --git a/osdep/ar/HIDRemote.h b/osdep/ar/HIDRemote.h new file mode 100644 index 0000000000..9dd16faa5a --- /dev/null +++ b/osdep/ar/HIDRemote.h @@ -0,0 +1,378 @@ +// +// HIDRemote.h +// HIDRemote V1.2 +// +// Created by Felix Schwarz on 06.04.07. +// Copyright 2007-2011 IOSPIRIT GmbH. All rights reserved. +// +// The latest version of this class is available at +// http://www.iospirit.com/developers/hidremote/ +// +// ** LICENSE ************************************************************************* +// +// Copyright (c) 2007-2011 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> + +#include <Carbon/Carbon.h> + +#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 -- + +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; + +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; + + // 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 + #ifdef 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; +- (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 -- +typedef enum +{ + kHIDRemoteCompatibilityFlagsStandardHIDRemoteDevice = 1L, +} HIDRemoteCompatibilityFlags; diff --git a/osdep/ar/HIDRemote.m b/osdep/ar/HIDRemote.m new file mode 100644 index 0000000000..f05628c040 --- /dev/null +++ b/osdep/ar/HIDRemote.m @@ -0,0 +1,2068 @@ +// +// HIDRemote.m +// HIDRemote V1.2 (27th May 2011) +// +// Created by Felix Schwarz on 06.04.07. +// Copyright 2007-2011 IOSPIRIT GmbH. All rights reserved. +// +// The latest version of this class is available at +// http://www.iospirit.com/developers/hidremote/ +// +// ** LICENSE ************************************************************************* +// +// Copyright (c) 2007-2011 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 "HIDRemote.h" + +// Callback Prototypes +static void HIDEventCallback( void * target, + IOReturn result, + void * refcon, + void * sender); + +static void ServiceMatchingCallback( void *refCon, + io_iterator_t iterator); + +static void ServiceNotificationCallback(void * refCon, + io_service_t service, + natural_t messageType, + void * messageArgument); + +static void SecureInputNotificationCallback( void * refCon, + io_service_t service, + natural_t messageType, + void * messageArgument); + +// Shared HIDRemote instance +static HIDRemote *sHIDRemote = nil; + +@implementation HIDRemote + +#pragma mark -- Init, dealloc & shared instance -- + ++ (HIDRemote *)sharedHIDRemote +{ + if (sHIDRemote==nil) + { + sHIDRemote = [[HIDRemote alloc] init]; + } + + return (sHIDRemote); +} + +- (id)init +{ + if ((self = [super init]) != nil) + { + #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + _runOnThread = [[NSThread currentThread] retain]; + #endif + + // Detect application becoming active/inactive + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationDidBecomeActiveNotification object:NSApp]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationWillResignActiveNotification object:NSApp]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationWillTerminateNotification object:NSApp]; + + // Handle distributed notifications + _pidString = [[NSString alloc] initWithFormat:@"%d", getpid()]; + + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemotePing object:nil]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:kHIDRemoteDNHIDRemoteRetryGlobalObject]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:_pidString]; + + // Enabled by default: simulate hold events for plus/minus + _simulateHoldEvents = YES; + + // Enabled by default: work around for a locking issue introduced with Security Update 2008-004 / 10.4.9 and beyond (credit for finding this workaround goes to Martin Kahr) + _secureEventInputWorkAround = YES; + _secureInputNotification = 0; + + // Initialize instance variables + _lastSeenRemoteID = -1; + _lastSeenModel = kHIDRemoteModelUndetermined; + _unusedButtonCodes = [[NSMutableArray alloc] init]; + _exclusiveLockLending = NO; + _sendExclusiveResourceReuseNotification = YES; + _applicationIsTerminating = NO; + + // Send status notifications + _sendStatusNotifications = YES; + } + + return (self); +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:NSApp]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillResignActiveNotification object:NSApp]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidBecomeActiveNotification object:NSApp]; + + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemotePing object:nil]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteRetry object:kHIDRemoteDNHIDRemoteRetryGlobalObject]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteRetry object:_pidString]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:nil object:nil]; /* As demanded by the documentation for -[NSDistributedNotificationCenter removeObserver:name:object:] */ + + [self stopRemoteControl]; + + [self setExclusiveLockLendingEnabled:NO]; + + [self setDelegate:nil]; + + if (_unusedButtonCodes != nil) + { + [_unusedButtonCodes release]; + _unusedButtonCodes = nil; + } + + #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + [_runOnThread release]; + _runOnThread = nil; + #endif + + [_pidString release]; + _pidString = nil; + + [super dealloc]; +} + +#pragma mark -- PUBLIC: System Information -- ++ (BOOL)isCandelairInstalled +{ + mach_port_t masterPort = 0; + kern_return_t kernResult; + io_service_t matchingService = 0; + BOOL isInstalled = NO; + + kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort); + if ((kernResult!=kIOReturnSuccess) || (masterPort==0)) { return(NO); } + + if ((matchingService = IOServiceGetMatchingService(masterPort, IOServiceMatching("IOSPIRITIRController"))) != 0) + { + isInstalled = YES; + IOObjectRelease((io_object_t) matchingService); + } + + mach_port_deallocate(mach_task_self(), masterPort); + + return (isInstalled); +} + ++ (BOOL)isCandelairInstallationRequiredForRemoteMode:(HIDRemoteMode)remoteMode +{ + SInt32 systemVersion = 0; + + // Determine OS version + if (Gestalt(gestaltSystemVersion, &systemVersion) == noErr) + { + switch (systemVersion) + { + case 0x1060: // OS 10.6 + case 0x1061: // OS 10.6.1 + // OS X 10.6(.0) and OS X 10.6.1 require the Candelair driver for to be installed, + // so that third party apps can acquire an exclusive lock on the receiver HID Device + // via IOKit. + + switch (remoteMode) + { + case kHIDRemoteModeExclusive: + case kHIDRemoteModeExclusiveAuto: + if (![self isCandelairInstalled]) + { + return (YES); + } + break; + } + break; + } + } + + return (NO); +} + +- (HIDRemoteAluminumRemoteSupportLevel)aluminiumRemoteSystemSupportLevel +{ + HIDRemoteAluminumRemoteSupportLevel supportLevel = kHIDRemoteAluminumRemoteSupportLevelNone; + NSEnumerator *attribDictsEnum; + NSDictionary *hidAttribsDict; + + attribDictsEnum = [_serviceAttribMap objectEnumerator]; + + while ((hidAttribsDict = [attribDictsEnum nextObject]) != nil) + { + NSNumber *deviceSupportLevel; + + if ((deviceSupportLevel = [hidAttribsDict objectForKey:kHIDRemoteAluminumRemoteSupportLevel]) != nil) + { + if ([deviceSupportLevel intValue] > (int)supportLevel) + { + supportLevel = [deviceSupportLevel intValue]; + } + } + } + + return (supportLevel); +} + +#pragma mark -- PUBLIC: Interface / API -- +- (BOOL)startRemoteControl:(HIDRemoteMode)hidRemoteMode +{ + if ((_mode == kHIDRemoteModeNone) && (hidRemoteMode != kHIDRemoteModeNone)) + { + kern_return_t kernReturn; + CFMutableDictionaryRef matchDict=NULL; + io_service_t rootService; + + do + { + // Get IOKit master port + kernReturn = IOMasterPort(bootstrap_port, &_masterPort); + if ((kernReturn!=kIOReturnSuccess) || (_masterPort==0)) { break; } + + // Setup notification port + _notifyPort = IONotificationPortCreate(_masterPort); + + if ((_notifyRLSource = IONotificationPortGetRunLoopSource(_notifyPort)) != NULL) + { + CFRunLoopAddSource( CFRunLoopGetCurrent(), + _notifyRLSource, + kCFRunLoopCommonModes); + } + else + { + break; + } + + // Setup SecureInput notification + if ((hidRemoteMode == kHIDRemoteModeExclusive) || (hidRemoteMode == kHIDRemoteModeExclusiveAuto)) + { + if ((rootService = IORegistryEntryFromPath(_masterPort, kIOServicePlane ":/")) != 0) + { + kernReturn = IOServiceAddInterestNotification( _notifyPort, + rootService, + kIOBusyInterest, + SecureInputNotificationCallback, + (void *)self, + |