diff --git a/XBMC-ATV2.xcodeproj/project.pbxproj b/XBMC-ATV2.xcodeproj/project.pbxproj index 426c628b84c26..12c3c2fc173cc 100644 --- a/XBMC-ATV2.xcodeproj/project.pbxproj +++ b/XBMC-ATV2.xcodeproj/project.pbxproj @@ -894,7 +894,7 @@ F56C7B8A131EC155000AD0F6 /* ViewDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C780C131EC154000AD0F6 /* ViewDatabase.cpp */; }; F56C7B8B131EC155000AD0F6 /* XBApplicationEx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C780E131EC154000AD0F6 /* XBApplicationEx.cpp */; }; F56C7B9B131EC1B4000AD0F6 /* AutoPool.mm in Sources */ = {isa = PBXBuildFile; fileRef = F56C7B9A131EC1B4000AD0F6 /* AutoPool.mm */; }; - F56C7BC9131EC2DB000AD0F6 /* XBMCAppliance.m in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BC2131EC2DB000AD0F6 /* XBMCAppliance.m */; }; + F56C7BC9131EC2DB000AD0F6 /* XBMCAppliance.mm in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BC2131EC2DB000AD0F6 /* XBMCAppliance.mm */; }; F56C7BCA131EC2DB000AD0F6 /* XBMCController.mm in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BC5131EC2DB000AD0F6 /* XBMCController.mm */; }; F56C7BD0131EC301000AD0F6 /* XBMC.png in Resources */ = {isa = PBXBuildFile; fileRef = F56C7BCD131EC301000AD0F6 /* XBMC.png */; }; F56C7BDC131EC390000AD0F6 /* WinEventsIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BD9131EC390000AD0F6 /* WinEventsIOS.mm */; }; @@ -2967,7 +2967,7 @@ F56C780F131EC154000AD0F6 /* XBApplicationEx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBApplicationEx.h; sourceTree = ""; }; F56C7B99131EC1B4000AD0F6 /* AutoPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoPool.h; sourceTree = ""; }; F56C7B9A131EC1B4000AD0F6 /* AutoPool.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoPool.mm; sourceTree = ""; }; - F56C7BC2131EC2DB000AD0F6 /* XBMCAppliance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBMCAppliance.m; sourceTree = ""; }; + F56C7BC2131EC2DB000AD0F6 /* XBMCAppliance.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = XBMCAppliance.mm; sourceTree = ""; }; F56C7BC3131EC2DB000AD0F6 /* XBMCAppliance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMCAppliance.h; sourceTree = ""; }; F56C7BC4131EC2DB000AD0F6 /* XBMCController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMCController.h; sourceTree = ""; }; F56C7BC5131EC2DB000AD0F6 /* XBMCController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = XBMCController.mm; sourceTree = ""; }; @@ -5997,7 +5997,7 @@ F56C7F2D131F0BB4000AD0F6 /* English.lproj */, F56C7BCD131EC301000AD0F6 /* XBMC.png */, F56C7BC3131EC2DB000AD0F6 /* XBMCAppliance.h */, - F56C7BC2131EC2DB000AD0F6 /* XBMCAppliance.m */, + F56C7BC2131EC2DB000AD0F6 /* XBMCAppliance.mm */, F56C7BCE131EC301000AD0F6 /* XBMCATV2-Info.plist */, F56C7BC4131EC2DB000AD0F6 /* XBMCController.h */, F56C7BC5131EC2DB000AD0F6 /* XBMCController.mm */, @@ -7309,7 +7309,7 @@ F56C7B8A131EC155000AD0F6 /* ViewDatabase.cpp in Sources */, F56C7B8B131EC155000AD0F6 /* XBApplicationEx.cpp in Sources */, F56C7B9B131EC1B4000AD0F6 /* AutoPool.mm in Sources */, - F56C7BC9131EC2DB000AD0F6 /* XBMCAppliance.m in Sources */, + F56C7BC9131EC2DB000AD0F6 /* XBMCAppliance.mm in Sources */, F56C7BCA131EC2DB000AD0F6 /* XBMCController.mm in Sources */, F56C7BDC131EC390000AD0F6 /* WinEventsIOS.mm in Sources */, F56C7BDD131EC390000AD0F6 /* WinSystemIOS.mm in Sources */, diff --git a/xbmc/osx/atv2/XBMCAppliance.h b/xbmc/osx/atv2/XBMCAppliance.h index f5ec705590e58..ba52ba8caa775 100644 --- a/xbmc/osx/atv2/XBMCAppliance.h +++ b/xbmc/osx/atv2/XBMCAppliance.h @@ -28,4 +28,10 @@ XBMCTopShelfController *_topShelfController; } @property(nonatomic, readonly, retain) id topShelfController; + +- (id) initWithApplianceInfo:(id) applianceInfo; +- (void) setTopShelfController:(id) topShelfControl; +- (void) setApplianceCategories:(id) applianceCategories; +- (void) XBMCfixUIDevice; +- (id) init; @end diff --git a/xbmc/osx/atv2/XBMCAppliance.m b/xbmc/osx/atv2/XBMCAppliance.m deleted file mode 100644 index 218687922b0be..0000000000000 --- a/xbmc/osx/atv2/XBMCAppliance.m +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2010-2012 Team XBMC - * http://www.xbmc.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBMC; see the file COPYING. If not, see - * . - * - */ - -#import -#import -#import -// objc-runtime.h is missing from iPhoneOS4.2SDK but present in iPhoneSimulator4.2.sdk -// pull it from runtime system for now -#import "/usr/include/objc/objc-runtime.h" - -#import "XBMCAppliance.h" -#import "XBMCController.h" - -#define XBMCAppliance_CAT [BRApplianceCategory categoryWithName:@"XBMC" identifier:@"xbmc" preferredOrder:-5] - -// ATVVersionInfo declare to shut up compiler warning -@interface ATVVersionInfo : NSObject -{ -} -+ (id)currentOSVersion; -@end -//-------------------------------------------------------------- -//-------------------------------------------------------------- -@interface BRTopShelfView (specialAdditions) -// -- (BRImageControl *)productImage; - -@end - -@implementation BRTopShelfView (specialAdditions) -- (BRImageControl *)productImage -{ - Ivar ivar = object_getInstanceVariable(self, "_productImage", NULL); - id result = object_getIvar(self, ivar); - return result; -} -@end - -//-------------------------------------------------------------- -//-------------------------------------------------------------- -@interface XBMCTopShelfController : NSObject -{ -} -- (void) selectCategoryWithIdentifier:(id)identifier; -- (id) topShelfView; -// added in 4.1+ -- (void) refresh; -@end - -@implementation XBMCTopShelfController -// -- (void) selectCategoryWithIdentifier:(id)identifier -{ -} - -- (BRTopShelfView *)topShelfView { - BRTopShelfView *topShelf = [[BRTopShelfView alloc] init]; - BRImageControl *imageControl = [topShelf productImage]; - BRImage *gpImage = [BRImage imageWithPath:[[NSBundle bundleForClass:[XBMCAppliance class]] pathForResource:@"XBMC" ofType:@"png"]]; - [imageControl setImage:gpImage]; - - return topShelf; -} -- (void) refresh -{ -} -@end - -//-------------------------------------------------------------- -//-------------------------------------------------------------- -@implementation XBMCAppliance -@synthesize topShelfController=_topShelfController; - --(void)XBMCfixUIDevice -{ - // iOS 5.x has removed the internal load of UIKit in AppleTV app - // and there is an overlap of some UIKit and AppleTV methods. - // This voodoo seems to clear up the wonkiness. :) - Class cls = NSClassFromString(@"ATVVersionInfo"); - if (cls != nil && [[cls currentOSVersion] rangeOfString:@"5."].location != NSNotFound) - { - id cd = nil; - - @try - { - cd = [UIDevice currentDevice]; - } - - @catch (NSException *e) - { - NSLog(@"exception: %@", e); - } - - @finally - { - //NSLog(@"will it work the second try?"); - cd = [UIDevice currentDevice]; - NSLog(@"current device fixed: %@", cd); - } - } -} - --(id) init -{ - //NSLog(@"%s", __PRETTY_FUNCTION__); - - if ((self = [super init]) != nil) - { - _topShelfController = [[XBMCTopShelfController alloc] init]; - _applianceCategories = [[NSArray alloc] initWithObjects:XBMCAppliance_CAT ,nil]; - } - - return self; -} - -- (void) dealloc -{ - //NSLog(@"%s", __PRETTY_FUNCTION__); - - [_applianceCategories release]; - [_topShelfController release]; - - [super dealloc]; -} - -- (id) applianceCategories -{ - // on ios 5.x this gets called whenever a user hits the xbmc icon - // in the frontrow mainmenu - // we use this indication for faking the "select" key. - // This leads to a one click start of XBMC instead of needing - // to hit select on the only XBMC category called "XBMC" again ;) - Class cls = NSClassFromString(@"ATVVersionInfo"); - if (cls != nil && [[cls currentOSVersion] rangeOfString:@"5."].location != NSNotFound) - { - // eventaction 5 == kBREventRemoteActionPlay from XBMCController.m - // value == 1 meanse we pressed that key - BREvent *eventKeySelect = [BREvent eventWithAction:5 value:1]; - // when we suppress the sound below - // this will even suppress the initial click - // sound because this is threaded - // thats why we just play that first click sound - // directly here before suppressing the sounds - // and doing the fake click (which would result in an unwanted - // second click sound without that hack) - [BRSoundHandler playSound:1];// sound number 1 is the ios click sound - // ios >= 5 only - so ignore the compiler warning on older SDKs - // since we guarded that code with the currentOSVersion above - [BRSoundHandler setSoundSuppressed:TRUE]; - [[BRApplication sharedApplication] postEvent:eventKeySelect]; - } - - return _applianceCategories; -} - -- (id) identifierForContentAlias:(id)contentAlias -{ - return @"xbmc"; -} - -- (id) selectCategoryWithIdentifier:(id)ident -{ - //NSLog(@"eglv2:selecteCategoryWithIdentifier: %@", ident); - - return nil; -} -- (BOOL) handleObjectSelection:(id)fp8 userInfo:(id)fp12 -{ - //NSLog(@"%s", __PRETTY_FUNCTION__); - - return YES; -} - -- (id) applianceSpecificControllerForIdentifier:(id)arg1 args:(id)arg2 -{ - return nil; -} -- (BOOL) handlePlay:(id)play userInfo:(id)info -{ - //NSLog(@"%s", __PRETTY_FUNCTION__); - - return YES; -} - -- (id) controllerForIdentifier:(id)identifier args:(id)args -{ - //NSLog(@"%s", __PRETTY_FUNCTION__); - - [self XBMCfixUIDevice]; - XBMCController *controller = [[[XBMCController alloc] init] autorelease]; - //XBMCController *controller = [XBMCController sharedInstance]; - return controller; -} - -- (id) localizedSearchTitle { return @"xbmc"; } -- (id) applianceName { return @"xbmc"; } -- (id) moduleName { return @"xbmc"; } -- (id) applianceKey { return @"xbmc"; } - -@end - diff --git a/xbmc/osx/atv2/XBMCAppliance.mm b/xbmc/osx/atv2/XBMCAppliance.mm new file mode 100644 index 0000000000000..d2cc0dde415c8 --- /dev/null +++ b/xbmc/osx/atv2/XBMCAppliance.mm @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2010-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + + +/* HowTo code in this file: + * Since AppleTV/iOS6.x (atv2 version 5.2) Apple removed the AppleTV.framework and put all those classes into the + * AppleTV.app. So we can't use standard obj-c coding here anymore. Instead we need to use the obj-c runtime + * functions for subclassing and adding methods to our instances during runtime (hooking). + * + * 1. For implementing a method of a base class: + * a) declare it in the form like the others + * b) these methods need to be static and have XBMCAppliance* self, SEL _cmd (replace XBMCAppliance with the class the method gets implemented for) as minimum params. + * c) add the method to the XBMCAppliance.h for getting rid of the compiler warnings of unresponsive selectors (declare the method like done in the baseclass). + * d) in initApplianceRuntimeClasses exchange the base class implementation with ours by calling MSHookMessageEx + * e) if we need to call the base class implementation as well we have to save the original implementation (see initWithApplianceInfo$Orig for reference) + * + * 2. For implementing a new method which is not part of the base class: + * a) same as 1.a + * b) same as 1.b + * c) same as 1.c + * d) in initApplianceRuntimeClasses add the method to our class via class_addMethod + * + * 3. Never access any BackRow classes directly - but always get the class via objc_getClass - if the class is used in multiple places + * save it as static (see BRApplianceCategoryCls) + * + * 4. Keep the structure of this file based on the section comments (marked with // SECTIONCOMMENT). + * 5. really - obey 4.! + * + * 6. for adding class members use associated objects - see topShelfControllerKey + * + * For further reference see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html + */ + +#import +#import +// objc-runtime.h is missing from iPhoneOS4.2SDK but present in iPhoneSimulator4.2.sdk +// pull it from runtime system for now +#import "/usr/include/objc/objc-runtime.h" + +#import "XBMCAppliance.h" +#import "XBMCController.h" +#include "substrate.h" + +// SECTIONCOMMENT +// classes we need multiple times +static Class BRApplianceCategoryCls; + +// category for ios5.x and higher is just a short text before xbmc auto starts +#define XBMCAppliance_CAT_5andhigher [BRApplianceCategoryCls categoryWithName:@"XBMC is starting..." identifier:@"xbmc" preferredOrder:0] +// category for ios4.x is the menu entry +#define XBMCAppliance_CAT_4 [BRApplianceCategoryCls categoryWithName:@"XBMC" identifier:@"xbmc" preferredOrder:0] + +// SECTIONCOMMENT +// forward declaration all referenced classes +@class XBMCAppliance; +@class BRTopShelfView; +@class XBMCApplianceInfo; + +// SECTIONCOMMENT +// orig method handlers we wanna call in hooked methods +static id (*XBMCAppliance$initWithApplianceInfo$Orig)(XBMCAppliance*, SEL, id); +static id (*XBMCAppliance$init$Orig)(XBMCAppliance*, SEL); +static id (*XBMCAppliance$applianceInfo$Orig)(XBMCAppliance*, SEL); + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +// ATVVersionInfo declare to shut up compiler warning +@interface ATVVersionInfo : NSObject +{ +} ++ (id)currentOSVersion; + +@end + +@interface XBMCATV2Detector : NSObject{} ++ (BOOL) hasOldGui; ++ (BOOL) isIos5; ++ (BOOL) needsApplianceInfoHack; +@end + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +// We need a real implementation (not a runtime generated one) +// for getting our NSBundle instance by calling +// [[NSBundle bundleForClass:objc_getClass("XBMCATV2Detector")] +// so we just implement some usefull helpers here +// and use those +@implementation XBMCATV2Detector : NSObject{} ++ (BOOL) hasOldGui +{ + Class cls = NSClassFromString(@"ATVVersionInfo"); + if (cls != nil && [[cls currentOSVersion] rangeOfString:@"4."].location != NSNotFound) + return TRUE; + if (cls != nil && [[cls currentOSVersion] rangeOfString:@"5.0"].location != NSNotFound) + return TRUE; + return FALSE; +} + ++ (BOOL) isIos5 +{ + Class cls = NSClassFromString(@"ATVVersionInfo"); + if (cls != nil && [[cls currentOSVersion] rangeOfString:@"5."].location != NSNotFound) + return TRUE; + return FALSE; +} + ++ (BOOL) needsApplianceInfoHack +{ + // if the runtime base class (BRBaseAppliance) doesn't have the initWithApplianceInfo selector + // we need to hack the appliance info in (id) applianceInfo (XBMCAppliance$applianceInfo) + if (class_respondsToSelector(objc_getClass("BRBaseAppliance"),@selector(initWithApplianceInfo:))) + return FALSE; + return TRUE; +} +@end + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +// XBMCApplication declare to shut up compiler warning of BRApplication +@interface XBMCApplication : NSObject +{ +} +- (void)setFirstResponder:(id)responder; +@end + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +@interface XBMCTopShelfController : NSObject +{ +} +- (void) selectCategoryWithIdentifier:(id)identifier; +- (id) topShelfView; +// added in 4.1+ +- (void) refresh; +@end + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +@implementation XBMCTopShelfController + +- (void) selectCategoryWithIdentifier:(id)identifier +{ +} + +- (BRTopShelfView *)topShelfView +{ + Class cls = objc_getClass("BRTopShelfView"); + id topShelf = [[cls alloc] init]; + + // diddle the topshelf logo on old gui + if ([XBMCATV2Detector hasOldGui]) + { + Class cls = objc_getClass("BRImage"); + BRImageControl *imageControl = (BRImageControl *)MSHookIvar(topShelf, "_productImage");// hook the productImage so we can diddle with it + BRImage *gpImage = [cls imageWithPath:[[NSBundle bundleForClass:[XBMCATV2Detector class]] pathForResource:@"XBMC" ofType:@"png"]]; + [imageControl setImage:gpImage]; + } + + return topShelf; +} + +- (void) refresh +{ +} +@end + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +// SECTIONCOMMENT +// since we can't inject ivars we need to use associated objects +// these are the keys for XBMCAppliance +//implementation XBMCAppliance +static char const * const topShelfControllerKey = "topShelfController"; +static char const * const applianceCategoriesKey = "applianceCategories"; + +static NSString* XBMCApplianceInfo$key(XBMCApplianceInfo* self, SEL _cmd) +{ + return [[[NSBundle bundleForClass:objc_getClass("XBMCATV2Detector")] infoDictionary] objectForKey:(NSString*)kCFBundleIdentifierKey]; +} + +static NSString* XBMCApplianceInfo$name(XBMCApplianceInfo* self, SEL _cmd) +{ + return [[[NSBundle bundleForClass:objc_getClass("XBMCATV2Detector")] infoDictionary] objectForKey:(NSString*)kCFBundleNameKey]; +} + +static id XBMCApplianceInfo$localizedStringsFileName(XBMCApplianceInfo* self, SEL _cmd) +{ + return @"xbmc"; +} + +static void XBMCAppliance$XBMCfixUIDevice(XBMCAppliance* self, SEL _cmd) +{ + // iOS 5.x has removed the internal load of UIKit in AppleTV app + // and there is an overlap of some UIKit and AppleTV methods. + // This voodoo seems to clear up the wonkiness. :) + if ([XBMCATV2Detector isIos5]) + { + id cd = nil; + + @try + { + cd = [UIDevice currentDevice]; + } + + @catch (NSException *e) + { + NSLog(@"exception: %@", e); + } + + @finally + { + //NSLog(@"will it work the second try?"); + cd = [UIDevice currentDevice]; + NSLog(@"current device fixed: %@", cd); + } + } +} + + +static id XBMCAppliance$init(XBMCAppliance* self, SEL _cmd) +{ + //NSLog(@"%s", __PRETTY_FUNCTION__); + if ([XBMCATV2Detector needsApplianceInfoHack]) + { + NSLog(@"%s for ios 4", __PRETTY_FUNCTION__); + if ((self = XBMCAppliance$init$Orig(self, _cmd))!= nil) + { + id topShelfControl = [[XBMCTopShelfController alloc] init]; + [self setTopShelfController:topShelfControl]; + + NSArray *catArray = [[NSArray alloc] initWithObjects:XBMCAppliance_CAT_4,nil]; + [self setApplianceCategories:catArray]; + return self; + } + } + else// ios >= 5 + { + NSLog(@"%s for ios 5 and newer", __PRETTY_FUNCTION__); + return [self initWithApplianceInfo:nil]; // legacy for ios < 6 + } + return self; +} + +static id XBMCAppliance$identifierForContentAlias(XBMCAppliance* self, SEL _cmd, id contentAlias) +{ + return@"xbmc"; +} + +static BOOL XBMCAppliance$handleObjectSelection(XBMCAppliance* self, SEL _cmd, id fp8, id fp12) +{ + //NSLog(@"%s", __PRETTY_FUNCTION__); + return YES; +} + +static id XBMCAppliance$applianceInfo(XBMCAppliance* self, SEL _cmd) +{ + //NSLog(@"%s", __PRETTY_FUNCTION) + + // load our plist into memory and merge it with + // the dict from the baseclass if needed + // cause ios seems to fail on that somehow (at least on 4.x) + if ([XBMCATV2Detector needsApplianceInfoHack] && self != nil) + { + id original = XBMCAppliance$applianceInfo$Orig(self, _cmd); + id info = MSHookIvar(original, "_info");// hook the infoDictionary so we can diddle with it + + NSString *plistPath = [[NSBundle bundleForClass:objc_getClass("XBMCATV2Detector")] pathForResource:@"Info" ofType:@"plist"]; + NSString *bundlePath = [[NSBundle bundleForClass:objc_getClass("XBMCATV2Detector")] bundlePath]; + NSMutableDictionary *ourInfoDict = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath]; + + if (ourInfoDict != nil && bundlePath != nil) + { + // inject this or we won't get shown up properly on ios4 + [ourInfoDict setObject:bundlePath forKey:@"NSBundleInitialPath"]; + + // add our plist info to the baseclass info and return it + [(NSMutableDictionary *)info addEntriesFromDictionary:ourInfoDict]; + [ourInfoDict release]; + } + return original; + } + else + { + Class cls = objc_getClass("XBMCApplianceInfo"); + return [[[cls alloc] init] autorelease]; + } + return nil; +} + + +static id XBMCAppliance$topShelfController(XBMCAppliance* self, SEL _cmd) +{ + return objc_getAssociatedObject(self, topShelfControllerKey); +} + + +static void XBMCAppliance$setTopShelfController(XBMCAppliance* self, SEL _cmd, id topShelfControl) +{ + objc_setAssociatedObject(self, topShelfControllerKey, topShelfControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +static id XBMCAppliance$applianceCategories(XBMCAppliance* self, SEL _cmd) +{ + return objc_getAssociatedObject(self, applianceCategoriesKey); +} + +static void XBMCAppliance$setApplianceCategories(XBMCAppliance* self, SEL _cmd, id applianceCategories) +{ + objc_setAssociatedObject(self, applianceCategoriesKey, applianceCategories, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +static id XBMCAppliance$initWithApplianceInfo(XBMCAppliance* self, SEL _cmd, id applianceInfo) +{ + //NSLog(@"%s", __PRETTY_FUNCTION__); + if((self = XBMCAppliance$initWithApplianceInfo$Orig(self, _cmd, applianceInfo)) != nil) + { + id topShelfControl = [[XBMCTopShelfController alloc] init]; + [self setTopShelfController:topShelfControl]; + + NSArray *catArray = [[NSArray alloc] initWithObjects:XBMCAppliance_CAT_5andhigher,nil]; + [self setApplianceCategories:catArray]; + } + return self; +} + +static id XBMCAppliance$controllerForIdentifier(XBMCAppliance* self, SEL _cmd, id identifier, id args) +{ + //NSLog(@"%s", __PRETTY_FUNCTION__); + id menuController = nil; + Class cls = objc_getClass("BRApplication"); + if ([identifier isEqualToString:@"xbmc"]) + { + [self XBMCfixUIDevice]; + menuController = [[objc_getClass("XBMCController") alloc] init]; + if (menuController == nil) + NSLog(@"initialise controller - fail"); + } + XBMCApplication *brapp = (XBMCApplication *)[cls sharedApplication]; + [brapp setFirstResponder:menuController]; + return menuController; +} + +// SECTIONCOMMENT +// c'tor - this sets up our class at runtime by +// 1. subclassing from the base classes +// 2. adding new methods to our class +// 3. exchanging (hooking) base class methods with ours +// 4. register the classes to the objc runtime system +static __attribute__((constructor)) void initApplianceRuntimeClasses() +{ + // subclass BRApplianceInfo into XBMCApplianceInfo + Class XBMCApplianceInfoCls = objc_allocateClassPair(objc_getClass("BRApplianceInfo"), "XBMCApplianceInfo", 0); + + // and hook up our methods (implementation of the base class methods) + // XBMCApplianceInfo::key + MSHookMessageEx(XBMCApplianceInfoCls,@selector(key), (IMP)&XBMCApplianceInfo$key, nil); + // XBMCApplianceInfo::name + MSHookMessageEx(XBMCApplianceInfoCls,@selector(name), (IMP)&XBMCApplianceInfo$name, nil); + + //not available in ios4.x - so probe or crash&burn + if (class_respondsToSelector(objc_getClass("BRApplianceInfo"),@selector(localizedStringsFileName))) + { + // XBMCApplianceInfo::localizedStringsFileName + MSHookMessageEx(XBMCApplianceInfoCls,@selector(localizedStringsFileName), (IMP)&XBMCApplianceInfo$localizedStringsFileName, nil); + } + else// else we need to add it + { + class_addMethod(XBMCApplianceInfoCls,@selector(localizedStringsFileName), (IMP)&XBMCApplianceInfo$localizedStringsFileName, "@@:"); + } + // and register the class to the runtime + objc_registerClassPair(XBMCApplianceInfoCls); + + // subclass BRBaseAppliance into XBMCAppliance + Class XBMCApplianceCls = objc_allocateClassPair(objc_getClass("BRBaseAppliance"), "XBMCAppliance", 0); + // add our custom methods which are not part of the baseclass + // XBMCAppliance::XBMCfixUIDevice + class_addMethod(XBMCApplianceCls,@selector(XBMCfixUIDevice), (IMP)XBMCAppliance$XBMCfixUIDevice, "v@:"); + class_addMethod(XBMCApplianceCls,@selector(setTopShelfController:), (IMP)&XBMCAppliance$setTopShelfController, "v@:@"); + class_addMethod(XBMCApplianceCls,@selector(setApplianceCategories:), (IMP)&XBMCAppliance$setApplianceCategories, "v@:@"); + + // and hook up our methods (implementation of the base class methods) + // XBMCAppliance::init + MSHookMessageEx(XBMCApplianceCls,@selector(init), (IMP)&XBMCAppliance$init, (IMP*)&XBMCAppliance$init$Orig); + // XBMCAppliance::identifierForContentAlias + MSHookMessageEx(XBMCApplianceCls,@selector(identifierForContentAlias:), (IMP)&XBMCAppliance$identifierForContentAlias, nil); + + // not there in ios6 - probing for getting rid of the syslog warning + if (class_respondsToSelector(objc_getClass("BRBaseAppliance"),@selector(handleObjectSelection:userInfo:))) + { + // XBMCAppliance::handleObjectSelection + MSHookMessageEx(XBMCApplianceCls,@selector(handleObjectSelection:userInfo:), (IMP)&XBMCAppliance$handleObjectSelection, nil); + } + + // XBMCAppliance::applianceInfo + MSHookMessageEx(XBMCApplianceCls,@selector(applianceInfo), (IMP)&XBMCAppliance$applianceInfo, (IMP *)&XBMCAppliance$applianceInfo$Orig); + // XBMCAppliance::topShelfController + MSHookMessageEx(XBMCApplianceCls,@selector(topShelfController), (IMP)&XBMCAppliance$topShelfController, nil); + // XBMCAppliance::applianceCategories + MSHookMessageEx(XBMCApplianceCls,@selector(applianceCategories), (IMP)&XBMCAppliance$applianceCategories, nil); + + // not there on ios 4.x - probing ... + if (class_respondsToSelector(objc_getClass("BRBaseAppliance"),@selector(initWithApplianceInfo:))) + { + // XBMCAppliance::initWithApplianceInfo + MSHookMessageEx(XBMCApplianceCls,@selector(initWithApplianceInfo:), (IMP)&XBMCAppliance$initWithApplianceInfo, (IMP*)&XBMCAppliance$initWithApplianceInfo$Orig); + } + + // XBMCAppliance::controllerForIdentifier + MSHookMessageEx(XBMCApplianceCls,@selector(controllerForIdentifier:args:), (IMP)&XBMCAppliance$controllerForIdentifier, nil); + + // and register the class to the runtime + objc_registerClassPair(XBMCApplianceCls); + + // save this as static for referencing it in the macro at the top of the file + BRApplianceCategoryCls = objc_getClass("BRApplianceCategory"); +} \ No newline at end of file diff --git a/xbmc/osx/atv2/XBMCController.h b/xbmc/osx/atv2/XBMCController.h index 2e0e0cf1f1546..141857f29c648 100644 --- a/xbmc/osx/atv2/XBMCController.h +++ b/xbmc/osx/atv2/XBMCController.h @@ -58,7 +58,18 @@ - (void) stopAnimation; - (bool) changeScreen: (unsigned int)screenIdx withMode:(UIScreenMode *)mode; - (void) activateScreen: (UIScreen *)screen; - +- (id) glView; +- (void) setGlView:(id)view; +- (BOOL) ATVClientEventFromBREvent:(id)event Repeatable:(bool *)isRepeatable ButtonState:(bool *)isPressed Result:(int *)xbmc_ir_key; +- (void) setUserEvent:(int) eventId withHoldTime:(unsigned int) holdTime; +- (void) startKeyPressTimer:(int) keyId; +- (void) stopKeyPressTimer; +- (void) setSystemSleepTimeout:(id) timeout; +- (id) systemSleepTimeout; +- (void) setKeyTimer:(id) timer; +- (id) keyTimer; +- (void) setSystemScreenSaverTimeout:(id) timeout; +- (id) systemScreenSaverTimeout; @end diff --git a/xbmc/osx/atv2/XBMCController.mm b/xbmc/osx/atv2/XBMCController.mm index 0dacb036dd92b..bf3b63c93de95 100644 --- a/xbmc/osx/atv2/XBMCController.mm +++ b/xbmc/osx/atv2/XBMCController.mm @@ -18,6 +18,36 @@ * */ + +/* HowTo code in this file: + * Since AppleTV/iOS6.x (atv2 version 5.2) Apple removed the AppleTV.framework and put all those classes into the + * AppleTV.app. So we can't use standard obj-c coding here anymore. Instead we need to use the obj-c runtime + * functions for subclassing and adding methods to our instances during runtime (hooking). + * + * 1. For implementing a method of a base class: + * a) declare it in the form like the others + * b) these methods need to be static and have XBMCController* self, SEL _cmd (replace XBMCAppliance with the class the method gets implemented for) as minimum params. + * c) add the method to the XBMCController.h for getting rid of the compiler warnings of unresponsive selectors (declare the method like done in the baseclass). + * d) in initControllerRuntimeClasses exchange the base class implementation with ours by calling MSHookMessageEx + * e) if we need to call the base class implementation as well we have to save the original implementation (see brEventAction$Orig for reference) + * + * 2. For implementing a new method which is not part of the base class: + * a) same as 1.a + * b) same as 1.b + * c) same as 1.c + * d) in initControllerRuntimeClasses add the method to our class via class_addMethod + * + * 3. Never access any BackRow classes directly - but always get the class via objc_getClass - if the class is used in multiple places + * save it as static (see BRWindowCls) + * + * 4. Keep the structure of this file based on the section comments (marked with // SECTIONCOMMENT). + * 5. really - obey 4.! + * + * 6. for adding class members use associated objects - see timerKey + * + * For further reference see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html + */ + //hack around problem with xbmc's typedef int BOOL // and obj-c's typedef unsigned char BOOL #define BOOL XBMC_BOOL @@ -31,13 +61,17 @@ #import #import -#import #import "XBMCController.h" #import "XBMCDebugHelpers.h" +#import "IOSEAGLView.h" +#import "IOSSCreenManager.h" +#include "XBMC_keysym.h" +#include "substrate.h" + //start repeating after 0.5s -#define REPEATED_KEYPRESS_DELAY_S 0.5 +#define REPEATED_KEYPRESS_DELAY_S 0.5 //pause 0.01s (10ms) between keypresses #define REPEATED_KEYPRESS_PAUSE_S 0.01 @@ -58,7 +92,7 @@ ATV_ALUMINIUM_PLAY = 12, ATV_ALUMINIUM_PLAY_H = 11, - //newly added remote buttons + //newly added remote buttons ATV_BUTTON_PAGEUP = 13, ATV_BUTTON_PAGEDOWN = 14, ATV_BUTTON_PAUSE = 15, @@ -102,6 +136,7 @@ ATV_INVALID_BUTTON } eATVClientEvent; + typedef enum { // for originator kBREventOriginatorRemote kBREventRemoteActionMenu = 1, @@ -157,410 +192,504 @@ kBREventRemoteActionHoldDown, } BREventRemoteAction; + XBMCController *g_xbmcController; //-------------------------------------------------------------- // so we don't have to include AppleTV.frameworks/PrivateHeaders/ATVSettingsFacade.h -@interface ATVSettingsFacade : BRSettingsFacade {} +@interface XBMCSettingsFacade : NSObject -(int)screenSaverTimeout; -(void)setScreenSaverTimeout:(int) f_timeout; -(void)setSleepTimeout:(int)timeout; -(int)sleepTimeout; +-(void)flushDiskChanges; @end // notification messages extern NSString* kBRScreenSaverActivated; extern NSString* kBRScreenSaverDismissed; + //-------------------------------------------------------------- //-------------------------------------------------------------- -@interface XBMCController (PrivateMethods) -- (void) observeDefaultCenterStuff: (NSNotification *) notification; -- (void) keyPressTimerCallback: (NSTimer*)theTimer; -- (void) startKeyPressTimer: (int) keyId; -- (void) stopKeyPressTimer; -- (void) setUserEvent:(int) id withHoldTime:(unsigned int) holdTime; -@end +// SECTIONCOMMENT +// orig method handlers we wanna call in hooked methods ([super method]) +static BOOL (*XBMCController$brEventAction$Orig)(XBMCController*, SEL, BREvent*); +static id (*XBMCController$init$Orig)(XBMCController*, SEL); +static void (*XBMCController$dealloc$Orig)(XBMCController*, SEL); +static void (*XBMCController$controlWasActivated$Orig)(XBMCController*, SEL); +static void (*XBMCController$controlWasDeactivated$Orig)(XBMCController*, SEL); + +// SECTIONCOMMENT +// classes we need multiple times +static Class BRWindowCls; + +int padding[16];//obsolete? - was commented with "credit is due here to SapphireCompatibilityClasses!!" + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +// SECTIONCOMMENT +// since we can't inject ivars we need to use associated objects +// these are the keys for XBMCController +static char const * const timerKey = "m_keyTimer"; +static char const * const glviewKey = "m_glView"; +static char const * const screensaverKey = "m_screenSaverTimeout"; +static char const * const systemsleepKey = "m_systemsleepTimeout"; + // // -@implementation XBMCController +// SECTIONCOMMENT +//implementation XBMCController + +static id XBMCController$keyTimer(XBMCController* self, SEL _cmd) +{ + return objc_getAssociatedObject(self, timerKey); +} -/* -+ (XBMCController*) sharedInstance -{ - // the instance of this class is stored here - static XBMCController *myInstance = nil; +static void XBMCController$setKeyTimer(XBMCController* self, SEL _cmd, id timer) +{ + objc_setAssociatedObject(self, timerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} - // check to see if an instance already exists - if (nil == myInstance) - myInstance = [[[[self class] alloc] init] autorelease]; +static id XBMCController$glView(XBMCController* self, SEL _cmd) +{ + return objc_getAssociatedObject(self, glviewKey); +} + +static void XBMCController$setGlView(XBMCController* self, SEL _cmd, id view) +{ + objc_setAssociatedObject(self, glviewKey, view, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} - // return the instance of this class - return myInstance; +static id XBMCController$systemScreenSaverTimeout(XBMCController* self, SEL _cmd) +{ + return objc_getAssociatedObject(self, screensaverKey); } -*/ -- (void) applicationDidExit +static void XBMCController$setSystemScreenSaverTimeout(XBMCController* self, SEL _cmd, id timeout) +{ + objc_setAssociatedObject(self, screensaverKey, timeout, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +static id XBMCController$systemSleepTimeout(XBMCController* self, SEL _cmd) +{ + return objc_getAssociatedObject(self, systemsleepKey); +} + +static void XBMCController$setSystemSleepTimeout(XBMCController* self, SEL _cmd, id timeout) +{ + objc_setAssociatedObject(self, systemsleepKey, timeout, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +static void XBMCController$applicationDidExit(XBMCController* self, SEL _cmd) { - [m_glView stopAnimation]; + //NSLog(@"%s", __PRETTY_FUNCTION__); + [[self glView] stopAnimation]; [self enableScreenSaver]; [self enableSystemSleep]; - [[self stack] popController]; } -- (void) initDisplayLink + +static void XBMCController$initDisplayLink(XBMCController* self, SEL _cmd) { - [m_glView initDisplayLink]; + //NSLog(@"%s", __PRETTY_FUNCTION__); + + [[self glView] initDisplayLink]; } -- (void) deinitDisplayLink + +static void XBMCController$deinitDisplayLink(XBMCController* self, SEL _cmd) { - [m_glView deinitDisplayLink]; + //NSLog(@"%s", __PRETTY_FUNCTION__); + + [[self glView] deinitDisplayLink]; } -- (double) getDisplayLinkFPS + +static double XBMCController$getDisplayLinkFPS(XBMCController* self, SEL _cmd) { - return [m_glView getDisplayLinkFPS]; + //NSLog(@"%s", __PRETTY_FUNCTION__); + + return [[self glView] getDisplayLinkFPS]; } -- (void) setFramebuffer -{ - [m_glView setFramebuffer]; + +static void XBMCController$setFramebuffer(XBMCController* self, SEL _cmd) +{ + [[self glView] setFramebuffer]; } -- (bool) presentFramebuffer -{ - return [m_glView presentFramebuffer]; + +static bool XBMCController$presentFramebuffer(XBMCController* self, SEL _cmd) +{ + return [[self glView] presentFramebuffer]; } -- (CGSize) getScreenSize + +static CGSize XBMCController$getScreenSize(XBMCController* self, SEL _cmd) { CGSize screensize; - - screensize.width = [BRWindow interfaceFrame].size.width; - screensize.height = [BRWindow interfaceFrame].size.height; - + screensize.width = [BRWindowCls interfaceFrame].size.width; + screensize.height = [BRWindowCls interfaceFrame].size.height; //NSLog(@"%s UpdateResolutions width=%f, height=%f", - // __PRETTY_FUNCTION__, screensize.width, screensize.height); - + //__PRETTY_FUNCTION__, screensize.width, screensize.height); return screensize; } -- (void) sendKey: (XBMCKey) key + +static void XBMCController$sendKey(XBMCController* self, SEL _cmd, XBMCKey key) { //empty because its not used here. Only implemented for getting rid //of "may not respond to selector" compile warnings in IOSExternalTouchController } - -- (id) init -{ - //NSLog(@"%s", __PRETTY_FUNCTION__); - - self = [super init]; - if ( !self ) - return ( nil ); - - NSNotificationCenter *center; - // first the default notification center, which is all - // notifications that only happen inside of our program - center = [NSNotificationCenter defaultCenter]; - [center addObserver: self - selector: @selector(observeDefaultCenterStuff:) - name: nil - object: nil]; - - m_glView = [[IOSEAGLView alloc] initWithFrame:[BRWindow interfaceFrame] withScreen:[UIScreen mainScreen]]; - [[IOSScreenManager sharedInstance] setView:m_glView]; - - g_xbmcController = self; - +static id XBMCController$init(XBMCController* self, SEL _cmd) +{ + if((self = XBMCController$init$Orig(self, _cmd)) != nil) + { + //NSLog(@"%s", __PRETTY_FUNCTION__); + + NSNotificationCenter *center; + // first the default notification center, which is all + // notifications that only happen inside of our program + center = [NSNotificationCenter defaultCenter]; + [center addObserver: self + selector: @selector(observeDefaultCenterStuff:) + name: nil + object: nil]; + + IOSEAGLView *view = [[IOSEAGLView alloc] initWithFrame:[BRWindowCls interfaceFrame] withScreen:[UIScreen mainScreen]]; + [self setGlView:view]; + + [[IOSScreenManager sharedInstance] setView:[self glView]]; + + g_xbmcController = self; + } return self; } -- (void)dealloc +static void XBMCController$dealloc(XBMCController* self, SEL _cmd) { //NSLog(@"%s", __PRETTY_FUNCTION__); - [m_glView stopAnimation]; - [m_glView release]; - + [[self glView] stopAnimation]; + [[self glView] release]; NSNotificationCenter *center; // take us off the default center for our app center = [NSNotificationCenter defaultCenter]; [center removeObserver: self]; - [super dealloc]; + XBMCController$dealloc$Orig(self, _cmd); } -- (void)controlWasActivated +static void XBMCController$controlWasActivated(XBMCController* self, SEL _cmd) { //NSLog(@"%s", __PRETTY_FUNCTION__); - - [super controlWasActivated]; + + XBMCController$controlWasActivated$Orig(self, _cmd); [self disableSystemSleep]; [self disableScreenSaver]; - + + IOSEAGLView *view = [self glView]; //inject our gles layer into the backrow root layer - [[BRWindow rootLayer] addSublayer:m_glView.layer]; + [[BRWindowCls rootLayer] addSublayer:view.layer]; - [m_glView startAnimation]; + [[self glView] startAnimation]; } -- (void)controlWasDeactivated +static void XBMCController$controlWasDeactivated(XBMCController* self, SEL _cmd) { NSLog(@"XBMC was forced by FrontRow to exit via controlWasDeactivated"); - [m_glView stopAnimation]; - [m_glView.layer removeFromSuperlayer]; + [[self glView] stopAnimation]; + [[[self glView] layer] removeFromSuperlayer]; [self enableScreenSaver]; [self enableSystemSleep]; - [super controlWasDeactivated]; + XBMCController$controlWasDeactivated$Orig(self, _cmd); } -- (BOOL) recreateOnReselect -{ +static BOOL XBMCController$recreateOnReselect(XBMCController* self, SEL _cmd) +{ //NSLog(@"%s", __PRETTY_FUNCTION__); return YES; } -- (eATVClientEvent) ATVClientEventFromBREvent:(BREvent*) f_event - Repeatable:(bool &) isRepeatable - ButtonState:(bool &) isPressed +static void XBMCController$ATVClientEventFromBREvent(XBMCController* self, SEL _cmd, BREvent* f_event, bool * isRepeatable, bool * isPressed, int * result) { + if(f_event == nil)// paranoia + return; + int remoteAction = [f_event remoteAction]; CLog::Log(LOGDEBUG,"XBMCPureController: Button press remoteAction = %i", remoteAction); - isRepeatable = false; - isPressed = false; + *isRepeatable = false; + *isPressed = false; switch (remoteAction) { // tap up case kBREventRemoteActionUp: case 65676: - isRepeatable = true; + *isRepeatable = true; if([f_event value] == 1) - isPressed = true; - return ATV_BUTTON_UP; + *isPressed = true; + *result = ATV_BUTTON_UP; + return; // tap down case kBREventRemoteActionDown: case 65677: - isRepeatable = true; + *isRepeatable = true; if([f_event value] == 1) - isPressed = true; - return ATV_BUTTON_DOWN; - + *isPressed = true; + *result = ATV_BUTTON_DOWN; + return; + // tap left case kBREventRemoteActionLeft: case 65675: - isRepeatable = true; + *isRepeatable = true; if([f_event value] == 1) - isPressed = true; - return ATV_BUTTON_LEFT; - + *isPressed = true; + *result = ATV_BUTTON_LEFT; + return; + // hold left case 786612: if([f_event value] == 1) - return ATV_LEARNED_REWIND; + *result = ATV_LEARNED_REWIND; else - return ATV_INVALID_BUTTON; - + *result = ATV_INVALID_BUTTON; + return; + // tap right case kBREventRemoteActionRight: case 65674: - isRepeatable = true; + *isRepeatable = true; if ([f_event value] == 1) - isPressed = true; - return ATV_BUTTON_RIGHT; - + *isPressed = true; + *result = ATV_BUTTON_RIGHT; + return ; + // hold right case 786611: if ([f_event value] == 1) - return ATV_LEARNED_FORWARD; + *result = ATV_LEARNED_FORWARD; else - return ATV_INVALID_BUTTON; - + *result = ATV_INVALID_BUTTON; + return ; + // tap play case kBREventRemoteActionPlay: case 65673: - return ATV_BUTTON_PLAY; - + *result = ATV_BUTTON_PLAY; + return ; + // hold play case kBREventRemoteActionPlayHold: case kBREventRemoteActionCenterHold: case kBREventRemoteActionCenterHold42: case 65668: - return ATV_BUTTON_PLAY_H; - + *result = ATV_BUTTON_PLAY_H; + return ; + // menu case kBREventRemoteActionMenu: case 65670: - return ATV_BUTTON_MENU; - + *result = ATV_BUTTON_MENU; + return ; + // hold menu case kBREventRemoteActionMenuHold: case 786496: - return ATV_BUTTON_MENU_H; - + *result = ATV_BUTTON_MENU_H; + return ; + // learned play case 786608: - return ATV_LEARNED_PLAY; - + *result = ATV_LEARNED_PLAY; + return ; + // learned pause case 786609: - return ATV_LEARNED_PAUSE; - + *result = ATV_LEARNED_PAUSE; + return ; + // learned stop case 786615: - return ATV_LEARNED_STOP; - + *result = ATV_LEARNED_STOP; + return ; + // learned next case 786613: - return ATV_LEARNED_NEXT; - + *result = ATV_LEARNED_NEXT; + return ; + // learned previous case 786614: - return ATV_LEARNED_PREVIOUS; - + *result = ATV_LEARNED_PREVIOUS; + return ; + // learned enter, like go into something case 786630: - return ATV_LEARNED_ENTER; - + *result = ATV_LEARNED_ENTER; + return ; + // learned return, like go back case 786631: - return ATV_LEARNED_RETURN; - + *result = ATV_LEARNED_RETURN; + return ; + // tap play on new Al IR remote case kBREventRemoteActionALPlay: case 786637: - return ATV_ALUMINIUM_PLAY; + *result = ATV_ALUMINIUM_PLAY; + return ; case kBREventRemoteActionKeyPress: case kBREventRemoteActionKeyPress42: - return ATV_BTKEYPRESS; - + *result = ATV_BTKEYPRESS; + return ; + // PageUp case kBREventRemoteActionPageUp: - return ATV_BUTTON_PAGEUP; - + *result = ATV_BUTTON_PAGEUP; + return ; + // PageDown case kBREventRemoteActionPageDown: - return ATV_BUTTON_PAGEDOWN; - + *result = ATV_BUTTON_PAGEDOWN; + return ; + // Pause case kBREventRemoteActionPause: - return ATV_BUTTON_PAUSE; - + *result = ATV_BUTTON_PAUSE; + return ; + // Play2 case kBREventRemoteActionPlay2: - return ATV_BUTTON_PLAY2; - + *result = ATV_BUTTON_PLAY2; + return ; + // Stop case kBREventRemoteActionStop: - return ATV_BUTTON_STOP; - + *result = ATV_BUTTON_STOP; + return ; + // Fast Forward case kBREventRemoteActionFastFwd: - return ATV_BUTTON_FASTFWD; - + *result = ATV_BUTTON_FASTFWD; + return ; + // Rewind case kBREventRemoteActionRewind: - return ATV_BUTTON_REWIND; + *result = ATV_BUTTON_REWIND; + return ; // Skip Forward case kBREventRemoteActionSkipFwd: - return ATV_BUTTON_SKIPFWD; + *result = ATV_BUTTON_SKIPFWD; + return ; - // Skip Back + // Skip Back case kBREventRemoteActionSkipBack: - return ATV_BUTTON_SKIPBACK; - + *result = ATV_BUTTON_SKIPBACK; + return ; + // Gesture Swipe Left case kBREventRemoteActionSwipeLeft: if ([f_event value] == 1) - return ATV_GESTURE_SWIPE_LEFT; + *result = ATV_GESTURE_SWIPE_LEFT; else - return ATV_INVALID_BUTTON; - + *result = ATV_INVALID_BUTTON; + return ; + // Gesture Swipe Right case kBREventRemoteActionSwipeRight: if ([f_event value] == 1) - return ATV_GESTURE_SWIPE_RIGHT; + *result = ATV_GESTURE_SWIPE_RIGHT; else - return ATV_INVALID_BUTTON; + *result = ATV_INVALID_BUTTON; + return ; // Gesture Swipe Up case kBREventRemoteActionSwipeUp: if ([f_event value] == 1) - return ATV_GESTURE_SWIPE_UP; + *result = ATV_GESTURE_SWIPE_UP; else - return ATV_INVALID_BUTTON; - + *result = ATV_INVALID_BUTTON; + return ; + // Gesture Swipe Down case kBREventRemoteActionSwipeDown: if ([f_event value] == 1) - return ATV_GESTURE_SWIPE_DOWN; + *result = ATV_GESTURE_SWIPE_DOWN; else - return ATV_INVALID_BUTTON; + *result = ATV_INVALID_BUTTON; + return; // Gesture Flick Left case kBREventRemoteActionFlickLeft: if ([f_event value] == 1) - return ATV_GESTURE_FLICK_LEFT; + *result = ATV_GESTURE_FLICK_LEFT; else - return ATV_INVALID_BUTTON; - + *result = ATV_INVALID_BUTTON; + return; + // Gesture Flick Right case kBREventRemoteActionFlickRight: if ([f_event value] == 1) - return ATV_GESTURE_FLICK_RIGHT; + *result = ATV_GESTURE_FLICK_RIGHT; else - return ATV_INVALID_BUTTON; - + *result = ATV_INVALID_BUTTON; + return; + // Gesture Flick Up case kBREventRemoteActionFlickUp: if ([f_event value] == 1) - return ATV_GESTURE_FLICK_UP; + *result = ATV_GESTURE_FLICK_UP; else - return ATV_INVALID_BUTTON; - + *result = ATV_INVALID_BUTTON; + return; + // Gesture Flick Down case kBREventRemoteActionFlickDown: if ([f_event value] == 1) - return ATV_GESTURE_FLICK_DOWN; + *result = ATV_GESTURE_FLICK_DOWN; else - return ATV_INVALID_BUTTON; - - + *result = ATV_INVALID_BUTTON; + return; default: ELOG(@"XBMCPureController: Unknown button press remoteAction = %i", remoteAction); - return ATV_INVALID_BUTTON; + *result = ATV_INVALID_BUTTON; } } -- (void)setUserEvent:(int) id withHoldTime:(unsigned int) holdTime +static void XBMCController$setUserEvent(XBMCController* self, SEL _cmd, int eventId, unsigned int holdTime) { + XBMC_Event newEvent; memset(&newEvent, 0, sizeof(newEvent)); newEvent.type = XBMC_USEREVENT; - newEvent.jbutton.which = id; + newEvent.jbutton.which = eventId; newEvent.jbutton.holdTime = holdTime; CWinEventsIOS::MessagePush(&newEvent); } -- (BOOL)brEventAction:(BREvent*)event + +static BOOL XBMCController$brEventAction(XBMCController* self, SEL _cmd, BREvent* event) { //NSLog(@"%s", __PRETTY_FUNCTION__); - if ([m_glView isAnimating]) + if ([[self glView] isAnimating]) { BOOL is_handled = NO; bool isRepeatable = false; bool isPressed = false; - eATVClientEvent xbmc_ir_key = [self ATVClientEventFromBREvent:event - Repeatable:isRepeatable - ButtonState:isPressed]; - + int xbmc_ir_key = ATV_INVALID_BUTTON; + [self ATVClientEventFromBREvent:event + Repeatable:&isRepeatable + ButtonState:&isPressed + Result:&xbmc_ir_key]; + if ( xbmc_ir_key != ATV_INVALID_BUTTON ) { if (xbmc_ir_key == ATV_BTKEYPRESS && [event value] == 1) @@ -579,7 +708,7 @@ - (BOOL)brEventAction:(BREvent*)event const char* wstr = [key_nsstring cStringUsingEncoding:NSUTF16StringEncoding]; //NSLog(@"%s, key: wstr[0] = %d, wstr[1] = %d", __PRETTY_FUNCTION__, wstr[0], wstr[1]); - if (wstr[0] != 92) // trap out "\" which toggle fullscreen/windowed + if (wstr[0] != 92) { if (wstr[0] == 62 && wstr[1] == -9) { @@ -607,8 +736,8 @@ - (BOOL)brEventAction:(BREvent*)event { if(isPressed) { - [self setUserEvent:xbmc_ir_key withHoldTime:0]; //fire event - [self startKeyPressTimer:xbmc_ir_key];//start repeat timer + [self setUserEvent:xbmc_ir_key withHoldTime:0]; + [self startKeyPressTimer:xbmc_ir_key]; } else { @@ -618,60 +747,62 @@ - (BOOL)brEventAction:(BREvent*)event } else { - [self setUserEvent:xbmc_ir_key withHoldTime:0]; + [self setUserEvent:xbmc_ir_key withHoldTime:0]; } is_handled = TRUE; } } return is_handled; - } + } else { - return [super brEventAction:event]; - } + return XBMCController$brEventAction$Orig(self, _cmd, event); + } } #pragma mark - #pragma mark private helper methods -- (void)startKeyPressTimer: (int) keyId +static void XBMCController$startKeyPressTimer(XBMCController* self, SEL _cmd, int keyId) { - NSNumber *number = [NSNumber numberWithInt:keyId]; + NSNumber *number = [NSNumber numberWithInt:keyId]; NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSDate date], @"StartDate", number, @"keyId", nil]; - NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:REPEATED_KEYPRESS_DELAY_S]; - + NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:REPEATED_KEYPRESS_DELAY_S]; [self stopKeyPressTimer]; - + //schedule repeated timer which starts after REPEATED_KEYPRESS_DELAY_S and fires //every REPEATED_KEYPRESS_PAUSE_S - m_keyTimer = [[NSTimer alloc] initWithFireDate:fireDate + NSTimer *timer = [[NSTimer alloc] initWithFireDate:fireDate interval:REPEATED_KEYPRESS_PAUSE_S target:self selector:@selector(keyPressTimerCallback:) userInfo:dict - repeats:YES]; + repeats:YES]; + //schedule the timer to the runloop - NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; - [runLoop addTimer:m_keyTimer forMode:NSDefaultRunLoopMode]; + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + [runLoop addTimer:timer forMode:NSDefaultRunLoopMode]; + [self setKeyTimer:timer]; } -- (void)stopKeyPressTimer +static void XBMCController$stopKeyPressTimer(XBMCController* self, SEL _cmd) { - if(m_keyTimer != nil) + if([self keyTimer] != nil) { - [m_keyTimer invalidate]; - [m_keyTimer release]; - m_keyTimer = nil; + [[self keyTimer] invalidate]; + [[self keyTimer] release]; + [self setKeyTimer:nil]; } } -- (void)keyPressTimerCallback:(NSTimer*)theTimer +static void XBMCController$keyPressTimerCallback(XBMCController* self, SEL _cmd, NSTimer* theTimer) { //if queue is empty - skip this timer event //for letting it process if(CWinEventsIOS::GetQueueSize()) return; + NSDate *startDate = [[theTimer userInfo] objectForKey:@"StartDate"]; int keyId = [[[theTimer userInfo] objectForKey:@"keyId"] intValue]; //calc the holdTime - timeIntervalSinceNow gives the @@ -681,14 +812,13 @@ - (void)keyPressTimerCallback:(NSTimer*)theTimer [self setUserEvent:keyId withHoldTime:(unsigned int)holdTime]; } -// -- (void)observeDefaultCenterStuff: (NSNotification *) notification +static void XBMCController$observeDefaultCenterStuff(XBMCController* self, SEL _cmd, NSNotification * notification) { //NSLog(@"default: %@", [notification name]); if ([notification name] == UIApplicationDidReceiveMemoryWarningNotification) NSLog(@"XBMC: %@", [notification name]); - + //if ([notification name] == kBRScreenSaverActivated) // [m_glView stopAnimation]; @@ -696,378 +826,578 @@ - (void)observeDefaultCenterStuff: (NSNotification *) notification // [m_glView startAnimation]; } -- (void) disableSystemSleep +static void XBMCController$disableSystemSleep(XBMCController* self, SEL _cmd) { - m_systemsleepTimeout = [[ATVSettingsFacade singleton] sleepTimeout]; - [[ATVSettingsFacade singleton] setSleepTimeout: -1]; - [[ATVSettingsFacade singleton] flushDiskChanges]; + Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade"); + XBMCSettingsFacade *single = (XBMCSettingsFacade *)[ATVSettingsFacadeCls singleton]; + + int tmpTimeout = [single sleepTimeout]; + NSNumber *timeout = [NSNumber numberWithInt:tmpTimeout]; + [self setSystemSleepTimeout:timeout]; + [single setSleepTimeout: -1]; + [single flushDiskChanges]; } -- (void) enableSystemSleep +static void XBMCController$enableSystemSleep(XBMCController* self, SEL _cmd) { - [[ATVSettingsFacade singleton] setSleepTimeout: m_systemsleepTimeout]; - [[ATVSettingsFacade singleton] flushDiskChanges]; + Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade"); + int timeoutInt = [[self systemSleepTimeout] intValue]; + [[ATVSettingsFacadeCls singleton] setSleepTimeout:timeoutInt]; + [[ATVSettingsFacadeCls singleton] flushDiskChanges]; } -- (void) disableScreenSaver +static void XBMCController$disableScreenSaver(XBMCController* self, SEL _cmd) { //NSLog(@"%s", __PRETTY_FUNCTION__); //store screen saver state and disable it - m_screensaverTimeout = [[ATVSettingsFacade singleton] screenSaverTimeout]; - [[ATVSettingsFacade singleton] setScreenSaverTimeout: -1]; - [[ATVSettingsFacade singleton] flushDiskChanges]; + Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade"); + XBMCSettingsFacade *single = (XBMCSettingsFacade *)[ATVSettingsFacadeCls singleton]; + + int tmpTimeout = [single screenSaverTimeout]; + NSNumber *timeout = [NSNumber numberWithInt:tmpTimeout]; + [self setSystemScreenSaverTimeout:timeout]; + [single setScreenSaverTimeout: -1]; + [single flushDiskChanges]; // breaks in 4.2.1 [[BRBackgroundTaskManager singleton] holdOffBackgroundTasks]; } -- (void) enableScreenSaver +static void XBMCController$enableScreenSaver(XBMCController* self, SEL _cmd) { //NSLog(@"%s", __PRETTY_FUNCTION__); //reset screen saver to user settings + Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade"); - [[ATVSettingsFacade singleton] setScreenSaverTimeout: m_screensaverTimeout]; - [[ATVSettingsFacade singleton] flushDiskChanges]; + int timeoutInt = [[self systemScreenSaverTimeout] intValue]; + [[ATVSettingsFacadeCls singleton] setScreenSaverTimeout:timeoutInt]; + [[ATVSettingsFacadeCls singleton] flushDiskChanges]; // breaks in 4.2.1 [[BRBackgroundTaskManager singleton] okToDoBackgroundProcessing]; } +/* - (XBMC_Event) translateCocoaToXBMCEvent: (unichar) c { XBMC_Event newEvent; memset(&newEvent, 0, sizeof(newEvent)); -/* - switch (c) - { - // Alt - case NSMenuFunctionKey: - return "Alt"; - - // "Apps" - // "BrowserBack" - // "BrowserForward" - // "BrowserHome" - // "BrowserRefresh" - // "BrowserSearch" - // "BrowserStop" - // "CapsLock" - - // "Clear" - case NSClearLineFunctionKey: - return "Clear"; - - // "CodeInput" - // "Compose" - // "Control" - // "Crsel" - // "Convert" - // "Copy" - // "Cut" - - // "Down" - case NSDownArrowFunctionKey: - return "Down"; - // "End" - case NSEndFunctionKey: - return "End"; - // "Enter" - case 0x3: case 0xA: case 0xD: // Macintosh calls the one on the main keyboard Return, but Windows calls it Enter, so we'll do the same for the DOM - return "Enter"; - - // "EraseEof" - - // "Execute" - case NSExecuteFunctionKey: - return "Execute"; - - // "Exsel" - - // "F1" - case NSF1FunctionKey: - return "F1"; - // "F2" - case NSF2FunctionKey: - return "F2"; - // "F3" - case NSF3FunctionKey: - return "F3"; - // "F4" - case NSF4FunctionKey: - return "F4"; - // "F5" - case NSF5FunctionKey: - return "F5"; - // "F6" - case NSF6FunctionKey: - return "F6"; - // "F7" - case NSF7FunctionKey: - return "F7"; - // "F8" - case NSF8FunctionKey: - return "F8"; - // "F9" - case NSF9FunctionKey: - return "F9"; - // "F10" - case NSF10FunctionKey: - return "F10"; - // "F11" - case NSF11FunctionKey: - return "F11"; - // "F12" - case NSF12FunctionKey: - return "F12"; - // "F13" - case NSF13FunctionKey: - return "F13"; - // "F14" - case NSF14FunctionKey: - return "F14"; - // "F15" - case NSF15FunctionKey: - return "F15"; - // "F16" - case NSF16FunctionKey: - return "F16"; - // "F17" - case NSF17FunctionKey: - return "F17"; - // "F18" - case NSF18FunctionKey: - return "F18"; - // "F19" - case NSF19FunctionKey: - return "F19"; - // "F20" - case NSF20FunctionKey: - return "F20"; - // "F21" - case NSF21FunctionKey: - return "F21"; - // "F22" - case NSF22FunctionKey: - return "F22"; - // "F23" - case NSF23FunctionKey: - return "F23"; - // "F24" - case NSF24FunctionKey: - return "F24"; - - // "FinalMode" - - // "Find" - case NSFindFunctionKey: - return "Find"; - - // "FullWidth" - // "HalfWidth" - // "HangulMode" - // "HanjaMode" - - // "Help" - case NSHelpFunctionKey: - return "Help"; - - // "Hiragana" - - // "Home" - case NSHomeFunctionKey: - return "Home"; - // "Insert" - case NSInsertFunctionKey: - return "Insert"; - - // "JapaneseHiragana" - // "JapaneseKatakana" - // "JapaneseRomaji" - // "JunjaMode" - // "KanaMode" - // "KanjiMode" - // "Katakana" - // "LaunchApplication1" - // "LaunchApplication2" - // "LaunchMail" - - // "Left" - case NSLeftArrowFunctionKey: - return "Left"; - - // "Meta" - // "MediaNextTrack" - // "MediaPlayPause" - // "MediaPreviousTrack" - // "MediaStop" - - // "ModeChange" - case NSModeSwitchFunctionKey: - return "ModeChange"; - - // "Nonconvert" - // "NumLock" - - // "PageDown" - case NSPageDownFunctionKey: - return "PageDown"; - // "PageUp" - case NSPageUpFunctionKey: - return "PageUp"; - - // "Paste" - - // "Pause" - case NSPauseFunctionKey: - return "Pause"; - - // "Play" - // "PreviousCandidate" - - // "PrintScreen" - case NSPrintScreenFunctionKey: - return "PrintScreen"; - - // "Process" - // "Props" - - // "Right" - case NSRightArrowFunctionKey: - return "Right"; - - // "RomanCharacters" - - // "Scroll" - case NSScrollLockFunctionKey: - return "Scroll"; - // "Select" - case NSSelectFunctionKey: - return "Select"; - - // "SelectMedia" - // "Shift" - - // "Stop" - case NSStopFunctionKey: - return "Stop"; - // "Up" - case NSUpArrowFunctionKey: - return "Up"; - // "Undo" - case NSUndoFunctionKey: - return "Undo"; - - // "VolumeDown" - // "VolumeMute" - // "VolumeUp" - // "Win" - // "Zoom" - - // More function keys, not in the key identifier specification. - case NSF25FunctionKey: - return "F25"; - case NSF26FunctionKey: - return "F26"; - case NSF27FunctionKey: - return "F27"; - case NSF28FunctionKey: - return "F28"; - case NSF29FunctionKey: - return "F29"; - case NSF30FunctionKey: - return "F30"; - case NSF31FunctionKey: - return "F31"; - case NSF32FunctionKey: - return "F32"; - case NSF33FunctionKey: - return "F33"; - case NSF34FunctionKey: - return "F34"; - case NSF35FunctionKey: - return "F35"; - - // Turn 0x7F into 0x08, because backspace needs to always be 0x08. - case 0x7F: - XBMCK_BACKSPACE - // Standard says that DEL becomes U+007F. - case NSDeleteFunctionKey: - XBMCK_DELETE; - - // Always use 0x09 for tab instead of AppKit's backtab character. - case NSBackTabCharacter: - return "U+0009"; - - case NSBeginFunctionKey: - case NSBreakFunctionKey: - case NSClearDisplayFunctionKey: - case NSDeleteCharFunctionKey: - case NSDeleteLineFunctionKey: - case NSInsertCharFunctionKey: - case NSInsertLineFunctionKey: - case NSNextFunctionKey: - case NSPrevFunctionKey: - case NSPrintFunctionKey: - case NSRedoFunctionKey: - case NSResetFunctionKey: - case NSSysReqFunctionKey: - case NSSystemFunctionKey: - case NSUserFunctionKey: + + switch (c) + { + // Alt + case NSMenuFunctionKey: + return "Alt"; + + // "Apps" + // "BrowserBack" + // "BrowserForward" + // "BrowserHome" + // "BrowserRefresh" + // "BrowserSearch" + // "BrowserStop" + // "CapsLock" + + // "Clear" + case NSClearLineFunctionKey: + return "Clear"; + + // "CodeInput" + // "Compose" + // "Control" + // "Crsel" + // "Convert" + // "Copy" + // "Cut" + + // "Down" + case NSDownArrowFunctionKey: + return "Down"; + // "End" + case NSEndFunctionKey: + return "End"; + // "Enter" + case 0x3: case 0xA: case 0xD: // Macintosh calls the one on the main keyboard Return, but Windows calls it Enter, so we'll do the same for the DOM + return "Enter"; + + // "EraseEof" + + // "Execute" + case NSExecuteFunctionKey: + return "Execute"; + + // "Exsel" + + // "F1" + case NSF1FunctionKey: + return "F1"; + // "F2" + case NSF2FunctionKey: + return "F2"; + // "F3" + case NSF3FunctionKey: + return "F3"; + // "F4" + case NSF4FunctionKey: + return "F4"; + // "F5" + case NSF5FunctionKey: + return "F5"; + // "F6" + case NSF6FunctionKey: + return "F6"; + // "F7" + case NSF7FunctionKey: + return "F7"; + // "F8" + case NSF8FunctionKey: + return "F8"; + // "F9" + case NSF9FunctionKey: + return "F9"; + // "F10" + case NSF10FunctionKey: + return "F10"; + // "F11" + case NSF11FunctionKey: + return "F11"; + // "F12" + case NSF12FunctionKey: + return "F12"; + // "F13" + case NSF13FunctionKey: + return "F13"; + // "F14" + case NSF14FunctionKey: + return "F14"; + // "F15" + case NSF15FunctionKey: + return "F15"; + // "F16" + case NSF16FunctionKey: + return "F16"; + // "F17" + case NSF17FunctionKey: + return "F17"; + // "F18" + case NSF18FunctionKey: + return "F18"; + // "F19" + case NSF19FunctionKey: + return "F19"; + // "F20" + case NSF20FunctionKey: + return "F20"; + // "F21" + case NSF21FunctionKey: + return "F21"; + // "F22" + case NSF22FunctionKey: + return "F22"; + // "F23" + case NSF23FunctionKey: + return "F23"; + // "F24" + case NSF24FunctionKey: + return "F24"; + + // "FinalMode" + + // "Find" + case NSFindFunctionKey: + return "Find"; + + // "FullWidth" + // "HalfWidth" + // "HangulMode" + // "HanjaMode" + + // "Help" + case NSHelpFunctionKey: + return "Help"; + + // "Hiragana" + + // "Home" + case NSHomeFunctionKey: + return "Home"; + // "Insert" + case NSInsertFunctionKey: + return "Insert"; + + // "JapaneseHiragana" + // "JapaneseKatakana" + // "JapaneseRomaji" + // "JunjaMode" + // "KanaMode" + // "KanjiMode" + // "Katakana" + // "LaunchApplication1" + // "LaunchApplication2" + // "LaunchMail" + + // "Left" + case NSLeftArrowFunctionKey: + return "Left"; + + // "Meta" + // "MediaNextTrack" + // "MediaPlayPause" + // "MediaPreviousTrack" + // "MediaStop" + + // "ModeChange" + case NSModeSwitchFunctionKey: + return "ModeChange"; + + // "Nonconvert" + // "NumLock" + + // "PageDown" + case NSPageDownFunctionKey: + return "PageDown"; + // "PageUp" + case NSPageUpFunctionKey: + return "PageUp"; + + // "Paste" + + // "Pause" + case NSPauseFunctionKey: + return "Pause"; + + // "Play" + // "PreviousCandidate" + + // "PrintScreen" + case NSPrintScreenFunctionKey: + return "PrintScreen"; + + // "Process" + // "Props" + + // "Right" + case NSRightArrowFunctionKey: + return "Right"; + + // "RomanCharacters" + + // "Scroll" + case NSScrollLockFunctionKey: + return "Scroll"; + // "Select" + case NSSelectFunctionKey: + return "Select"; + + // "SelectMedia" + // "Shift" + + // "Stop" + case NSStopFunctionKey: + return "Stop"; + // "Up" + case NSUpArrowFunctionKey: + return "Up"; + // "Undo" + case NSUndoFunctionKey: + return "Undo"; + + // "VolumeDown" + // "VolumeMute" + // "VolumeUp" + // "Win" + // "Zoom" + + // More function keys, not in the key identifier specification. + case NSF25FunctionKey: + return "F25"; + case NSF26FunctionKey: + return "F26"; + case NSF27FunctionKey: + return "F27"; + case NSF28FunctionKey: + return "F28"; + case NSF29FunctionKey: + return "F29"; + case NSF30FunctionKey: + return "F30"; + case NSF31FunctionKey: + return "F31"; + case NSF32FunctionKey: + return "F32"; + case NSF33FunctionKey: + return "F33"; + case NSF34FunctionKey: + return "F34"; + case NSF35FunctionKey: + return "F35"; + + // Turn 0x7F into 0x08, because backspace needs to always be 0x08. + case 0x7F: + XBMCK_BACKSPACE + // Standard says that DEL becomes U+007F. + case NSDeleteFunctionKey: + XBMCK_DELETE; + + // Always use 0x09 for tab instead of AppKit's backtab character. + case NSBackTabCharacter: + return "U+0009"; + + case NSBeginFunctionKey: + case NSBreakFunctionKey: + case NSClearDisplayFunctionKey: + case NSDeleteCharFunctionKey: + case NSDeleteLineFunctionKey: + case NSInsertCharFunctionKey: + case NSInsertLineFunctionKey: + case NSNextFunctionKey: + case NSPrevFunctionKey: + case NSPrintFunctionKey: + case NSRedoFunctionKey: + case NSResetFunctionKey: + case NSSysReqFunctionKey: + case NSSystemFunctionKey: + case NSUserFunctionKey: // FIXME: We should use something other than the vendor-area Unicode values for the above keys. // For now, just fall through to the default. default: return String::format("U+%04X", toASCIIUpper(c)); } -*/ return newEvent; -} +}*/ //-------------------------------------------------------------- -- (void)pauseAnimation +static void XBMCController$pauseAnimation(XBMCController* self, SEL _cmd) { XBMC_Event newEvent; memset(&newEvent, 0, sizeof(XBMC_Event)); - + newEvent.appcommand.type = XBMC_APPCOMMAND; newEvent.appcommand.action = ACTION_PLAYER_PLAYPAUSE; CWinEventsIOS::MessagePush(&newEvent); - /* Give player time to pause */ - Sleep(2000); - //NSLog(@"%s", __PRETTY_FUNCTION__); - - [m_glView pauseAnimation]; - + Sleep(2000); + [[self glView] pauseAnimation]; } //-------------------------------------------------------------- -- (void)resumeAnimation +static void XBMCController$resumeAnimation(XBMCController* self, SEL _cmd) { + NSLog(@"%s", __PRETTY_FUNCTION__); + XBMC_Event newEvent; memset(&newEvent, 0, sizeof(XBMC_Event)); - + newEvent.appcommand.type = XBMC_APPCOMMAND; newEvent.appcommand.action = ACTION_PLAYER_PLAY; - CWinEventsIOS::MessagePush(&newEvent); - - [m_glView resumeAnimation]; + CWinEventsIOS::MessagePush(&newEvent); + + [[self glView] resumeAnimation]; } //-------------------------------------------------------------- -- (void)startAnimation +static void XBMCController$startAnimation(XBMCController* self, SEL _cmd) { - [m_glView startAnimation]; + NSLog(@"%s", __PRETTY_FUNCTION__); + + [[self glView] startAnimation]; } //-------------------------------------------------------------- -- (void)stopAnimation +static void XBMCController$stopAnimation(XBMCController* self, SEL _cmd) { - [m_glView stopAnimation]; + NSLog(@"%s", __PRETTY_FUNCTION__); + + [[self glView] stopAnimation]; } //-------------------------------------------------------------- -- (bool) changeScreen: (unsigned int)screenIdx withMode:(UIScreenMode *)mode +static bool XBMCController$changeScreen(XBMCController* self, SEL _cmd, unsigned int screenIdx, UIScreenMode * mode) { return [[IOSScreenManager sharedInstance] changeScreen: screenIdx withMode: mode]; } //-------------------------------------------------------------- -- (void) activateScreen: (UIScreen *)screen +static void XBMCController$activateScreen(XBMCController* self, SEL _cmd, UIScreen * screen) { } -@end +// SECTIONCOMMENT +// c'tor - this sets up our class at runtime by +// 1. subclassing from the base classes +// 2. adding new methods to our class +// 3. exchanging (hooking) base class methods with ours +// 4. register the classes to the objc runtime system +static __attribute__((constructor)) void initControllerRuntimeClasses() +{ + char _typeEncoding[1024]; + unsigned int i = 0; + + // subclass BRController into XBMCController + Class XBMCControllerCls = objc_allocateClassPair(objc_getClass("BRController"), "XBMCController", 0); + // add our custom methods which are not part of the baseclass + // XBMCController::keyTimer + class_addMethod(XBMCControllerCls, @selector(keyTimer), (IMP)&XBMCController$keyTimer, "@@:"); + // XBMCController::setKeyTimer + class_addMethod(XBMCControllerCls, @selector(setKeyTimer:), (IMP)&XBMCController$setKeyTimer, "v@:@"); + // XBMCController::glView + class_addMethod(XBMCControllerCls, @selector(glView), (IMP)&XBMCController$glView, "@@:"); + // XBMCController::setGlView + class_addMethod(XBMCControllerCls, @selector(setGlView:), (IMP)&XBMCController$setGlView, "v@:@"); + // XBMCController::systemScreenSaverTimeout + class_addMethod(XBMCControllerCls, @selector(systemScreenSaverTimeout), (IMP)&XBMCController$systemScreenSaverTimeout, "@@:"); + // XBMCController::setSystemScreenSaverTimeout + class_addMethod(XBMCControllerCls, @selector(setSystemScreenSaverTimeout:), (IMP)&XBMCController$setSystemScreenSaverTimeout, "v@:@"); + // XBMCController::systemSleepTimeout + class_addMethod(XBMCControllerCls, @selector(systemSleepTimeout), (IMP)&XBMCController$systemSleepTimeout, "@@:"); + // XBMCController::setSystemSleepTimeout + class_addMethod(XBMCControllerCls, @selector(setSystemSleepTimeout:), (IMP)&XBMCController$setSystemSleepTimeout, "v@:@"); + // XBMCController::applicationDidExit + class_addMethod(XBMCControllerCls, @selector(applicationDidExit), (IMP)&XBMCController$applicationDidExit, "v@:"); + // XBMCController::initDisplayLink + class_addMethod(XBMCControllerCls, @selector(initDisplayLink), (IMP)&XBMCController$initDisplayLink, "v@:"); + // XBMCController::deinitDisplayLink + class_addMethod(XBMCControllerCls, @selector(deinitDisplayLink), (IMP)&XBMCController$deinitDisplayLink, "v@:"); + // XBMCController::getDisplayLinkFPS + class_addMethod(XBMCControllerCls, @selector(getDisplayLinkFPS), (IMP)&XBMCController$getDisplayLinkFPS, "d@:"); + // XBMCController::setFramebuffer + class_addMethod(XBMCControllerCls, @selector(setFramebuffer), (IMP)&XBMCController$setFramebuffer, "v@:"); + // XBMCController::presentFramebuffer + class_addMethod(XBMCControllerCls, @selector(presentFramebuffer), (IMP)&XBMCController$presentFramebuffer, "B@:"); + // XBMCController::setUserEvent + class_addMethod(XBMCControllerCls, @selector(setUserEvent:withHoldTime:), (IMP)&XBMCController$setUserEvent, "v@:iI"); + // XBMCController::startKeyPressTimer + class_addMethod(XBMCControllerCls, @selector(startKeyPressTimer:), (IMP)&XBMCController$startKeyPressTimer, "v@:i"); + // XBMCController::stopKeyPressTimer + class_addMethod(XBMCControllerCls, @selector(stopKeyPressTimer), (IMP)&XBMCController$stopKeyPressTimer, "v@:"); + // XBMCController::disableSystemSleep + class_addMethod(XBMCControllerCls, @selector(disableSystemSleep), (IMP)&XBMCController$disableSystemSleep, "v@:"); + // XBMCController__enableSystemSleep + class_addMethod(XBMCControllerCls, @selector(enableSystemSleep), (IMP)&XBMCController$enableSystemSleep, "v@:"); + // XBMCController::disableScreenSaver + class_addMethod(XBMCControllerCls, @selector(disableScreenSaver), (IMP)&XBMCController$disableScreenSaver, "v@:"); + // XBMCController::enableScreenSaver + class_addMethod(XBMCControllerCls, @selector(enableScreenSaver), (IMP)&XBMCController$enableScreenSaver, "v@:"); + // XBMCController::pauseAnimation + class_addMethod(XBMCControllerCls, @selector(pauseAnimation), (IMP)&XBMCController$pauseAnimation, "v@:"); + // XBMCController::resumeAnimation + class_addMethod(XBMCControllerCls, @selector(resumeAnimation), (IMP)&XBMCController$resumeAnimation, "v@:"); + // XBMCController::startAnimation + class_addMethod(XBMCControllerCls, @selector(startAnimation), (IMP)&XBMCController$startAnimation, "v@:"); + // XBMCController::stopAnimation + class_addMethod(XBMCControllerCls, @selector(stopAnimation), (IMP)&XBMCController$stopAnimation, "v@:"); + + i = 0; + memcpy(_typeEncoding + i, @encode(CGSize), strlen(@encode(CGSize))); + i += strlen(@encode(CGSize)); + _typeEncoding[i] = '@'; + i += 1; + _typeEncoding[i] = ':'; + i += 1; + _typeEncoding[i] = '\0'; + // XBMCController::getScreenSize + class_addMethod(XBMCControllerCls, @selector(getScreenSize), (IMP)&XBMCController$getScreenSize, _typeEncoding); + + i = 0; + _typeEncoding[i] = 'v'; + i += 1; + _typeEncoding[i] = '@'; + i += 1; + _typeEncoding[i] = ':'; + i += 1; + memcpy(_typeEncoding + i, @encode(XBMCKey), strlen(@encode(XBMCKey))); + i += strlen(@encode(XBMCKey)); + _typeEncoding[i] = '\0'; + // XBMCController::sendKey + class_addMethod(XBMCControllerCls, @selector(sendKey:), (IMP)&XBMCController$sendKey, _typeEncoding); + + i = 0; + _typeEncoding[i] = 'v'; + i += 1; + _typeEncoding[i] = '@'; + i += 1; + _typeEncoding[i] = ':'; + i += 1; + memcpy(_typeEncoding + i, @encode(BREvent*), strlen(@encode(BREvent*))); + i += strlen(@encode(BREvent*)); + _typeEncoding[i] = '^'; + _typeEncoding[i + 1] = 'B'; + i += 2; + _typeEncoding[i] = '^'; + _typeEncoding[i + 1] = 'B'; + i += 2; + _typeEncoding[i] = '^'; + _typeEncoding[i + 1] = 'i'; + i += 2; + _typeEncoding[i] = '\0'; + // XBMCController::ATVClientEventFromBREvent + class_addMethod(XBMCControllerCls, @selector(ATVClientEventFromBREvent:Repeatable:ButtonState:Result:), (IMP)&XBMCController$ATVClientEventFromBREvent, _typeEncoding); + + i = 0; + _typeEncoding[i] = 'v'; + i += 1; + _typeEncoding[i] = '@'; + i += 1; + _typeEncoding[i] = ':'; + i += 1; + memcpy(_typeEncoding + i, @encode(NSTimer*), strlen(@encode(NSTimer*))); + i += strlen(@encode(NSTimer*)); + _typeEncoding[i] = '\0'; + // XBMCController::keyPressTimerCallback + class_addMethod(XBMCControllerCls, @selector(keyPressTimerCallback:), (IMP)&XBMCController$keyPressTimerCallback, _typeEncoding); + + i = 0; + _typeEncoding[i] = 'v'; + i += 1; + _typeEncoding[i] = '@'; + i += 1; + _typeEncoding[i] = ':'; + i += 1; + memcpy(_typeEncoding + i, @encode(NSNotification *), strlen(@encode(NSNotification *))); + i += strlen(@encode(NSNotification *)); + _typeEncoding[i] = '\0'; + // XBMCController:observeDefaultCenterStuff + class_addMethod(XBMCControllerCls, @selector(observeDefaultCenterStuff:), (IMP)&XBMCController$observeDefaultCenterStuff, _typeEncoding); + + i = 0; + _typeEncoding[i] = 'B'; + i += 1; + _typeEncoding[i] = '@'; + i += 1; + _typeEncoding[i] = ':'; + i += 1; + _typeEncoding[i] = 'I'; + i += 1; + memcpy(_typeEncoding + i, @encode(UIScreenMode *), strlen(@encode(UIScreenMode *))); + i += strlen(@encode(UIScreenMode *)); + _typeEncoding[i] = '\0'; + // XBMCController::changeScreen + class_addMethod(XBMCControllerCls, @selector(changeScreen:withMode:), (IMP)&XBMCController$changeScreen, _typeEncoding); + + i = 0; + _typeEncoding[i] = 'v'; + i += 1; + _typeEncoding[i] = '@'; + i += 1; + _typeEncoding[i] = ':'; + i += 1; + memcpy(_typeEncoding + i, @encode(UIScreen *), strlen(@encode(UIScreen *))); + i += strlen(@encode(UIScreen *)); + _typeEncoding[i] = '\0'; + // XBMCController::activateScreen$ + class_addMethod(XBMCControllerCls, @selector(activateScreen:), (IMP)&XBMCController$activateScreen, _typeEncoding); + + // and hook up our methods (implementation of the base class methods) + // XBMCController::brEventAction + MSHookMessageEx(XBMCControllerCls, @selector(brEventAction:), (IMP)&XBMCController$brEventAction, (IMP*)&XBMCController$brEventAction$Orig); + // XBMCController::init + MSHookMessageEx(XBMCControllerCls, @selector(init), (IMP)&XBMCController$init, (IMP*)&XBMCController$init$Orig); + // XBMCController::dealloc + MSHookMessageEx(XBMCControllerCls, @selector(dealloc), (IMP)&XBMCController$dealloc, (IMP*)&XBMCController$dealloc$Orig); + // XBMCController::controlWasActivated + MSHookMessageEx(XBMCControllerCls, @selector(controlWasActivated), (IMP)&XBMCController$controlWasActivated, (IMP*)&XBMCController$controlWasActivated$Orig); + // XBMCController::controlWasDeactivated + MSHookMessageEx(XBMCControllerCls, @selector(controlWasDeactivated), (IMP)&XBMCController$controlWasDeactivated, (IMP*)&XBMCController$controlWasDeactivated$Orig); + // XBMCController::recreateOnReselect + MSHookMessageEx(XBMCControllerCls, @selector(recreateOnReselect), (IMP)&XBMCController$recreateOnReselect, nil); + + // and register the class to the runtime + objc_registerClassPair(XBMCControllerCls); + + // save this as static for referencing it in multiple methods + BRWindowCls = objc_getClass("BRWindow"); +}