I forgot to commit the weekend's testing release.
[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/SBAwayView.h>
46 #import <SpringBoard/SBAwayWindow.h>
47 #import <SpringBoard/SpringBoard.h>
48
49 MSClassHook(SpringBoard)
50 MSClassHook(SBAwayController)
51 MSClassHook(SBAwayView)
52 MSClassHook(SBAwayWindow)
53
54 #define _trace() \
55     NSLog(@"_trace(%s:%u)@%s %d", __FILE__, __LINE__, __FUNCTION__, active_)
56 #define _not(type) \
57     static_cast<type>(~type())
58
59 static bool menu_;
60 static unsigned lock_;
61
62 static _H<NSArray> settings_;
63 static _H<NSArray> cydgets_;
64 static size_t active_;
65 static unsigned online_;
66
67 @interface CydgetController : NSObject {
68 }
69
70 + (NSDictionary *) currentConfiguration;
71
72 @end
73
74 @implementation CydgetController
75
76 + (NSDictionary *) currentConfiguration {
77     NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
78     return [cydget objectForKey:@"CYConfiguration"] ?: [cydget objectForKey:@"Configuration"];
79 }
80
81 @end
82
83 @interface NSDictionary (Cydgets)
84 - (void) enableCydget:(SBAwayController *)away;
85 - (void) disableCydget:(SBAwayController *)away;
86 @end
87
88 @implementation NSDictionary (Cydgets)
89
90 - (void) enableCydget:(SBAwayController *)away {
91     if (NSString *plugin = [self objectForKey:@"CYPlugin"] ?: [self objectForKey:@"Plugin"]) {
92         [away enableLockScreenBundleWithName:plugin];
93         ++online_;
94     }
95 }
96
97 - (void) disableCydget:(SBAwayController *)away {
98     if (NSString *plugin = [self objectForKey:@"CYPlugin"] ?: [self objectForKey:@"Plugin"]) {
99         [away disableLockScreenBundleWithName:plugin];
100         --online_;
101     }
102 }
103
104 @end
105
106 MSInstanceMessageHook0(BOOL, SBAwayController, handleMenuButtonTap) {
107     unsigned lock(lock_);
108
109     if (!MSOldCall() && lock != 2) {
110         [[cydgets_ objectAtIndex:active_] disableCydget:self];
111         active_ = (active_ + 1) % [cydgets_ count];
112         [[cydgets_ objectAtIndex:active_] enableCydget:self];
113     } else if (lock == 2)
114         lock_ = 0;
115
116     return YES;
117 }
118
119 MSInstanceMessageHook0(void, SBAwayController, _undimScreen) {
120     if (lock_ == 1)
121         lock_ = 2;
122     [[cydgets_ objectAtIndex:active_] enableCydget:[$SBAwayController sharedAwayController]];
123     MSOldCall();
124 }
125
126 MSInstanceMessageHook0(void, SBAwayController, undimScreen) {
127     if (lock_ != 2)
128         lock_ = menu_ ? 1 : 0;
129     MSOldCall();
130 }
131
132 static void Deactivate_(SBAwayController *self) {
133     [[cydgets_ objectAtIndex:active_] disableCydget:self];
134     active_ = 0;
135 }
136
137 #define MSNotificationHook(notification) \
138     static void N_mas(); \
139     static void burple(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef info) { \
140         N_mas(); \
141     } \
142     static class N_MSq { public: _finline N_MSq() { \
143         CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, &burple, CFSTR(notification), NULL, 0); \
144     } } n_msq; \
145     static void N_mas()
146
147 MSNotificationHook("SBDidTurnOffDisplayNotification") {
148     Deactivate_([$SBAwayController sharedAwayController]);
149 }
150
151 MSInstanceMessageHook1(void, SpringBoard, menuButtonUp, GSEventRef, event) {
152     menu_ = true;
153     MSOldCall(event);
154     menu_ = false;
155 }
156
157 MSInstanceMessageHook1(void, SBAwayView, addGestureRecognizer, id, recognizer) {
158     // MSOldCall(recognizer);
159 }
160
161 MSInstanceMessageHook1(void, SBAwayWindow, sendGSEvent, GSEventRef, event) {
162     if (online_ != 0)
163         MSSuperCall(event);
164     else
165         MSOldCall(event);
166 }
167
168 #define Cydgets_ @"/System/Library/LockCydgets"
169
170 static void CydgetSetup() {
171     NSString *plist([NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()]);
172     settings_ = [NSMutableDictionary dictionaryWithContentsOfFile:plist] ?: [NSMutableDictionary dictionary];
173
174     NSArray *cydgets([settings_ objectForKey:@"LockCydgets"] ?: [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:
175         @"Welcome", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
176     ], [NSDictionary dictionaryWithObjectsAndKeys:
177         @"AwayView", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
178     ], nil]);
179
180     cydgets_ = [NSMutableArray arrayWithCapacity:4];
181     for (NSDictionary *cydget in cydgets)
182         if ([[cydget objectForKey:@"Active"] boolValue])
183             if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/%@.cydget/Info.plist", Cydgets_, [cydget objectForKey:@"Name"]]])
184                 [cydgets_ addObject:info];
185
186     if ([cydgets_ count] == 0)
187         cydgets_ = nil;
188 }
189
190 // XXX: this could happen while it is unlocked
191 MSInstanceMessageHook1(id, SBAwayView, initWithFrame, CGRect, frame) {
192     self = MSOldCall(frame);
193     CydgetSetup();
194     [[cydgets_ objectAtIndex:active_] enableCydget:[$SBAwayController sharedAwayController]];
195     return self;
196 }
197
198 MSInstanceMessageHook1(void, SBAwayController, _finishedUnlockAttemptWithStatus, BOOL, status) {
199     if (status)
200         Deactivate_(self);
201     MSOldCall(status);
202 }
203
204 MSInstanceMessageHook0(void, SBAwayView, updateInterface) {
205     MSOldCall();
206
207     NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
208
209     NSString *background([cydget objectForKey:@"CYBackground"]);
210     if ([background isEqualToString:@"Wallpaper"]) {
211         MSIvarHook(UIView *, _backgroundView);
212         [_backgroundView setAlpha:1.0f];
213     }
214
215     if ([[cydget objectForKey:@"CYShowDateTime"] boolValue])
216         [self addDateView];
217 }