Port WebCycript parser (CDATA only) to iOS 5.0.
[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 @end
93
94 MSClassHook(UIPeripheralHost)
95
96 @interface UITextEffectsWindow : UIWindow
97 + (UIWindow *) sharedTextEffectsWindow;
98 @end
99
100 @implementation NSDictionary (Cydgets)
101
102 - (void) enableCydget:(SBAwayController *)away {
103     if (NSString *plugin = [self objectForKey:@"CYPlugin"] ?: [self objectForKey:@"Plugin"]) {
104         ++online_;
105         UIKeyboardEnableAutomaticAppearance();
106
107         [[UITextEffectsWindow sharedTextEffectsWindow] setWindowLevel:1000];
108
109         [away enableLockScreenBundleWithName:plugin];
110     }
111 }
112
113 - (void) disableCydget:(SBAwayController *)away {
114     if (NSString *plugin = [self objectForKey:@"CYPlugin"] ?: [self objectForKey:@"Plugin"]) {
115         [away disableLockScreenBundleWithName:plugin];
116
117         [$UIPeripheralHost _releaseSharedInstance];
118
119         UIKeyboardDisableAutomaticAppearance();
120         --online_;
121     }
122 }
123
124 @end
125
126 // avoid rendering a keyboard onto the default SBAwayView while automatic keyboard is online
127 MSInstanceMessageHook0(UIView *, SBAwayView, inputView) {
128     // XXX: there is a conceptual error here
129     if (online_ == 0 && false)
130         return MSOldCall();
131
132     return [[[UIView alloc] init] autorelease];
133 }
134
135 // by default, keyboard actions are redirected to SBAwayController and press menu button
136 MSInstanceMessageHook1(void, SpringBoard, handleKeyEvent, GSEventRef, event) {
137     if (online_ == 0)
138         return MSOldCall(event);
139
140     return MSSuperCall(event);
141 }
142
143 MSInstanceMessageHook0(BOOL, SBAwayController, handleMenuButtonTap) {
144     if (!MSOldCall() && menu_) {
145         [[cydgets_ objectAtIndex:active_] disableCydget:self];
146         active_ = (active_ + 1) % [cydgets_ count];
147         [[cydgets_ objectAtIndex:active_] enableCydget:self];
148     }
149
150     return YES;
151 }
152
153 MSInstanceMessageHook0(void, SBAwayController, undimScreen) {
154     if ([self isDimmed]) {
155         menu_ = false;
156         [[cydgets_ objectAtIndex:active_] enableCydget:self];
157         [[[self awayView] window] makeKeyWindow];
158     }
159
160     MSOldCall();
161 }
162
163 static void Deactivate_(SBAwayController *self) {
164     [[cydgets_ objectAtIndex:active_] disableCydget:self];
165     active_ = 0;
166 }
167
168 MSInstanceMessageHook1(void, SBAwayController, dimScreen, BOOL, dim) {
169     Deactivate_([$SBAwayController sharedAwayController]);
170     MSOldCall(dim);
171 }
172
173 MSInstanceMessageHook1(void, SpringBoard, menuButtonDown, GSEventRef, event) {
174     menu_ = true;
175     MSOldCall(event);
176 }
177
178 MSInstanceMessageHook1(void, SBAwayView, addGestureRecognizer, id, recognizer) {
179     if (online_ == 0)
180         return MSOldCall(recognizer);
181 }
182
183 MSInstanceMessageHook1(void, SBAwayWindow, sendGSEvent, GSEventRef, event) {
184     NSLog(@"sendGSEvent");
185     if (online_ == 0)
186         return MSOldCall(event);
187
188     return MSSuperCall(event);
189 }
190
191 #define Cydgets_ @"/System/Library/LockCydgets"
192
193 static void CydgetSetup() {
194     NSString *plist([NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()]);
195     settings_ = [NSMutableDictionary dictionaryWithContentsOfFile:plist] ?: [NSMutableDictionary dictionary];
196
197     NSArray *cydgets([settings_ objectForKey:@"LockCydgets"] ?: [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:
198         @"Welcome", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
199     ], [NSDictionary dictionaryWithObjectsAndKeys:
200         @"AwayView", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
201     ], nil]);
202
203     cydgets_ = [NSMutableArray arrayWithCapacity:4];
204     for (NSDictionary *cydget in cydgets)
205         if ([[cydget objectForKey:@"Active"] boolValue])
206             if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/%@.cydget/Info.plist", Cydgets_, [cydget objectForKey:@"Name"]]])
207                 [cydgets_ addObject:info];
208
209     if ([cydgets_ count] == 0)
210         cydgets_ = nil;
211 }
212
213 // XXX: this could happen while it is unlocked
214 MSInstanceMessageHook1(id, SBAwayView, initWithFrame, CGRect, frame) {
215     self = MSOldCall(frame);
216     CydgetSetup();
217     [[cydgets_ objectAtIndex:active_] enableCydget:[$SBAwayController sharedAwayController]];
218     return self;
219 }
220
221 MSInstanceMessageHook1(void, SBAwayController, _finishedUnlockAttemptWithStatus, BOOL, status) {
222     if (status)
223         Deactivate_(self);
224     MSOldCall(status);
225 }
226
227 // this is called occasionally by -[SBAwayView updateInterface] and takes away our keyboard
228 MSInstanceMessageHook0(void, SBAwayView, _fixupFirstResponder) {
229     if (online_ == 0)
230         return MSOldCall();
231 }
232
233 MSInstanceMessageHook0(void, SBAwayView, updateInterface) {
234     MSOldCall();
235
236     NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
237
238     NSString *background([cydget objectForKey:@"CYBackground"]);
239     if ([background isEqualToString:@"Wallpaper"]) {
240         MSIvarHook(UIView *, _backgroundView);
241         [_backgroundView setAlpha:1.0f];
242     }
243
244     if ([[cydget objectForKey:@"CYShowDateTime"] boolValue])
245         [self addDateView];
246 }