]> git.saurik.com Git - cydget.git/blob - CydgetLoader.mm
Just in case, cancel menu action when menu is held.
[cydget.git] / CydgetLoader.mm
1 /* Cydget - open-source AwayView plugin multiplexer
2 * Copyright (C) 2009-2014 Jay Freeman (saurik)
3 */
4
5 /* GNU General Public License, Version 3 {{{ */
6 /*
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.
11 *
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.
16 *
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 /* }}} */
21
22 #include <CydiaSubstrate/CydiaSubstrate.h>
23
24 #include <Foundation/Foundation.h>
25 #include <UIKit/UIKit.h>
26
27 #import <SpringBoard/SBAwayController.h>
28 #import <SpringBoard/SBAwayView.h>
29 #import <SpringBoard/SBAwayWindow.h>
30 #import <SpringBoard/SpringBoard.h>
31
32 #include "Handle.hpp"
33
34 #define _trace() \
35 NSLog(@"_trace(%s:%u)@%s %zd", __FILE__, __LINE__, __FUNCTION__, active_)
36 #define _not(type) \
37 static_cast<type>(~type())
38
39 typedef void *GSEventRef;
40
41 extern "C" void UIKeyboardEnableAutomaticAppearance();
42 extern "C" void UIKeyboardDisableAutomaticAppearance();
43
44 MSClassHook(SpringBoard)
45
46 MSClassHook(SBAwayController)
47 MSClassHook(SBAwayView)
48 MSClassHook(SBAwayWindow)
49 MSClassHook(SBLockScreenManager)
50 MSClassHook(SBLockScreenView)
51 MSClassHook(SBLockScreenViewController)
52 MSClassHook(SBScreenFadeAnimationController)
53 MSClassHook(SBUserAgent)
54
55 MSInitialize {
56 if (kCFCoreFoundationVersionNumber < 800) {
57 $SBUserAgent = $SBAwayController;
58 $SBLockScreenManager = $SBAwayController;
59 $SBLockScreenView = $SBAwayView;
60 }
61 }
62
63 @interface SBLockScreenViewController : UIViewController
64 - (void) _toggleMediaControls;
65 @end
66
67 static bool menu_;
68
69 static _H<NSDictionary> settings_;
70 static _H<NSMutableArray> cydgets_;
71 static size_t active_;
72 static unsigned online_;
73
74 @interface CydgetController : NSObject {
75 }
76
77 + (NSDictionary *) currentConfiguration;
78 + (NSString *) currentPath;
79
80 @end
81
82 @implementation CydgetController
83
84 + (NSDictionary *) currentConfiguration {
85 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
86 return [cydget objectForKey:@"CYConfiguration"] ?: [cydget objectForKey:@"Configuration"];
87 }
88
89 + (NSDictionary *) currentPath {
90 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
91 return [cydget objectForKey:@"CYPath"];
92 }
93
94 @end
95
96 @interface SBUserAgent : NSObject
97 + (SBUserAgent *) sharedUserAgent;
98 - (void) enableLockScreenBundleNamed:(NSString *)bundle activationContext:(id)context;
99 - (void) disableLockScreenBundleNamed:(NSString *)bundle deactivationContext:(id)context;
100 @end
101
102 @interface UIPeripheralHost : NSObject
103 + (UIPeripheralHost *) sharedInstance;
104 + (void) _releaseSharedInstance;
105 @end
106
107 MSClassHook(UIPeripheralHost)
108
109 @interface UITextEffectsWindow : UIWindow
110 + (UIWindow *) sharedTextEffectsWindow;
111 @end
112
113 @implementation NSDictionary (CydgetLoader)
114
115 - (NSString *) cydget {
116 return [self objectForKey:@"CYPlugin"] ?: [self objectForKey:@"Plugin"];
117 }
118
119 - (void) enableCydget {
120 if (NSString *plugin = [self cydget]) {
121 ++online_;
122
123 if (kCFCoreFoundationVersionNumber < 600)
124 UIKeyboardEnableAutomaticAppearance();
125
126 [[UITextEffectsWindow sharedTextEffectsWindow] setWindowLevel:1000];
127
128 if (kCFCoreFoundationVersionNumber < 800)
129 [[$SBAwayController sharedAwayController] enableLockScreenBundleWithName:plugin];
130 else
131 [[$SBUserAgent sharedUserAgent] enableLockScreenBundleNamed:plugin activationContext:nil];
132 }
133 }
134
135 - (void) disableCydget {
136 if (NSString *plugin = [self cydget]) {
137 if (kCFCoreFoundationVersionNumber < 800)
138 [[$SBAwayController sharedAwayController] disableLockScreenBundleWithName:plugin];
139 else
140 [[$SBUserAgent sharedUserAgent] disableLockScreenBundleNamed:plugin deactivationContext:nil];
141
142 [$UIPeripheralHost _releaseSharedInstance];
143
144 if (kCFCoreFoundationVersionNumber < 600)
145 UIKeyboardDisableAutomaticAppearance();
146
147 --online_;
148 }
149 }
150
151 @end
152
153 // avoid rendering a keyboard onto the default SBAwayView while automatic keyboard is online
154 MSInstanceMessageHook0(UIView *, SBAwayView, inputView) {
155 // XXX: there is a conceptual error here
156 if (online_ == 0 && false || kCFCoreFoundationVersionNumber > 600)
157 return MSOldCall();
158
159 return [[[UIView alloc] init] autorelease];
160 }
161
162 // by default, keyboard actions are redirected to SBAwayController and press menu button
163 MSInstanceMessageHook1(void, SpringBoard, handleKeyEvent, GSEventRef, event) {
164 if (online_ == 0 || kCFCoreFoundationVersionNumber > 600)
165 return MSOldCall(event);
166
167 return MSSuperCall(event);
168 }
169
170 bool media_;
171
172 MSInstanceMessageHook0(void, SBLockScreenViewController, _toggleMediaControls) {
173 if (!media_)
174 MSOldCall();
175 }
176
177 MSInstanceMessageHook0(BOOL, SBLockScreenViewController, handleMenuButtonDoubleTap) {
178 menu_ = false;
179 BOOL value(MSOldCall());
180 if (kCFCoreFoundationVersionNumber >= 800)
181 [self _toggleMediaControls];
182 return value;
183 }
184
185 MSInstanceMessageHook0(BOOL, SBLockScreenManager, handleMenuButtonTap) {
186 media_ = true;
187 BOOL value(MSOldCall());
188 media_ = false;
189
190 if (!value && menu_) {
191 [[cydgets_ objectAtIndex:active_] disableCydget];
192 active_ = (active_ + 1) % [cydgets_ count];
193 [[cydgets_ objectAtIndex:active_] enableCydget];
194 // XXX: or siri doesn't disappear correctly
195 return kCFCoreFoundationVersionNumber >= 800 ? value : YES;
196 }
197
198 return value;
199 }
200
201 void Activate_() {
202 menu_ = false;
203 [[cydgets_ objectAtIndex:active_] enableCydget];
204 }
205
206 static void Undim_(SBAwayController *self) {
207 if ([self isDimmed]) {
208 Activate_();
209 [[[self awayView] window] makeKeyWindow];
210 }
211 }
212
213 MSInstanceMessageHook1(void, SBLockScreenViewController, startLockScreenFadeInAnimationForSource, int, source) {
214 Activate_();
215 MSOldCall(source);
216 }
217
218 MSInstanceMessageHook0(void, SBAwayController, undimScreen) {
219 Undim_(self);
220 MSOldCall();
221 }
222
223 MSInstanceMessageHook1(void, SBAwayController, undimScreen, BOOL, undim) {
224 Undim_(self);
225 MSOldCall(undim);
226 }
227
228 static void Deactivate_() {
229 [[cydgets_ objectAtIndex:active_] disableCydget];
230 active_ = 0;
231 }
232
233 MSHook(void, BKSDisplayServicesSetScreenBlanked, BOOL blanked) {
234 if (blanked)
235 Deactivate_();
236 _BKSDisplayServicesSetScreenBlanked(blanked);
237 }
238
239 MSInitialize {
240 MSHookFunction("_BKSDisplayServicesSetScreenBlanked", MSHake(BKSDisplayServicesSetScreenBlanked));
241 }
242
243 MSInstanceMessageHook1(void, SBUserAgent, dimScreen, BOOL, dim) {
244 Deactivate_();
245 MSOldCall(dim);
246 }
247
248 MSInstanceMessageHook0(void, SpringBoard, _menuButtonWasHeld) {
249 menu_ = false;
250 MSOldCall();
251 }
252
253 MSInstanceMessageHook1(void, SpringBoard, _menuButtonDown, GSEventRef, event) {
254 menu_ = true;
255 MSOldCall(event);
256 }
257
258 MSInstanceMessageHook1(void, SpringBoard, menuButtonDown, GSEventRef, event) {
259 menu_ = true;
260 MSOldCall(event);
261 }
262
263 MSInstanceMessageHook1(void, SBAwayView, addGestureRecognizer, id, recognizer) {
264 if (online_ == 0 || kCFCoreFoundationVersionNumber > 600)
265 return MSOldCall(recognizer);
266 }
267
268 MSInstanceMessageHook1(void, SBAwayWindow, sendGSEvent, GSEventRef, event) {
269 if (online_ == 0 || kCFCoreFoundationVersionNumber > 600)
270 return MSOldCall(event);
271
272 return MSSuperCall(event);
273 }
274
275 #define Cydgets_ @"/System/Library/LockCydgets"
276
277 static void CydgetSetup() {
278 NSString *plist([NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()]);
279 settings_ = [NSMutableDictionary dictionaryWithContentsOfFile:plist] ?: [NSMutableDictionary dictionary];
280
281 NSArray *cydgets([settings_ objectForKey:@"LockCydgets"] ?: [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:
282 @"Welcome", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
283 ], [NSDictionary dictionaryWithObjectsAndKeys:
284 @"AwayView", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
285 ], nil]);
286
287 cydgets_ = [NSMutableArray arrayWithCapacity:4];
288 for (NSDictionary *cydget in cydgets)
289 if ([[cydget objectForKey:@"Active"] boolValue]) {
290 NSString *path([NSString stringWithFormat:@"%@/%@.cydget/Info.plist", Cydgets_, [cydget objectForKey:@"Name"]]);
291 if (NSMutableDictionary *info = [NSMutableDictionary dictionaryWithContentsOfFile:path]) {
292 [info setObject:path forKey:@"CYPath"];
293 [cydgets_ addObject:info];
294 }
295 }
296
297 if ([cydgets_ count] == 0)
298 cydgets_ = nil;
299 }
300
301 // XXX: this could happen while it is unlocked
302 MSInstanceMessageHook1(id, SBLockScreenView, initWithFrame, CGRect, frame) {
303 self = MSOldCall(frame);
304 CydgetSetup();
305 [[cydgets_ objectAtIndex:active_] enableCydget];
306 return self;
307 }
308
309 MSInstanceMessageHook1(void, SBAwayController, _finishedUnlockAttemptWithStatus, BOOL, status) {
310 if (status)
311 Deactivate_();
312 MSOldCall(status);
313 }
314
315 // this is called occasionally by -[SBAwayView updateInterface] and takes away our keyboard
316 MSInstanceMessageHook0(void, SBAwayView, _fixupFirstResponder) {
317 if (online_ == 0)
318 return MSOldCall();
319 }
320
321 MSInstanceMessageHook0(void, SBAwayView, updateInterface) {
322 MSOldCall();
323
324 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
325
326 NSString *background([cydget objectForKey:@"CYBackground"]);
327 if ([background isEqualToString:@"Wallpaper"]) {
328 MSIvarHook(UIView *, _backgroundView);
329 [_backgroundView setAlpha:1.0f];
330 }
331
332 if ([[cydget objectForKey:@"CYShowDateTime"] boolValue])
333 [self addDateView];
334 }