]> git.saurik.com Git - winterboard.git/blob - Library.mm
992563c663e759f3abfcc8fbcf8ad9c9281397f3
[winterboard.git] / Library.mm
1 /* WinterBoard - Theme Manager for the iPhone
2 * Copyright (C) 2008 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 #define _trace() NSLog(@"_trace(%u)", __LINE__);
39
40 #include <objc/runtime.h>
41 #include <objc/message.h>
42
43 #import <Foundation/Foundation.h>
44
45 #import <UIKit/UIColor.h>
46 #import <UIKit/UIImage.h>
47 #import <UIKit/UIImageView.h>
48
49 #import <UIKit/UIView-Geometry.h>
50 #import <UIKit/UIView-Hierarchy.h>
51 #import <UIKit/UIView-Rendering.h>
52
53 #import <SpringBoard/SBApplication.h>
54 #import <SpringBoard/SBAppWindow.h>
55 #import <SpringBoard/SBButtonBar.h>
56 #import <SpringBoard/SBContentLayer.h>
57 #import <SpringBoard/SBUIController.h>
58
59 #import <CoreGraphics/CGGeometry.h>
60
61 /* WinterBoard Backend {{{ */
62 #define WBPrefix "wb_"
63
64 void WBInject(const char *classname, const char *oldname, IMP newimp, const char *type) {
65 Class _class = objc_getClass(classname);
66 if (_class == nil)
67 return;
68 if (!class_addMethod(_class, sel_registerName(oldname), newimp, type))
69 NSLog(@"WB: failed to inject [%s %s]", classname, oldname);
70 }
71
72 void WBRename(const char *classname, const char *oldname, IMP newimp) {
73 Class _class = objc_getClass(classname);
74 if (_class == nil)
75 return;
76 size_t namelen = strlen(oldname);
77 char newname[sizeof(WBPrefix) + namelen];
78 memcpy(newname, WBPrefix, sizeof(WBPrefix) - 1);
79 memcpy(newname + sizeof(WBPrefix) - 1, oldname, namelen + 1);
80 Method method = class_getInstanceMethod(_class, sel_getUid(oldname));
81 if (method == nil)
82 return;
83 const char *type = method_getTypeEncoding(method);
84 if (!class_addMethod(_class, sel_registerName(newname), method_getImplementation(method), type))
85 NSLog(@"WB: failed to rename [%s %s]", classname, oldname);
86 unsigned int count;
87 Method *methods = class_copyMethodList(_class, &count);
88 for (unsigned int index(0); index != count; ++index)
89 if (methods[index] == method)
90 goto found;
91 if (newimp != NULL)
92 if (!class_addMethod(_class, sel_getUid(oldname), newimp, type))
93 NSLog(@"WB: failed to rename [%s %s]", classname, oldname);
94 goto done;
95 found:
96 if (newimp != NULL)
97 method_setImplementation(method, newimp);
98 done:
99 free(methods);
100 }
101
102 static NSString *Dylib_ = @"/Applications/WinterBoard.app/WinterBoard.dylib";
103 /* }}} */
104
105 @protocol WinterBoard
106 - (NSString *) wb_pathForIcon;
107 - (NSString *) wb_pathForResource:(NSString *)resource ofType:(NSString *)type;
108 - (id) wb_initWithSize:(CGSize)size;
109 - (id) wb_initWithFrame:(CGRect)frame;
110 - (void) wb_setFrame:(CGRect)frame;
111 - (void) wb_setBackgroundColor:(id)color;
112 - (void) wb_setAlpha:(float)value;
113 - (void) wb_addSubview:(UIView *)addSubview;
114 @end
115
116 NSString *Themes_ = @"/Library/Themes";
117 NSString *theme_;
118 NSString *Wallpaper_;
119
120 NSString *SBApplication$pathForIcon(SBApplication<WinterBoard> *self, SEL sel) {
121 if (theme_ != nil) {
122 NSFileManager *manager([NSFileManager defaultManager]);
123
124 #define testForIcon(Name) \
125 if (NSString *name = Name) { \
126 NSString *path = [NSString stringWithFormat:@"%@/%@/Icons/%@.png", Themes_, theme_, name]; \
127 if ([manager fileExistsAtPath:path]) \
128 return path; \
129 }
130
131 testForIcon([self displayName]);
132 testForIcon([self bundleIdentifier]);
133 }
134
135 return [self wb_pathForIcon];
136 }
137
138 NSString *NSBundle$pathForResource$ofType$(NSBundle<WinterBoard> *self, SEL sel, NSString *resource, NSString *type) {
139 if (theme_ != nil && [resource isEqualToString:@"SBDockBG"] && [type isEqualToString:@"png"]) {
140 NSFileManager *manager([NSFileManager defaultManager]);
141 NSString *path = [NSString stringWithFormat:@"%@/%@/Dock.png", Themes_, theme_];
142 if ([manager fileExistsAtPath:path])
143 return path;
144 }
145
146 return [self wb_pathForResource:resource ofType:type];
147 }
148
149 void SBAppWindow$setBackgroundColor$(SBAppWindow<WinterBoard> *self, SEL sel, UIColor *color) {
150 if (Wallpaper_ != nil)
151 return [self wb_setBackgroundColor:[UIColor clearColor]];
152 return [self wb_setBackgroundColor:color];
153 }
154
155 /*id SBButtonBar$initWithFrame$(SBButtonBar<WinterBoard> *self, SEL sel, CGRect frame) {
156 self = [self wb_initWithFrame:frame];
157 if (self == nil)
158 return nil;
159 if (Wallpaper_ != nil)
160 [self setBackgroundColor:[UIColor clearColor]];
161 return self;
162 }*/
163
164 id SBContentLayer$initWithSize$(SBContentLayer<WinterBoard> *self, SEL sel, CGSize size) {
165 self = [self wb_initWithSize:size];
166 if (self == nil)
167 return nil;
168
169 if (Wallpaper_ != nil)
170 if (UIImage *image = [[UIImage alloc] initWithContentsOfFile:Wallpaper_])
171 [self addSubview:[[[UIImageView alloc] initWithImage:image] autorelease]];
172
173 return self;
174 }
175
176 extern "C" void WBInitialize() {
177 /* WinterBoard FrontEnd {{{ */
178 if (NSClassFromString(@"SpringBoard") == nil)
179 return;
180 NSLog(@"WB: installing WinterBoard...");
181
182 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
183
184 char *dil = getenv("DYLD_INSERT_LIBRARIES");
185 if (dil == NULL)
186 NSLog(@"WB: DYLD_INSERT_LIBRARIES is unset?");
187 else {
188 NSArray *dylibs = [[NSString stringWithUTF8String:dil] componentsSeparatedByString:@":"];
189 int index = [dylibs indexOfObject:Dylib_];
190 if (index == INT_MAX)
191 NSLog(@"WB: dylib not in DYLD_INSERT_LIBRARIES?");
192 else if ([dylibs count] == 1)
193 unsetenv("DYLD_INSERT_LIBRARIES");
194 else {
195 NSMutableArray *value = [[NSMutableArray alloc] init];
196 [value setArray:dylibs];
197 [value removeObjectAtIndex:index];
198 setenv("DYLD_INSERT_LIBRARIES", [[value componentsJoinedByString:@":"] UTF8String], !0);
199 }
200 }
201 /* }}} */
202
203 WBRename("SBApplication", "pathForIcon", (IMP) &SBApplication$pathForIcon);
204 WBRename("NSBundle", "pathForResource:ofType:", (IMP) &NSBundle$pathForResource$ofType$);
205 WBRename("SBAppWindow", "setBackgroundColor:", (IMP) &SBAppWindow$setBackgroundColor$);
206 WBRename("SBContentLayer", "initWithSize:", (IMP) &SBContentLayer$initWithSize$);
207
208 if (NSDictionary *settings = [[NSDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.WinterBoard.plist", NSHomeDirectory()]]) {
209 [settings autorelease];
210 theme_ = [[settings objectForKey:@"Theme"] retain];
211
212 NSFileManager *manager([NSFileManager defaultManager]);
213 NSString *path = [NSString stringWithFormat:@"%@/%@/Wallpaper.png", Themes_, theme_];
214 if ([manager fileExistsAtPath:path])
215 Wallpaper_ = [path retain];
216 }
217
218 [pool release];
219 }