1// 2// XBMCHelper.m 3// xbmchelper 4// 5// Created by Stephan Diederich on 11/12/08. 6// Copyright 2008 University Heidelberg. All rights reserved. 7// 8 9#import "XBMCHelper.h" 10#import "XBMCDebugHelpers.h" 11 12//---------------------------------------------------------------------------- 13//---------------------------------------------------------------------------- 14@interface XBMCHelper (private) 15 16- (NSString *)buttonNameForButtonCode:(HIDRemoteButtonCode)buttonCode; 17- (void) checkAndLaunchApp; 18 19@end 20 21@implementation XBMCHelper 22- (id) init{ 23 if( (self = [super init]) ){ 24 if ((remote = [HIDRemote sharedHIDRemote])) 25 { 26 [remote setDelegate:self]; 27 [remote setSimulateHoldEvents:NO]; 28 //for now, we're using lending of exclusive lock 29 //kHIDRemoteModeExclusiveAuto isn't working, as we're a background daemon 30 //one possibility would be to know when XBMC is running. Once we know that, 31 //we could acquire exclusive lock when it's running, and release _exclusive_ 32 //access once done 33 [remote setExclusiveLockLendingEnabled:YES]; 34 35 if ([HIDRemote isCandelairInstallationRequiredForRemoteMode:kHIDRemoteModeExclusive]) 36 { 37 //setup failed. user needs to install CandelaIR driver 38 NSLog(@"Error! Candelair driver installation necessary. XBMCHelper won't function properly!"); 39 NSLog(@"Due to an issue in the OS version you are running, an additional driver needs to be installed before XBMC(Helper) can reliably access the remote."); 40 NSLog(@"See http://www.candelair.com/download/ for details"); 41 return nil; 42 } 43 else 44 { 45 if ([remote startRemoteControl:kHIDRemoteModeExclusive]) 46 { 47 DLOG(@"Driver has started successfully."); 48 if ([remote activeRemoteControlCount]) 49 DLOG(@"Driver has found %d remotes.", [remote activeRemoteControlCount]); 50 else 51 ELOG(@"Driver has not found any remotes it could use. Will use remotes as they become available."); 52 } 53 else 54 { 55 ELOG(@"Failed to start remote control."); 56 //setup failed, cleanup 57 [remote setDelegate:nil]; 58 return nil; 59 } 60 } 61 } 62 } 63 return self; 64} 65 66//---------------------------------------------------------------------------- 67- (void) dealloc{ 68 [remote stopRemoteControl]; 69 if( [remote delegate] == self) 70 [remote setDelegate:nil]; 71} 72 73//---------------------------------------------------------------------------- 74- (void) connectToServer:(NSString*) fp_server onPort:(int) f_port withMode:(eRemoteMode) f_mode withTimeout:(double) f_timeout{ 75 if(mp_wrapper) 76 [self disconnect]; 77 mp_wrapper = [[XBMCClientWrapper alloc] initWithMode:f_mode serverAddress:fp_server port:f_port verbose:m_verbose]; 78 [mp_wrapper setUniversalModeTimeout:f_timeout]; 79} 80 81//---------------------------------------------------------------------------- 82- (void) disconnect{ 83 mp_wrapper = nil; 84} 85 86//---------------------------------------------------------------------------- 87- (void) enableVerboseMode:(bool) f_really{ 88 m_verbose = f_really; 89 [mp_wrapper enableVerboseMode:f_really]; 90} 91 92//---------------------------------------------------------------------------- 93- (void) setApplicationPath:(NSString*) fp_app_path{ 94 if (mp_app_path != fp_app_path) { 95 mp_app_path = [fp_app_path stringByStandardizingPath]; 96 } 97} 98 99//---------------------------------------------------------------------------- 100- (void) setApplicationHome:(NSString*) fp_home_path{ 101 if (mp_home_path != fp_home_path) { 102 mp_home_path = [fp_home_path stringByStandardizingPath]; 103 } 104} 105 106#pragma mark - 107#pragma mark HIDRemote delegate methods 108 109// Notification of button events 110- (void)hidRemote:(HIDRemote *)hidRemote eventWithButton:(HIDRemoteButtonCode)buttonCode 111 isPressed:(BOOL)isPressed fromHardwareWithAttributes:(NSMutableDictionary *)attributes 112{ 113 if(m_verbose){ 114 NSLog(@"Received button '%@' %@ event", [self buttonNameForButtonCode:buttonCode], (isPressed)?@"press":@"release"); 115 } 116 switch(buttonCode) 117 { 118 case kHIDRemoteButtonCodeUp: 119 if(isPressed) 120 [mp_wrapper handleEvent:ATV_BUTTON_UP]; 121 else 122 [mp_wrapper handleEvent:ATV_BUTTON_UP_RELEASE]; 123 break; 124 case kHIDRemoteButtonCodeDown: 125 if(isPressed) 126 [mp_wrapper handleEvent:ATV_BUTTON_DOWN]; 127 else 128 [mp_wrapper handleEvent:ATV_BUTTON_DOWN_RELEASE]; 129 break; 130 case kHIDRemoteButtonCodeLeft: 131 if(isPressed) 132 [mp_wrapper handleEvent:ATV_BUTTON_LEFT]; 133 else 134 [mp_wrapper handleEvent:ATV_BUTTON_LEFT_RELEASE]; 135 break; 136 case kHIDRemoteButtonCodeRight: 137 if(isPressed) 138 [mp_wrapper handleEvent:ATV_BUTTON_RIGHT]; 139 else 140 [mp_wrapper handleEvent:ATV_BUTTON_RIGHT_RELEASE]; 141 break; 142 case kHIDRemoteButtonCodeCenter: 143 if(isPressed) [mp_wrapper handleEvent:ATV_BUTTON_CENTER]; 144 break; 145 case kHIDRemoteButtonCodeMenu: 146 if(isPressed){ 147 [self checkAndLaunchApp]; //launch mp_app_path if it's not running 148 [mp_wrapper handleEvent:ATV_BUTTON_MENU]; 149 } 150 break; 151 case kHIDRemoteButtonCodePlay: //aluminium remote 152 if(isPressed) { 153 [mp_wrapper handleEvent:ATV_BUTTON_PLAY]; 154 } 155 break; 156// case kHIDRemoteButtonCodeUpHold: 157// //TODO 158// break; 159// case kHIDRemoteButtonCodeDownHold: 160// //TODO 161 break; 162 case kHIDRemoteButtonCodeLeftHold: 163 if(isPressed) 164 [mp_wrapper handleEvent:ATV_BUTTON_LEFT_H]; 165 else 166 [mp_wrapper handleEvent:ATV_BUTTON_LEFT_H_RELEASE]; 167 break; 168 case kHIDRemoteButtonCodeRightHold: 169 if(isPressed) 170 [mp_wrapper handleEvent:ATV_BUTTON_RIGHT_H]; 171 else 172 [mp_wrapper handleEvent:ATV_BUTTON_RIGHT_H_RELEASE]; 173 break; 174 case kHIDRemoteButtonCodeCenterHold: 175 if(isPressed) [mp_wrapper handleEvent:ATV_BUTTON_CENTER_H]; 176 break; 177 case kHIDRemoteButtonCodeMenuHold: 178 if(isPressed) { 179 [self checkAndLaunchApp]; //launch mp_app_path if it's not running 180 [mp_wrapper handleEvent:ATV_BUTTON_MENU_H]; 181 } 182 break; 183 case kHIDRemoteButtonCodePlayHold: //aluminium remote 184 if(isPressed) { 185 [mp_wrapper handleEvent:ATV_BUTTON_PLAY_H]; 186 } 187 break; 188 default: 189 NSLog(@"Oha, remote button not recognized %i pressed/released %i", buttonCode, isPressed); 190 } 191} 192 193 194// Notification of ID changes 195- (void)hidRemote:(HIDRemote *)hidRemote remoteIDChangedOldID:(SInt32)old 196 newID:(SInt32)newID forHardwareWithAttributes:(NSMutableDictionary *)attributes 197{ 198 if(m_verbose) 199 NSLog(@"Change of remote ID from %d to %d", old, newID); 200 [mp_wrapper switchRemote: newID]; 201 202} 203 204#pragma mark - 205#pragma mark Helper methods 206 207- (NSString *)buttonNameForButtonCode:(HIDRemoteButtonCode)buttonCode 208{ 209 switch (buttonCode) 210 { 211 case kHIDRemoteButtonCodePlus: 212 return (@"Plus"); 213 break; 214 case kHIDRemoteButtonCodeMinus: 215 return (@"Minus"); 216 break; 217 case kHIDRemoteButtonCodeLeft: 218 return (@"Left"); 219 break; 220 case kHIDRemoteButtonCodeRight: 221 return (@"Right"); 222 break; 223 case kHIDRemoteButtonCodePlayPause: 224 return (@"Play/Pause"); 225 break; 226 case kHIDRemoteButtonCodeMenu: 227 return (@"Menu"); 228 break; 229 case kHIDRemoteButtonCodePlusHold: 230 return (@"Plus (hold)"); 231 break; 232 case kHIDRemoteButtonCodeMinusHold: 233 return (@"Minus (hold)"); 234 break; 235 case kHIDRemoteButtonCodeLeftHold: 236 return (@"Left (hold)"); 237 break; 238 case kHIDRemoteButtonCodeRightHold: 239 return (@"Right (hold)"); 240 break; 241 case kHIDRemoteButtonCodePlayPauseHold: 242 return (@"Play/Pause (hold)"); 243 break; 244 case kHIDRemoteButtonCodeMenuHold: 245 return (@"Menu (hold)"); 246 break; 247 case kHIDRemoteButtonCodePlay: 248 return (@"Play"); 249 break; 250 default: 251 break; 252 } 253 return ([NSString stringWithFormat:@"Button %x", (int)buttonCode]); 254} 255 256//---------------------------------------------------------------------------- 257- (void) checkAndLaunchApp 258{ 259 if(!mp_app_path || ![mp_app_path length]){ 260 ELOG(@"No executable set. Nothing to launch"); 261 return; 262 } 263 NSFileManager *fileManager = [NSFileManager defaultManager]; 264 if([fileManager fileExistsAtPath:mp_app_path]){ 265 if(mp_home_path && [mp_home_path length]) 266 setenv("KODI_HOME", [mp_home_path UTF8String], 1); 267 //launch or activate xbmc 268 if(![[NSWorkspace sharedWorkspace] launchApplication:mp_app_path]) 269 ELOG(@"Error launching %@", mp_app_path); 270 } else 271 ELOG(@"Path does not exist: %@. Cannot launch executable", mp_app_path); 272} 273 274 275#pragma mark - 276#pragma mark Other (unused) HIDRemoteDelegate methods 277//- (BOOL)hidRemote:(HIDRemote *)aHidRemote 278//lendExclusiveLockToApplicationWithInfo:(NSDictionary *)applicationInfo 279//{ 280// NSLog(@"Lending exclusive lock to %@ (pid %@)", [applicationInfo objectForKey:(id)kCFBundleIdentifierKey], [applicationInfo objectForKey:kHIDRemoteDNStatusPIDKey]); 281// return (YES); 282//} 283// 284//- (void)hidRemote:(HIDRemote *)aHidRemote 285//exclusiveLockReleasedByApplicationWithInfo:(NSDictionary *)applicationInfo 286//{ 287// NSLog(@"Exclusive lock released by %@ (pid %@)", [applicationInfo objectForKey:(id)kCFBundleIdentifierKey], [applicationInfo objectForKey:kHIDRemoteDNStatusPIDKey]); 288// [aHidRemote startRemoteControl:kHIDRemoteModeExclusive]; 289//} 290// 291//- (BOOL)hidRemote:(HIDRemote *)aHidRemote 292//shouldRetryExclusiveLockWithInfo:(NSDictionary *)applicationInfo 293//{ 294// NSLog(@"%@ (pid %@) says I should retry to acquire exclusive locks", [applicationInfo objectForKey:(id)kCFBundleIdentifierKey], [applicationInfo objectForKey:kHIDRemoteDNStatusPIDKey]); 295// return (YES); 296//} 297// 298// 299//// Notification about hardware additions/removals 300//- (void)hidRemote:(HIDRemote *)aHidRemote foundNewHardwareWithAttributes:(NSMutableDictionary *)attributes 301//{ 302// NSLog(@"Found hardware: %@ by %@ (Transport: %@)", [attributes objectForKey:kHIDRemoteProduct], [attributes objectForKey:kHIDRemoteManufacturer], [attributes objectForKey:kHIDRemoteTransport]); 303//} 304// 305//- (void)hidRemote:(HIDRemote *)aHidRemote failedNewHardwareWithError:(NSError *)error 306//{ 307// NSLog(@"Initialization of hardware failed with error %@ (%@)", [error localizedDescription], [[error userInfo] objectForKey:@"InternalErrorCode"]); 308//} 309// 310//- (void)hidRemote:(HIDRemote *)aHidRemote releasedHardwareWithAttributes:(NSMutableDictionary *)attributes 311//{ 312// NSLog(@"Released hardware: %@ by %@ (Transport: %@)", [attributes objectForKey:kHIDRemoteProduct], [attributes objectForKey:kHIDRemoteManufacturer], [attributes objectForKey:kHIDRemoteTransport]); 313//} 314 315@end 316