1 /* WinterBoard - Theme Manager for the iPhone
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
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
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.
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.
38 #define _trace() NSLog(@"WB:_trace(%u)", __LINE__);
41 #include <objc/runtime.h>
42 #include <objc/message.h>
47 #include <mach-o/nlist.h>
50 #include <mach/mach_init.h>
51 #include <mach/vm_map.h>
55 #import <Foundation/Foundation.h>
56 #import <CoreGraphics/CoreGraphics.h>
58 #import <UIKit/UIColor.h>
59 #import <UIKit/UIFont.h>
60 #import <UIKit/UIImage.h>
61 #import <UIKit/UIImageView.h>
62 #import <UIKit/UINavigationBar.h>
63 #import <UIKit/UINavigationBarBackground.h>
64 #import <UIKit/UIToolbar.h>
65 #import <UIKit/UIWebDocumentView.h>
67 #import <UIKit/NSString-UIStringDrawing.h>
68 #import <UIKit/NSString-UIStringDrawingDeprecated.h>
70 #import <UIKit/UIImage-UIImageDeprecated.h>
72 #import <UIKit/UIView-Geometry.h>
73 #import <UIKit/UIView-Hierarchy.h>
74 #import <UIKit/UIView-Rendering.h>
76 #import <SpringBoard/SBApplication.h>
77 #import <SpringBoard/SBApplicationIcon.h>
78 #import <SpringBoard/SBAppWindow.h>
79 #import <SpringBoard/SBBookmarkIcon.h>
80 #import <SpringBoard/SBButtonBar.h>
81 #import <SpringBoard/SBContentLayer.h>
82 #import <SpringBoard/SBIconController.h>
83 #import <SpringBoard/SBIconLabel.h>
84 #import <SpringBoard/SBSlidingAlertDisplay.h>
85 #import <SpringBoard/SBStatusBarContentsView.h>
86 #import <SpringBoard/SBStatusBarController.h>
87 #import <SpringBoard/SBStatusBarTimeView.h>
88 #import <SpringBoard/SBUIController.h>
90 #import <MediaPlayer/MPVideoView.h>
91 #import <MediaPlayer/MPVideoView-PlaybackControl.h>
93 #import <CoreGraphics/CGGeometry.h>
95 extern "C" void __clear_cache (char *beg, char *end);
102 Class $UIWebDocumentView;
104 @interface NSDictionary (WinterBoard)
105 - (UIColor *) colorForKey:(NSString *)key;
106 - (BOOL) boolForKey:(NSString *)key;
109 @implementation NSDictionary (WinterBoard)
111 - (UIColor *) colorForKey:(NSString *)key {
112 NSString *value = [self objectForKey:key];
119 - (BOOL) boolForKey:(NSString *)key {
120 if (NSString *value = [self objectForKey:key])
121 return [value boolValue];
128 bool Engineer_ = true;
130 /* WinterBoard Backend {{{ */
131 #define WBPrefix "wb_"
133 void WBInject(const char *classname, const char *oldname, IMP newimp, const char *type) {
134 Class _class = objc_getClass(classname);
137 if (!class_addMethod(_class, sel_registerName(oldname), newimp, type))
138 NSLog(@"WB:Error: failed to inject [%s %s]", classname, oldname);
141 void WBRename(bool instance, const char *classname, const char *oldname, IMP newimp) {
142 Class _class = objc_getClass(classname);
145 NSLog(@"WB:Warning: cannot find class [%s]", classname);
149 _class = object_getClass(_class);
150 Method method = class_getInstanceMethod(_class, sel_getUid(oldname));
153 NSLog(@"WB:Warning: cannot find method [%s %s]", classname, oldname);
156 size_t namelen = strlen(oldname);
157 char newname[sizeof(WBPrefix) + namelen];
158 memcpy(newname, WBPrefix, sizeof(WBPrefix) - 1);
159 memcpy(newname + sizeof(WBPrefix) - 1, oldname, namelen + 1);
160 const char *type = method_getTypeEncoding(method);
161 if (!class_addMethod(_class, sel_registerName(newname), method_getImplementation(method), type))
162 NSLog(@"WB:Error: failed to rename [%s %s]", classname, oldname);
164 Method *methods = class_copyMethodList(_class, &count);
165 for (unsigned int index(0); index != count; ++index)
166 if (methods[index] == method)
169 if (!class_addMethod(_class, sel_getUid(oldname), newimp, type))
170 NSLog(@"WB:Error: failed to rename [%s %s]", classname, oldname);
174 method_setImplementation(method, newimp);
180 @protocol WinterBoard
181 - (void) wb_updateDesktopImage:(UIImage *)image;
182 - (UIImage *) wb_defaultDesktopImage;
183 - (NSString *) wb_bundlePath;
184 - (NSString *) wb_pathForIcon;
185 - (NSString *) wb_pathForResource:(NSString *)resource ofType:(NSString *)type;
188 - (id) wb_initWithSize:(CGSize)size;
189 - (id) wb_initWithSize:(CGSize)size label:(NSString *)label;
190 - (id) wb_initWithFrame:(CGRect)frame;
191 - (id) wb_initWithCoder:(NSCoder *)coder;
192 - (void) wb_setFrame:(CGRect)frame;
193 - (void) wb_drawRect:(CGRect)rect;
194 - (void) wb_setBackgroundColor:(id)color;
195 - (void) wb_setAlpha:(float)value;
196 - (void) wb_setBarStyle:(int)style;
197 - (id) wb_initWithFrame:(CGRect)frame withBarStyle:(int)style withTintColor:(UIColor *)color;
198 - (void) wb_setOpaque:(BOOL)opaque;
199 - (void) wb_setInDock:(BOOL)docked;
200 - (void) wb_didMoveToSuperview;
201 + (UIImage *) wb_imageNamed:(NSString *)name inBundle:(NSBundle *)bundle;
202 + (UIImage *) wb_applicationImageNamed:(NSString *)name;
203 - (NSDictionary *) wb_infoDictionary;
204 - (UIImage *) wb_icon;
205 - (void) wb_appendIconList:(SBIconList *)list;
206 - (id) wb_initWithStatusBar:(id)bar mode:(int)mode;
207 - (id) wb_initWithMode:(int)mode orientation:(int)orientation;
208 - (id) wb_imageAtPath:(NSString *)path;
209 - (id) wb_initWithContentsOfFile:(NSString *)file;
210 - (id) wb_initWithContentsOfFile:(NSString *)file cache:(BOOL)cache;
211 - (void) wb_setStatusBarMode:(int)mode orientation:(int)orientation duration:(float)duration fenceID:(int)id animation:(int)animation;
214 static NSMutableDictionary **__mappedImages;
215 static NSMutableDictionary *UIImages_;
216 static NSMutableDictionary *PathImages_;
218 static NSFileManager *Manager_;
219 static NSDictionary *English_;
220 static NSMutableDictionary *Info_;
221 static NSMutableArray *themes_;
223 static NSString *$getTheme$(NSArray *files) {
224 for (NSString *theme in themes_)
225 for (NSString *file in files) {
226 NSString *path([NSString stringWithFormat:@"%@/%@", theme, file]);
227 if ([Manager_ fileExistsAtPath:path])
234 static NSString *$pathForIcon$(SBApplication<WinterBoard> *self) {
235 for (NSString *theme in themes_) {
236 NSString *identifier = [self bundleIdentifier];
237 NSString *folder = [[self path] lastPathComponent];
238 NSString *dname = [self displayName];
239 NSString *didentifier = [self displayIdentifier];
242 NSLog(@"WB:Debug: [SBApplication(%@:%@:%@:%@) pathForIcon]", identifier, folder, dname, didentifier);
244 #define testForIcon(Name) \
245 if (NSString *name = Name) { \
246 NSString *path = [NSString stringWithFormat:@"%@/Icons/%@.png", theme, name]; \
247 if ([Manager_ fileExistsAtPath:path]) \
251 if (identifier != nil) {
252 NSString *path = [NSString stringWithFormat:@"%@/Bundles/%@/icon.png", theme, identifier];
253 if ([Manager_ fileExistsAtPath:path])
258 NSString *path = [NSString stringWithFormat:@"%@/Folders/%@/icon.png", theme, folder];
259 if ([Manager_ fileExistsAtPath:path])
263 testForIcon(identifier);
266 if (didentifier != nil) {
267 testForIcon([English_ objectForKey:didentifier]);
269 NSArray *parts = [didentifier componentsSeparatedByString:@"-"];
270 if ([parts count] != 1)
271 if (NSDictionary *english = [[[NSDictionary alloc] initWithContentsOfFile:[[self path] stringByAppendingString:@"/English.lproj/UIRoleDisplayNames.strings"]] autorelease])
272 testForIcon([english objectForKey:[parts lastObject]]);
279 static UIImage *SBApplicationIcon$icon(SBApplicationIcon<WinterBoard> *self, SEL sel) {
280 if (![Info_ boolForKey:@"ComposeStoreIcons"])
281 if (NSString *path = $pathForIcon$([self application]))
282 return [$UIImage imageWithContentsOfFile:path];
283 return [self wb_icon];
286 static UIImage *SBBookmarkIcon$icon(SBBookmarkIcon<WinterBoard> *self, SEL sel) {
288 NSLog(@"WB:Debug:Bookmark(%@:%@)", [self displayIdentifier], [self displayName]);
289 if (NSString *path = $getTheme$([NSArray arrayWithObject:[NSString stringWithFormat:@"Icons/%@.png", [self displayName]]]))
290 return [$UIImage imageWithContentsOfFile:path];
291 return [self wb_icon];
294 static NSString *SBApplication$pathForIcon(SBApplication<WinterBoard> *self, SEL sel) {
295 if (NSString *path = $pathForIcon$(self))
297 return [self wb_pathForIcon];
300 static NSString *$pathForFile$inBundle$(NSString *file, NSBundle<WinterBoard> *bundle, bool ui) {
301 for (NSString *theme in themes_) {
302 NSString *identifier = [bundle bundleIdentifier];
304 if (identifier != nil) {
305 NSString *path = [NSString stringWithFormat:@"%@/Bundles/%@/%@", theme, identifier, file];
306 if ([Manager_ fileExistsAtPath:path])
310 if (NSString *folder = [[bundle wb_bundlePath] lastPathComponent]) {
311 NSString *path = [NSString stringWithFormat:@"%@/Folders/%@/%@", theme, folder, file];
312 if ([Manager_ fileExistsAtPath:path])
316 #define remapResourceName(oldname, newname) \
317 else if ([file isEqualToString:oldname]) { \
318 NSString *path = [NSString stringWithFormat:@"%@/%@.png", theme, newname]; \
319 if ([Manager_ fileExistsAtPath:path]) \
323 if (identifier == nil || ![identifier isEqualToString:@"com.apple.springboard"]);
324 remapResourceName(@"FSO_BG.png", @"StatusBar")
325 remapResourceName(@"SBDockBG.png", @"Dock")
326 remapResourceName(@"SBWeatherCelsius.png", @"Icons/Weather")
329 NSString *path = [NSString stringWithFormat:@"%@/UIImages/%@", theme, file];
330 if ([Manager_ fileExistsAtPath:path])
338 static UIImage *CachedImageAtPath(NSString *path) {
339 UIImage *image = [PathImages_ objectForKey:path];
341 return reinterpret_cast<id>(image) == [NSNull null] ? nil : image;
342 image = [[$UIImage alloc] wb_initWithContentsOfFile:path cache:true];
344 image = [image autorelease];
345 [PathImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:path];
349 static UIImage *UIImage$imageNamed$inBundle$(Class<WinterBoard> self, SEL sel, NSString *name, NSBundle *bundle) {
350 NSString *key = [NSString stringWithFormat:@"%@/%@", [bundle bundleIdentifier], name];
351 UIImage *image = [PathImages_ objectForKey:key];
353 return reinterpret_cast<id>(image) == [NSNull null] ? nil : image;
355 NSLog(@"WB:Debug: [UIImage(%@) imageNamed:\"%@\"]", [bundle bundleIdentifier], name);
356 if (NSString *path = $pathForFile$inBundle$(name, bundle, false))
357 image = CachedImageAtPath(path);
359 image = [self wb_imageNamed:name inBundle:bundle];
360 [PathImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:key];
364 static UIImage *UIImage$imageNamed$(Class<WinterBoard> self, SEL sel, NSString *name) {
365 return UIImage$imageNamed$inBundle$(self, sel, name, [NSBundle mainBundle]);
368 static UIImage *UIImage$applicationImageNamed$(Class<WinterBoard> self, SEL sel, NSString *name) {
369 NSBundle *bundle = [NSBundle mainBundle];
371 NSLog(@"WB:Debug: [UIImage(%@) applicationImageNamed:\"%@\"]", [bundle bundleIdentifier], name);
372 if (NSString *path = $pathForFile$inBundle$(name, bundle, false))
373 return CachedImageAtPath(path);
374 return [self wb_applicationImageNamed:name];
377 @interface NSString (WinterBoard)
378 - (NSString *) wb_themedPath;
381 @implementation NSString (WinterBoard)
383 - (NSString *) wb_themedPath {
384 NSLog(@"WB:Debug:Bypass(\"%@\")", self);
390 static NSMutableDictionary *Files_;
392 #define WBDelegate(delegate) \
393 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel { \
395 NSLog(@"WB:MS:%s:(%s)", class_getName([self class]), sel_getName(sel)); \
396 if (NSMethodSignature *sig = [delegate methodSignatureForSelector:sel]) \
398 NSLog(@"WB:Error: [%s methodSignatureForSelector:(%s)]", class_getName([self class]), sel_getName(sel)); \
402 - (void) forwardInvocation:(NSInvocation*)inv { \
403 SEL sel = [inv selector]; \
404 if ([delegate respondsToSelector:sel]) \
405 [inv invokeWithTarget:delegate]; \
407 NSLog(@"WB:Error: [%s forwardInvocation:(%s)]", class_getName([self class]), sel_getName(sel)); \
410 /*@interface WBBundlePath : NSProxy {
411 NSBundle<WinterBoard> *bundle_;
415 - (id) initWithBundle:(NSBundle *)bundle path:(NSString *)path;
417 - (NSString *) wb_themedPath;
421 @implementation WBBundlePath
429 - (id) initWithBundle:(NSBundle *)bundle path:(NSString *)path {
430 bundle_ = [bundle retain];
431 path_ = [path retain];
437 - (NSString *) stringByAppendingPathComponent:(NSString *)component {
438 NSLog(@"WB:Debug:app:%@:%@", path_, component);
439 return [[[WBBundlePath alloc] initWithBundle:bundle_ path:[path_ stringByAppendingPathComponent:component]] autorelease];
442 - (NSString *) stringByAppendingPathExtension:(NSString *)extension {
443 return [[[WBBundlePath alloc] initWithBundle:bundle_ path:[path_ stringByAppendingPathExtension:extension]] autorelease];
446 - (const char *) UTF8String {
447 const char *string = [path_ UTF8String];
448 NSLog(@"WB:Debug:UTF=%s", string);
452 - (NSString *) description {
453 return [path_ description];
456 - (NSString *) wb_themedPath {
458 NSString *path = [Files_ objectForKey:path_];
460 NSString *path = [bundle_ wb_bundlePath];
461 if (![path_ hasPrefix:path]) {
462 NSLog(@"WB:Error:![@\"%@\" hasPrefix:@\"%@\"]", path_, path);
465 path = [path_ substringFromIndex:([path length] + 1)];
466 path = $pathForFile$inBundle$(path, bundle_, false);
468 path = reinterpret_cast<NSString *>([NSNull null]);
469 [Files_ setObject:path forKey:path_];
471 NSLog(@"WB:Debug:ThemePath(\"%@\")->\"%@\"", path_, path);
473 if (reinterpret_cast<id>(path) == [NSNull null])
475 NSLog(@"WB:Debug:ThemePath=%@", path);
481 static NSString *NSBundle$bundlePath$(NSBundle<WinterBoard> *self, SEL sel) {
482 //return [[WBBundlePath alloc] initWithBundle:self path:[self wb_bundlePath]];
483 return [self wb_bundlePath];
486 static NSString *NSBundle$pathForResource$ofType$(NSBundle<WinterBoard> *self, SEL sel, NSString *resource, NSString *type) {
487 NSString *file = type == nil ? resource : [NSString stringWithFormat:@"%@.%@", resource, type];
489 NSLog(@"WB:Debug: [NSBundle(%@) pathForResource:\"%@\"]", [self bundleIdentifier], file);
490 if (NSString *path = $pathForFile$inBundle$(file, self, false))
492 return [self wb_pathForResource:resource ofType:type];
495 static bool $setBarStyle$_(NSString *name, UIView<WinterBoard> *self, int style) {
497 NSLog(@"WB:Debug:%@Style:%d", name, style);
498 NSNumber *number = nil;
500 number = [Info_ objectForKey:[NSString stringWithFormat:@"%@Style-%d", name, style]];
502 number = [Info_ objectForKey:[NSString stringWithFormat:@"%@Style", name]];
506 style = [number intValue];
508 NSLog(@"WB:Debug:%@Style=%d", name, style);
509 [self wb_setBarStyle:style];
514 /*static id UINavigationBarBackground$initWithFrame$withBarStyle$withTintColor$(UINavigationBarBackground<WinterBoard> *self, SEL sel, CGRect frame, int style, UIColor *tint) {
517 if (NSNumber *number = [Info_ objectForKey:@"NavigationBarStyle"])
518 style = [number intValue];
520 if (UIColor *color = [Info_ colorForKey:@"NavigationBarTint"])
523 return [self wb_initWithFrame:frame withBarStyle:style withTintColor:tint];
526 /*static id UINavigationBar$initWithCoder$(SBAppWindow<WinterBoard> *self, SEL sel, CGRect frame, NSCoder *coder) {
527 self = [self wb_initWithCoder:coder];
530 UINavigationBar$setBarStyle$_(self);
534 static id UINavigationBar$initWithFrame$(SBAppWindow<WinterBoard> *self, SEL sel, CGRect frame) {
535 self = [self wb_initWithFrame:frame];
538 UINavigationBar$setBarStyle$_(self);
542 static void UIToolbar$setBarStyle$(UIToolbar<WinterBoard> *self, SEL sel, int style) {
543 if ($setBarStyle$_(@"Toolbar", self, style))
545 return [self wb_setBarStyle:style];
548 static void UINavigationBar$setBarStyle$(UINavigationBar<WinterBoard> *self, SEL sel, int style) {
549 if ($setBarStyle$_(@"NavigationBar", self, style))
551 return [self wb_setBarStyle:style];
554 static void $didMoveToSuperview(SBButtonBar<WinterBoard> *self, SEL sel) {
555 [[self superview] setBackgroundColor:[$UIColor clearColor]];
556 [self wb_didMoveToSuperview];
559 static id UIImage$imageAtPath$(NSObject<WinterBoard> *self, SEL sel, NSString *path) {
560 return [self wb_imageAtPath:[path wb_themedPath]];
563 static id $initWithContentsOfFile$(NSObject<WinterBoard> *self, SEL sel, NSString *file) {
564 return [self wb_initWithContentsOfFile:[file wb_themedPath]];
567 static id UIImage$initWithContentsOfFile$cache$(UIImage<WinterBoard> *self, SEL sel, NSString *file, BOOL cache) {
568 return [self wb_initWithContentsOfFile:[file wb_themedPath] cache:cache];
571 static UIImage *UIImage$defaultDesktopImage$(UIImage<WinterBoard> *self, SEL sel) {
573 NSLog(@"WB:Debug:DefaultDesktopImage");
574 if (NSString *path = $getTheme$([NSArray arrayWithObjects:@"LockBackground.png", @"LockBackground.jpg", nil]))
575 return [$UIImage imageWithContentsOfFile:path];
576 return [self wb_defaultDesktopImage];
579 static UIImageView *WallpaperImage_;
580 static UIWebDocumentView *WallpaperPage_;
581 static NSURL *WallpaperURL_;
583 static id SBContentLayer$initWithSize$(SBContentLayer<WinterBoard> *self, SEL sel, CGSize size) {
584 self = [self wb_initWithSize:size];
588 if (NSString *path = $getTheme$([NSArray arrayWithObject:@"Wallpaper.mp4"])) {
589 MPVideoView *video = [[[$MPVideoView alloc] initWithFrame:[self bounds]] autorelease];
590 [video setMovieWithPath:path];
591 [video setRepeatMode:1];
592 [video setRepeatGap:0];
593 [self addSubview:video];
594 [video playFromBeginning];;
598 if (NSString *path = $getTheme$([NSArray arrayWithObjects:@"Wallpaper.png", @"Wallpaper.jpg", nil])) {
599 image = [[$UIImage alloc] wb_initWithContentsOfFile:path];
601 image = [image autorelease];
604 if (WallpaperImage_ != nil)
605 [WallpaperImage_ release];
606 WallpaperImage_ = [[$UIImageView alloc] initWithImage:image];
607 [self addSubview:WallpaperImage_];
609 if (NSString *path = $getTheme$([NSArray arrayWithObject:@"Wallpaper.html"])) {
610 CGRect bounds = [self bounds];
612 UIWebDocumentView *view([[[$UIWebDocumentView alloc] initWithFrame:bounds] autorelease]);
613 [view setAutoresizes:true];
615 if (WallpaperPage_ != nil)
616 [WallpaperPage_ release];
617 WallpaperPage_ = [view retain];
619 if (WallpaperURL_ != nil)
620 [WallpaperURL_ release];
621 WallpaperURL_ = [[NSURL fileURLWithPath:path] retain];
623 [WallpaperPage_ loadRequest:[NSURLRequest requestWithURL:WallpaperURL_]];
625 [[view webView] setDrawsBackground:false];
626 [view setBackgroundColor:[$UIColor clearColor]];
628 [self addSubview:view];
634 static void SBSlidingAlertDisplay$updateDesktopImage$(SBSlidingAlertDisplay<WinterBoard> *self, SEL sel, UIImage *image) {
635 NSString *path = $getTheme$([NSArray arrayWithObject:@"LockBackground.html"]);
639 object_getInstanceVariable(self, "_backgroundView", reinterpret_cast<void **>(&background));
640 if (background != nil)
644 [self wb_updateDesktopImage:image];
647 CGRect bounds = [self bounds];
649 UIWebDocumentView *view([[[$UIWebDocumentView alloc] initWithFrame:bounds] autorelease]);
650 [view setAutoresizes:true];
652 if (WallpaperPage_ != nil)
653 [WallpaperPage_ release];
654 WallpaperPage_ = [view retain];
656 if (WallpaperURL_ != nil)
657 [WallpaperURL_ release];
658 WallpaperURL_ = [[NSURL fileURLWithPath:path] retain];
660 [WallpaperPage_ loadRequest:[NSURLRequest requestWithURL:WallpaperURL_]];
662 [[view webView] setDrawsBackground:false];
663 [view setBackgroundColor:[$UIColor clearColor]];
666 object_getInstanceVariable(self, "_backgroundView", reinterpret_cast<void **>(&background));
667 NSLog(@"back:%@", background);
669 [self insertSubview:view aboveSubview:background];
673 static unsigned *__currentContextCount;
674 static void ***__currentContextStack;
676 /*extern "C" CGColorRef CGGStateGetSystemColor(void *);
677 extern "C" CGColorRef CGGStateGetFillColor(void *);
678 extern "C" CGColorRef CGGStateGetStrokeColor(void *);
679 extern "C" NSString *UIStyleStringFromColor(CGColorRef);*/
681 @interface WBTime : NSProxy {
683 _transient SBStatusBarTimeView *view_;
686 - (id) initWithTime:(NSString *)time view:(SBStatusBarTimeView *)view;
690 @implementation WBTime
697 - (id) initWithTime:(NSString *)time view:(SBStatusBarTimeView *)view {
698 time_ = [time retain];
705 - (CGSize) drawAtPoint:(CGPoint)point forWidth:(float)width withFont:(UIFont *)font lineBreakMode:(int)mode {
706 if (NSString *custom = [Info_ objectForKey:@"TimeStyle"]) {
708 object_getInstanceVariable(view_, "_mode", reinterpret_cast<void **>(&mode));
710 [time_ drawAtPoint:point withStyle:[NSString stringWithFormat:@""
711 "font-family: Helvetica; "
712 "font-weight: bold; "
715 "%@", mode ? @"white" : @"black", custom]];
720 return [time_ drawAtPoint:point forWidth:width withFont:font lineBreakMode:mode];
725 @interface WBIconLabel : NSProxy {
730 - (id) initWithString:(NSString *)string;
734 @implementation WBIconLabel
741 - (id) initWithString:(NSString *)string {
742 string_ = [string retain];
748 - (NSString *) _iconLabelStyle {
749 NSString *key = docked_ ? @"DockedIconLabelStyle" : @"UndockedIconLabelStyle";
750 NSString *style = [Info_ objectForKey:key];
754 - (CGSize) drawInRect:(CGRect)rect withFont:(UIFont *)font lineBreakMode:(int)mode alignment:(int)alignment {
755 if (NSString *custom = [self _iconLabelStyle]) {
756 NSString *style = [NSString stringWithFormat:@""
757 "font-family: Helvetica; "
758 "font-weight: bold; "
760 "text-align: center; "
762 "%@", docked_ ? @"white" : @"#b3b3b3", custom];
765 NSLog(@"WB:Debug:style = %@", style);
766 [string_ drawInRect:rect withStyle:style];
770 return [string_ drawInRect:rect withFont:font lineBreakMode:mode alignment:alignment];
773 - (void) drawInRect:(CGRect)rect withStyle:(NSString *)style {
774 if (NSString *custom = [self _iconLabelStyle]) {
775 NSString *combined = [NSString stringWithFormat:@"%@; %@", style, custom];
777 NSLog(@"WB:Debug:combined = %@", combined);
778 return [string_ drawInRect:rect withStyle:combined];
780 return [string_ drawInRect:rect withStyle:style];
783 - (BOOL) respondsToSelector:(SEL)sel {
785 sel == @selector(setInDock:)
786 ? true : [super respondsToSelector:sel];
789 - (void) setInDock:(BOOL)docked {
795 static void SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$(SBStatusBarController<WinterBoard> *self, SEL sel, int mode, int orientation, float duration, int id, int animation) {
797 NSLog(@"WB:Debug:setStatusBarMode:%d", mode);
798 if (mode < 100) // 104:hidden 105:glowing
799 if (NSNumber *number = [Info_ objectForKey:@"StatusBarMode"])
800 mode = [number intValue];
801 return [self wb_setStatusBarMode:mode orientation:orientation duration:duration fenceID:id animation:animation];
804 /*static id SBStatusBar$initWithMode$orientation$(SBStatusBar<WinterBoard> *self, SEL sel, int mode, int orientation) {
805 return [self wb_initWithMode:mode orientation:orientation];
808 static id SBStatusBarContentsView$initWithStatusBar$mode$(SBStatusBarContentsView<WinterBoard> *self, SEL sel, id bar, int mode) {
809 if (NSNumber *number = [Info_ objectForKey:@"StatusBarContentsMode"])
810 mode = [number intValue];
811 return [self wb_initWithStatusBar:bar mode:mode];
814 static void SBStatusBarTimeView$drawRect$(SBStatusBarTimeView<WinterBoard> *self, SEL sel, CGRect rect) {
816 object_getInstanceVariable(self, "_time", reinterpret_cast<void **>(&time));
817 if (time != nil && [time class] != [WBTime class])
818 object_setInstanceVariable(self, "_time", reinterpret_cast<void *>([[WBTime alloc] initWithTime:[time autorelease] view:self]));
819 return [self wb_drawRect:rect];
822 static void SBIconController$appendIconList$(SBIconController<WinterBoard> *self, SEL sel, SBIconList *list) {
824 NSLog(@"appendIconList:%@", list);
825 return [self wb_appendIconList:list];
828 static void SBIconLabel$setInDock$(SBIconLabel<WinterBoard> *self, SEL sel, BOOL docked) {
830 object_getInstanceVariable(self, "_label", reinterpret_cast<void **>(&label));
831 if (![Info_ boolForKey:@"UndockedIconLabels"])
833 if (label != nil && [label respondsToSelector:@selector(setInDock:)])
834 [label setInDock:docked];
835 return [self wb_setInDock:docked];
838 static id SBIconLabel$initWithSize$label$(SBIconLabel<WinterBoard> *self, SEL sel, CGSize size, NSString *label) {
839 // XXX: technically I'm misusing self here
840 return [self wb_initWithSize:size label:[[[WBIconLabel alloc] initWithString:label] autorelease]];
841 //return [self wb_initWithSize:size label:label];
844 extern "C" void FindMappedImages(void);
845 extern "C" NSData *UIImagePNGRepresentation(UIImage *);
847 static UIImage *(*_UIImageAtPath)(NSString *name, NSBundle *path);
848 static CGImageRef *(*_UIImageRefAtPath)(NSString *path, bool cache, UIImageOrientation *orientation);
849 static UIImage *(*_UIImageWithName)(NSString *name);
850 static NSBundle *(*_UIKitBundle)();
851 static void (*_UISharedImageInitialize)(bool);
852 static int (*_UISharedImageNameGetIdentifier)(NSString *);
853 static UIImage *(*_UISharedImageWithIdentifier)(int);
855 static UIImage *$_UIImageWithName(NSString *name) {
856 int id(_UISharedImageNameGetIdentifier(name));
858 NSLog(@"WB:Debug: UIImageWithName(\"%@\", %d)", name, id);
861 return _UIImageAtPath(name, _UIKitBundle());
863 NSNumber *key([NSNumber numberWithInt:id]);
864 UIImage *image = [UIImages_ objectForKey:key];
866 return reinterpret_cast<id>(image) == [NSNull null] ? nil : image;
867 if (NSString *path = $pathForFile$inBundle$(name, _UIKitBundle(), true)) {
868 image = [[$UIImage alloc] wb_initWithContentsOfFile:path];
873 image = _UISharedImageWithIdentifier(id);
874 [UIImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:key];
880 #define ldr_pc_$pc_m4$ 0xe51ff004
882 template <typename Type_>
883 static void WBReplace(Type_ *symbol, Type_ *replace, Type_ **result) {
885 NSLog(@"WB:Notice: Remapping %p to %p", symbol, replace);
889 int page = getpagesize();
890 uintptr_t base = reinterpret_cast<uintptr_t>(symbol) / page * page;
892 mach_port_t self = mach_task_self();
894 if (kern_return_t error = vm_protect(self, base, page, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY)) {
895 NSLog(@"WB:Error:vm_protect():%d", error);
899 uint32_t *code = reinterpret_cast<uint32_t *>(symbol);
900 uint32_t backup[2] = {code[0], code[1]};
902 code[0] = ldr_pc_$pc_m4$;
903 code[1] = reinterpret_cast<uint32_t>(replace);
905 __clear_cache(reinterpret_cast<char *>(code), reinterpret_cast<char *>(code + 2));
907 if (kern_return_t error = vm_protect(self, base, page, FALSE, VM_PROT_READ | VM_PROT_EXECUTE))
908 NSLog(@"WB:Error:vm_protect():%d", error);
910 if (result != NULL) {
911 uint32_t *buffer = reinterpret_cast<uint32_t *>(mmap(
912 NULL, sizeof(uint32_t) * 4,
913 PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
917 if (buffer == MAP_FAILED) {
918 NSLog(@"WB:Error:mmap():%d", errno);
922 buffer[0] = backup[0];
923 buffer[1] = backup[1];
924 buffer[2] = ldr_pc_$pc_m4$;
925 buffer[3] = reinterpret_cast<uint32_t>(code + 2);
927 if (mprotect(buffer, sizeof(uint32_t) * 4, PROT_READ | PROT_EXEC) == -1) {
928 NSLog(@"WB:Error:mprotect():%d", errno);
932 *result = reinterpret_cast<Type_ *>(buffer);
936 template <typename Type_>
937 static void WBReplace(Type_ *symbol, Type_ *replace) {
938 return WBReplace(symbol, replace, static_cast<Type_ **>(NULL));
941 #define AudioToolbox "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"
942 #define UIKit "/System/Library/Frameworks/UIKit.framework/UIKit"
944 /*static void UIWebDocumentView$setViewportSize$forDocumentTypes$(UIWebDocumentView *self, SEL sel, CGSize size, int type) {
945 NSLog(@"WB:Examine: %f:%f:%u", size.width, size.height, type);
948 static bool (*_Z24GetFileNameForThisActionmPcRb)(unsigned long, char *, bool &);
950 static bool $_Z24GetFileNameForThisActionmPcRb(unsigned long a0, char *a1, bool &a2) {
951 bool value = _Z24GetFileNameForThisActionmPcRb(a0, a1, a2);
953 NSLog(@"WB:Debug:GetFileNameForThisAction(%u, %s, %u) = %u", a0, value ? a1 : NULL, a2, value);
956 NSString *path = [NSString stringWithUTF8String:a1];
957 if ([path hasPrefix:@"/System/Library/Audio/UISounds/"]) {
958 NSString *file = [path substringFromIndex:31];
960 for (NSString *theme in themes_) {
961 NSString *path([NSString stringWithFormat:@"%@/UISounds/%@", theme, file]);
962 if ([Manager_ fileExistsAtPath:path]) {
963 strcpy(a1, [path UTF8String]);
972 static void ChangeWallpaper(
973 CFNotificationCenterRef center,
980 NSLog(@"WB:Debug:ChangeWallpaper!");
983 if (NSString *path = $getTheme$([NSArray arrayWithObjects:@"Wallpaper.png", @"Wallpaper.jpg", nil])) {
984 image = [[$UIImage alloc] wb_initWithContentsOfFile:path];
986 image = [image autorelease];
989 if (WallpaperImage_ != nil)
990 [WallpaperImage_ setImage:image];
991 if (WallpaperPage_ != nil)
992 [WallpaperPage_ loadRequest:[NSURLRequest requestWithURL:WallpaperURL_]];
996 extern "C" void WBInitialize() {
997 if (dlopen(UIKit, RTLD_LAZY | RTLD_NOLOAD) == NULL)
999 NSLog(@"WB:Notice: Installing WinterBoard...");
1001 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1003 NSBundle *MediaPlayer = [NSBundle bundleWithPath:@"/System/Library/Frameworks/MediaPlayer.framework"];
1004 if (MediaPlayer != nil)
1007 $MPVideoView = objc_getClass("MPVideoView");
1009 $UIColor = objc_getClass("UIColor");
1010 $UIImage = objc_getClass("UIImage");
1011 $UIImageView = objc_getClass("UIImageView");
1012 $UIWebDocumentView = objc_getClass("UIWebDocumentView");
1014 struct nlist nl[11];
1015 memset(nl, 0, sizeof(nl));
1017 nl[0].n_un.n_name = (char *) "___currentContextCount";
1018 nl[1].n_un.n_name = (char *) "___currentContextStack";
1019 nl[2].n_un.n_name = (char *) "___mappedImages";
1020 nl[3].n_un.n_name = (char *) "__UIImageAtPath";
1021 nl[4].n_un.n_name = (char *) "__UIImageRefAtPath";
1022 nl[5].n_un.n_name = (char *) "__UIImageWithName";
1023 nl[6].n_un.n_name = (char *) "__UIKitBundle";
1024 nl[7].n_un.n_name = (char *) "__UISharedImageInitialize";
1025 nl[8].n_un.n_name = (char *) "__UISharedImageNameGetIdentifier";
1026 nl[9].n_un.n_name = (char *) "__UISharedImageWithIdentifier";
1030 __currentContextCount = (unsigned *) nl[0].n_value;
1031 __currentContextStack = (void ***) nl[1].n_value;
1032 __mappedImages = (id *) nl[2].n_value;
1033 _UIImageAtPath = (UIImage *(*)(NSString *, NSBundle *)) nl[3].n_value;
1034 _UIImageRefAtPath = (CGImageRef *(*)(NSString *, bool, UIImageOrientation *)) nl[4].n_value;
1035 _UIImageWithName = (UIImage *(*)(NSString *)) nl[5].n_value;
1036 _UIKitBundle = (NSBundle *(*)()) nl[6].n_value;
1037 _UISharedImageInitialize = (void (*)(bool)) nl[7].n_value;
1038 _UISharedImageNameGetIdentifier = (int (*)(NSString *)) nl[8].n_value;
1039 _UISharedImageWithIdentifier = (UIImage *(*)(int)) nl[9].n_value;
1041 WBReplace(_UIImageWithName, &$_UIImageWithName);
1043 if (dlopen(AudioToolbox, RTLD_LAZY | RTLD_NOLOAD) != NULL) {
1045 memset(nl, 0, sizeof(nl));
1046 nl[0].n_un.n_name = (char *) "__Z24GetFileNameForThisActionmPcRb";
1047 nlist(AudioToolbox, nl);
1048 _Z24GetFileNameForThisActionmPcRb = (bool (*)(unsigned long, char *, bool &)) nl[0].n_value;
1049 WBReplace(_Z24GetFileNameForThisActionmPcRb, &$_Z24GetFileNameForThisActionmPcRb, &_Z24GetFileNameForThisActionmPcRb);
1052 WBRename(false, "UIImage", "applicationImageNamed:", (IMP) &UIImage$applicationImageNamed$);
1053 WBRename(false, "UIImage", "defaultDesktopImage", (IMP) &UIImage$defaultDesktopImage$);
1054 WBRename(false, "UIImage", "imageAtPath:", (IMP) &UIImage$imageAtPath$);
1055 WBRename(false, "UIImage", "imageNamed:", (IMP) &UIImage$imageNamed$);
1056 WBRename(false, "UIImage", "imageNamed:inBundle:", (IMP) &UIImage$imageNamed$inBundle$);
1058 //WBRename("UINavigationBar", "initWithCoder:", (IMP) &UINavigationBar$initWithCoder$);
1059 //WBRename("UINavigationBarBackground", "initWithFrame:withBarStyle:withTintColor:", (IMP) &UINavigationBarBackground$initWithFrame$withBarStyle$withTintColor$);
1060 //WBRename(true, "SBStatusBar", "initWithMode:orientation:", (IMP) &SBStatusBar$initWithMode$orientation$);
1061 //WBRename(true, "UIWebDocumentView", "setViewportSize:forDocumentTypes:", (IMP) &UIWebDocumentView$setViewportSize$forDocumentTypes$);
1063 WBRename(true, "NSBundle", "bundlePath", (IMP) &NSBundle$bundlePath$);
1064 WBRename(true, "NSBundle", "pathForResource:ofType:", (IMP) &NSBundle$pathForResource$ofType$);
1066 WBRename(true, "UIImage", "initWithContentsOfFile:", (IMP) &$initWithContentsOfFile$);
1067 WBRename(true, "UIImage", "initWithContentsOfFile:cache:", (IMP) &UIImage$initWithContentsOfFile$cache$);
1068 WBRename(true, "UINavigationBar", "setBarStyle:", (IMP) &UINavigationBar$setBarStyle$);
1069 WBRename(true, "UIToolbar", "setBarStyle:", (IMP) &UIToolbar$setBarStyle$);
1071 WBRename(true, "SBApplication", "pathForIcon", (IMP) &SBApplication$pathForIcon);
1072 WBRename(true, "SBApplicationIcon", "icon", (IMP) &SBApplicationIcon$icon);
1073 WBRename(true, "SBBookmarkIcon", "icon", (IMP) &SBBookmarkIcon$icon);
1074 WBRename(true, "SBButtonBar", "didMoveToSuperview", (IMP) &$didMoveToSuperview);
1075 WBRename(true, "SBContentLayer", "initWithSize:", (IMP) &SBContentLayer$initWithSize$);
1076 WBRename(true, "SBIconLabel", "setInDock:", (IMP) &SBIconLabel$setInDock$);
1077 WBRename(true, "SBIconLabel", "initWithSize:label:", (IMP) &SBIconLabel$initWithSize$label$);
1078 WBRename(true, "SBSlidingAlertDisplay", "updateDesktopImage:", (IMP) &SBSlidingAlertDisplay$updateDesktopImage$);
1079 WBRename(true, "SBStatusBarContentsView", "didMoveToSuperview", (IMP) &$didMoveToSuperview);
1080 WBRename(true, "SBStatusBarContentsView", "initWithStatusBar:mode:", (IMP) &SBStatusBarContentsView$initWithStatusBar$mode$);
1081 WBRename(true, "SBStatusBarController", "setStatusBarMode:orientation:duration:fenceID:animation:", (IMP) &SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$);
1082 WBRename(true, "SBStatusBarTimeView", "drawRect:", (IMP) &SBStatusBarTimeView$drawRect$);
1083 WBRename(true, "SBIconController", "appendIconList:", (IMP) &SBIconController$appendIconList$);
1085 _UISharedImageInitialize(false);
1087 English_ = [[NSDictionary alloc] initWithContentsOfFile:@"/System/Library/CoreServices/SpringBoard.app/English.lproj/LocalizedApplicationNames.strings"];
1088 if (English_ != nil)
1089 English_ = [English_ retain];
1091 Manager_ = [[NSFileManager defaultManager] retain];
1092 UIImages_ = [[NSMutableDictionary alloc] initWithCapacity:16];
1093 PathImages_ = [[NSMutableDictionary alloc] initWithCapacity:16];
1094 Files_ = [[NSMutableDictionary alloc] initWithCapacity:16];
1096 themes_ = [[NSMutableArray alloc] initWithCapacity:8];
1098 if (NSDictionary *settings = [[NSDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"/User/Library/Preferences/com.saurik.WinterBoard.plist"]]) {
1099 [settings autorelease];
1101 if (NSNumber *debug = [settings objectForKey:@"Debug"])
1102 Debug_ = [debug boolValue];
1104 NSArray *themes = [settings objectForKey:@"Themes"];
1106 if (NSString *theme = [settings objectForKey:@"Theme"])
1107 themes = [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:
1109 [NSNumber numberWithBool:true], @"Active",
1112 for (NSDictionary *theme in themes) {
1113 NSNumber *active = [theme objectForKey:@"Active"];
1114 if (![active boolValue])
1117 NSString *name = [theme objectForKey:@"Name"];
1121 NSString *theme = nil;
1123 #define testForTheme(format...) \
1124 if (theme == nil) { \
1125 NSString *path = [NSString stringWithFormat:format]; \
1126 if ([Manager_ fileExistsAtPath:path]) { \
1127 [themes_ addObject:path]; \
1132 testForTheme(@"/Library/Themes/%@.theme", name)
1133 testForTheme(@"/Library/Themes/%@", name)
1134 testForTheme(@"%@/Library/SummerBoard/Themes/%@", NSHomeDirectory(), name)
1138 Info_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1140 for (NSString *theme in themes_)
1141 if (NSDictionary *info = [[NSDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", theme]])
1142 for (NSString *key in [info allKeys])
1143 if ([Info_ objectForKey:key] == nil)
1144 [Info_ setObject:[info objectForKey:key] forKey:key];
1146 if (objc_getClass("SpringBoard") != nil)
1147 CFNotificationCenterAddObserver(
1148 CFNotificationCenterGetDarwinNotifyCenter(),
1149 NULL, &ChangeWallpaper, (CFStringRef) @"com.saurik.winterboard.lockbackground", NULL, 0
1152 if ([Info_ objectForKey:@"UndockedIconLabels"] == nil)
1153 [Info_ setObject:[NSNumber numberWithBool:(
1154 [Info_ objectForKey:@"DockedIconLabelStyle"] != nil ||
1155 [Info_ objectForKey:@"UndockedIconLabelStyle"] != nil
1156 )] forKey:@"UndockedIconLabels"];
1158 if (![Info_ boolForKey:@"UndockedIconLabels"])
1160 NSLog(@"WB:Debug:Info = %@", [Info_ description]);