#include <sys/sysctl.h>
+#include "WBMarkup.h"
+
extern "C" void __clear_cache (char *beg, char *end);
@protocol WinterBoard
Class $MPVideoView;
MSClassHook(NSBundle)
+MSClassHook(NSString)
MSClassHook(UIImage)
+MSMetaClassHook(UIImage)
MSClassHook(UINavigationBar)
MSClassHook(UIToolbar)
MSClassHook(CKMessageCell)
MSClassHook(CKTimestampView)
+MSClassHook(CKTranscriptCell)
MSClassHook(CKTranscriptController)
MSClassHook(CKTranscriptTableView)
MSClassHook(SBAwayView)
MSClassHook(SBBookmarkIcon)
MSClassHook(SBButtonBar)
+MSClassHook(SBCalendarApplicationIcon)
MSClassHook(SBCalendarIconContentsView)
MSClassHook(SBDockIconListView)
MSClassHook(SBIcon)
MSClassHook(SBIconBadge)
+MSClassHook(SBIconBadgeFactory)
MSClassHook(SBIconController)
MSClassHook(SBIconLabel)
MSClassHook(SBIconList)
//MSClassHook(SBImageCache)
MSClassHook(SBSearchView)
MSClassHook(SBSearchTableViewCell)
+MSClassHook(SBSlidingAlertDisplay)
MSClassHook(SBStatusBarContentsView)
MSClassHook(SBStatusBarController)
MSClassHook(SBStatusBarOperatorNameView)
MSClassHook(SBStatusBarTimeView)
MSClassHook(SBUIController)
+MSClassHook(SBWallpaperView)
MSClassHook(SBWidgetApplicationIcon)
+extern "C" void WKSetCurrentGraphicsContext(CGContextRef);
+
__attribute__((__constructor__))
static void MSFixClass() {
if ($SBIcon == nil)
(_GSFontGetUseLegacyFontMetrics == NULL ? YES : _GSFontGetUseLegacyFontMetrics())
static bool Debug_ = false;
+static bool UIDebug_ = false;
static bool Engineer_ = false;
static bool SummerBoard_ = true;
static bool SpringBoard_;
static unsigned Scale_ = 0;
-static NSString *$getTheme$(NSArray *files, bool rescale = false) {
- if (NSString *path = [Themed_ objectForKey:files])
- return reinterpret_cast<id>(path) == [NSNull null] ? nil : path;
+static unsigned $getScale$(NSString *path) {
+ NSString *name(path);
+
+ #define StripName(strip) \
+ if ([name hasSuffix:@ strip]) \
+ name = [name substringWithRange:NSMakeRange(0, [name length] - sizeof(strip) - 1)];
+
+ StripName(".png");
+ StripName(".jpg");
+ StripName("~iphone");
+ StripName("~ipad");
+
+ return [name hasSuffix:@"@2x"] ? 2 : 1;
+}
- if (rescale && Scale_ == 0) {
+static NSArray *$useScale$(NSArray *files, bool use = true) {
+ if (Scale_ == 0) {
UIScreen *screen([UIScreen mainScreen]);
if ([screen respondsToSelector:@selector(scale)])
Scale_ = [screen scale];
Scale_ = 1;
}
+ if (Scale_ == 1)
+ return files;
+
+ NSString *idiom(IsWild_ ? @"ipad" : @"iphone");
+
+ NSMutableArray *scaled([NSMutableArray arrayWithCapacity:([files count] * 4)]);
+
+ for (NSString *file in files) {
+ NSString *base([file stringByDeletingPathExtension]);
+ NSString *extension([file pathExtension]);
+
+ if (use) {
+ if (Scale_ == 2) {
+ [scaled addObject:[NSString stringWithFormat:@"%@@2x~%@.%@", base, idiom, extension]];
+ if (!IsWild_)
+ [scaled addObject:[NSString stringWithFormat:@"%@@2x.%@", base, extension]];
+ }
+
+ [scaled addObject:[NSString stringWithFormat:@"%@~%@.%@", base, idiom, extension]];
+
+ // if (!IsWild_) <- support old themes
+ [scaled addObject:file];
+ } else if ([base hasSuffix: @"@2x"]) {
+ [scaled addObject:[NSString stringWithFormat:@"%@~iphone.%@", base, extension]];
+ [scaled addObject:file];
+
+ NSString *rest([base substringWithRange:NSMakeRange(0, [base length] - 3)]);
+ [scaled addObject:[NSString stringWithFormat:@"%@~iphone.%@", rest, extension]];
+ [scaled addObject:[rest stringByAppendingPathExtension:extension]];
+ } else {
+ // XXX: this code isn't really complete
+
+ [scaled addObject:file];
+
+ if ([base hasSuffix:@"~iphone"])
+ [scaled addObject:[[base substringWithRange:NSMakeRange(0, [base length] - 7)] stringByAppendingPathExtension:extension]];
+ }
+ }
+
+ return scaled;
+}
+
+static NSString *$getTheme$(NSArray *files, NSArray *themes = Themes_) {
+ if (NSString *path = [Themed_ objectForKey:files])
+ return reinterpret_cast<id>(path) == [NSNull null] ? nil : path;
+
if (Debug_)
NSLog(@"WB:Debug: %@", [files description]);
remapResourceName(Four_ ? @"SBDockBG-old.png" : @"SBDockBG.png", @"Dock")
remapResourceName(@"SBWeatherCelsius.png", @"Icons/Weather")
- if (NSString *path = $getTheme$(names, ui))
+ if (NSString *path = $getTheme$($useScale$(names, ui)))
return path;
return nil;
@implementation NSString (WinterBoard)
- (NSString *) wb$themedPath {
+ if ([self hasPrefix:@"/Library/Themes/"])
+ return self;
+
if (Debug_)
NSLog(@"WB:Debug:Bypass(\"%@\")", self);
}
// }}}
+static struct WBStringDrawingState {
+ WBStringDrawingState *next_;
+ unsigned count_;
+ NSString *base_;
+ NSString *info_;
+} *stringDrawingState_;
+
+MSInstanceMessageHook4(CGSize, NSString, drawAtPoint,forWidth,withFont,lineBreakMode, CGPoint, point, float, width, UIFont *, font, int, mode) {
+ WBStringDrawingState *state(stringDrawingState_);
+ if (state == NULL)
+ return MSOldCall(point, width, font, mode);
+
+ if (--state->count_ == 0)
+ stringDrawingState_ = state->next_;
+ if (state->info_ == nil)
+ return MSOldCall(point, width, font, mode);
+
+ NSString *info([Info_ objectForKey:state->info_]);
+ if (info == nil)
+ return MSOldCall(point, width, font, mode);
+
+ NSString *base(state->base_ ?: @"");
+ [self drawAtPoint:point withStyle:[NSString stringWithFormat:@"%@;%@;%@", [font markupDescription], base, info]];
+ return CGSizeZero;
+}
+
+MSInstanceMessageHook2(CGSize, NSString, drawAtPoint,withFont, CGPoint, point, UIFont *, font) {
+ WBStringDrawingState *state(stringDrawingState_);
+ if (state == NULL)
+ return MSOldCall(point, font);
+
+ if (--state->count_ == 0)
+ stringDrawingState_ = state->next_;
+ if (state->info_ == nil)
+ return MSOldCall(point, font);
+
+ NSString *info([Info_ objectForKey:state->info_]);
+ if (info == nil)
+ return MSOldCall(point, font);
+
+ NSString *base(state->base_ ?: @"");
+ [self drawAtPoint:point withStyle:[NSString stringWithFormat:@"%@;%@;%@", [font markupDescription], base, info]];
+ return CGSizeZero;
+}
+
+MSInstanceMessageHook1(CGSize, NSString, sizeWithFont, UIFont *, font) {
+ WBStringDrawingState *state(stringDrawingState_);
+ if (state == NULL)
+ return MSOldCall(font);
+
+ if (--state->count_ == 0)
+ stringDrawingState_ = state->next_;
+ if (state->info_ == nil)
+ return MSOldCall(font);
+
+ NSString *info([Info_ objectForKey:state->info_]);
+ if (info == nil)
+ return MSOldCall(font);
+
+ NSString *base(state->base_ ?: @"");
+ return [self sizeWithStyle:[NSString stringWithFormat:@"%@;%@;%@", [font markupDescription], base, info] forWidth:65535];
+}
+
+MSInstanceMessageHook1(UIImage *, SBIconBadgeFactory, checkoutBadgeImageForText, NSString *, text) {
+ WBStringDrawingState badgeState = {NULL, 1, @""
+ "color: white;"
+ , @"BadgeStyle"};
+
+ stringDrawingState_ = &badgeState;
+
+ UIImage *image(MSOldCall(text));
+
+ stringDrawingState_ = NULL;
+ return image;
+}
+
+MSInstanceMessageHook1(UIImage *, SBCalendarApplicationIcon, generateIconImage, int, type) {
+ WBStringDrawingState dayState = {NULL, 2, @""
+ "color: white;"
+ // XXX: this is only correct on an iPod dock
+ "text-shadow: rgba(0, 0, 0, 0.2) -1px -1px 2px;"
+ , @"CalendarIconDayStyle"};
+
+ WBStringDrawingState sizeState = {&dayState, 7, nil, nil};
+
+ WBStringDrawingState dateState = {&sizeState, 2, @""
+ "color: #333333;"
+ , @"CalendarIconDateStyle"};
+
+ stringDrawingState_ = &dateState;
+
+ UIImage *image(MSOldCall(type));
+
+ stringDrawingState_ = NULL;
+ return image;
+}
+
MSHook(void, SBCalendarIconContentsView$drawRect$, SBCalendarIconContentsView *self, SEL sel, CGRect rect) {
NSBundle *bundle([NSBundle mainBundle]);
_SBStatusBarContentsView$didMoveToSuperview(self, sel);
}
-MSHook(UIImage *, UIImage$defaultDesktopImage, UIImage *self, SEL sel) {
- if (Debug_)
- NSLog(@"WB:Debug:DefaultDesktopImage");
- if (NSString *path = $getTheme$([NSArray arrayWithObjects:@"LockBackground.png", @"LockBackground.jpg", nil]))
- return [UIImage imageWithContentsOfFile:path];
- return _UIImage$defaultDesktopImage(self, sel);
-}
-
static NSArray *Wallpapers_;
static bool Papered_;
static bool Docked_;
} while (false)
static UIImage *$getImage$(NSString *path) {
- NSString *name(path);
-
- #define StripName(strip) \
- if ([name hasSuffix:@ strip]) \
- name = [name substringWithRange:NSMakeRange(0, [name length] - sizeof(strip) - 1)];
-
- StripName(".png");
- StripName(".jpg");
- StripName("~iphone");
- StripName("~ipad");
-
- unsigned scale([name hasSuffix:@"@2x"] ? 2 : 1);
-
UIImage *image([UIImage imageWithContentsOfFile:path]);
+ unsigned scale($getScale$(path));
if (scale != 1 && [image respondsToSelector:@selector(setScale)])
[image setScale:scale];
return image;
}
+static UIImage *$getDefaultDesktopImage$() {
+ if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObjects:@"LockBackground.png", @"LockBackground.jpg", nil])))
+ return $getImage$(path);
+ return nil;
+}
+
+MSClassMessageHook0(UIImage *, UIImage, defaultDesktopImage) {
+ return $getDefaultDesktopImage$() ?: MSOldCall();
+}
+
+MSInstanceMessageHook0(UIImage *, SBSlidingAlertDisplay, _defaultDesktopImage) {
+ return $getDefaultDesktopImage$() ?: MSOldCall();
+}
+
+MSInstanceMessageHook0(void, SBWallpaperView, resetCurrentImageToWallpaper) {
+ for (UIView *parent([self superview]); parent != nil; parent = [parent superview])
+ if ([parent isKindOfClass:$SBSlidingAlertDisplay]) {
+ if (UIImage *image = $getDefaultDesktopImage$()) {
+ [self setImage:image];
+ return;
+ }
+
+ break;
+ }
+
+ MSOldCall();
+}
+
// %hook -[SBUIController init] {{{
MSInstanceMessageHook0(id, SBUIController, init) {
self = MSOldCall();
if (self == nil)
return nil;
- NSString *paper($getTheme$(Wallpapers_, true));
+ NSString *paper($getTheme$(Wallpapers_));
+ if (paper != nil)
+ paper = [paper stringByDeletingLastPathComponent];
{
size_t size;
_release(WallpaperPage_);
_release(WallpaperURL_);
- if (NSString *path = paper) {
- if ([path hasSuffix:@".mp4"]) {
+ if (paper != nil) {
+ NSArray *themes([NSArray arrayWithObject:paper]);
+
+ if (NSString *path = $getTheme$([NSArray arrayWithObject:@"Wallpaper.mp4"], themes)) {
#if UseAVController
NSError *error;
[indirect addSubview:video];
}
- if ([path hasSuffix:@".png"] || [path hasSuffix:@".jpg"]) {
+ if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObjects:@"Wallpaper.png", @"Wallpaper.jpg", nil]), themes)) {
if (UIImage *image = $getImage$(path)) {
WallpaperFile_ = [path retain];
WallpaperImage_ = [[UIImageView alloc] initWithImage:image];
}
}
- if ([path hasSuffix:@".html"]) {
+ if (NSString *path = $getTheme$([NSArray arrayWithObject:@"Wallpaper.html"], themes)) {
CGRect bounds = [indirect bounds];
UIWebDocumentView *view([[[UIWebDocumentView alloc] initWithFrame:bounds] autorelease]);
id &_label(MSHookIvar<id>(self, "_label"));
if (![Info_ wb$boolForKey:@"UndockedIconLabels"])
docked = true;
+
if (_label != nil && [_label respondsToSelector:@selector(setInDock:)])
[_label setInDock:docked];
- return _SBIconLabel$setInDock$(self, sel, docked);
+
+ _SBIconLabel$setInDock$(self, sel, docked);
+ [self setNeedsDisplay];
}
MSHook(BOOL, SBDockIconListView$shouldShowNewDock, id self, SEL sel) {
}
// }}}
-MSHook(void, SBIconLabel$drawRect$, SBIconLabel *self, SEL sel, CGRect rect) {
+MSInstanceMessageHook1(void, SBIconLabel, drawRect, CGRect, rect) {
CGRect bounds = [self bounds];
static Ivar drawMoreLegibly = object_getInstanceVariable(self, "_drawMoreLegibly", NULL);
style = [style stringByAppendingString:@"text-shadow: rgba(0, 0, 0, 0.5) 0px -1px 0px; "];
bool ellipsis(false);
- float max = 75, width;
+ float max = [self frame].size.width - 11, width;
width:
width = [(ellipsis ? [label stringByAppendingString:@"..."] : label) sizeWithStyle:style forWidth:320].width;
[balloon setBackgroundColor:[UIColor clearColor]];
}
+MSInstanceMessageHook1(void, CKTranscriptCell, setBackgroundColor, UIColor *, color) {
+ MSOldCall([UIColor clearColor]);
+}
+
+MSInstanceMessageHook2(id, CKTranscriptCell, initWithStyle,reuseIdentifier, int, style, NSString *, reuse) {
+ if ((self = MSOldCall(style, reuse)) != nil) {
+ [self setBackgroundColor:[UIColor clearColor]];
+ [[self contentView] setBackgroundColor:[UIColor clearColor]];
+ } return self;
+}
+
MSInstanceMessageHook2(id, CKMessageCell, initWithStyle,reuseIdentifier, int, style, NSString *, reuse) {
if ((self = MSOldCall(style, reuse)) != nil) {
+ [self setBackgroundColor:[UIColor clearColor]];
[[self contentView] setBackgroundColor:[UIColor clearColor]];
} return self;
}
MSInstanceMessageHook0(void, CKTranscriptController, loadView) {
MSOldCall();
- if (NSString *path = $getTheme$([NSArray arrayWithObjects:@"SMSBackground.png", @"SMSBackground.jpg", nil]))
- if (UIImage *image = [[UIImage alloc] initWithContentsOfFile:path]) {
- [image autorelease];
-
+ if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObjects:@"SMSBackground.png", @"SMSBackground.jpg", nil])))
+ if (UIImage *image = $getImage$(path)) {
UIView *&_transcriptTable(MSHookIvar<UIView *>(self, "_transcriptTable"));
UIView *&_transcriptLayer(MSHookIvar<UIView *>(self, "_transcriptLayer"));
UIView *table;
UIImage *image([UIImages_ objectForKey:key]);
if (image != nil)
return reinterpret_cast<id>(image) == [NSNull null] ? __UIImageWithName(name) : image;
- if (NSString *path = $pathForFile$inBundle$(name, _UIKitBundle(), true)) {
- image = [[UIImage alloc] initWithContentsOfFile:path cache:true];
- if (image != nil)
- [image autorelease];
- }
+ if (NSString *path = $pathForFile$inBundle$(name, _UIKitBundle(), true))
+ image = $getImage$(path);
[UIImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:key];
- return image == nil ? __UIImageWithName(name) : image;
+ if (image != nil)
+ return image;
+
+ image = __UIImageWithName(name);
+
+ if (UIDebug_) {
+ NSString *path([@"/tmp/UIImages/" stringByAppendingString:name]);
+ if (![Manager_ fileExistsAtPath:path])
+ [UIImagePNGRepresentation(image) writeToFile:path atomically:YES];
+ }
+
+ return image;
}
}
// }}}
return reinterpret_cast<id>(image) == [NSNull null] ? __UIImageWithNameInDomain(name, domain) : image;
if (Debug_)
NSLog(@"WB:Debug: UIImageWithNameInDomain(\"%@\", \"%@\")", name, domain);
- if (NSString *path = $getTheme$([NSArray arrayWithObject:[NSString stringWithFormat:@"Domains/%@/%@", domain, name]], true)) {
- image = [[UIImage alloc] initWithContentsOfFile:path];
- if (image != nil)
- [image autorelease];
- }
+ if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObject:[NSString stringWithFormat:@"Domains/%@/%@", domain, name]])))
+ image = $getImage$(path);
[PathImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:key];
return image == nil ? __UIImageWithNameInDomain(name, domain) : image;
}
}
// }}}
-static void SBInitialize() {
- _UIImage$defaultDesktopImage = MSHookMessage(object_getClass($UIImage), @selector(defaultDesktopImage), &$UIImage$defaultDesktopImage);
+static void NSString$drawAtPoint$withStyle$(NSString *self, SEL _cmd, CGPoint point, NSString *style) {
+ WKSetCurrentGraphicsContext(UIGraphicsGetCurrentContext());
+ if (style == nil || [style length] == 0)
+ style = @"font-family: Helvetica; font-size: 12px";
+ return [[WBMarkup sharedMarkup] drawString:self atPoint:point withStyle:style];
+}
+
+static CGSize NSString$sizeWithStyle$forWidth$(NSString *self, SEL _cmd, NSString *style, float width) {
+ if (style == nil || [style length] == 0)
+ style = @"font-family: Helvetica; font-size: 12px";
+ return [[WBMarkup sharedMarkup] sizeOfString:self withStyle:style forWidth:width];
+}
- bool olden(dlsym(RTLD_DEFAULT, "GSLibraryCopyGenerationInfoValueForKey") == NULL);
+static void SBInitialize() {
+ class_addMethod($NSString, @selector(drawAtPoint:withStyle:), (IMP) &NSString$drawAtPoint$withStyle$, "v20@0:4{CGPoint=ff}8@16");
+ class_addMethod($NSString, @selector(sizeWithStyle:forWidth:), (IMP) &NSString$sizeWithStyle$forWidth$, "{CGSize=ff}16@0:4@8f12");
if (SummerBoard_) {
WBRename(SBApplication, pathForIcon, pathForIcon);
WBRename(SBDockIconListView, setFrame:, setFrame$);
MSHookMessage(object_getClass($SBDockIconListView), @selector(shouldShowNewDock), &$SBDockIconListView$shouldShowNewDock, &_SBDockIconListView$shouldShowNewDock);
- if (olden)
- WBRename(SBIconLabel, drawRect:, drawRect$);
-
WBRename(SBIconLabel, initWithSize:label:, initWithSize$label$);
WBRename(SBIconLabel, setInDock:, setInDock$);
SummerBoard_ = [value boolValue];
if (NSNumber *value = [settings objectForKey:@"Debug"])
Debug_ = [value boolValue];
+ if (NSNumber *value = [settings objectForKey:@"RecordUI"])
+ UIDebug_ = [value boolValue];
NSArray *themes([settings objectForKey:@"Themes"]);
if (themes == nil)
// }}}
// SpringBoard {{{
if (SpringBoard_) {
- Wallpapers_ = [[NSArray arrayWithObjects:@"Wallpaper.mp4", @"Wallpaper.png", @"Wallpaper.jpg", @"Wallpaper.html", nil] retain];
+ Wallpapers_ = [[NSArray arrayWithObjects:@"Wallpaper.mp4", @"Wallpaper@2x.png", @"Wallpaper@2x.jpg", @"Wallpaper.png", @"Wallpaper.jpg", @"Wallpaper.html", nil] retain];
Docked_ = $getTheme$([NSArray arrayWithObjects:@"Dock.png", nil]);
CFNotificationCenterAddObserver(
}
// }}}
+ if (UIDebug_ && ![Manager_ fileExistsAtPath:@"/tmp/UIImages"]) {
+ NSError *error(nil);
+ if (![Manager_ createDirectoryAtPath:@"/tmp/UIImages" withIntermediateDirectories:NO attributes:[NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithShort:0777], NSFilePosixPermissions,
+ nil] error:&error])
+ NSLog(@"WB:Error: cannot create /tmp/UIImages (%@)", error);
+ }
+
[pool release];
}