]> git.saurik.com Git - cydget.git/blame - CydgetLoader.mm
Use the new @media to rethink how legacy works.
[cydget.git] / CydgetLoader.mm
CommitLineData
36228fbc 1/* Cydget - open-source AwayView plugin multiplexer
5bc15b17 2 * Copyright (C) 2009-2014 Jay Freeman (saurik)
e6cd4dee
JF
3*/
4
5bc15b17 5/* GNU General Public License, Version 3 {{{ */
e6cd4dee 6/*
5bc15b17
JF
7 * Cydia is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published
9 * by the Free Software Foundation, either version 3 of the License,
10 * or (at your option) any later version.
e6cd4dee 11 *
5bc15b17
JF
12 * Cydia is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
e6cd4dee 16 *
5bc15b17
JF
17 * You should have received a copy of the GNU General Public License
18 * along with Cydia. If not, see <http://www.gnu.org/licenses/>.
19**/
20/* }}} */
e6cd4dee 21
5bc15b17 22#include <CydiaSubstrate/CydiaSubstrate.h>
e6cd4dee
JF
23
24#include <Foundation/Foundation.h>
545370d8 25#include <UIKit/UIKit.h>
e6cd4dee
JF
26
27#import <SpringBoard/SBAwayController.h>
d86d4327 28#import <SpringBoard/SBAwayView.h>
e6cd4dee 29#import <SpringBoard/SBAwayWindow.h>
3a0081b3 30#import <SpringBoard/SpringBoard.h>
e6cd4dee 31
0c1e3ef0
JF
32#include "Handle.hpp"
33
5bc15b17
JF
34#define _trace() \
35 NSLog(@"_trace(%s:%u)@%s %zd", __FILE__, __LINE__, __FUNCTION__, active_)
36#define _not(type) \
37 static_cast<type>(~type())
38
39typedef void *GSEventRef;
40
41extern "C" void UIKeyboardEnableAutomaticAppearance();
42extern "C" void UIKeyboardDisableAutomaticAppearance();
43
e6cd4dee 44MSClassHook(SpringBoard)
fde9eca1 45
e6cd4dee
JF
46MSClassHook(SBAwayController)
47MSClassHook(SBAwayView)
3aa346e5 48MSClassHook(SBAwayWindow)
5bc15b17
JF
49MSClassHook(SBLockScreenManager)
50MSClassHook(SBLockScreenView)
51MSClassHook(SBLockScreenViewController)
52MSClassHook(SBScreenFadeAnimationController)
53MSClassHook(SBUserAgent)
54
55MSInitialize {
56 if (kCFCoreFoundationVersionNumber < 800) {
57 $SBUserAgent = $SBAwayController;
58 $SBLockScreenManager = $SBAwayController;
59 $SBLockScreenView = $SBAwayView;
60 }
61}
e6cd4dee 62
7d9e43da
JF
63@interface SBLockScreenNowPlayingPluginController : NSObject
64- (BOOL) isNowPlayingPluginActive;
65@end
66
c6daf1f5 67@interface SBLockScreenViewController : UIViewController
7d9e43da
JF
68- (SBLockScreenView *) lockScreenView;
69- (BOOL) isShowingMediaControls;
c6daf1f5
JF
70- (void) _toggleMediaControls;
71@end
72
7d9e43da
JF
73@interface SBLockScreenManager : NSObject
74+ (SBLockScreenManager *) sharedInstance;
75- (SBLockScreenViewController *) lockScreenViewController;
76@end
77
e6cd4dee 78static bool menu_;
e6cd4dee 79
5bc15b17
JF
80static _H<NSDictionary> settings_;
81static _H<NSMutableArray> cydgets_;
3a0081b3 82static size_t active_;
3aa346e5 83static unsigned online_;
7d9e43da
JF
84static bool nowplaying_;
85
86static bool NowPlaying() {
87 if (kCFCoreFoundationVersionNumber < 800)
88 return false;
89 if (!nowplaying_)
90 return false;
91
92 SBLockScreenViewController *controller([[$SBLockScreenManager sharedInstance] lockScreenViewController]);
93 if (controller != nil) {
94 SBLockScreenNowPlayingPluginController *now(MSHookIvar<SBLockScreenNowPlayingPluginController *>(controller, "_nowPlayingController"));
95 if (now != nil && [now isNowPlayingPluginActive])
96 return true;
97 }
98
99 return false;
100}
e6cd4dee 101
d8165c74
JF
102@interface CydgetController : NSObject {
103}
104
105+ (NSDictionary *) currentConfiguration;
3215ed85 106+ (NSString *) currentPath;
d8165c74
JF
107
108@end
109
110@implementation CydgetController
111
112+ (NSDictionary *) currentConfiguration {
d86d4327
JF
113 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
114 return [cydget objectForKey:@"CYConfiguration"] ?: [cydget objectForKey:@"Configuration"];
d8165c74
JF
115}
116
3215ed85
JF
117+ (NSDictionary *) currentPath {
118 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
119 return [cydget objectForKey:@"CYPath"];
120}
121
d8165c74
JF
122@end
123
5bc15b17
JF
124@interface SBUserAgent : NSObject
125+ (SBUserAgent *) sharedUserAgent;
126- (void) enableLockScreenBundleNamed:(NSString *)bundle activationContext:(id)context;
127- (void) disableLockScreenBundleNamed:(NSString *)bundle deactivationContext:(id)context;
e6cd4dee
JF
128@end
129
57df9ddb
JF
130@interface UIPeripheralHost : NSObject
131+ (UIPeripheralHost *) sharedInstance;
132+ (void) _releaseSharedInstance;
57df9ddb
JF
133@end
134
135MSClassHook(UIPeripheralHost)
136
137@interface UITextEffectsWindow : UIWindow
138+ (UIWindow *) sharedTextEffectsWindow;
139@end
140
5bc15b17
JF
141@implementation NSDictionary (CydgetLoader)
142
143- (NSString *) cydget {
144 return [self objectForKey:@"CYPlugin"] ?: [self objectForKey:@"Plugin"];
145}
e6cd4dee 146
5bc15b17
JF
147- (void) enableCydget {
148 if (NSString *plugin = [self cydget]) {
fde9eca1 149 ++online_;
fde9eca1 150
1dad64a0
JF
151 if (kCFCoreFoundationVersionNumber < 600)
152 UIKeyboardEnableAutomaticAppearance();
57df9ddb 153
1dad64a0 154 [[UITextEffectsWindow sharedTextEffectsWindow] setWindowLevel:1000];
5bc15b17
JF
155
156 if (kCFCoreFoundationVersionNumber < 800)
157 [[$SBAwayController sharedAwayController] enableLockScreenBundleWithName:plugin];
158 else
159 [[$SBUserAgent sharedUserAgent] enableLockScreenBundleNamed:plugin activationContext:nil];
3aa346e5 160 }
e6cd4dee
JF
161}
162
5bc15b17
JF
163- (void) disableCydget {
164 if (NSString *plugin = [self cydget]) {
165 if (kCFCoreFoundationVersionNumber < 800)
166 [[$SBAwayController sharedAwayController] disableLockScreenBundleWithName:plugin];
167 else
168 [[$SBUserAgent sharedUserAgent] disableLockScreenBundleNamed:plugin deactivationContext:nil];
169
57df9ddb 170 [$UIPeripheralHost _releaseSharedInstance];
fde9eca1 171
1dad64a0
JF
172 if (kCFCoreFoundationVersionNumber < 600)
173 UIKeyboardDisableAutomaticAppearance();
174
fde9eca1 175 --online_;
3aa346e5 176 }
e6cd4dee
JF
177}
178
179@end
180
fde9eca1
JF
181// avoid rendering a keyboard onto the default SBAwayView while automatic keyboard is online
182MSInstanceMessageHook0(UIView *, SBAwayView, inputView) {
d373498d 183 // XXX: there is a conceptual error here
1dad64a0 184 if (online_ == 0 && false || kCFCoreFoundationVersionNumber > 600)
fde9eca1
JF
185 return MSOldCall();
186
187 return [[[UIView alloc] init] autorelease];
188}
189
190// by default, keyboard actions are redirected to SBAwayController and press menu button
57df9ddb 191MSInstanceMessageHook1(void, SpringBoard, handleKeyEvent, GSEventRef, event) {
1dad64a0 192 if (online_ == 0 || kCFCoreFoundationVersionNumber > 600)
fde9eca1
JF
193 return MSOldCall(event);
194
195 return MSSuperCall(event);
57df9ddb
JF
196}
197
c6daf1f5
JF
198bool media_;
199
200MSInstanceMessageHook0(void, SBLockScreenViewController, _toggleMediaControls) {
201 if (!media_)
202 MSOldCall();
203}
204
205MSInstanceMessageHook0(BOOL, SBLockScreenViewController, handleMenuButtonDoubleTap) {
206 menu_ = false;
207 BOOL value(MSOldCall());
208 if (kCFCoreFoundationVersionNumber >= 800)
209 [self _toggleMediaControls];
210 return value;
211}
212
5bc15b17 213MSInstanceMessageHook0(BOOL, SBLockScreenManager, handleMenuButtonTap) {
c6daf1f5
JF
214 media_ = true;
215 BOOL value(MSOldCall());
216 media_ = false;
217
218 if (!value && menu_) {
7d9e43da
JF
219 if (active_ != _not(size_t))
220 [[cydgets_ objectAtIndex:active_] disableCydget];
3a0081b3 221 active_ = (active_ + 1) % [cydgets_ count];
5bc15b17 222 [[cydgets_ objectAtIndex:active_] enableCydget];
33da59d4
JF
223 // XXX: or siri doesn't disappear correctly
224 return kCFCoreFoundationVersionNumber >= 800 ? value : YES;
1b616711 225 }
e6cd4dee 226
c6daf1f5 227 return value;
e6cd4dee
JF
228}
229
5bc15b17
JF
230void Activate_() {
231 menu_ = false;
7d9e43da
JF
232 if (NowPlaying()) {
233 for (active_ = 0; active_ != [cydgets_ count]; ++active_)
234 if ([[[cydgets_ objectAtIndex:active_] objectForKey:@"CYName"] isEqualToString:@"AwayView"])
235 break;
236 if (active_ == [cydgets_ count])
237 active_ = _not(size_t);
238 }
239 if (active_ != _not(size_t))
240 [[cydgets_ objectAtIndex:active_] enableCydget];
5bc15b17
JF
241}
242
1a274bdf 243static void Undim_(SBAwayController *self) {
f83bba74 244 if ([self isDimmed]) {
5bc15b17 245 Activate_();
f83bba74
JF
246 [[[self awayView] window] makeKeyWindow];
247 }
1a274bdf 248}
f83bba74 249
5bc15b17
JF
250MSInstanceMessageHook1(void, SBLockScreenViewController, startLockScreenFadeInAnimationForSource, int, source) {
251 Activate_();
252 MSOldCall(source);
253}
254
1a274bdf
JF
255MSInstanceMessageHook0(void, SBAwayController, undimScreen) {
256 Undim_(self);
f83bba74 257 MSOldCall();
e6cd4dee
JF
258}
259
1a274bdf
JF
260MSInstanceMessageHook1(void, SBAwayController, undimScreen, BOOL, undim) {
261 Undim_(self);
262 MSOldCall(undim);
263}
264
5bc15b17 265static void Deactivate_() {
7d9e43da
JF
266 if (active_ != _not(size_t))
267 [[cydgets_ objectAtIndex:active_] disableCydget];
3a0081b3 268 active_ = 0;
e6cd4dee
JF
269}
270
5bc15b17
JF
271MSHook(void, BKSDisplayServicesSetScreenBlanked, BOOL blanked) {
272 if (blanked)
273 Deactivate_();
274 _BKSDisplayServicesSetScreenBlanked(blanked);
275}
276
277MSInitialize {
278 MSHookFunction("_BKSDisplayServicesSetScreenBlanked", MSHake(BKSDisplayServicesSetScreenBlanked));
279}
280
281MSInstanceMessageHook1(void, SBUserAgent, dimScreen, BOOL, dim) {
282 Deactivate_();
1b616711 283 MSOldCall(dim);
e6cd4dee
JF
284}
285
b5e122ba
JF
286MSInstanceMessageHook0(void, SpringBoard, _menuButtonWasHeld) {
287 menu_ = false;
288 MSOldCall();
289}
290
5bc15b17
JF
291MSInstanceMessageHook1(void, SpringBoard, _menuButtonDown, GSEventRef, event) {
292 menu_ = true;
293 MSOldCall(event);
294}
295
f83bba74 296MSInstanceMessageHook1(void, SpringBoard, menuButtonDown, GSEventRef, event) {
e6cd4dee 297 menu_ = true;
3a0081b3 298 MSOldCall(event);
e6cd4dee
JF
299}
300
3a0081b3 301MSInstanceMessageHook1(void, SBAwayView, addGestureRecognizer, id, recognizer) {
1dad64a0 302 if (online_ == 0 || kCFCoreFoundationVersionNumber > 600)
fde9eca1 303 return MSOldCall(recognizer);
545370d8
JF
304}
305
3aa346e5 306MSInstanceMessageHook1(void, SBAwayWindow, sendGSEvent, GSEventRef, event) {
1dad64a0 307 if (online_ == 0 || kCFCoreFoundationVersionNumber > 600)
fde9eca1
JF
308 return MSOldCall(event);
309
310 return MSSuperCall(event);
3aa346e5
JF
311}
312
3a0081b3 313#define Cydgets_ @"/System/Library/LockCydgets"
545370d8 314
3a0081b3
JF
315static void CydgetSetup() {
316 NSString *plist([NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()]);
317 settings_ = [NSMutableDictionary dictionaryWithContentsOfFile:plist] ?: [NSMutableDictionary dictionary];
545370d8 318
7d9e43da
JF
319 nowplaying_ = [[settings_ objectForKey:@"NowPlaying"] boolValue];
320
3a0081b3 321 NSArray *cydgets([settings_ objectForKey:@"LockCydgets"] ?: [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:
3aa346e5 322 @"Welcome", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
3a0081b3 323 ], [NSDictionary dictionaryWithObjectsAndKeys:
3aa346e5 324 @"AwayView", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
3a0081b3 325 ], nil]);
545370d8 326
3a0081b3
JF
327 cydgets_ = [NSMutableArray arrayWithCapacity:4];
328 for (NSDictionary *cydget in cydgets)
3215ed85 329 if ([[cydget objectForKey:@"Active"] boolValue]) {
7d9e43da
JF
330 NSString *name([cydget objectForKey:@"Name"]);
331 NSString *path([NSString stringWithFormat:@"%@/%@.cydget/Info.plist", Cydgets_, name]);
3215ed85 332 if (NSMutableDictionary *info = [NSMutableDictionary dictionaryWithContentsOfFile:path]) {
7d9e43da 333 [info setObject:name forKey:@"CYName"];
3215ed85 334 [info setObject:path forKey:@"CYPath"];
3a0081b3 335 [cydgets_ addObject:info];
3215ed85
JF
336 }
337 }
81c3ce7d
JF
338
339 if ([cydgets_ count] == 0)
340 cydgets_ = nil;
545370d8
JF
341}
342
3a0081b3 343// XXX: this could happen while it is unlocked
5bc15b17 344MSInstanceMessageHook1(id, SBLockScreenView, initWithFrame, CGRect, frame) {
3a0081b3
JF
345 self = MSOldCall(frame);
346 CydgetSetup();
7d9e43da 347 Activate_();
3a0081b3 348 return self;
545370d8
JF
349}
350
3a0081b3
JF
351MSInstanceMessageHook1(void, SBAwayController, _finishedUnlockAttemptWithStatus, BOOL, status) {
352 if (status)
5bc15b17 353 Deactivate_();
3a0081b3 354 MSOldCall(status);
e6cd4dee 355}
d86d4327 356
672fa884
JF
357// this is called occasionally by -[SBAwayView updateInterface] and takes away our keyboard
358MSInstanceMessageHook0(void, SBAwayView, _fixupFirstResponder) {
359 if (online_ == 0)
360 return MSOldCall();
361}
362
d86d4327
JF
363MSInstanceMessageHook0(void, SBAwayView, updateInterface) {
364 MSOldCall();
365
366 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
367
368 NSString *background([cydget objectForKey:@"CYBackground"]);
369 if ([background isEqualToString:@"Wallpaper"]) {
370 MSIvarHook(UIView *, _backgroundView);
371 [_backgroundView setAlpha:1.0f];
372 }
373
374 if ([[cydget objectForKey:@"CYShowDateTime"] boolValue])
375 [self addDateView];
376}