]> git.saurik.com Git - cydget.git/blob - Tweak.mm
Reorganize state machine (again).
[cydget.git] / Tweak.mm
1 /* Cydget - open-source AwayView plugin multiplexer
2 * Copyright (C) 2009-2011 Jay Freeman (saurik)
3 */
4
5 /*
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
17 * distribution.
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include <substrate.h>
39
40 #include <Foundation/Foundation.h>
41 #include <GraphicsServices/GraphicsServices.h>
42 #include <UIKit/UIKit.h>
43
44 #import <SpringBoard/SBAwayController.h>
45 #import <SpringBoard/SBAwayView.h>
46 #import <SpringBoard/SBAwayWindow.h>
47 #import <SpringBoard/SpringBoard.h>
48
49 MSClassHook(SpringBoard)
50
51 MSClassHook(SBAwayController)
52 MSClassHook(SBAwayView)
53 MSClassHook(SBAwayViewController)
54 MSClassHook(SBAwayWindow)
55
56 #define _trace() \
57 NSLog(@"_trace(%s:%u)@%s %d", __FILE__, __LINE__, __FUNCTION__, active_)
58 #define _not(type) \
59 static_cast<type>(~type())
60
61 static bool menu_;
62
63 static _H<NSArray> settings_;
64 static _H<NSArray> cydgets_;
65 static size_t active_;
66 static unsigned online_;
67
68 @interface CydgetController : NSObject {
69 }
70
71 + (NSDictionary *) currentConfiguration;
72
73 @end
74
75 @implementation CydgetController
76
77 + (NSDictionary *) currentConfiguration {
78 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
79 return [cydget objectForKey:@"CYConfiguration"] ?: [cydget objectForKey:@"Configuration"];
80 }
81
82 @end
83
84 @interface NSDictionary (Cydgets)
85 - (void) enableCydget:(SBAwayController *)away;
86 - (void) disableCydget:(SBAwayController *)away;
87 @end
88
89 @interface UIPeripheralHost : NSObject
90 + (UIPeripheralHost *) sharedInstance;
91 + (void) _releaseSharedInstance;
92 - (void) createAutomaticKeyboardIfNeeded;
93 @end
94
95 MSClassHook(UIPeripheralHost)
96
97 @interface UITextEffectsWindow : UIWindow
98 + (UIWindow *) sharedTextEffectsWindow;
99 @end
100
101 @implementation NSDictionary (Cydgets)
102
103 - (void) enableCydget:(SBAwayController *)away {
104 if (NSString *plugin = [self objectForKey:@"CYPlugin"] ?: [self objectForKey:@"Plugin"]) {
105 ++online_;
106 UIKeyboardEnableAutomaticAppearance();
107
108 [[$UIPeripheralHost sharedInstance] createAutomaticKeyboardIfNeeded];
109 [[UITextEffectsWindow sharedTextEffectsWindow] setWindowLevel:1000];
110
111 [away enableLockScreenBundleWithName:plugin];
112 }
113 }
114
115 - (void) disableCydget:(SBAwayController *)away {
116 if (NSString *plugin = [self objectForKey:@"CYPlugin"] ?: [self objectForKey:@"Plugin"]) {
117 [away disableLockScreenBundleWithName:plugin];
118
119 [$UIPeripheralHost _releaseSharedInstance];
120
121 UIKeyboardDisableAutomaticAppearance();
122 --online_;
123 }
124 }
125
126 @end
127
128 // avoid rendering a keyboard onto the default SBAwayView while automatic keyboard is online
129 MSInstanceMessageHook0(UIView *, SBAwayView, inputView) {
130 if (online_ == 0)
131 return MSOldCall();
132
133 return [[[UIView alloc] init] autorelease];
134 }
135
136 // by default, keyboard actions are redirected to SBAwayController and press menu button
137 MSInstanceMessageHook1(void, SpringBoard, handleKeyEvent, GSEventRef, event) {
138 if (online_ == 0)
139 return MSOldCall(event);
140
141 return MSSuperCall(event);
142 }
143
144 MSInstanceMessageHook0(BOOL, SBAwayController, handleMenuButtonTap) {
145 if (!MSOldCall() && menu_) {
146 [[cydgets_ objectAtIndex:active_] disableCydget:self];
147 active_ = (active_ + 1) % [cydgets_ count];
148 [[cydgets_ objectAtIndex:active_] enableCydget:self];
149 }
150
151 return YES;
152 }
153
154 MSInstanceMessageHook0(void, SBAwayController, _undimScreen) {
155 menu_ = false;
156 [[cydgets_ objectAtIndex:active_] enableCydget:self];
157 [[[self awayView] window] makeKeyWindow];
158 return MSOldCall();
159 }
160
161 static void Deactivate_(SBAwayController *self) {
162 [[cydgets_ objectAtIndex:active_] disableCydget:self];
163 active_ = 0;
164 }
165
166 MSInstanceMessageHook1(void, SBAwayController, dimScreen, BOOL, dim) {
167 Deactivate_([$SBAwayController sharedAwayController]);
168 MSOldCall(dim);
169 }
170
171 MSInstanceMessageHook1(void, SpringBoard, menuButtonUp, GSEventRef, event) {
172 menu_ = true;
173 MSOldCall(event);
174 }
175
176 MSInstanceMessageHook1(void, SBAwayView, addGestureRecognizer, id, recognizer) {
177 if (online_ == 0)
178 return MSOldCall(recognizer);
179 }
180
181 MSInstanceMessageHook1(void, SBAwayWindow, sendGSEvent, GSEventRef, event) {
182 NSLog(@"sendGSEvent");
183 if (online_ == 0)
184 return MSOldCall(event);
185
186 return MSSuperCall(event);
187 }
188
189 #define Cydgets_ @"/System/Library/LockCydgets"
190
191 static void CydgetSetup() {
192 NSString *plist([NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()]);
193 settings_ = [NSMutableDictionary dictionaryWithContentsOfFile:plist] ?: [NSMutableDictionary dictionary];
194
195 NSArray *cydgets([settings_ objectForKey:@"LockCydgets"] ?: [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:
196 @"Welcome", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
197 ], [NSDictionary dictionaryWithObjectsAndKeys:
198 @"AwayView", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
199 ], nil]);
200
201 cydgets_ = [NSMutableArray arrayWithCapacity:4];
202 for (NSDictionary *cydget in cydgets)
203 if ([[cydget objectForKey:@"Active"] boolValue])
204 if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/%@.cydget/Info.plist", Cydgets_, [cydget objectForKey:@"Name"]]])
205 [cydgets_ addObject:info];
206
207 if ([cydgets_ count] == 0)
208 cydgets_ = nil;
209 }
210
211 // XXX: this could happen while it is unlocked
212 MSInstanceMessageHook1(id, SBAwayView, initWithFrame, CGRect, frame) {
213 self = MSOldCall(frame);
214 CydgetSetup();
215 [[cydgets_ objectAtIndex:active_] enableCydget:[$SBAwayController sharedAwayController]];
216 return self;
217 }
218
219 MSInstanceMessageHook1(void, SBAwayController, _finishedUnlockAttemptWithStatus, BOOL, status) {
220 if (status)
221 Deactivate_(self);
222 MSOldCall(status);
223 }
224
225 MSInstanceMessageHook0(void, SBAwayView, updateInterface) {
226 MSOldCall();
227
228 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
229
230 NSString *background([cydget objectForKey:@"CYBackground"]);
231 if ([background isEqualToString:@"Wallpaper"]) {
232 MSIvarHook(UIView *, _backgroundView);
233 [_backgroundView setAlpha:1.0f];
234 }
235
236 if ([[cydget objectForKey:@"CYShowDateTime"] boolValue])
237 [self addDateView];
238 }