2 * Copyright (C) 2010-2013 Team XBMC
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)
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.
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/>.
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).
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)
34 * 2. For implementing a new method which is not part of the base class:
38 * d) in initControllerRuntimeClasses add the method to our class via class_addMethod
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)
43 * 4. Keep the structure of this file based on the section comments (marked with // SECTIONCOMMENT).
44 * 5. really - obey 4.!
46 * 6. for adding class members use associated objects - see timerKey
48 * For further reference see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html
51 //hack around problem with xbmc's typedef int BOOL
52 // and obj-c's typedef unsigned char BOOL
53 #define BOOL XBMC_BOOL
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"
63 #import <Foundation/Foundation.h>
64 #import <UIKit/UIKit.h>
66 #import "XBMCController.h"
67 #import "XBMCDebugHelpers.h"
69 #import "IOSEAGLView.h"
70 #import "IOSSCreenManager.h"
71 #include "XBMC_keysym.h"
72 #include "substrate.h"
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
87 ATV_BUTTON_PLAY_H = 7,
88 ATV_BUTTON_MENU_H = 8,
89 ATV_BUTTON_LEFT_H = 9,
90 ATV_BUTTON_RIGHT_H = 10,
92 //new aluminium remote buttons
93 ATV_ALUMINIUM_PLAY = 12,
94 ATV_ALUMINIUM_PLAY_H = 11,
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,
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,
124 ATV_GESTURE_SWIPE_LEFT = 80,
125 ATV_GESTURE_SWIPE_RIGHT = 81,
126 ATV_GESTURE_SWIPE_UP = 82,
127 ATV_GESTURE_SWIPE_DOWN = 83,
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,
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,
153 kBREventRemoteActionALPlay = 10,
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,
166 kBREventRemoteActionPlayHold = 22,
167 kBREventRemoteActionCenterHold,
168 kBREventRemoteActionCenterHold42,
170 // Gestures, for originator kBREventOriginatorGesture
171 kBREventRemoteActionTouchBegin= 31,
172 kBREventRemoteActionTouchMove = 32,
173 kBREventRemoteActionTouchEnd = 33,
175 kBREventRemoteActionSwipeLeft = 34,
176 kBREventRemoteActionSwipeRight= 35,
177 kBREventRemoteActionSwipeUp = 36,
178 kBREventRemoteActionSwipeDown = 37,
180 kBREventRemoteActionFlickLeft = 38,
181 kBREventRemoteActionFlickRight= 39,
182 kBREventRemoteActionFlickUp = 40,
183 kBREventRemoteActionFlickDown = 41,
185 kBREventRemoteActionTouchHold = 46,
187 // keypresses, for originator kBREventOriginatorKeyboard
188 kBREventRemoteActionKeyPress = 47,
189 kBREventRemoteActionKeyPress42,
191 kBREventRemoteActionKeyTab = 53,
193 // Custom remote actions for old remote actions
194 kBREventRemoteActionHoldLeft = 0xfeed0001,
195 kBREventRemoteActionHoldRight,
196 kBREventRemoteActionHoldUp,
197 kBREventRemoteActionHoldDown,
198 } BREventRemoteAction;
201 kBREventModifierCommandLeft = 0x10000,
202 kBREventModifierShiftLeft = 0x20000,
203 kBREventModifierOptionLeft = 0x80000,
204 kBREventModifierCtrlLeft = 0x100000,
205 kBREventModifierShiftRight = 0x200000,
206 kBREventModifierOptionRight = 0x400000,
207 kBREventModifierCommandRight = 0x1000000,
211 kBREventOriginatorRemote = 1,
212 kBREventOriginatorKeyboard = 2,
213 kBREventOriginatorGesture = 3,
217 XBMCController *g_xbmcController;
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;
226 -(void)flushDiskChanges;
229 // notification messages
230 extern NSString* kBRScreenSaverActivated;
231 extern NSString* kBRScreenSaverDismissed;
233 //--------------------------------------------------------------
234 //--------------------------------------------------------------
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);
244 // classes we need multiple times
245 static Class BRWindowCls;
247 int padding[16];//obsolete? - was commented with "credit is due here to SapphireCompatibilityClasses!!"
249 //--------------------------------------------------------------
250 //--------------------------------------------------------------
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;
262 //implementation XBMCController
264 static id XBMCController$keyTimer(XBMCController* self, SEL _cmd)
266 return objc_getAssociatedObject(self, &timerKey);
269 static void XBMCController$setKeyTimer(XBMCController* self, SEL _cmd, id timer)
271 objc_setAssociatedObject(self, &timerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
274 static id XBMCController$glView(XBMCController* self, SEL _cmd)
276 return objc_getAssociatedObject(self, &glviewKey);
279 static void XBMCController$setGlView(XBMCController* self, SEL _cmd, id view)
281 objc_setAssociatedObject(self, &glviewKey, view, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
284 static id XBMCController$systemScreenSaverTimeout(XBMCController* self, SEL _cmd)
286 return objc_getAssociatedObject(self, &screensaverKey);
289 static void XBMCController$setSystemScreenSaverTimeout(XBMCController* self, SEL _cmd, id timeout)
291 objc_setAssociatedObject(self, &screensaverKey, timeout, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
294 static id XBMCController$systemSleepTimeout(XBMCController* self, SEL _cmd)
296 return objc_getAssociatedObject(self, &systemsleepKey);
299 static void XBMCController$setSystemSleepTimeout(XBMCController* self, SEL _cmd, id timeout)
301 objc_setAssociatedObject(self, &systemsleepKey, timeout, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
304 static void XBMCController$applicationDidExit(XBMCController* self, SEL _cmd)
306 //NSLog(@"%s", __PRETTY_FUNCTION__);
308 [[self glView] stopAnimation];
309 [self enableScreenSaver];
310 [self enableSystemSleep];
311 [[self stack] popController];
314 static void XBMCController$initDisplayLink(XBMCController* self, SEL _cmd)
316 //NSLog(@"%s", __PRETTY_FUNCTION__);
318 [[self glView] initDisplayLink];
321 static void XBMCController$deinitDisplayLink(XBMCController* self, SEL _cmd)
323 //NSLog(@"%s", __PRETTY_FUNCTION__);
325 [[self glView] deinitDisplayLink];
328 static double XBMCController$getDisplayLinkFPS(XBMCController* self, SEL _cmd)
330 //NSLog(@"%s", __PRETTY_FUNCTION__);
332 return [[self glView] getDisplayLinkFPS];
335 static void XBMCController$setFramebuffer(XBMCController* self, SEL _cmd)
337 [[self glView] setFramebuffer];
340 static bool XBMCController$presentFramebuffer(XBMCController* self, SEL _cmd)
342 return [[self glView] presentFramebuffer];
345 static CGSize XBMCController$getScreenSize(XBMCController* self, SEL _cmd)
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);
355 static void XBMCController$sendKey(XBMCController* self, SEL _cmd, XBMCKey key)
357 //empty because its not used here. Only implemented for getting rid
358 //of "may not respond to selector" compile warnings in IOSExternalTouchController
361 static id XBMCController$init(XBMCController* self, SEL _cmd)
363 if((self = XBMCController$init$Orig(self, _cmd)) != nil)
365 //NSLog(@"%s", __PRETTY_FUNCTION__);
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:)
376 IOSEAGLView *view = [[IOSEAGLView alloc] initWithFrame:[BRWindowCls interfaceFrame] withScreen:[UIScreen mainScreen]];
377 [self setGlView:view];
379 [[IOSScreenManager sharedInstance] setView:[self glView]];
381 g_xbmcController = self;
386 static void XBMCController$dealloc(XBMCController* self, SEL _cmd)
388 //NSLog(@"%s", __PRETTY_FUNCTION__);
389 [[self glView] stopAnimation];
390 [[self glView] release];
392 NSNotificationCenter *center;
393 // take us off the default center for our app
394 center = [NSNotificationCenter defaultCenter];
395 [center removeObserver: self];
397 XBMCController$dealloc$Orig(self, _cmd);
400 static void XBMCController$controlWasActivated(XBMCController* self, SEL _cmd)
402 //NSLog(@"%s", __PRETTY_FUNCTION__);
404 XBMCController$controlWasActivated$Orig(self, _cmd);
406 [self disableSystemSleep];
407 [self disableScreenSaver];
409 IOSEAGLView *view = [self glView];
410 //inject our gles layer into the backrow root layer
411 [[BRWindowCls rootLayer] addSublayer:view.layer];
413 [[self glView] startAnimation];
416 static void XBMCController$controlWasDeactivated(XBMCController* self, SEL _cmd)
418 NSLog(@"XBMC was forced by FrontRow to exit via controlWasDeactivated");
420 [[self glView] stopAnimation];
421 [[[self glView] layer] removeFromSuperlayer];
423 [self enableScreenSaver];
424 [self enableSystemSleep];
426 XBMCController$controlWasDeactivated$Orig(self, _cmd);
429 static BOOL XBMCController$recreateOnReselect(XBMCController* self, SEL _cmd)
431 //NSLog(@"%s", __PRETTY_FUNCTION__);
435 static void XBMCController$ATVClientEventFromBREvent(XBMCController* self, SEL _cmd, BREvent* f_event, bool * isRepeatable, bool * isPressed, int * result)
437 if(f_event == nil)// paranoia
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;
446 switch (remoteAction)
449 case kBREventRemoteActionUp:
451 *isRepeatable = true;
452 if([f_event value] == 1)
454 *result = ATV_BUTTON_UP;
458 case kBREventRemoteActionDown:
460 *isRepeatable = true;
461 if([f_event value] == 1)
463 *result = ATV_BUTTON_DOWN;
467 case kBREventRemoteActionLeft:
469 *isRepeatable = true;
470 if([f_event value] == 1)
472 *result = ATV_BUTTON_LEFT;
477 if([f_event value] == 1)
478 *result = ATV_LEARNED_REWIND;
480 *result = ATV_INVALID_BUTTON;
484 case kBREventRemoteActionRight:
486 *isRepeatable = true;
487 if ([f_event value] == 1)
489 *result = ATV_BUTTON_RIGHT;
494 if ([f_event value] == 1)
495 *result = ATV_LEARNED_FORWARD;
497 *result = ATV_INVALID_BUTTON;
501 case kBREventRemoteActionPlay:
503 if (originator == kBREventOriginatorKeyboard) // on bt keyboard play == return!
504 *result = ATV_BTKEYPRESS;
506 *result = ATV_BUTTON_PLAY;
510 case kBREventRemoteActionPlayHold:
511 case kBREventRemoteActionCenterHold:
512 case kBREventRemoteActionCenterHold42:
514 if (originator == kBREventOriginatorKeyboard) // invalid on bt keyboard
515 *result = ATV_INVALID_BUTTON;
517 *result = ATV_BUTTON_PLAY_H;
521 case kBREventRemoteActionMenu:
523 if (originator == kBREventOriginatorKeyboard) // on bt keyboard menu == esc!
524 *result = ATV_BTKEYPRESS;
526 *result = ATV_BUTTON_MENU;
530 case kBREventRemoteActionMenuHold:
532 if (originator == kBREventOriginatorKeyboard) // invalid on bt keyboard
533 *result = ATV_INVALID_BUTTON;
535 *result = ATV_BUTTON_MENU_H;
540 *result = ATV_LEARNED_PLAY;
545 *result = ATV_LEARNED_PAUSE;
550 *result = ATV_LEARNED_STOP;
555 *result = ATV_LEARNED_NEXT;
560 *result = ATV_LEARNED_PREVIOUS;
563 // learned enter, like go into something
565 *result = ATV_LEARNED_ENTER;
568 // learned return, like go back
570 *result = ATV_LEARNED_RETURN;
573 // tap play on new Al IR remote
574 case kBREventRemoteActionALPlay:
576 if (originator == kBREventOriginatorKeyboard) // on bt keyboard alplay == space!
577 *result = ATV_BTKEYPRESS;
579 *result = ATV_ALUMINIUM_PLAY;
582 case kBREventRemoteActionKeyPress:
583 case kBREventRemoteActionKeyPress42:
584 *isRepeatable = true;
585 if (originator == kBREventOriginatorKeyboard) // only valid on bt keyboard
586 *result = ATV_BTKEYPRESS;
588 *result = ATV_INVALID_BUTTON;
591 case kBREventRemoteActionKeyTab:
592 *isRepeatable = true;
593 if (originator == kBREventOriginatorKeyboard) // only valid on bt keyboard
594 *result = ATV_BTKEYPRESS;
596 *result = ATV_INVALID_BUTTON;
600 case kBREventRemoteActionPageUp:
601 *result = ATV_BUTTON_PAGEUP;
605 case kBREventRemoteActionPageDown:
606 *result = ATV_BUTTON_PAGEDOWN;
610 case kBREventRemoteActionPause:
611 *result = ATV_BUTTON_PAUSE;
615 case kBREventRemoteActionPlay2:
616 *result = ATV_BUTTON_PLAY2;
620 case kBREventRemoteActionStop:
621 *result = ATV_BUTTON_STOP;
625 case kBREventRemoteActionFastFwd:
626 case kBREventRemoteActionFastFwd2:
627 *isRepeatable = true;
628 if([f_event value] == 1)
630 *result = ATV_BUTTON_FASTFWD;
634 case kBREventRemoteActionRewind:
635 case kBREventRemoteActionRewind2:
636 *isRepeatable = true;
637 if([f_event value] == 1)
639 *result = ATV_BUTTON_REWIND;
643 case kBREventRemoteActionSkipFwd:
644 *result = ATV_BUTTON_SKIPFWD;
648 case kBREventRemoteActionSkipBack:
649 *result = ATV_BUTTON_SKIPBACK;
652 // Gesture Swipe Left
653 case kBREventRemoteActionSwipeLeft:
654 if ([f_event value] == 1)
655 *result = ATV_GESTURE_SWIPE_LEFT;
657 *result = ATV_INVALID_BUTTON;
660 // Gesture Swipe Right
661 case kBREventRemoteActionSwipeRight:
662 if ([f_event value] == 1)
663 *result = ATV_GESTURE_SWIPE_RIGHT;
665 *result = ATV_INVALID_BUTTON;
669 case kBREventRemoteActionSwipeUp:
670 if ([f_event value] == 1)
671 *result = ATV_GESTURE_SWIPE_UP;
673 *result = ATV_INVALID_BUTTON;
676 // Gesture Swipe Down
677 case kBREventRemoteActionSwipeDown:
678 if ([f_event value] == 1)
679 *result = ATV_GESTURE_SWIPE_DOWN;
681 *result = ATV_INVALID_BUTTON;
684 // Gesture Flick Left
685 case kBREventRemoteActionFlickLeft:
686 if ([f_event value] == 1)
687 *result = ATV_GESTURE_FLICK_LEFT;
689 *result = ATV_INVALID_BUTTON;
692 // Gesture Flick Right
693 case kBREventRemoteActionFlickRight:
694 if ([f_event value] == 1)
695 *result = ATV_GESTURE_FLICK_RIGHT;
697 *result = ATV_INVALID_BUTTON;
701 case kBREventRemoteActionFlickUp:
702 if ([f_event value] == 1)
703 *result = ATV_GESTURE_FLICK_UP;
705 *result = ATV_INVALID_BUTTON;
708 // Gesture Flick Down
709 case kBREventRemoteActionFlickDown:
710 if ([f_event value] == 1)
711 *result = ATV_GESTURE_FLICK_DOWN;
713 *result = ATV_INVALID_BUTTON;
717 ELOG(@"XBMCPureController: Unknown button press remoteAction = %i", remoteAction);
718 *result = ATV_INVALID_BUTTON;
722 static void XBMCController$setUserEvent(XBMCController* self, SEL _cmd, int eventId, unsigned int holdTime)
726 memset(&newEvent, 0, sizeof(newEvent));
728 newEvent.type = XBMC_USEREVENT;
729 newEvent.jbutton.which = eventId;
730 newEvent.jbutton.holdTime = holdTime;
731 CWinEvents::MessagePush(&newEvent);
734 static unsigned int XBMCController$appleModKeyToXbmcModKey(XBMCController* self, SEL _cmd, unsigned int appleModifier)
736 unsigned int xbmcModifier = XBMCKMOD_NONE;
738 if (appleModifier & kBREventModifierShiftLeft)
739 xbmcModifier |= XBMCKMOD_LSHIFT;
741 if (appleModifier & kBREventModifierShiftRight)
742 xbmcModifier |= XBMCKMOD_RSHIFT;
744 if (appleModifier & kBREventModifierCtrlLeft)
745 xbmcModifier |= XBMCKMOD_LCTRL;
747 if (appleModifier & kBREventModifierOptionLeft)
748 xbmcModifier |= XBMCKMOD_LALT;
749 // right alt/altgr/option
750 if (appleModifier & kBREventModifierOptionRight)
751 xbmcModifier |= XBMCKMOD_RALT;
753 if (appleModifier & kBREventModifierCommandLeft)
754 xbmcModifier |= XBMCKMOD_LMETA;
756 if (appleModifier & kBREventModifierCommandRight)
757 xbmcModifier |= XBMCKMOD_RMETA;
762 static BOOL XBMCController$brEventAction(XBMCController* self, SEL _cmd, BREvent* event)
764 //NSLog(@"%s", __PRETTY_FUNCTION__);
766 if ([[self glView] isAnimating])
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];
777 if ( xbmc_ir_key != ATV_INVALID_BUTTON )
779 if (xbmc_ir_key == ATV_BTKEYPRESS)
782 memset(&newEvent, 0, sizeof(newEvent));
784 NSDictionary *dict = [event eventDictionary];
785 NSString *key_nsstring = [dict objectForKey:@"kBRKeyEventCharactersKey"];
786 unsigned int modifier = [[dict objectForKey:@"kBRKeyEventModifiersKey"] unsignedIntValue];
787 bool fireTheKey = false;
789 if (key_nsstring != nil && [key_nsstring length] == 1)
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]);
799 if (wstr[0] == 62 && wstr[1] == -9)
802 newEvent.key.keysym.sym = (XBMCKey)8;
803 newEvent.key.keysym.unicode = 8;
807 newEvent.key.keysym.sym = (XBMCKey)wstr[0];
808 newEvent.key.keysym.unicode = wstr[0] | (wstr[1] << 8);
813 else // this must be one of those duped functions when using the bt keyboard
815 int remoteAction = [event remoteAction];
817 switch (remoteAction)
819 case kBREventRemoteActionALPlay:// play maps to space
821 newEvent.key.keysym.sym = XBMCK_SPACE;
822 newEvent.key.keysym.unicode = XBMCK_SPACE;
824 case kBREventRemoteActionMenu:// menu maps to escape!
826 newEvent.key.keysym.sym = XBMCK_ESCAPE;
827 newEvent.key.keysym.unicode = XBMCK_ESCAPE;
829 case kBREventRemoteActionKeyTab:
830 newEvent.key.keysym.sym = XBMCK_TAB;
831 newEvent.key.keysym.unicode = XBMCK_TAB;
833 case kBREventRemoteActionPlay:// play maps to return
835 newEvent.key.keysym.sym = XBMCK_RETURN;
836 newEvent.key.keysym.unicode = XBMCK_RETURN;
838 default: // unsupported duped function
844 if (fireTheKey && (!isRepeatable || [event value] == 1)) // some keys might be repeatable - only fire once here
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);
860 [self setUserEvent:xbmc_ir_key withHoldTime:0];
861 [self startKeyPressTimer:xbmc_ir_key];
866 [self stopKeyPressTimer];
871 [self setUserEvent:xbmc_ir_key withHoldTime:0];
880 return XBMCController$brEventAction$Orig(self, _cmd, event);
885 #pragma mark private helper methods
886 static void XBMCController$startKeyPressTimer(XBMCController* self, SEL _cmd, int keyId)
888 NSNumber *number = [NSNumber numberWithInt:keyId];
889 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSDate date], @"StartDate",
890 number, @"keyId", nil];
892 NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:REPEATED_KEYPRESS_DELAY_S];
893 [self stopKeyPressTimer];
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
900 selector:@selector(keyPressTimerCallback:)
904 //schedule the timer to the runloop
905 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
906 [runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
907 [self setKeyTimer:timer];
910 static void XBMCController$stopKeyPressTimer(XBMCController* self, SEL _cmd)
912 if([self keyTimer] != nil)
914 [[self keyTimer] invalidate];
915 [[self keyTimer] release];
916 [self setKeyTimer:nil];
920 static void XBMCController$keyPressTimerCallback(XBMCController* self, SEL _cmd, NSTimer* theTimer)
922 //if queue is empty - skip this timer event
923 //for letting it process
924 if(CWinEvents::GetQueueSize())
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];
936 static void XBMCController$observeDefaultCenterStuff(XBMCController* self, SEL _cmd, NSNotification * notification)
938 //NSLog(@"default: %@", [notification name]);
940 if ([notification name] == UIApplicationDidReceiveMemoryWarningNotification)
941 NSLog(@"XBMC: %@", [notification name]);
943 //if ([notification name] == kBRScreenSaverActivated)
944 // [m_glView stopAnimation];
946 //if ([notification name] == kBRScreenSaverDismissed)
947 // [m_glView startAnimation];
950 static void XBMCController$disableSystemSleep(XBMCController* self, SEL _cmd)
952 Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade");
953 XBMCSettingsFacade *single = (XBMCSettingsFacade *)[ATVSettingsFacadeCls singleton];
955 int tmpTimeout = [single sleepTimeout];
956 NSNumber *timeout = [NSNumber numberWithInt:tmpTimeout];
957 [self setSystemSleepTimeout:timeout];
958 [single setSleepTimeout: -1];
959 [single flushDiskChanges];
962 static void XBMCController$enableSystemSleep(XBMCController* self, SEL _cmd)
964 Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade");
965 int timeoutInt = [[self systemSleepTimeout] intValue];
966 [[ATVSettingsFacadeCls singleton] setSleepTimeout:timeoutInt];
967 [[ATVSettingsFacadeCls singleton] flushDiskChanges];
970 static void XBMCController$disableScreenSaver(XBMCController* self, SEL _cmd)
972 //NSLog(@"%s", __PRETTY_FUNCTION__);
973 //store screen saver state and disable it
975 Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade");
976 XBMCSettingsFacade *single = (XBMCSettingsFacade *)[ATVSettingsFacadeCls singleton];
978 int tmpTimeout = [single screenSaverTimeout];
979 NSNumber *timeout = [NSNumber numberWithInt:tmpTimeout];
980 [self setSystemScreenSaverTimeout:timeout];
981 [single setScreenSaverTimeout: -1];
982 [single flushDiskChanges];
984 // breaks in 4.2.1 [[BRBackgroundTaskManager singleton] holdOffBackgroundTasks];
987 static void XBMCController$enableScreenSaver(XBMCController* self, SEL _cmd)
989 //NSLog(@"%s", __PRETTY_FUNCTION__);
990 //reset screen saver to user settings
991 Class ATVSettingsFacadeCls = objc_getClass("ATVSettingsFacade");
993 int timeoutInt = [[self systemScreenSaverTimeout] intValue];
994 [[ATVSettingsFacadeCls singleton] setScreenSaverTimeout:timeoutInt];
995 [[ATVSettingsFacadeCls singleton] flushDiskChanges];
997 // breaks in 4.2.1 [[BRBackgroundTaskManager singleton] okToDoBackgroundProcessing];
1001 - (XBMC_Event) translateCocoaToXBMCEvent: (unichar) c
1003 XBMC_Event newEvent;
1004 memset(&newEvent, 0, sizeof(newEvent));
1009 case NSMenuFunctionKey:
1022 case NSClearLineFunctionKey:
1034 case NSDownArrowFunctionKey:
1037 case NSEndFunctionKey:
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
1046 case NSExecuteFunctionKey:
1052 case NSF1FunctionKey:
1055 case NSF2FunctionKey:
1058 case NSF3FunctionKey:
1061 case NSF4FunctionKey:
1064 case NSF5FunctionKey:
1067 case NSF6FunctionKey:
1070 case NSF7FunctionKey:
1073 case NSF8FunctionKey:
1076 case NSF9FunctionKey:
1079 case NSF10FunctionKey:
1082 case NSF11FunctionKey:
1085 case NSF12FunctionKey:
1088 case NSF13FunctionKey:
1091 case NSF14FunctionKey:
1094 case NSF15FunctionKey:
1097 case NSF16FunctionKey:
1100 case NSF17FunctionKey:
1103 case NSF18FunctionKey:
1106 case NSF19FunctionKey:
1109 case NSF20FunctionKey:
1112 case NSF21FunctionKey:
1115 case NSF22FunctionKey:
1118 case NSF23FunctionKey:
1121 case NSF24FunctionKey:
1127 case NSFindFunctionKey:
1136 case NSHelpFunctionKey:
1142 case NSHomeFunctionKey:
1145 case NSInsertFunctionKey:
1148 // "JapaneseHiragana"
1149 // "JapaneseKatakana"
1155 // "LaunchApplication1"
1156 // "LaunchApplication2"
1160 case NSLeftArrowFunctionKey:
1166 // "MediaPreviousTrack"
1170 case NSModeSwitchFunctionKey:
1171 return "ModeChange";
1177 case NSPageDownFunctionKey:
1180 case NSPageUpFunctionKey:
1186 case NSPauseFunctionKey:
1190 // "PreviousCandidate"
1193 case NSPrintScreenFunctionKey:
1194 return "PrintScreen";
1200 case NSRightArrowFunctionKey:
1203 // "RomanCharacters"
1206 case NSScrollLockFunctionKey:
1209 case NSSelectFunctionKey:
1216 case NSStopFunctionKey:
1219 case NSUpArrowFunctionKey:
1222 case NSUndoFunctionKey:
1231 // More function keys, not in the key identifier specification.
1232 case NSF25FunctionKey:
1234 case NSF26FunctionKey:
1236 case NSF27FunctionKey:
1238 case NSF28FunctionKey:
1240 case NSF29FunctionKey:
1242 case NSF30FunctionKey:
1244 case NSF31FunctionKey:
1246 case NSF32FunctionKey:
1248 case NSF33FunctionKey:
1250 case NSF34FunctionKey:
1252 case NSF35FunctionKey:
1255 // Turn 0x7F into 0x08, because backspace needs to always be 0x08.
1258 // Standard says that DEL becomes U+007F.
1259 case NSDeleteFunctionKey:
1262 // Always use 0x09 for tab instead of AppKit's backtab character.
1263 case NSBackTabCharacter:
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.
1284 return String::format("U+%04X", toASCIIUpper(c));
1289 //--------------------------------------------------------------
1290 static void XBMCController$pauseAnimation(XBMCController* self, SEL _cmd)
1292 XBMC_Event newEvent;
1293 memset(&newEvent, 0, sizeof(XBMC_Event));
1295 newEvent.appcommand.type = XBMC_APPCOMMAND;
1296 newEvent.appcommand.action = ACTION_PLAYER_PLAYPAUSE;
1297 CWinEvents::MessagePush(&newEvent);
1300 [[self glView] pauseAnimation];
1302 //--------------------------------------------------------------
1303 static void XBMCController$resumeAnimation(XBMCController* self, SEL _cmd)
1305 NSLog(@"%s", __PRETTY_FUNCTION__);
1307 XBMC_Event newEvent;
1308 memset(&newEvent, 0, sizeof(XBMC_Event));
1310 newEvent.appcommand.type = XBMC_APPCOMMAND;
1311 newEvent.appcommand.action = ACTION_PLAYER_PLAY;
1312 CWinEvents::MessagePush(&newEvent);
1314 [[self glView] resumeAnimation];
1316 //--------------------------------------------------------------
1317 static void XBMCController$startAnimation(XBMCController* self, SEL _cmd)
1319 NSLog(@"%s", __PRETTY_FUNCTION__);
1321 [[self glView] startAnimation];
1323 //--------------------------------------------------------------
1324 static void XBMCController$stopAnimation(XBMCController* self, SEL _cmd)
1326 NSLog(@"%s", __PRETTY_FUNCTION__);
1328 [[self glView] stopAnimation];
1330 //--------------------------------------------------------------
1331 static bool XBMCController$changeScreen(XBMCController* self, SEL _cmd, unsigned int screenIdx, UIScreenMode * mode)
1333 return [[IOSScreenManager sharedInstance] changeScreen: screenIdx withMode: mode];
1335 //--------------------------------------------------------------
1336 static void XBMCController$activateScreen(XBMCController* self, SEL _cmd, UIScreen * screen)
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()
1348 char _typeEncoding[1024];
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@:");
1408 memcpy(_typeEncoding + i, @encode(CGSize), strlen(@encode(CGSize)));
1409 i += strlen(@encode(CGSize));
1410 _typeEncoding[i] = '@';
1412 _typeEncoding[i] = ':';
1414 _typeEncoding[i] = '\0';
1415 // XBMCController::getScreenSize
1416 class_addMethod(XBMCControllerCls, @selector(getScreenSize), (IMP)&XBMCController$getScreenSize, _typeEncoding);
1419 _typeEncoding[i] = 'v';
1421 _typeEncoding[i] = '@';
1423 _typeEncoding[i] = ':';
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);
1432 _typeEncoding[i] = 'v';
1434 _typeEncoding[i] = '@';
1436 _typeEncoding[i] = ':';
1438 memcpy(_typeEncoding + i, @encode(BREvent*), strlen(@encode(BREvent*)));
1439 i += strlen(@encode(BREvent*));
1440 _typeEncoding[i] = '^';
1441 _typeEncoding[i + 1] = 'B';
1443 _typeEncoding[i] = '^';
1444 _typeEncoding[i + 1] = 'B';
1446 _typeEncoding[i] = '^';
1447 _typeEncoding[i + 1] = 'i';
1449 _typeEncoding[i] = '\0';
1450 // XBMCController::ATVClientEventFromBREvent
1451 class_addMethod(XBMCControllerCls, @selector(ATVClientEventFromBREvent:Repeatable:ButtonState:Result:), (IMP)&XBMCController$ATVClientEventFromBREvent, _typeEncoding);
1454 _typeEncoding[i] = 'v';
1456 _typeEncoding[i] = '@';
1458 _typeEncoding[i] = ':';
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);
1467 _typeEncoding[i] = 'v';
1469 _typeEncoding[i] = '@';
1471 _typeEncoding[i] = ':';
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);
1480 _typeEncoding[i] = 'B';
1482 _typeEncoding[i] = '@';
1484 _typeEncoding[i] = ':';
1486 _typeEncoding[i] = 'I';
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);
1495 _typeEncoding[i] = 'v';
1497 _typeEncoding[i] = '@';
1499 _typeEncoding[i] = ':';
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);
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);
1521 // and register the class to the runtime
1522 objc_registerClassPair(XBMCControllerCls);
1524 // save this as static for referencing it in multiple methods
1525 BRWindowCls = objc_getClass("BRWindow");