_itv = true; \
_ltv = _ctv; \
} \
- NSLog(@"%lu.%.6u[%f]:_trace()@%s:%u[%s]\n", \
+ NSLog(@"%lu.%.6u[%f]:WB:_trace()@%s:%u[%s]\n", \
_ctv.tv_sec, _ctv.tv_usec, \
(_ctv.tv_sec - _ltv.tv_sec) + (_ctv.tv_usec - _ltv.tv_usec) / 1000000.0, \
__FILE__, __LINE__, __FUNCTION__\
MSClassHook(UINavigationBar)
MSClassHook(UIToolbar)
+MSClassHook(CKBalloonView)
MSClassHook(CKMessageCell)
MSClassHook(CKTimestampView)
MSClassHook(CKTranscriptCell)
MSClassHook(CKTranscriptController)
+MSClassHook(CKTranscriptHeaderView)
MSClassHook(CKTranscriptTableView)
MSClassHook(SBApplication)
}
static NSArray *$useScale$(NSArray *files, bool use = true) {
- if (Scale_ == 0) {
+ if (use && Scale_ == 0) {
UIScreen *screen([UIScreen mainScreen]);
if ([screen respondsToSelector:@selector(scale)])
Scale_ = [screen scale];
[scaled addObject:[NSString stringWithFormat:@"%@~iphone.%@", base, extension]];
[scaled addObject:file];
- NSString *rest([base substringWithRange:NSMakeRange(0, [base length] - 3)]);
+ // XXX: this actually can't be used, as the person loading the file doesn't realize that the @2x changed
+ /*NSString *rest([base substringWithRange:NSMakeRange(0, [base length] - 3)]);
[scaled addObject:[NSString stringWithFormat:@"%@~iphone.%@", rest, extension]];
- [scaled addObject:[rest stringByAppendingPathExtension:extension]];
+ [scaled addObject:[rest stringByAppendingPathExtension:extension]];*/
} else {
// XXX: this code isn't really complete
}
static NSString *$getTheme$(NSArray *files, NSArray *themes = Themes_) {
- if (NSString *path = [Themed_ objectForKey:files])
- return reinterpret_cast<id>(path) == [NSNull null] ? nil : path;
+ // XXX: this is not reasonable; OMG
+ id key(files);
+
+ @synchronized (Themed_) {
+ if (NSString *path = [Themed_ objectForKey:key])
+ return reinterpret_cast<id>(path) == [NSNull null] ? nil : path;
+ }
if (Debug_)
NSLog(@"WB:Debug: %@", [files description]);
path = nil;
set:
- [Themed_ setObject:(path == nil ? [NSNull null] : reinterpret_cast<id>(path)) forKey:files];
+ @synchronized (Themed_) {
+ [Themed_ setObject:(path == nil ? [NSNull null] : reinterpret_cast<id>(path)) forKey:key];
+ }
+
return path;
}
// }}}
if (identifier != nil)
[names addObject:[NSString stringWithFormat:@"Bundles/%@/%@", identifier, file]];
- if (NSString *folder = [[bundle bundlePath] lastPathComponent])
+ if (NSString *folder = [[bundle bundlePath] lastPathComponent]) {
[names addObject:[NSString stringWithFormat:@"Folders/%@/%@", folder, file]];
+ NSString *base([folder stringByDeletingPathExtension]);
+ if ([base hasSuffix:@"~iphone"])
+ [names addObject:[NSString stringWithFormat:@"Folders/%@.%@/%@", [base substringWithRange:NSMakeRange(0, [base length] - 7)], [folder pathExtension], file]];
+ }
if (ui)
[names addObject:[NSString stringWithFormat:@"UIImages/%@", file]];
remapResourceName(Four_ ? @"SBDockBG-old.png" : @"SBDockBG.png", @"Dock")
remapResourceName(@"SBWeatherCelsius.png", @"Icons/Weather")
+ [names addObject:[NSString stringWithFormat:@"Fallback/%@", file]];
+
if (NSString *path = $getTheme$($useScale$(names, ui)))
return path;
NSString *info_;
} *stringDrawingState_;
-MSInstanceMessageHook4(CGSize, NSString, drawAtPoint,forWidth,withFont,lineBreakMode, CGPoint, point, float, width, UIFont *, font, int, mode) {
+MSInstanceMessageHook6(CGSize, NSString, drawAtPoint,forWidth,withFont,lineBreakMode,letterSpacing,includeEmoji, CGPoint, point, float, width, UIFont *, font, int, mode, float, spacing, BOOL, emoji) {
WBStringDrawingState *state(stringDrawingState_);
if (state == NULL)
- return MSOldCall(point, width, font, mode);
+ return MSOldCall(point, width, font, mode, spacing, emoji);
if (--state->count_ == 0)
stringDrawingState_ = state->next_;
if (state->info_ == nil)
- return MSOldCall(point, width, font, mode);
+ return MSOldCall(point, width, font, mode, spacing, emoji);
NSString *info([Info_ objectForKey:state->info_]);
if (info == nil)
- return MSOldCall(point, width, font, mode);
+ return MSOldCall(point, width, font, mode, spacing, emoji);
NSString *base(state->base_ ?: @"");
- [self drawAtPoint:point withStyle:[NSString stringWithFormat:@"%@;%@;%@", [font markupDescription], base, info]];
+ NSString *extra([NSString stringWithFormat:@"letter-spacing: %gpx", spacing]);
+ [self drawAtPoint:point withStyle:[NSString stringWithFormat:@"%@;%@;%@;%@", [font markupDescription], extra, base, info]];
return CGSizeZero;
}
-MSInstanceMessageHook2(CGSize, NSString, drawAtPoint,withFont, CGPoint, point, UIFont *, font) {
+MSInstanceMessageHook7(CGSize, NSString, _drawInRect,withFont,lineBreakMode,alignment,lineSpacing,includeEmoji,truncationRect, CGRect, rect, UIFont *, font, int, mode, int, alignment, float, spacing, BOOL, emoji, CGRect, truncation) {
WBStringDrawingState *state(stringDrawingState_);
if (state == NULL)
- return MSOldCall(point, font);
+ return MSOldCall(rect, font, mode, alignment, spacing, emoji, truncation);
if (--state->count_ == 0)
stringDrawingState_ = state->next_;
if (state->info_ == nil)
- return MSOldCall(point, font);
+ return MSOldCall(rect, font, mode, alignment, spacing, emoji, truncation);
NSString *info([Info_ objectForKey:state->info_]);
if (info == nil)
- return MSOldCall(point, font);
+ return MSOldCall(rect, font, mode, alignment, spacing, emoji, truncation);
+
+ NSString *textAlign;
+ switch (alignment) {
+ default:
+ case WebTextAlignmentLeft:
+ textAlign = @"left";
+ break;
+ case WebTextAlignmentCenter:
+ textAlign = @"center";
+ break;
+ case WebTextAlignmentRight:
+ textAlign = @"right";
+ break;
+ }
NSString *base(state->base_ ?: @"");
- [self drawAtPoint:point withStyle:[NSString stringWithFormat:@"%@;%@;%@", [font markupDescription], base, info]];
+ NSString *extra([NSString stringWithFormat:@"text-align: %@", textAlign]);
+ [self drawInRect:rect withStyle:[NSString stringWithFormat:@"%@;%@;%@;%@", [font markupDescription], extra, base, info]];
return CGSizeZero;
}
+MSInstanceMessageHook4(CGSize, NSString, sizeWithFont,forWidth,lineBreakMode,letterSpacing, UIFont *, font, float, width, int, mode, float, spacing) {
+ WBStringDrawingState *state(stringDrawingState_);
+ if (state == NULL)
+ return MSOldCall(font, width, mode, spacing);
+
+ if (--state->count_ == 0)
+ stringDrawingState_ = state->next_;
+ if (state->info_ == nil)
+ return MSOldCall(font, width, mode, spacing);
+
+ NSString *info([Info_ objectForKey:state->info_]);
+ if (info == nil)
+ return MSOldCall(font, width, mode, spacing);
+
+ NSString *base(state->base_ ?: @"");
+ NSString *extra([NSString stringWithFormat:@"letter-spacing: %gpx", spacing]);
+ return [self sizeWithStyle:[NSString stringWithFormat:@"%@;%@;%@;%@", [font markupDescription], extra, base, info] forWidth:width];
+}
+
MSInstanceMessageHook1(CGSize, NSString, sizeWithFont, UIFont *, font) {
WBStringDrawingState *state(stringDrawingState_);
if (state == NULL)
static NSArray *Wallpapers_;
static bool Papered_;
static bool Docked_;
+static bool SMSBackgrounded_;
static NSString *WallpaperFile_;
static UIImageView *WallpaperImage_;
static UIWebDocumentView *WallpaperPage_;
IsWild_ = machine != NULL && strncmp(machine, "iPad", 4) == 0;
}
- BOOL (*GSSystemHasCapability)(CFStringRef) = reinterpret_cast<BOOL (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, "GSSystemHasCapability"));
-
- if ([Info_ objectForKey:@"UndockedIconLabels"] == nil)
- [Info_ setObject:[NSNumber numberWithBool:(
- !(paper != nil || GSSystemHasCapability != NULL && GSSystemHasCapability(CFSTR("homescreen-wallpaper"))) ||
- [Info_ objectForKey:@"DockedIconLabelStyle"] != nil ||
- [Info_ objectForKey:@"UndockedIconLabelStyle"] != nil
- )] forKey:@"UndockedIconLabels"];
-
if (Debug_)
NSLog(@"WB:Debug:Info = %@", [Info_ description]);
@interface UIView (WinterBoard)
- (bool) wb$isWBImageView;
- (void) wb$logHierarchy;
+- (void) wb$setBackgroundColor:(UIColor *)color;
@end
@implementation UIView (WinterBoard)
WBLogHierarchy(self);
}
+- (void) wb$setBackgroundColor:(UIColor *)color {
+ [self setBackgroundColor:color];
+ for (UIView *child in [self subviews])
+ [child wb$setBackgroundColor:color];
+}
+
@end
@interface WBImageView : UIImageView {
}
MSHook(void, SBIconLabel$setInDock$, SBIconLabel *self, SEL sel, BOOL docked) {
+ static bool gssc(false);
+ if (!gssc) {
+ BOOL (*GSSystemHasCapability)(CFStringRef) = reinterpret_cast<BOOL (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, "GSSystemHasCapability"));
+ Papered_ |= GSSystemHasCapability != NULL && GSSystemHasCapability(CFSTR("homescreen-wallpaper"));
+ gssc = true;
+
+ if ([Info_ objectForKey:@"UndockedIconLabels"] == nil)
+ [Info_ setObject:[NSNumber numberWithBool:(
+ !Papered_ ||
+ [Info_ objectForKey:@"DockedIconLabelStyle"] != nil ||
+ [Info_ objectForKey:@"UndockedIconLabelStyle"] != nil
+ )] forKey:@"UndockedIconLabels"];
+ }
+
id &_label(MSHookIvar<id>(self, "_label"));
if (![Info_ wb$boolForKey:@"UndockedIconLabels"])
docked = true;
}
// }}}
-MSInstanceMessageHook1(void, SBIconLabel, drawRect, CGRect, rect) {
+MSInstanceMessage1(void, SBIconLabel, drawRect, CGRect, rect) {
CGRect bounds = [self bounds];
static Ivar drawMoreLegibly = object_getInstanceVariable(self, "_drawMoreLegibly", NULL);
[label drawAtPoint:CGPointMake((bounds.size.width - size.width) / 2, 0) withStyle:style];
}
+MSInstanceMessage0(CGImageRef, SBIconLabel, buildLabelImage) {
+ bool docked((MSHookIvar<unsigned>(self, "_inDock") & 0x2) != 0);
+
+ WBStringDrawingState labelState = {NULL, 0, [NSString stringWithFormat:@""
+ "color: %@;"
+ ,
+ (docked || !SummerBoard_ ? @"white" : @"#b3b3b3")
+ ], docked ? @"DockedIconLabelStyle" : @"UndockedIconLabelStyle"};
+
+ stringDrawingState_ = &labelState;
+
+ CGImageRef image(MSOldCall());
+
+ stringDrawingState_ = NULL;
+ return image;
+}
+
// ChatKit {{{
+MSInstanceMessageHook2(id, CKBalloonView, initWithFrame,delegate, CGRect, frame, id, delegate) {
+ if ((self = MSOldCall(frame, delegate)) != nil) {
+ [self setBackgroundColor:[UIColor clearColor]];
+ } return self;
+}
+
+MSInstanceMessageHook0(BOOL, CKBalloonView, _canUseLayerBackedBalloon) {
+ return SMSBackgrounded_ ? NO : MSOldCall();
+}
+
+MSInstanceMessageHook0(void, CKTranscriptHeaderView, layoutSubviews) {
+ [self wb$setBackgroundColor:[UIColor clearColor]];
+ return MSOldCall();
+}
+
MSInstanceMessageHook1(void, CKMessageCell, addBalloonView, CKBalloonView *, balloon) {
MSOldCall(balloon);
[balloon setBackgroundColor:[UIColor clearColor]];
MSInstanceMessageHook1(void, CKTranscriptCell, setBackgroundColor, UIColor *, color) {
MSOldCall([UIColor clearColor]);
+ [[self contentView] wb$setBackgroundColor:[UIColor clearColor]];
}
+// iOS >= 5.0
MSInstanceMessageHook2(id, CKTranscriptCell, initWithStyle,reuseIdentifier, int, style, NSString *, reuse) {
if ((self = MSOldCall(style, reuse)) != nil) {
[self setBackgroundColor:[UIColor clearColor]];
- [[self contentView] setBackgroundColor:[UIColor clearColor]];
+ [[self contentView] wb$setBackgroundColor:[UIColor clearColor]];
} return self;
}
+// iOS << 5.0
MSInstanceMessageHook2(id, CKMessageCell, initWithStyle,reuseIdentifier, int, style, NSString *, reuse) {
if ((self = MSOldCall(style, reuse)) != nil) {
[self setBackgroundColor:[UIColor clearColor]];
if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObjects:@"SMSBackground.png", @"SMSBackground.jpg", nil])))
if (UIImage *image = $getImage$(path)) {
+ SMSBackgrounded_ = true;
+
UIView *&_transcriptTable(MSHookIvar<UIView *>(self, "_transcriptTable"));
UIView *&_transcriptLayer(MSHookIvar<UIView *>(self, "_transcriptLayer"));
UIView *table;
return [[WBMarkup sharedMarkup] drawString:self atPoint:point withStyle:style];
}
+static void NSString$drawInRect$withStyle$(NSString *self, SEL _cmd, CGRect rect, NSString *style) {
+ WKSetCurrentGraphicsContext(UIGraphicsGetCurrentContext());
+ if (style == nil || [style length] == 0)
+ style = @"font-family: Helvetica; font-size: 12px";
+ return [[WBMarkup sharedMarkup] drawString:self inRect:rect 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";
static void SBInitialize() {
class_addMethod($NSString, @selector(drawAtPoint:withStyle:), (IMP) &NSString$drawAtPoint$withStyle$, "v20@0:4{CGPoint=ff}8@16");
+ class_addMethod($NSString, @selector(drawInRect:withStyle:), (IMP) &NSString$drawInRect$withStyle$, "v28@0:4{CGRect={CGSize=ff}{CGSize=ff}}8@24");
class_addMethod($NSString, @selector(sizeWithStyle:forWidth:), (IMP) &NSString$sizeWithStyle$forWidth$, "{CGSize=ff}16@0:4@8f12");
if (SummerBoard_) {
WBRename(SBDockIconListView, setFrame:, setFrame$);
MSHookMessage(object_getClass($SBDockIconListView), @selector(shouldShowNewDock), &$SBDockIconListView$shouldShowNewDock, &_SBDockIconListView$shouldShowNewDock);
+ if (kCFCoreFoundationVersionNumber < 600)
+ WBRename(SBIconLabel, drawRect:, drawRect$);
+ else
+ WBRename(SBIconLabel, buildLabelImage, buildLabelImage);
+
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@2x.png", @"Wallpaper@2x.jpg", @"Wallpaper.png", @"Wallpaper.jpg", @"Wallpaper.html", nil] retain];
- Docked_ = $getTheme$([NSArray arrayWithObjects:@"Dock.png", nil]);
+ Papered_ = $getTheme$(Wallpapers_) != nil;
+ Docked_ = $getTheme$([NSArray arrayWithObjects:@"Dock.png", nil]) != nil;
CFNotificationCenterAddObserver(
CFNotificationCenterGetDarwinNotifyCenter(),
}
// }}}
- if (Debug_ && [Manager_ fileExistsAtPath:@"/tmp/UIImages"])
- UIDebug_ = true;
+ 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];
}