[release] version bump to 13.0 beta1
[vuplus_xbmc] / xbmc / osx / atv2 / XBMCController.mm
1 /*
2  *      Copyright (C) 2010-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21
22 /* HowTo code in this file:
23  * Since AppleTV/iOS6.x (atv2 version 5.2) Apple removed the AppleTV.framework and put all those classes into the
24  * AppleTV.app. So we can't use standard obj-c coding here anymore. Instead we need to use the obj-c runtime
25  * functions for subclassing and adding methods to our instances during runtime (hooking).
26  * 
27  * 1. For implementing a method of a base class:
28  *  a) declare it in the form <XBMCController$nameOfMethod> like the others 
29  *  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. 
30  *  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).
31  *  d) in initControllerRuntimeClasses exchange the base class implementation with ours by calling MSHookMessageEx
32  *  e) if we need to call the base class implementation as well we have to save the original implementation (see brEventAction$Orig for reference)
33  *
34  * 2. For implementing a new method which is not part of the base class:
35  *  a) same as 1.a
36  *  b) same as 1.b
37  *  c) same as 1.c
38  *  d) in initControllerRuntimeClasses add the method to our class via class_addMethod
39  *
40  * 3. Never access any BackRow classes directly - but always get the class via objc_getClass - if the class is used in multiple places 
41  *    save it as static (see BRWindowCls)
42  * 
43  * 4. Keep the structure of this file based on the section comments (marked with // SECTIONCOMMENT).
44  * 5. really - obey 4.!
45  *
46  * 6. for adding class members use associated objects - see timerKey
47  *
48  * For further reference see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html
49  */
50
51 //hack around problem with xbmc's typedef int BOOL
52 // and obj-c's typedef unsigned char BOOL
53 #define BOOL XBMC_BOOL 
54 #import "WinEvents.h"
55 #import "XBMC_events.h"
56 #include "utils/log.h"
57 #include "osx/DarwinUtils.h"
58 #include "threads/Event.h"
59 #include "Application.h"
60 #include "guilib/Key.h"
61 #undef BOOL
62
63 #import <Foundation/Foundation.h>
64 #import <UIKit/UIKit.h>
65
66 #import "XBMCController.h"
67 #import "XBMCDebugHelpers.h"
68
69 #import "IOSEAGLView.h"
70 #import "IOSSCreenManager.h"
71 #include "XBMC_keysym.h"
72 #include "substrate.h"
73
74 //start repeating after 0.5s
75 #define REPEATED_KEYPRESS_DELAY_S 0.5
76 //pause 0.01s (10ms) between keypresses
77 #define REPEATED_KEYPRESS_PAUSE_S 0.01
78
79 typedef enum {
80
81   ATV_BUTTON_UP                 = 1,
82   ATV_BUTTON_DOWN               = 2,
83   ATV_BUTTON_LEFT               = 3,
84   ATV_BUTTON_RIGHT              = 4,
85   ATV_BUTTON_PLAY               = 5,
86   ATV_BUTTON_MENU               = 6,
87   ATV_BUTTON_PLAY_H             = 7,
88   ATV_BUTTON_MENU_H             = 8,
89   ATV_BUTTON_LEFT_H             = 9,
90   ATV_BUTTON_RIGHT_H            = 10,
91
92   //new aluminium remote buttons
93   ATV_ALUMINIUM_PLAY            = 12,
94   ATV_ALUMINIUM_PLAY_H          = 11,
95
96   //newly added remote buttons
97   ATV_BUTTON_PAGEUP             = 13,
98   ATV_BUTTON_PAGEDOWN           = 14,
99   ATV_BUTTON_PAUSE              = 15,
100   ATV_BUTTON_PLAY2              = 16,
101   ATV_BUTTON_STOP               = 17,
102   ATV_BUTTON_STOP_RELEASE       = 17,
103   ATV_BUTTON_FASTFWD            = 18,
104   ATV_BUTTON_FASTFWD_RELEASE    = 18,
105   ATV_BUTTON_REWIND             = 19,
106   ATV_BUTTON_REWIND_RELEASE     = 19,
107   ATV_BUTTON_SKIPFWD            = 20,
108   ATV_BUTTON_SKIPBACK           = 21,
109
110   //learned remote buttons
111   ATV_LEARNED_PLAY              = 70,
112   ATV_LEARNED_PAUSE             = 71,
113   ATV_LEARNED_STOP              = 72,
114   ATV_LEARNED_PREVIOUS          = 73,
115   ATV_LEARNED_NEXT              = 74,
116   ATV_LEARNED_REWIND            = 75,
117   ATV_LEARNED_REWIND_RELEASE    = 75,
118   ATV_LEARNED_FORWARD           = 76,
119   ATV_LEARNED_FORWARD_RELEASE   = 76,
120   ATV_LEARNED_RETURN            = 77,
121   ATV_LEARNED_ENTER             = 78,
122
123   //gestures
124   ATV_GESTURE_SWIPE_LEFT        = 80,
125   ATV_GESTURE_SWIPE_RIGHT       = 81,
126   ATV_GESTURE_SWIPE_UP          = 82,
127   ATV_GESTURE_SWIPE_DOWN        = 83,
128
129   ATV_GESTURE_FLICK_LEFT        = 85,
130   ATV_GESTURE_FLICK_RIGHT       = 86,
131   ATV_GESTURE_FLICK_UP          = 87,
132   ATV_GESTURE_FLICK_DOWN        = 88,
133   ATV_GESTURE_TOUCHHOLD         = 89,
134
135   ATV_BTKEYPRESS                = 84,
136
137   ATV_INVALID_BUTTON
138 } eATVClientEvent;
139
140
141 typedef enum {
142   // for originator kBREventOriginatorRemote
143   kBREventRemoteActionMenu      = 1,
144   kBREventRemoteActionMenuHold  = 2,
145   kBREventRemoteActionUp        = 3,
146   kBREventRemoteActionDown      = 4,
147   kBREventRemoteActionPlay      = 5,
148   kBREventRemoteActionLeft      = 6,
149   kBREventRemoteActionRight     = 7,
150   kBREventRemoteActionRewind2   = 8,
151   kBREventRemoteActionFastFwd2  = 9,
152
153   kBREventRemoteActionALPlay    = 10,
154
155   kBREventRemoteActionPageUp    = 13,
156   kBREventRemoteActionPageDown  = 14,
157   kBREventRemoteActionPause     = 15,
158   kBREventRemoteActionPlay2     = 16,
159   kBREventRemoteActionStop      = 17,
160   kBREventRemoteActionFastFwd   = 18,
161   kBREventRemoteActionRewind    = 19,
162   kBREventRemoteActionSkipFwd   = 20,
163   kBREventRemoteActionSkipBack  = 21,
164
165
166   kBREventRemoteActionPlayHold  = 22,
167   kBREventRemoteActionCenterHold,
168   kBREventRemoteActionCenterHold42,
169
170   // Gestures, for originator kBREventOriginatorGesture
171   kBREventRemoteActionTouchBegin= 31,
172   kBREventRemoteActionTouchMove = 32,
173   kBREventRemoteActionTouchEnd  = 33,
174
175   kBREventRemoteActionSwipeLeft = 34,
176   kBREventRemoteActionSwipeRight= 35,
177   kBREventRemoteActionSwipeUp   = 36,
178   kBREventRemoteActionSwipeDown = 37,
179
180   kBREventRemoteActionFlickLeft = 38,
181   kBREventRemoteActionFlickRight= 39,
182   kBREventRemoteActionFlickUp   = 40,
183   kBREventRemoteActionFlickDown = 41,
184
185   kBREventRemoteActionTouchHold = 46,
186
187   // keypresses, for originator kBREventOriginatorKeyboard
188   kBREventRemoteActionKeyPress  = 47,
189   kBREventRemoteActionKeyPress42,
190
191   kBREventRemoteActionKeyTab    = 53,
192
193   // Custom remote actions for old remote actions
194   kBREventRemoteActionHoldLeft = 0xfeed0001,
195   kBREventRemoteActionHoldRight,
196   kBREventRemoteActionHoldUp,
197   kBREventRemoteActionHoldDown,
198 } BREventRemoteAction;
199
200 typedef enum {
201   kBREventModifierCommandLeft   = 0x10000,
202   kBREventModifierShiftLeft     = 0x20000,
203   kBREventModifierOptionLeft    = 0x80000,
204   kBREventModifierCtrlLeft      = 0x100000,
205   kBREventModifierShiftRight    = 0x200000,
206   kBREventModifierOptionRight   = 0x400000,
207   kBREventModifierCommandRight  = 0x1000000,
208 }BREventModifier;
209
210 typedef enum {
211   kBREventOriginatorRemote    = 1,
212   kBREventOriginatorKeyboard  = 2,
213   kBREventOriginatorGesture   = 3,
214 }BREventOriginiator;
215
216
217 XBMCController *g_xbmcController;
218
219 //--------------------------------------------------------------
220 // so we don't have to include AppleTV.frameworks/PrivateHeaders/ATVSettingsFacade.h
221 @interface XBMCSettingsFacade : NSObject
222 -(int)screenSaverTimeout;
223 -(void)setScreenSaverTimeout:(int) f_timeout;
224 -(void)setSleepTimeout:(int)timeout;
225 -(int)sleepTimeout;
226 -(void)flushDiskChanges;
227 @end
228
229 // notification messages
230 extern NSString* kBRScreenSaverActivated;
231 extern NSString* kBRScreenSaverDismissed;
232
233 //--------------------------------------------------------------
234 //--------------------------------------------------------------
235 // SECTIONCOMMENT
236 // orig method handlers we wanna call in hooked methods ([super method])
237 static BOOL (*XBMCController$brEventAction$Orig)(XBMCController*, SEL, BREvent*);
238 static id (*XBMCController$init$Orig)(XBMCController*, SEL);
239 static void (*XBMCController$dealloc$Orig)(XBMCController*, SEL);
240 static void (*XBMCController$controlWasActivated$Orig)(XBMCController*, SEL);
241 static void (*XBMCController$controlWasDeactivated$Orig)(XBMCController*, SEL);
242
243 // SECTIONCOMMENT
244 // classes we need multiple times
245 static Class BRWindowCls;
246
247 int padding[16];//obsolete? - was commented with "credit is due here to SapphireCompatibilityClasses!!"
248   
249 //--------------------------------------------------------------
250 //--------------------------------------------------------------
251 // SECTIONCOMMENT
252 // since we can't inject ivars we need to use associated objects
253 // these are the keys for XBMCController
254 static char timerKey;
255 static char glviewKey;
256 static char screensaverKey;
257 static char systemsleepKey;
258
259 //
260 //
261 // SECTIONCOMMENT
262 //implementation XBMCController
263  
264 static id XBMCController$keyTimer(XBMCController* self, SEL _cmd) 
265
266   return objc_getAssociatedObject(self, &timerKey);
267 }
268
269 static void XBMCController$setKeyTimer(XBMCController* self, SEL _cmd, id timer) 
270
271   objc_setAssociatedObject(self, &timerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
272 }
273
274 static id XBMCController$glView(XBMCController* self, SEL _cmd) 
275
276   return objc_getAssociatedObject(self, &glviewKey);
277 }
278
279 static void XBMCController$setGlView(XBMCController* self, SEL _cmd, id view) 
280
281   objc_setAssociatedObject(self, &glviewKey, view, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
282 }
283
284 static id XBMCController$systemScreenSaverTimeout(XBMCController* self, SEL _cmd) 
285
286   return objc_getAssociatedObject(self, &screensaverKey);
287 }
288
289 static void XBMCController$setSystemScreenSaverTimeout(XBMCController* self, SEL _cmd, id timeout) 
290
291   objc_setAssociatedObject(self, &screensaverKey, timeout, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
292 }
293
294 static id XBMCController$systemSleepTimeout(XBMCController* self, SEL _cmd) 
295
296   return objc_getAssociatedObject(self, &systemsleepKey);
297 }
298
299 static void XBMCController$setSystemSleepTimeout(XBMCController* self, SEL _cmd, id timeout) 
300
301   objc_setAssociatedObject(self, &systemsleepKey, timeout, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
302 }
303
304 static void XBMCController$applicationDidExit(XBMCController* self, SEL _cmd) 
305 {
306   //NSLog(@"%s", __PRETTY_FUNCTION__);
307
308   [[self glView] stopAnimation];
309   [self enableScreenSaver];
310   [self enableSystemSleep];
311   [[self stack] popController];
312 }
313
314 static void XBMCController$initDisplayLink(XBMCController* self, SEL _cmd) 
315 {
316   //NSLog(@"%s", __PRETTY_FUNCTION__);
317
318   [[self glView] initDisplayLink];
319 }
320
321 static void XBMCController$deinitDisplayLink(XBMCController* self, SEL _cmd) 
322 {
323   //NSLog(@"%s", __PRETTY_FUNCTION__);
324
325   [[self glView] deinitDisplayLink];
326 }
327
328 static double XBMCController$getDisplayLinkFPS(XBMCController* self, SEL _cmd) 
329 {
330   //NSLog(@"%s", __PRETTY_FUNCTION__);
331
332   return [[self glView] getDisplayLinkFPS];
333 }
334
335 static void XBMCController$setFramebuffer(XBMCController* self, SEL _cmd) 
336 {   
337   [[self glView] setFramebuffer];
338 }
339
340 static bool XBMCController$presentFramebuffer(XBMCController* self, SEL _cmd) 
341 {    
342   return [[self glView] presentFramebuffer];
343 }
344
345 static CGSize XBMCController$getScreenSize(XBMCController* self, SEL _cmd) 
346 {
347   CGSize screensize;
348   screensize.width  = [BRWindowCls interfaceFrame].size.width;
349   screensize.height = [BRWindowCls interfaceFrame].size.height;
350   //NSLog(@"%s UpdateResolutions width=%f, height=%f", 
351   //__PRETTY_FUNCTION__, screensize.width, screensize.height);
352   return screensize;
353 }
354
355 static void XBMCController$sendKey(XBMCController* self, SEL _cmd, XBMCKey key) 
356 {
357   //empty because its not used here. Only implemented for getting rid
358   //of "may not respond to selector" compile warnings in IOSExternalTouchController
359 }
360
361 static id XBMCController$init(XBMCController* self, SEL _cmd) 
362 {
363   if((self = XBMCController$init$Orig(self, _cmd)) != nil)  
364   {
365     //NSLog(@"%s", __PRETTY_FUNCTION__);
366
367     NSNotificationCenter *center;
368     // first the default notification center, which is all
369     // notifications that only happen inside of our program
370     center = [NSNotificationCenter defaultCenter];
371     [center addObserver: self
372       selector: @selector(observeDefaultCenterStuff:)
373       name: nil
374       object: nil];
375
376     IOSEAGLView *view = [[IOSEAGLView alloc] initWithFrame:[BRWindowCls interfaceFrame] withScreen:[UIScreen mainScreen]];
377     [self setGlView:view];
378  
379     [[IOSScreenManager sharedInstance] setView:[self glView]];
380
381     g_xbmcController = self;
382   }
383   return self;
384 }
385
386 static void XBMCController$dealloc(XBMCController* self, SEL _cmd) 
387 {
388   //NSLog(@"%s", __PRETTY_FUNCTION__);
389   [[self glView] stopAnimation];
390   [[self glView] release];
391
392   NSNotificationCenter *center;
393   // take us off the default center for our app
394   center = [NSNotificationCenter defaultCenter];
395   [center removeObserver: self];
396
397   XBMCController$dealloc$Orig(self, _cmd);
398 }
399
400 static void XBMCController$controlWasActivated(XBMCController* self, SEL _cmd) 
401 {
402   //NSLog(@"%s", __PRETTY_FUNCTION__);
403
404   XBMCController$controlWasActivated$Orig(self, _cmd);
405
406   [self disableSystemSleep];
407   [self disableScreenSaver];
408   
409   IOSEAGLView *view = [self glView];
410   //inject our gles layer into the backrow root layer
411   [[BRWindowCls rootLayer] addSublayer:view.layer];
412
413   [[self glView] startAnimation];
414 }
415
416 static void XBMCController$controlWasDeactivated(XBMCController* self, SEL _cmd) 
417 {
418   NSLog(@"XBMC was forced by FrontRow to exit via controlWasDeactivated");
419
420   [[self glView] stopAnimation];
421   [[[self glView] layer] removeFromSuperlayer];
422
423   [self enableScreenSaver];
424   [self enableSystemSleep];
425
426   XBMCController$controlWasDeactivated$Orig(self, _cmd);
427 }
428
429 static BOOL XBMCController$recreateOnReselect(XBMCController* self, SEL _cmd) 
430 {
431   //NSLog(@"%s", __PRETTY_FUNCTION__);
432   return YES;
433 }
434
435 static void XBMCController$ATVClientEventFromBREvent(XBMCController* self, SEL _cmd, BREvent* f_event, bool * isRepeatable, bool * isPressed, int * result) 
436 {
437   if(f_event == nil)// paranoia
438     return;
439
440   int remoteAction = [f_event remoteAction];
441   unsigned int originator = [f_event originator];
442   CLog::Log(LOGDEBUG,"XBMCPureController: Button press remoteAction = %i originator = %i", remoteAction, originator);
443   *isRepeatable = false;
444   *isPressed = false;
445
446   switch (remoteAction)
447   {
448     // tap up
449     case kBREventRemoteActionUp:
450     case 65676:
451       *isRepeatable = true;
452       if([f_event value] == 1)
453         *isPressed = true;
454       *result = ATV_BUTTON_UP;
455       return;
456
457     // tap down
458     case kBREventRemoteActionDown:
459     case 65677:
460       *isRepeatable = true;
461       if([f_event value] == 1)
462         *isPressed = true;
463       *result = ATV_BUTTON_DOWN;
464       return;
465     
466     // tap left
467     case kBREventRemoteActionLeft:
468     case 65675:
469       *isRepeatable = true;
470       if([f_event value] == 1)
471         *isPressed = true;
472       *result = ATV_BUTTON_LEFT;
473       return;
474     
475     // hold left
476     case 786612:
477       if([f_event value] == 1)
478         *result = ATV_LEARNED_REWIND;
479       else
480         *result = ATV_INVALID_BUTTON;
481       return;
482     
483     // tap right
484     case kBREventRemoteActionRight:
485     case 65674:
486       *isRepeatable = true;
487       if ([f_event value] == 1)
488         *isPressed = true;
489       *result = ATV_BUTTON_RIGHT;
490       return ;
491     
492     // hold right
493     case 786611:
494       if ([f_event value] == 1)
495         *result = ATV_LEARNED_FORWARD;
496       else
497         *result = ATV_INVALID_BUTTON;
498       return ;
499    
500     // tap play
501     case kBREventRemoteActionPlay:
502     case 65673:
503       if (originator == kBREventOriginatorKeyboard) // on bt keyboard play == return!
504         *result = ATV_BTKEYPRESS;
505       else
506         *result = ATV_BUTTON_PLAY;
507       return ;
508     
509     // hold play
510     case kBREventRemoteActionPlayHold:
511     case kBREventRemoteActionCenterHold:
512     case kBREventRemoteActionCenterHold42:
513     case 65668:
514       if (originator == kBREventOriginatorKeyboard) // invalid on bt keyboard
515         *result = ATV_INVALID_BUTTON;
516       else
517         *result = ATV_BUTTON_PLAY_H;
518       return ;
519     
520     // menu
521     case kBREventRemoteActionMenu:
522     case 65670:
523       if (originator == kBREventOriginatorKeyboard) // on bt keyboard menu == esc!
524         *result = ATV_BTKEYPRESS;
525       else
526         *result = ATV_BUTTON_MENU;
527       return ;
528     
529     // hold menu
530     case kBREventRemoteActionMenuHold:
531     case 786496:
532       if (originator == kBREventOriginatorKeyboard) // invalid on bt keyboard
533         *result = ATV_INVALID_BUTTON;
534       else
535         *result = ATV_BUTTON_MENU_H;
536       return ;
537     
538     // learned play
539     case 786608:
540       *result = ATV_LEARNED_PLAY;
541       return ;
542     
543     // learned pause
544     case 786609:
545       *result = ATV_LEARNED_PAUSE;
546       return ;
547     
548     // learned stop
549     case 786615:
550       *result = ATV_LEARNED_STOP;
551       return ;
552     
553     // learned next
554     case 786613:
555       *result = ATV_LEARNED_NEXT;
556       return ;
557     
558     // learned previous
559     case 786614:
560       *result = ATV_LEARNED_PREVIOUS;
561       return ;
562     
563     // learned enter, like go into something
564     case 786630:
565       *result = ATV_LEARNED_ENTER;
566       return ;
567     
568     // learned return, like go back
569     case 786631:
570       *result = ATV_LEARNED_RETURN;
571       return ;
572     
573     // tap play on new Al IR remote
574     case kBREventRemoteActionALPlay:
575     case 786637:
576       if (originator == kBREventOriginatorKeyboard) // on bt keyboard alplay == space!
577         *result = ATV_BTKEYPRESS;
578       else
579         *result = ATV_ALUMINIUM_PLAY;
580       return ;
581
582     case kBREventRemoteActionKeyPress:
583     case kBREventRemoteActionKeyPress42:
584       *isRepeatable = true;
585       if (originator == kBREventOriginatorKeyboard) // only valid on bt keyboard
586         *result = ATV_BTKEYPRESS;
587       else
588         *result = ATV_INVALID_BUTTON;
589       return ;
590
591     case kBREventRemoteActionKeyTab:
592       *isRepeatable = true;
593       if (originator == kBREventOriginatorKeyboard) // only valid on bt keyboard
594         *result = ATV_BTKEYPRESS;
595       else
596         *result = ATV_INVALID_BUTTON;
597       return ;
598       
599     // PageUp
600     case kBREventRemoteActionPageUp:
601       *result = ATV_BUTTON_PAGEUP;
602       return ;
603     
604     // PageDown
605     case kBREventRemoteActionPageDown:
606       *result = ATV_BUTTON_PAGEDOWN;
607       return ;
608     
609     // Pause
610     case kBREventRemoteActionPause:
611       *result = ATV_BUTTON_PAUSE;
612       return ;
613     
614     // Play2
615     case kBREventRemoteActionPlay2:
616       *result = ATV_BUTTON_PLAY2;
617       return ;
618     
619     // Stop
620     case kBREventRemoteActionStop:
621       *result = ATV_BUTTON_STOP;
622       return ;
623     
624     // Fast Forward
625     case kBREventRemoteActionFastFwd:
626     case kBREventRemoteActionFastFwd2:
627       *isRepeatable = true;
628       if([f_event value] == 1)
629         *isPressed = true;
630       *result = ATV_BUTTON_FASTFWD;
631       return;
632     
633     // Rewind
634     case kBREventRemoteActionRewind:
635     case kBREventRemoteActionRewind2:
636       *isRepeatable = true;
637       if([f_event value] == 1)
638         *isPressed = true;
639       *result = ATV_BUTTON_REWIND;
640       return;
641
642     // Skip Forward
643     case kBREventRemoteActionSkipFwd:
644       *result = ATV_BUTTON_SKIPFWD;
645       return ;
646
647     // Skip Back      
648     case kBREventRemoteActionSkipBack:
649       *result = ATV_BUTTON_SKIPBACK;
650       return ;
651     
652     // Gesture Swipe Left
653     case kBREventRemoteActionSwipeLeft:
654       if ([f_event value] == 1)
655         *result = ATV_GESTURE_SWIPE_LEFT;
656       else
657         *result = ATV_INVALID_BUTTON;
658       return ;
659     
660     // Gesture Swipe Right
661     case kBREventRemoteActionSwipeRight:
662       if ([f_event value] == 1)
663         *result = ATV_GESTURE_SWIPE_RIGHT;
664       else
665         *result = ATV_INVALID_BUTTON;
666       return ;
667
668     // Gesture Swipe Up
669     case kBREventRemoteActionSwipeUp:
670       if ([f_event value] == 1)
671         *result = ATV_GESTURE_SWIPE_UP;
672       else
673         *result = ATV_INVALID_BUTTON;
674       return ;
675     
676     // Gesture Swipe Down
677     case kBREventRemoteActionSwipeDown:
678       if ([f_event value] == 1)
679         *result = ATV_GESTURE_SWIPE_DOWN;
680       else
681         *result = ATV_INVALID_BUTTON;
682       return;
683
684     // Gesture Flick Left
685     case kBREventRemoteActionFlickLeft:
686       if ([f_event value] == 1)
687         *result = ATV_GESTURE_FLICK_LEFT;
688       else
689         *result = ATV_INVALID_BUTTON;
690       return;
691     
692     // Gesture Flick Right
693     case kBREventRemoteActionFlickRight:
694       if ([f_event value] == 1)
695         *result = ATV_GESTURE_FLICK_RIGHT;
696       else
697         *result = ATV_INVALID_BUTTON;
698       return;
699     
700     // Gesture Flick Up
701     case kBREventRemoteActionFlickUp:
702       if ([f_event value] == 1)
703         *result = ATV_GESTURE_FLICK_UP;
704       else
705         *result = ATV_INVALID_BUTTON;
706       return;
707     
708     // Gesture Flick Down
709     case kBREventRemoteActionFlickDown:
710       if ([f_event value] == 1)
711         *result = ATV_GESTURE_FLICK_DOWN;
712       else
713         *result = ATV_INVALID_BUTTON;
714       return;
715
716     default:
717       ELOG(@"XBMCPureController: Unknown button press remoteAction = %i", remoteAction);
718       *result = ATV_INVALID_BUTTON;
719   }
720 }
721
722 static void XBMCController$setUserEvent(XBMCController* self, SEL _cmd, int eventId, unsigned int holdTime) 
723 {
724   
725   XBMC_Event newEvent;
726   memset(&newEvent, 0, sizeof(newEvent));
727
728   newEvent.type = XBMC_USEREVENT;
729   newEvent.jbutton.which = eventId;
730   newEvent.jbutton.holdTime = holdTime;
731   CWinEvents::MessagePush(&newEvent);
732 }
733
734 static unsigned int XBMCController$appleModKeyToXbmcModKey(XBMCController* self, SEL _cmd, unsigned int appleModifier)
735 {
736   unsigned int xbmcModifier = XBMCKMOD_NONE;
737   // shift left
738   if (appleModifier & kBREventModifierShiftLeft)
739     xbmcModifier |= XBMCKMOD_LSHIFT;
740   // shift right
741   if (appleModifier & kBREventModifierShiftRight)
742     xbmcModifier |= XBMCKMOD_RSHIFT;
743   // left ctrl
744   if (appleModifier & kBREventModifierCtrlLeft)
745     xbmcModifier |= XBMCKMOD_LCTRL;
746   // left alt/option
747   if (appleModifier & kBREventModifierOptionLeft)
748     xbmcModifier |= XBMCKMOD_LALT;
749   // right alt/altgr/option
750   if (appleModifier & kBREventModifierOptionRight)
751     xbmcModifier |= XBMCKMOD_RALT;
752   // left command
753   if (appleModifier & kBREventModifierCommandLeft)
754     xbmcModifier |= XBMCKMOD_LMETA;
755   // right command
756   if (appleModifier & kBREventModifierCommandRight)
757     xbmcModifier |= XBMCKMOD_RMETA;
758
759   return xbmcModifier;
760 }
761
762 static BOOL XBMCController$brEventAction(XBMCController* self, SEL _cmd, BREvent* event) 
763 {
764   //NSLog(@"%s", __PRETTY_FUNCTION__);
765
766   if ([[self glView] isAnimating])
767   {
768     BOOL is_handled = NO;
769     bool isRepeatable = false;
770     bool isPressed = false;
771     int xbmc_ir_key = ATV_INVALID_BUTTON;
772     [self ATVClientEventFromBREvent:event 
773                                         Repeatable:&isRepeatable
774                                         ButtonState:&isPressed
775                                         Result:&xbmc_ir_key];
776
777     if ( xbmc_ir_key != ATV_INVALID_BUTTON )
778     {
779       if (xbmc_ir_key == ATV_BTKEYPRESS)
780       {
781         XBMC_Event newEvent;
782         memset(&newEvent, 0, sizeof(newEvent));
783
784         NSDictionary *dict = [event eventDictionary];
785         NSString *key_nsstring = [dict objectForKey:@"kBRKeyEventCharactersKey"];
786         unsigned int modifier = [[dict objectForKey:@"kBRKeyEventModifiersKey"] unsignedIntValue];
787         bool fireTheKey = false;
788
789         if (key_nsstring != nil && [key_nsstring length] == 1)
790         {
791           //ns_string contains the letter you want to input
792           //unichar c = [key_nsstring characterAtIndex:0];
793           //keyEvent = translateCocoaToXBMCEvent(c);
794           const char* wstr = [key_nsstring cStringUsingEncoding:NSUTF16StringEncoding];
795           //NSLog(@"%s, key: wstr[0] = %d, wstr[1] = %d", __PRETTY_FUNCTION__, wstr[0], wstr[1]);
796
797           if (wstr[0] != 92)
798           {
799             if (wstr[0] == 62 && wstr[1] == -9)
800             {
801               // stupid delete key
802               newEvent.key.keysym.sym = (XBMCKey)8;
803               newEvent.key.keysym.unicode = 8;
804             }
805             else
806             {
807               newEvent.key.keysym.sym = (XBMCKey)wstr[0];
808               newEvent.key.keysym.unicode = wstr[0] | (wstr[1] << 8);
809             }
810             fireTheKey = true;
811           }
812         }
813         else // this must be one of those duped functions when using the bt keyboard
814         {
815           int remoteAction = [event remoteAction];
816           fireTheKey = true;
817           switch (remoteAction)
818           {
819             case kBREventRemoteActionALPlay:// play maps to space
820             case 786637:
821               newEvent.key.keysym.sym = XBMCK_SPACE;
822               newEvent.key.keysym.unicode = XBMCK_SPACE;
823               break;
824             case kBREventRemoteActionMenu:// menu maps to escape!
825             case 65670:
826               newEvent.key.keysym.sym = XBMCK_ESCAPE;
827               newEvent.key.keysym.unicode = XBMCK_ESCAPE;
828               break;
829             case kBREventRemoteActionKeyTab:
830               newEvent.key.keysym.sym = XBMCK_TAB;
831               newEvent.key.keysym.unicode = XBMCK_TAB;
832               break;
833             case kBREventRemoteActionPlay:// play maps to return
834             case 65673:
835               newEvent.key.keysym.sym = XBMCK_RETURN;
836               newEvent.key.keysym.unicode = XBMCK_RETURN;
837               break;
838             default: // unsupported duped function
839               fireTheKey = false;
840               break;
841           }
842         }
843
844         if (fireTheKey && (!isRepeatable || [event value] == 1)) // some keys might be repeatable - only fire once here
845         {
846           newEvent.key.keysym.mod = (XBMCMod)[self appleModKeyToXbmcModKey:modifier];
847           newEvent.type = XBMC_KEYDOWN;
848           CWinEvents::MessagePush(&newEvent);
849           newEvent.type = XBMC_KEYUP;
850           CWinEvents::MessagePush(&newEvent);
851           is_handled = TRUE;
852         }
853       }
854       else
855       {
856         if(isRepeatable)
857         {
858           if(isPressed)
859           {
860             [self setUserEvent:xbmc_ir_key withHoldTime:0];
861             [self startKeyPressTimer:xbmc_ir_key];
862           }
863           else
864           {
865             //stop the timer
866             [self stopKeyPressTimer];
867           }
868         }
869         else
870         {
871           [self setUserEvent:xbmc_ir_key withHoldTime:0];
872         }
873         is_handled = TRUE;
874       }
875     }
876     return is_handled;
877   }
878   else
879   {
880     return XBMCController$brEventAction$Orig(self, _cmd, event);
881   }
882 }
883
884 #pragma mark -
885 #pragma mark private helper methods
886 static void XBMCController$startKeyPressTimer(XBMCController* self, SEL _cmd, int keyId) 
887
888   NSNumber *number = [NSNumber numberWithInt:keyId];
889   NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSDate date], @"StartDate", 
890                                                                   number, @"keyId", nil];
891   
892   NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:REPEATED_KEYPRESS_DELAY_S];
893   [self stopKeyPressTimer];
894   
895   //schedule repeated timer which starts after REPEATED_KEYPRESS_DELAY_S and fires
896   //every REPEATED_KEYPRESS_PAUSE_S
897   NSTimer *timer       = [[NSTimer alloc] initWithFireDate:fireDate 
898                                       interval:REPEATED_KEYPRESS_PAUSE_S 
899                                       target:self 
900                                       selector:@selector(keyPressTimerCallback:) 
901                                       userInfo:dict 
902                                       repeats:YES];
903  
904   //schedule the timer to the runloop
905   NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
906   [runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
907   [self setKeyTimer:timer];
908
909
910 static void XBMCController$stopKeyPressTimer(XBMCController* self, SEL _cmd) 
911 {
912   if([self keyTimer] != nil)
913   {
914     [[self keyTimer] invalidate];
915     [[self keyTimer] release];
916     [self setKeyTimer:nil];
917   }
918 }
919
920 static void XBMCController$keyPressTimerCallback(XBMCController* self, SEL _cmd, NSTimer* theTimer)  
921
922   //if queue is empty - skip this timer event
923   //for letting it process
924   if(CWinEvents::GetQueueSize())
925     return;
926
927   NSDate *startDate = [[theTimer userInfo] objectForKey:@"StartDate"];
928   int keyId = [[[theTimer userInfo] objectForKey:@"keyId"] intValue];
929   //calc the holdTime - timeIntervalSinceNow gives the
930   //passed time since startDate in seconds as negative number
931   //so multiply with -1000 for getting the positive ms
932   NSTimeInterval holdTime = [startDate timeIntervalSinceNow] * -1000.0f;
933   [self setUserEvent:keyId withHoldTime:(unsigned int)holdTime];
934
935
936 static void XBMCController$observeDefaultCenterStuff(XBMCController* self, SEL _cmd, NSNotification * notification) 
937 {
938   //NSLog(@"default: %@", [notification name]);
939
940   if ([notification name] == UIApplicationDidReceiveMemoryWarningNotification)
941     NSLog(@"XBMC: %@", [notification name]);
942   
943   //if ([notification name] == kBRScreenSaverActivated)
944   //  [m_glView stopAnimation];
945   
946   //if ([notification name] == kBRScreenSaverDismissed)
947   //  [m_glView startAnimation];
948 }
949
950 static void XBMCController$disableSystemSleep(XBMCController* self, SEL _cmd) 
951 {
952   Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade");
953   XBMCSettingsFacade *single = (XBMCSettingsFacade *)[ATVSettingsFacadeCls singleton];
954
955   int tmpTimeout = [single sleepTimeout];
956   NSNumber *timeout = [NSNumber numberWithInt:tmpTimeout];
957   [self setSystemSleepTimeout:timeout];
958   [single setSleepTimeout: -1];
959   [single flushDiskChanges];
960 }
961
962 static void XBMCController$enableSystemSleep(XBMCController* self, SEL _cmd) 
963 {
964   Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade");
965   int timeoutInt = [[self systemSleepTimeout] intValue];
966   [[ATVSettingsFacadeCls singleton] setSleepTimeout:timeoutInt];
967   [[ATVSettingsFacadeCls singleton] flushDiskChanges];
968 }
969
970 static void XBMCController$disableScreenSaver(XBMCController* self, SEL _cmd) 
971 {
972   //NSLog(@"%s", __PRETTY_FUNCTION__);
973   //store screen saver state and disable it
974
975   Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade");
976   XBMCSettingsFacade *single = (XBMCSettingsFacade *)[ATVSettingsFacadeCls singleton];
977
978   int tmpTimeout = [single screenSaverTimeout];
979   NSNumber *timeout = [NSNumber numberWithInt:tmpTimeout];
980   [self setSystemScreenSaverTimeout:timeout];
981   [single setScreenSaverTimeout: -1];
982   [single flushDiskChanges];
983
984   // breaks in 4.2.1 [[BRBackgroundTaskManager singleton] holdOffBackgroundTasks];
985 }
986
987 static void XBMCController$enableScreenSaver(XBMCController* self, SEL _cmd) 
988 {
989   //NSLog(@"%s", __PRETTY_FUNCTION__);
990   //reset screen saver to user settings
991   Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade");
992
993   int timeoutInt = [[self systemScreenSaverTimeout] intValue];
994   [[ATVSettingsFacadeCls singleton] setScreenSaverTimeout:timeoutInt];
995   [[ATVSettingsFacadeCls singleton] flushDiskChanges];
996
997   // breaks in 4.2.1 [[BRBackgroundTaskManager singleton] okToDoBackgroundProcessing];
998 }
999
1000 /*
1001 - (XBMC_Event) translateCocoaToXBMCEvent: (unichar) c
1002 {
1003   XBMC_Event newEvent;
1004   memset(&newEvent, 0, sizeof(newEvent));
1005   
1006    switch (c)
1007    {
1008    // Alt
1009    case NSMenuFunctionKey: 
1010    return "Alt";
1011    
1012    // "Apps"
1013    // "BrowserBack"
1014    // "BrowserForward"
1015    // "BrowserHome"
1016    // "BrowserRefresh"
1017    // "BrowserSearch"
1018    // "BrowserStop"
1019    // "CapsLock"
1020    
1021    // "Clear"
1022    case NSClearLineFunctionKey:
1023    return "Clear";
1024    
1025    // "CodeInput"
1026    // "Compose"
1027    // "Control"
1028    // "Crsel"
1029    // "Convert"
1030    // "Copy"
1031    // "Cut"
1032    
1033    // "Down"
1034    case NSDownArrowFunctionKey:
1035    return "Down";
1036    // "End"
1037    case NSEndFunctionKey:
1038    return "End";
1039    // "Enter"
1040    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
1041    return "Enter";
1042    
1043    // "EraseEof"
1044    
1045    // "Execute"
1046    case NSExecuteFunctionKey:
1047    return "Execute";
1048    
1049    // "Exsel"
1050    
1051    // "F1"
1052    case NSF1FunctionKey:
1053    return "F1";
1054    // "F2"
1055    case NSF2FunctionKey:
1056    return "F2";
1057    // "F3"
1058    case NSF3FunctionKey:
1059    return "F3";
1060    // "F4"
1061    case NSF4FunctionKey:
1062    return "F4";
1063    // "F5"
1064    case NSF5FunctionKey:
1065    return "F5";
1066    // "F6"
1067    case NSF6FunctionKey:
1068    return "F6";
1069    // "F7"
1070    case NSF7FunctionKey:
1071    return "F7";
1072    // "F8"
1073    case NSF8FunctionKey:
1074    return "F8";
1075    // "F9"
1076    case NSF9FunctionKey:
1077    return "F9";
1078    // "F10"
1079    case NSF10FunctionKey:
1080    return "F10";
1081    // "F11"
1082    case NSF11FunctionKey:
1083    return "F11";
1084    // "F12"
1085    case NSF12FunctionKey:
1086    return "F12";
1087    // "F13"
1088    case NSF13FunctionKey:
1089    return "F13";
1090    // "F14"
1091    case NSF14FunctionKey:
1092    return "F14";
1093    // "F15"
1094    case NSF15FunctionKey:
1095    return "F15";
1096    // "F16"
1097    case NSF16FunctionKey:
1098    return "F16";
1099    // "F17"
1100    case NSF17FunctionKey:
1101    return "F17";
1102    // "F18"
1103    case NSF18FunctionKey:
1104    return "F18";
1105    // "F19"
1106    case NSF19FunctionKey:
1107    return "F19";
1108    // "F20"
1109    case NSF20FunctionKey:
1110    return "F20";
1111    // "F21"
1112    case NSF21FunctionKey:
1113    return "F21";
1114    // "F22"
1115    case NSF22FunctionKey:
1116    return "F22";
1117    // "F23"
1118    case NSF23FunctionKey:
1119    return "F23";
1120    // "F24"
1121    case NSF24FunctionKey:
1122    return "F24";
1123    
1124    // "FinalMode"
1125    
1126    // "Find"
1127    case NSFindFunctionKey:
1128    return "Find";
1129    
1130    // "FullWidth"
1131    // "HalfWidth"
1132    // "HangulMode"
1133    // "HanjaMode"
1134    
1135    // "Help"
1136    case NSHelpFunctionKey:
1137    return "Help";
1138    
1139    // "Hiragana"
1140    
1141    // "Home"
1142    case NSHomeFunctionKey:
1143    return "Home";
1144    // "Insert"
1145    case NSInsertFunctionKey:
1146    return "Insert";
1147    
1148    // "JapaneseHiragana"
1149    // "JapaneseKatakana"
1150    // "JapaneseRomaji"
1151    // "JunjaMode"
1152    // "KanaMode"
1153    // "KanjiMode"
1154    // "Katakana"
1155    // "LaunchApplication1"
1156    // "LaunchApplication2"
1157    // "LaunchMail"
1158    
1159    // "Left"
1160    case NSLeftArrowFunctionKey:
1161    return "Left";
1162    
1163    // "Meta"
1164    // "MediaNextTrack"
1165    // "MediaPlayPause"
1166    // "MediaPreviousTrack"
1167    // "MediaStop"
1168    
1169    // "ModeChange"
1170    case NSModeSwitchFunctionKey:
1171    return "ModeChange";
1172    
1173    // "Nonconvert"
1174    // "NumLock"
1175    
1176    // "PageDown"
1177    case NSPageDownFunctionKey:
1178    return "PageDown";
1179    // "PageUp"
1180    case NSPageUpFunctionKey:
1181    return "PageUp";
1182    
1183    // "Paste"
1184    
1185    // "Pause"
1186    case NSPauseFunctionKey:
1187    return "Pause";
1188    
1189    // "Play"
1190    // "PreviousCandidate"
1191    
1192    // "PrintScreen"
1193    case NSPrintScreenFunctionKey:
1194    return "PrintScreen";
1195    
1196    // "Process"
1197    // "Props"
1198    
1199    // "Right"
1200    case NSRightArrowFunctionKey:
1201    return "Right";
1202    
1203    // "RomanCharacters"
1204    
1205    // "Scroll"
1206    case NSScrollLockFunctionKey:
1207    return "Scroll";
1208    // "Select"
1209    case NSSelectFunctionKey:
1210    return "Select";
1211    
1212    // "SelectMedia"
1213    // "Shift"
1214    
1215    // "Stop"
1216    case NSStopFunctionKey:
1217    return "Stop";
1218    // "Up"
1219    case NSUpArrowFunctionKey:
1220    return "Up";
1221    // "Undo"
1222    case NSUndoFunctionKey:
1223    return "Undo";
1224    
1225    // "VolumeDown"
1226    // "VolumeMute"
1227    // "VolumeUp"
1228    // "Win"
1229    // "Zoom"
1230    
1231    // More function keys, not in the key identifier specification.
1232    case NSF25FunctionKey:
1233    return "F25";
1234    case NSF26FunctionKey:
1235    return "F26";
1236    case NSF27FunctionKey:
1237    return "F27";
1238    case NSF28FunctionKey:
1239    return "F28";
1240    case NSF29FunctionKey:
1241    return "F29";
1242    case NSF30FunctionKey:
1243    return "F30";
1244    case NSF31FunctionKey:
1245    return "F31";
1246    case NSF32FunctionKey:
1247    return "F32";
1248    case NSF33FunctionKey:
1249    return "F33";
1250    case NSF34FunctionKey:
1251    return "F34";
1252    case NSF35FunctionKey:
1253    return "F35";
1254    
1255    // Turn 0x7F into 0x08, because backspace needs to always be 0x08.
1256    case 0x7F:
1257    XBMCK_BACKSPACE
1258    // Standard says that DEL becomes U+007F.
1259    case NSDeleteFunctionKey:
1260    XBMCK_DELETE;
1261    
1262    // Always use 0x09 for tab instead of AppKit's backtab character.
1263    case NSBackTabCharacter:
1264    return "U+0009";
1265    
1266    case NSBeginFunctionKey:
1267    case NSBreakFunctionKey:
1268    case NSClearDisplayFunctionKey:
1269    case NSDeleteCharFunctionKey:
1270    case NSDeleteLineFunctionKey:
1271    case NSInsertCharFunctionKey:
1272    case NSInsertLineFunctionKey:
1273    case NSNextFunctionKey:
1274    case NSPrevFunctionKey:
1275    case NSPrintFunctionKey:
1276    case NSRedoFunctionKey:
1277    case NSResetFunctionKey:
1278    case NSSysReqFunctionKey:
1279    case NSSystemFunctionKey:
1280    case NSUserFunctionKey:
1281           // FIXME: We should use something other than the vendor-area Unicode values for the above keys.
1282           // For now, just fall through to the default.
1283       default:
1284           return String::format("U+%04X", toASCIIUpper(c));
1285   }
1286   return newEvent;
1287 }*/
1288
1289 //--------------------------------------------------------------
1290 static void XBMCController$pauseAnimation(XBMCController* self, SEL _cmd) 
1291 {
1292   XBMC_Event newEvent;
1293   memset(&newEvent, 0, sizeof(XBMC_Event));
1294  
1295   newEvent.appcommand.type = XBMC_APPCOMMAND;
1296   newEvent.appcommand.action = ACTION_PLAYER_PLAYPAUSE;
1297   CWinEvents::MessagePush(&newEvent);
1298   
1299   Sleep(2000); 
1300   [[self glView] pauseAnimation];
1301 }
1302 //--------------------------------------------------------------
1303 static void XBMCController$resumeAnimation(XBMCController* self, SEL _cmd) 
1304 {  
1305   NSLog(@"%s", __PRETTY_FUNCTION__);
1306
1307   XBMC_Event newEvent;
1308   memset(&newEvent, 0, sizeof(XBMC_Event));
1309
1310   newEvent.appcommand.type = XBMC_APPCOMMAND;
1311   newEvent.appcommand.action = ACTION_PLAYER_PLAY;
1312   CWinEvents::MessagePush(&newEvent);
1313  
1314   [[self glView] resumeAnimation];
1315 }
1316 //--------------------------------------------------------------
1317 static void XBMCController$startAnimation(XBMCController* self, SEL _cmd) 
1318 {
1319   NSLog(@"%s", __PRETTY_FUNCTION__);
1320
1321   [[self glView] startAnimation];
1322 }
1323 //--------------------------------------------------------------
1324 static void XBMCController$stopAnimation(XBMCController* self, SEL _cmd) 
1325 {
1326   NSLog(@"%s", __PRETTY_FUNCTION__);
1327
1328   [[self glView] stopAnimation];
1329 }
1330 //--------------------------------------------------------------
1331 static bool XBMCController$changeScreen(XBMCController* self, SEL _cmd, unsigned int screenIdx, UIScreenMode * mode) 
1332 {
1333   return [[IOSScreenManager sharedInstance] changeScreen: screenIdx withMode: mode];
1334 }
1335 //--------------------------------------------------------------
1336 static void XBMCController$activateScreen(XBMCController* self, SEL _cmd, UIScreen * screen) 
1337 {
1338 }
1339
1340 // SECTIONCOMMENT
1341 // c'tor - this sets up our class at runtime by 
1342 // 1. subclassing from the base classes
1343 // 2. adding new methods to our class
1344 // 3. exchanging (hooking) base class methods with ours
1345 // 4. register the classes to the objc runtime system
1346 static __attribute__((constructor)) void initControllerRuntimeClasses() 
1347 {
1348   char _typeEncoding[1024];
1349   unsigned int i = 0;
1350
1351   // subclass BRController into XBMCController
1352   Class XBMCControllerCls = objc_allocateClassPair(objc_getClass("BRController"), "XBMCController", 0);
1353   // add our custom methods which are not part of the baseclass
1354   // XBMCController::keyTimer
1355   class_addMethod(XBMCControllerCls, @selector(keyTimer), (IMP)&XBMCController$keyTimer, "@@:");
1356   // XBMCController::setKeyTimer
1357   class_addMethod(XBMCControllerCls, @selector(setKeyTimer:), (IMP)&XBMCController$setKeyTimer, "v@:@");
1358   // XBMCController::glView
1359   class_addMethod(XBMCControllerCls, @selector(glView), (IMP)&XBMCController$glView, "@@:");
1360   // XBMCController::setGlView
1361   class_addMethod(XBMCControllerCls, @selector(setGlView:), (IMP)&XBMCController$setGlView, "v@:@");
1362   // XBMCController::systemScreenSaverTimeout
1363   class_addMethod(XBMCControllerCls, @selector(systemScreenSaverTimeout), (IMP)&XBMCController$systemScreenSaverTimeout, "@@:");
1364   // XBMCController::setSystemScreenSaverTimeout
1365   class_addMethod(XBMCControllerCls, @selector(setSystemScreenSaverTimeout:), (IMP)&XBMCController$setSystemScreenSaverTimeout, "v@:@");
1366   // XBMCController::systemSleepTimeout
1367   class_addMethod(XBMCControllerCls, @selector(systemSleepTimeout), (IMP)&XBMCController$systemSleepTimeout, "@@:");
1368   // XBMCController::setSystemSleepTimeout
1369   class_addMethod(XBMCControllerCls, @selector(setSystemSleepTimeout:), (IMP)&XBMCController$setSystemSleepTimeout, "v@:@");
1370   // XBMCController::applicationDidExit
1371   class_addMethod(XBMCControllerCls, @selector(applicationDidExit), (IMP)&XBMCController$applicationDidExit, "v@:");
1372   // XBMCController::initDisplayLink
1373   class_addMethod(XBMCControllerCls, @selector(initDisplayLink), (IMP)&XBMCController$initDisplayLink, "v@:");
1374   // XBMCController::deinitDisplayLink
1375   class_addMethod(XBMCControllerCls, @selector(deinitDisplayLink), (IMP)&XBMCController$deinitDisplayLink, "v@:");
1376   // XBMCController::getDisplayLinkFPS
1377   class_addMethod(XBMCControllerCls, @selector(getDisplayLinkFPS), (IMP)&XBMCController$getDisplayLinkFPS, "d@:");
1378   // XBMCController::setFramebuffer
1379   class_addMethod(XBMCControllerCls, @selector(setFramebuffer), (IMP)&XBMCController$setFramebuffer, "v@:");
1380   // XBMCController::presentFramebuffer
1381   class_addMethod(XBMCControllerCls, @selector(presentFramebuffer), (IMP)&XBMCController$presentFramebuffer, "B@:");
1382   // XBMCController::setUserEvent
1383   class_addMethod(XBMCControllerCls, @selector(setUserEvent:withHoldTime:), (IMP)&XBMCController$setUserEvent, "v@:iI");  
1384   // XBMCController::appleModKeyToXbmcModKey
1385   class_addMethod(XBMCControllerCls, @selector(appleModKeyToXbmcModKey:), (IMP)&XBMCController$appleModKeyToXbmcModKey, "I@:I");
1386   // XBMCController::startKeyPressTimer
1387   class_addMethod(XBMCControllerCls, @selector(startKeyPressTimer:), (IMP)&XBMCController$startKeyPressTimer, "v@:i");  
1388   // XBMCController::stopKeyPressTimer
1389   class_addMethod(XBMCControllerCls, @selector(stopKeyPressTimer), (IMP)&XBMCController$stopKeyPressTimer, "v@:");
1390   // XBMCController::disableSystemSleep
1391   class_addMethod(XBMCControllerCls, @selector(disableSystemSleep), (IMP)&XBMCController$disableSystemSleep, "v@:");
1392   // XBMCController__enableSystemSleep
1393   class_addMethod(XBMCControllerCls, @selector(enableSystemSleep), (IMP)&XBMCController$enableSystemSleep, "v@:");
1394   // XBMCController::disableScreenSaver
1395   class_addMethod(XBMCControllerCls, @selector(disableScreenSaver), (IMP)&XBMCController$disableScreenSaver, "v@:");
1396   // XBMCController::enableScreenSaver
1397   class_addMethod(XBMCControllerCls, @selector(enableScreenSaver), (IMP)&XBMCController$enableScreenSaver, "v@:");
1398   // XBMCController::pauseAnimation
1399   class_addMethod(XBMCControllerCls, @selector(pauseAnimation), (IMP)&XBMCController$pauseAnimation, "v@:");
1400   // XBMCController::resumeAnimation
1401   class_addMethod(XBMCControllerCls, @selector(resumeAnimation), (IMP)&XBMCController$resumeAnimation, "v@:");
1402   // XBMCController::startAnimation
1403   class_addMethod(XBMCControllerCls, @selector(startAnimation), (IMP)&XBMCController$startAnimation, "v@:");
1404   // XBMCController::stopAnimation
1405   class_addMethod(XBMCControllerCls, @selector(stopAnimation), (IMP)&XBMCController$stopAnimation, "v@:");
1406
1407   i = 0;
1408   memcpy(_typeEncoding + i, @encode(CGSize), strlen(@encode(CGSize)));
1409   i += strlen(@encode(CGSize));
1410   _typeEncoding[i] = '@';
1411   i += 1;
1412   _typeEncoding[i] = ':';
1413   i += 1;
1414   _typeEncoding[i] = '\0';
1415   // XBMCController::getScreenSize
1416   class_addMethod(XBMCControllerCls, @selector(getScreenSize), (IMP)&XBMCController$getScreenSize, _typeEncoding);
1417
1418   i = 0;
1419   _typeEncoding[i] = 'v';
1420   i += 1;
1421   _typeEncoding[i] = '@';
1422   i += 1;
1423   _typeEncoding[i] = ':';
1424   i += 1;
1425   memcpy(_typeEncoding + i, @encode(XBMCKey), strlen(@encode(XBMCKey)));
1426   i += strlen(@encode(XBMCKey));
1427   _typeEncoding[i] = '\0';
1428   // XBMCController::sendKey
1429   class_addMethod(XBMCControllerCls, @selector(sendKey:), (IMP)&XBMCController$sendKey, _typeEncoding);
1430  
1431   i = 0;
1432   _typeEncoding[i] = 'v';
1433   i += 1;
1434   _typeEncoding[i] = '@';
1435   i += 1;
1436   _typeEncoding[i] = ':';
1437   i += 1;
1438   memcpy(_typeEncoding + i, @encode(BREvent*), strlen(@encode(BREvent*)));
1439   i += strlen(@encode(BREvent*));
1440   _typeEncoding[i] = '^';
1441   _typeEncoding[i + 1] = 'B';
1442   i += 2;
1443   _typeEncoding[i] = '^';
1444   _typeEncoding[i + 1] = 'B';
1445   i += 2;
1446   _typeEncoding[i] = '^';
1447   _typeEncoding[i + 1] = 'i';
1448   i += 2;
1449   _typeEncoding[i] = '\0';
1450   // XBMCController::ATVClientEventFromBREvent
1451   class_addMethod(XBMCControllerCls, @selector(ATVClientEventFromBREvent:Repeatable:ButtonState:Result:), (IMP)&XBMCController$ATVClientEventFromBREvent, _typeEncoding);
1452
1453   i = 0;
1454   _typeEncoding[i] = 'v';
1455   i += 1;
1456   _typeEncoding[i] = '@';
1457   i += 1;
1458   _typeEncoding[i] = ':';
1459   i += 1;
1460   memcpy(_typeEncoding + i, @encode(NSTimer*), strlen(@encode(NSTimer*)));
1461   i += strlen(@encode(NSTimer*));
1462   _typeEncoding[i] = '\0';
1463   // XBMCController::keyPressTimerCallback
1464   class_addMethod(XBMCControllerCls, @selector(keyPressTimerCallback:), (IMP)&XBMCController$keyPressTimerCallback, _typeEncoding);
1465  
1466   i = 0;
1467   _typeEncoding[i] = 'v';
1468   i += 1;
1469   _typeEncoding[i] = '@';
1470   i += 1;
1471   _typeEncoding[i] = ':';
1472   i += 1;
1473   memcpy(_typeEncoding + i, @encode(NSNotification *), strlen(@encode(NSNotification *)));
1474   i += strlen(@encode(NSNotification *));
1475   _typeEncoding[i] = '\0';
1476   // XBMCController:observeDefaultCenterStuff
1477   class_addMethod(XBMCControllerCls, @selector(observeDefaultCenterStuff:), (IMP)&XBMCController$observeDefaultCenterStuff, _typeEncoding);
1478
1479   i = 0;
1480   _typeEncoding[i] = 'B';
1481   i += 1;
1482   _typeEncoding[i] = '@';
1483   i += 1;
1484   _typeEncoding[i] = ':';
1485   i += 1;
1486   _typeEncoding[i] = 'I';
1487   i += 1;
1488   memcpy(_typeEncoding + i, @encode(UIScreenMode *), strlen(@encode(UIScreenMode *)));
1489   i += strlen(@encode(UIScreenMode *));
1490   _typeEncoding[i] = '\0';
1491   // XBMCController::changeScreen
1492   class_addMethod(XBMCControllerCls, @selector(changeScreen:withMode:), (IMP)&XBMCController$changeScreen, _typeEncoding);
1493
1494   i = 0;
1495   _typeEncoding[i] = 'v';
1496   i += 1;
1497   _typeEncoding[i] = '@';
1498   i += 1;
1499   _typeEncoding[i] = ':';
1500   i += 1;
1501   memcpy(_typeEncoding + i, @encode(UIScreen *), strlen(@encode(UIScreen *)));
1502   i += strlen(@encode(UIScreen *));
1503   _typeEncoding[i] = '\0';
1504   // XBMCController::activateScreen$
1505   class_addMethod(XBMCControllerCls, @selector(activateScreen:), (IMP)&XBMCController$activateScreen, _typeEncoding);
1506
1507   // and hook up our methods (implementation of the base class methods)
1508   // XBMCController::brEventAction
1509   MSHookMessageEx(XBMCControllerCls, @selector(brEventAction:), (IMP)&XBMCController$brEventAction, (IMP*)&XBMCController$brEventAction$Orig); 
1510   // XBMCController::init
1511   MSHookMessageEx(XBMCControllerCls, @selector(init), (IMP)&XBMCController$init, (IMP*)&XBMCController$init$Orig);
1512   // XBMCController::dealloc
1513   MSHookMessageEx(XBMCControllerCls, @selector(dealloc), (IMP)&XBMCController$dealloc, (IMP*)&XBMCController$dealloc$Orig);
1514   // XBMCController::controlWasActivated
1515   MSHookMessageEx(XBMCControllerCls, @selector(controlWasActivated), (IMP)&XBMCController$controlWasActivated, (IMP*)&XBMCController$controlWasActivated$Orig);
1516   // XBMCController::controlWasDeactivated
1517   MSHookMessageEx(XBMCControllerCls, @selector(controlWasDeactivated), (IMP)&XBMCController$controlWasDeactivated, (IMP*)&XBMCController$controlWasDeactivated$Orig);
1518   // XBMCController::recreateOnReselect
1519   MSHookMessageEx(XBMCControllerCls, @selector(recreateOnReselect), (IMP)&XBMCController$recreateOnReselect, nil);
1520
1521   // and register the class to the runtime
1522   objc_registerClassPair(XBMCControllerCls);
1523   
1524   // save this as static for referencing it in multiple methods
1525   BRWindowCls = objc_getClass("BRWindow");
1526 }