Checkpoint for Optimo test.
[cydget.git] / Tweak.mm
1 /* Cydget - open-source IntelliScreen replacement
2  * Copyright (C) 2009  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/SBAwayWindow.h>
46
47 MSClassHook(SpringBoard)
48 MSClassHook(SBAwayController)
49 MSClassHook(SBAwayView)
50
51 #define _trace() \
52     NSLog(@"_trace(%s:%u)@%s %d", __FILE__, __LINE__, __FUNCTION__, active_)
53 #define _not(type) \
54     static_cast<type>(~type())
55
56 static bool menu_;
57 static unsigned lock_;
58
59 static _H<NSArray> cydgets_;
60 static size_t active_ = _not(size_t);
61
62 @interface CydgetController : NSObject {
63 }
64
65 + (NSDictionary *) currentConfiguration;
66
67 @end
68
69 @implementation CydgetController
70
71 + (NSDictionary *) currentConfiguration {
72     return active_ == _not(size_t) ? nil : [[cydgets_ objectAtIndex:active_] objectForKey:@"Configuration"];
73 }
74
75 @end
76
77 @interface NSDictionary (Cydgets)
78 - (void) enableCydget:(SBAwayController *)away;
79 - (void) disableCydget:(SBAwayController *)away;
80 @end
81
82 @implementation NSDictionary (Cydgets)
83
84 - (void) enableCydget:(SBAwayController *)away {
85     [away enableLockScreenBundleWithName:[self objectForKey:@"Plugin"]];
86 }
87
88 - (void) disableCydget:(SBAwayController *)away {
89     [away disableLockScreenBundleWithName:[self objectForKey:@"Plugin"]];
90 }
91
92 @end
93
94 MSHook(BOOL, SBAwayController$handleMenuButtonTap, SBAwayController *self, SEL sel) {
95     unsigned lock(lock_);
96
97     if (!_SBAwayController$handleMenuButtonTap(self, sel) && lock != 2) {
98         if (active_ != _not(size_t))
99             [[cydgets_ objectAtIndex:active_] disableCydget:self];
100
101         size_t count([cydgets_ count]);
102         if ((++active_ %= count + 1) == count)
103             active_ = _not(size_t);
104         else
105             [[cydgets_ objectAtIndex:active_] enableCydget:self];
106     }
107
108     return YES;
109 }
110
111 MSHook(void, SBAwayController$_undimScreen, SBAwayController *self, SEL sel) {
112     if (lock_ == 1)
113         lock_ = 2;
114     _SBAwayController$_undimScreen(self, sel);
115 }
116
117 MSHook(void, SBAwayController$undimScreen, SBAwayController *self, SEL sel) {
118     lock_ = menu_ ? 1 : 0;
119     _SBAwayController$undimScreen(self, sel);
120 }
121
122 static void Deactivate_(SBAwayController *self) {
123     if (active_ != _not(size_t)) {
124         [[cydgets_ objectAtIndex:active_] disableCydget:self];
125         active_ = _not(size_t);
126     }
127 }
128
129 MSHook(void, SBAwayController$finishedDimmingScreen, SBAwayController *self, SEL sel) {
130     Deactivate_(self);
131     _SBAwayController$finishedDimmingScreen(self, sel);
132 }
133
134 MSHook(void, SpringBoard$menuButtonUp$, UIView *self, SEL sel, GSEventRef event) {
135     menu_ = true;
136     _SpringBoard$menuButtonUp$(self, sel, event);
137     menu_ = false;
138 }
139
140 MSHook(void, SBAwayView$addGestureRecognizer$, UIView *self, SEL sel, id gr) {
141     //_SBAwayView$addGestureRecognizer$(self, sel, gr);
142 }
143
144 MSInstanceMessageHook1(void, SBAwayController, _finishedUnlockAttemptWithStatus, BOOL, status) {
145     if (status)
146         Deactivate_(self);
147     MSOldCall(status);
148 }
149
150 /* Cydget:// Protocol {{{ */
151 @interface CydgetURLProtocol : NSURLProtocol {
152 }
153
154 @end
155
156 @implementation CydgetURLProtocol
157
158 + (BOOL) canInitWithRequest:(NSURLRequest *)request {
159     NSURL *url([request URL]);
160     if (url == nil)
161         return NO;
162     NSString *scheme([[url scheme] lowercaseString]);
163     if (scheme == nil || ![scheme isEqualToString:@"cydget"])
164         return NO;
165     return YES;
166 }
167
168 + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
169     return request;
170 }
171
172 - (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
173     id<NSURLProtocolClient> client([self client]);
174     if (icon == nil)
175         [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
176     else {
177         NSData *data(UIImagePNGRepresentation(icon));
178
179         NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
180         [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
181         [client URLProtocol:self didLoadData:data];
182         [client URLProtocolDidFinishLoading:self];
183     }
184 }
185
186 - (void) startLoading {
187     id<NSURLProtocolClient> client([self client]);
188     NSURLRequest *request([self request]);
189
190     NSURL *url([request URL]);
191     NSString *href([url absoluteString]);
192
193     NSString *path([href substringFromIndex:9]);
194     NSRange slash([path rangeOfString:@"/"]);
195
196     NSString *command;
197     if (slash.location == NSNotFound) {
198         command = path;
199         path = nil;
200     } else {
201         command = [path substringToIndex:slash.location];
202         path = [path substringFromIndex:(slash.location + 1)];
203     }
204
205     if ([command isEqualToString:@"_UIImageWithName"]) {
206         if (path == nil)
207             goto fail;
208         path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
209         UIImage *icon(_UIImageWithName(path));
210         [self _returnPNGWithImage:icon forRequest:request];
211     } else fail: {
212         [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
213     }
214 }
215
216 - (void) stopLoading {
217 }
218
219 @end
220 /* }}} */
221
222 #define Cydgets_ @"/System/Library/LockCydgets"
223
224 MSInitialize { _pooled
225     [NSURLProtocol registerClass:[CydgetURLProtocol class]];
226
227     cydgets_ = [NSMutableArray arrayWithCapacity:4];
228
229     for (NSString *folder in [[NSFileManager defaultManager] directoryContentsAtPath:Cydgets_])
230         if ([folder hasSuffix:@".cydget"])
231             [cydgets_ addObject:[NSDictionary dictionaryWithContentsOfFile:[[Cydgets_ stringByAppendingPathComponent:folder] stringByAppendingPathComponent:@"Info.plist"]]];
232
233     MSHookMessage1(SpringBoard, menuButtonUp);
234
235     MSHookMessage0(SBAwayController, handleMenuButtonTap);
236     MSHookMessage0(SBAwayController, _undimScreen);
237     MSHookMessage0(SBAwayController, undimScreen);
238     MSHookMessage0(SBAwayController, finishedDimmingScreen);
239
240     MSHookMessage1(SBAwayView, addGestureRecognizer);
241 }