]> git.saurik.com Git - winterboard.git/blob - Library.mm
9a04fb556c6817f5dfd8bcda475ad7d536610972
[winterboard.git] / Library.mm
1 /* WinterBoard - Theme Manager for the iPhone
2 * Copyright (C) 2008-2011 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 <sys/time.h>
39
40 struct timeval _ltv;
41 bool _itv;
42
43 #define _trace() do { \
44 struct timeval _ctv; \
45 gettimeofday(&_ctv, NULL); \
46 if (!_itv) { \
47 _itv = true; \
48 _ltv = _ctv; \
49 } \
50 NSLog(@"%lu.%.6u[%f]:WB:_trace()@%s:%u[%s]\n", \
51 _ctv.tv_sec, _ctv.tv_usec, \
52 (_ctv.tv_sec - _ltv.tv_sec) + (_ctv.tv_usec - _ltv.tv_usec) / 1000000.0, \
53 __FILE__, __LINE__, __FUNCTION__\
54 ); \
55 _ltv = _ctv; \
56 } while (false)
57
58 #define _transient
59
60 #import <CoreFoundation/CoreFoundation.h>
61 #import <Foundation/Foundation.h>
62 #import <CoreGraphics/CoreGraphics.h>
63 #import <ImageIO/CGImageSource.h>
64
65 #import <Celestial/AVController.h>
66 #import <Celestial/AVItem.h>
67 #import <Celestial/AVQueue.h>
68
69 #include <substrate.h>
70
71 #import <UIKit/UIKit.h>
72
73 #import <SpringBoard/SBApplication.h>
74 #import <SpringBoard/SBApplicationIcon.h>
75 #import <SpringBoard/SBAppWindow.h>
76 #import <SpringBoard/SBAwayView.h>
77 #import <SpringBoard/SBBookmarkIcon.h>
78 #import <SpringBoard/SBButtonBar.h>
79 #import <SpringBoard/SBCalendarIconContentsView.h>
80 #import <SpringBoard/SBIconController.h>
81 #import <SpringBoard/SBIconLabel.h>
82 #import <SpringBoard/SBIconList.h>
83 #import <SpringBoard/SBIconModel.h>
84 #import <SpringBoard/SBImageCache.h>
85 // XXX: #import <SpringBoard/SBSearchView.h>
86 #import <SpringBoard/SBSearchTableViewCell.h>
87 #import <SpringBoard/SBStatusBarContentsView.h>
88 #import <SpringBoard/SBStatusBarController.h>
89 #import <SpringBoard/SBStatusBarOperatorNameView.h>
90 #import <SpringBoard/SBStatusBarTimeView.h>
91 #import <SpringBoard/SBUIController.h>
92 #import <SpringBoard/SBWidgetApplicationIcon.h>
93
94 #import <MobileSMS/mSMSMessageTranscriptController.h>
95
96 #import <MediaPlayer/MPMoviePlayerController.h>
97 #import <MediaPlayer/MPVideoView.h>
98 #import <MediaPlayer/MPVideoView-PlaybackControl.h>
99
100 #import <CoreGraphics/CGGeometry.h>
101
102 #import <ChatKit/CKMessageCell.h>
103
104 #include <sys/sysctl.h>
105
106 #include "WBMarkup.h"
107
108 extern "C" void __clear_cache (char *beg, char *end);
109
110 @protocol WinterBoard
111 - (void *) _node;
112 @end
113
114 Class $MPMoviePlayerController;
115 Class $MPVideoView;
116
117 MSClassHook(NSBundle)
118 MSClassHook(NSString)
119
120 MSClassHook(UIImage)
121 MSMetaClassHook(UIImage)
122 MSClassHook(UINavigationBar)
123 MSClassHook(UIToolbar)
124
125 MSClassHook(CKBalloonView)
126 MSClassHook(CKMessageCell)
127 MSClassHook(CKTimestampView)
128 MSClassHook(CKTranscriptCell)
129 MSClassHook(CKTranscriptController)
130 MSClassHook(CKTranscriptHeaderView)
131 MSClassHook(CKTranscriptTableView)
132
133 MSClassHook(SBApplication)
134 MSClassHook(SBApplicationIcon)
135 MSClassHook(SBAwayView)
136 MSClassHook(SBBookmarkIcon)
137 MSClassHook(SBButtonBar)
138 MSClassHook(SBCalendarApplicationIcon)
139 MSClassHook(SBCalendarIconContentsView)
140 MSClassHook(SBDockIconListView)
141 MSClassHook(SBIcon)
142 MSClassHook(SBIconBadge)
143 MSClassHook(SBIconBadgeFactory)
144 MSClassHook(SBIconContentView)
145 MSClassHook(SBIconController)
146 MSClassHook(SBIconLabel)
147 MSClassHook(SBIconList)
148 MSClassHook(SBIconModel)
149 //MSClassHook(SBImageCache)
150 MSClassHook(SBSearchView)
151 MSClassHook(SBSearchTableViewCell)
152 MSClassHook(SBSlidingAlertDisplay)
153 MSClassHook(SBStatusBarContentsView)
154 MSClassHook(SBStatusBarController)
155 MSClassHook(SBStatusBarOperatorNameView)
156 MSClassHook(SBStatusBarTimeView)
157 MSClassHook(SBUIController)
158 MSClassHook(SBWallpaperView)
159 MSClassHook(SBWidgetApplicationIcon)
160
161 extern "C" void WKSetCurrentGraphicsContext(CGContextRef);
162
163 static struct MSFixClass { MSFixClass() {
164 $SBIcon = objc_getClass("SBIconView") ?: $SBIcon;
165
166 if ($SBIconList == nil)
167 $SBIconList = objc_getClass("SBIconListView");
168 if ($CKTranscriptController == nil)
169 $CKTranscriptController = objc_getClass("mSMSMessageTranscriptController");
170 } } MSFixClass;
171
172 static bool IsWild_;
173 static bool Four_($SBDockIconListView != nil);
174
175 @interface NSDictionary (WinterBoard)
176 - (UIColor *) wb$colorForKey:(NSString *)key;
177 - (BOOL) wb$boolForKey:(NSString *)key;
178 @end
179
180 @implementation NSDictionary (WinterBoard)
181
182 - (UIColor *) wb$colorForKey:(NSString *)key {
183 NSString *value = [self objectForKey:key];
184 if (value == nil)
185 return nil;
186 /* XXX: incorrect */
187 return nil;
188 }
189
190 - (BOOL) wb$boolForKey:(NSString *)key {
191 if (NSString *value = [self objectForKey:key])
192 return [value boolValue];
193 return false;
194 }
195
196 @end
197
198 static BOOL (*_GSFontGetUseLegacyFontMetrics)();
199 #define $GSFontGetUseLegacyFontMetrics() \
200 (_GSFontGetUseLegacyFontMetrics == NULL ? YES : _GSFontGetUseLegacyFontMetrics())
201
202 static bool Debug_ = false;
203 static bool UIDebug_ = false;
204 static bool Engineer_ = false;
205 static bool SummerBoard_ = false;
206 static bool SpringBoard_;
207
208 static UIImage *(*_UIApplicationImageWithName)(NSString *name);
209 static UIImage *(*_UIImageWithNameInDomain)(NSString *name, NSString *domain);
210 static NSBundle *(*_UIKitBundle)();
211 static bool (*_UIPackedImageTableGetIdentifierForName)(NSString *, int *);
212 static int (*_UISharedImageNameGetIdentifier)(NSString *);
213
214 static NSMutableDictionary *UIImages_ = [[NSMutableDictionary alloc] initWithCapacity:32];
215 static NSMutableDictionary *PathImages_ = [[NSMutableDictionary alloc] initWithCapacity:16];
216 static NSMutableDictionary *Cache_ = [[NSMutableDictionary alloc] initWithCapacity:64];
217 static NSMutableDictionary *Strings_ = [[NSMutableDictionary alloc] initWithCapacity:0];
218 static NSMutableDictionary *Bundles_ = [[NSMutableDictionary alloc] initWithCapacity:2];
219
220 static NSFileManager *Manager_;
221 static NSMutableArray *Themes_;
222
223 static NSDictionary *English_;
224 static NSMutableDictionary *Info_;
225
226 // $getTheme$() {{{
227 static NSMutableDictionary *Themed_ = [[NSMutableDictionary alloc] initWithCapacity:128];
228
229 static unsigned Scale_ = 0;
230
231 static unsigned $getScale$(NSString *path) {
232 NSString *name(path);
233
234 #define StripName(strip) \
235 if ([name hasSuffix:@ strip]) \
236 name = [name substringWithRange:NSMakeRange(0, [name length] - sizeof(strip) - 1)];
237
238 StripName(".png");
239 StripName(".jpg");
240 StripName("~iphone");
241 StripName("~ipad");
242
243 return [name hasSuffix:@"@2x"] ? 2 : 1;
244 }
245
246 static NSArray *$useScale$(NSArray *files, bool use = true) {
247 if (use && Scale_ == 0) {
248 UIScreen *screen([UIScreen mainScreen]);
249 if ([screen respondsToSelector:@selector(scale)])
250 Scale_ = [screen scale];
251 else
252 Scale_ = 1;
253 }
254
255 if (Scale_ == 1)
256 return files;
257
258 NSString *idiom(IsWild_ ? @"ipad" : @"iphone");
259
260 NSMutableArray *scaled([NSMutableArray arrayWithCapacity:([files count] * 4)]);
261
262 for (NSString *file in files) {
263 NSString *base([file stringByDeletingPathExtension]);
264 NSString *extension([file pathExtension]);
265
266 if (use) {
267 if (Scale_ == 2) {
268 [scaled addObject:[NSString stringWithFormat:@"%@@2x~%@.%@", base, idiom, extension]];
269 if (!IsWild_)
270 [scaled addObject:[NSString stringWithFormat:@"%@@2x.%@", base, extension]];
271 }
272
273 [scaled addObject:[NSString stringWithFormat:@"%@~%@.%@", base, idiom, extension]];
274
275 // if (!IsWild_) <- support old themes
276 [scaled addObject:file];
277 } else if ([base hasSuffix: @"@2x"]) {
278 [scaled addObject:[NSString stringWithFormat:@"%@~iphone.%@", base, extension]];
279 [scaled addObject:file];
280
281 // XXX: this actually can't be used, as the person loading the file doesn't realize that the @2x changed
282 /*NSString *rest([base substringWithRange:NSMakeRange(0, [base length] - 3)]);
283 [scaled addObject:[NSString stringWithFormat:@"%@~iphone.%@", rest, extension]];
284 [scaled addObject:[rest stringByAppendingPathExtension:extension]];*/
285 } else {
286 // XXX: this code isn't really complete
287
288 [scaled addObject:file];
289
290 if ([base hasSuffix:@"~iphone"])
291 [scaled addObject:[[base substringWithRange:NSMakeRange(0, [base length] - 7)] stringByAppendingPathExtension:extension]];
292 }
293 }
294
295 return scaled;
296 }
297
298 static NSString *$getTheme$(NSArray *files, NSArray *themes = Themes_) {
299 // XXX: this is not reasonable; OMG
300 id key(files);
301
302 @synchronized (Themed_) {
303 if (NSString *path = [Themed_ objectForKey:key])
304 return reinterpret_cast<id>(path) == [NSNull null] ? nil : path;
305 }
306
307 if (Debug_)
308 NSLog(@"WB:Debug: %@", [files description]);
309
310 NSString *path;
311
312 for (NSString *theme in Themes_)
313 for (NSString *file in files) {
314 path = [NSString stringWithFormat:@"%@/%@", theme, file];
315 if ([Manager_ fileExistsAtPath:path])
316 goto set;
317 }
318
319 path = nil;
320 set:
321
322 @synchronized (Themed_) {
323 [Themed_ setObject:(path == nil ? [NSNull null] : reinterpret_cast<id>(path)) forKey:key];
324 }
325
326 return path;
327 }
328 // }}}
329 // $pathForFile$inBundle$() {{{
330 static NSString *$pathForFile$inBundle$(NSString *file, NSBundle *bundle, bool ui) {
331 NSString *identifier = [bundle bundleIdentifier];
332 NSMutableArray *names = [NSMutableArray arrayWithCapacity:8];
333
334 if (identifier != nil)
335 [names addObject:[NSString stringWithFormat:@"Bundles/%@/%@", identifier, file]];
336 if (NSString *folder = [[bundle bundlePath] lastPathComponent]) {
337 [names addObject:[NSString stringWithFormat:@"Folders/%@/%@", folder, file]];
338 NSString *base([folder stringByDeletingPathExtension]);
339 if ([base hasSuffix:@"~iphone"])
340 [names addObject:[NSString stringWithFormat:@"Folders/%@.%@/%@", [base substringWithRange:NSMakeRange(0, [base length] - 7)], [folder pathExtension], file]];
341 }
342 if (ui)
343 [names addObject:[NSString stringWithFormat:@"UIImages/%@", file]];
344
345 #define remapResourceName(oldname, newname) \
346 else if ([file isEqualToString:(oldname)]) \
347 [names addObject:[NSString stringWithFormat:@"%@.png", newname]]; \
348
349 bool summer(SpringBoard_ && SummerBoard_);
350
351 if (identifier == nil);
352 else if ([identifier isEqualToString:@"com.apple.chatkit"])
353 [names addObject:[NSString stringWithFormat:@"Bundles/com.apple.MobileSMS/%@", file]];
354 else if ([identifier isEqualToString:@"com.apple.calculator"])
355 [names addObject:[NSString stringWithFormat:@"Files/Applications/Calculator.app/%@", file]];
356 else if (!summer);
357 remapResourceName(@"FSO_BG.png", @"StatusBar")
358 remapResourceName(Four_ ? @"SBDockBG-old.png" : @"SBDockBG.png", @"Dock")
359 remapResourceName(@"SBWeatherCelsius.png", @"Icons/Weather")
360
361 [names addObject:[NSString stringWithFormat:@"Fallback/%@", file]];
362
363 if (NSString *path = $getTheme$($useScale$(names, ui)))
364 return path;
365
366 return nil;
367 }
368 // }}}
369
370 static NSString *$pathForIcon$(SBApplication *self, NSString *suffix = @"") {
371 NSString *identifier = [self bundleIdentifier];
372 NSString *path = [self path];
373 NSString *folder = [path lastPathComponent];
374 NSString *dname = [self displayName];
375 NSString *didentifier = [self displayIdentifier];
376
377 if (Debug_)
378 NSLog(@"WB:Debug: [SBApplication(%@:%@:%@:%@) pathForIcon]", identifier, folder, dname, didentifier);
379
380 NSMutableArray *names = [NSMutableArray arrayWithCapacity:8];
381
382 /* XXX: I might need to keep this for backwards compatibility
383 if (identifier != nil)
384 [names addObject:[NSString stringWithFormat:@"Bundles/%@/icon.png", identifier]];
385 if (folder != nil)
386 [names addObject:[NSString stringWithFormat:@"Folders/%@/icon.png", folder]]; */
387
388 #define testForIcon(Name) \
389 if (NSString *name = Name) \
390 [names addObject:[NSString stringWithFormat:@"Icons%@/%@.png", suffix, name]];
391
392 if (![didentifier isEqualToString:identifier])
393 testForIcon(didentifier);
394
395 testForIcon(identifier);
396 testForIcon(dname);
397
398 if ([identifier isEqualToString:@"com.apple.MobileSMS"])
399 testForIcon(@"SMS");
400
401 if (didentifier != nil) {
402 testForIcon([English_ objectForKey:didentifier]);
403
404 NSArray *parts = [didentifier componentsSeparatedByString:@"-"];
405 if ([parts count] != 1)
406 if (NSDictionary *english = [[[NSDictionary alloc] initWithContentsOfFile:[path stringByAppendingString:@"/English.lproj/UIRoleDisplayNames.strings"]] autorelease])
407 testForIcon([english objectForKey:[parts lastObject]]);
408 }
409
410 if (NSString *path = $getTheme$(names))
411 return path;
412
413 return nil;
414 }
415
416 // -[NSBundle wb$bundleWithFile] {{{
417 @interface NSBundle (WinterBoard)
418 + (NSBundle *) wb$bundleWithFile:(NSString *)path;
419 @end
420
421 @implementation NSBundle (WinterBoard)
422
423 + (NSBundle *) wb$bundleWithFile:(NSString *)path {
424 path = [path stringByDeletingLastPathComponent];
425 if (path == nil || [path length] == 0 || [path isEqualToString:@"/"])
426 return nil;
427
428 NSBundle *bundle([Bundles_ objectForKey:path]);
429 if (reinterpret_cast<id>(bundle) == [NSNull null])
430 return nil;
431 else if (bundle == nil) {
432 if ([Manager_ fileExistsAtPath:[path stringByAppendingPathComponent:@"Info.plist"]])
433 bundle = [NSBundle bundleWithPath:path];
434 if (bundle == nil)
435 bundle = [NSBundle wb$bundleWithFile:path];
436 if (Debug_)
437 NSLog(@"WB:Debug:PathBundle(%@, %@)", path, bundle);
438 [Bundles_ setObject:(bundle == nil ? [NSNull null] : reinterpret_cast<id>(bundle)) forKey:path];
439 }
440
441 return bundle;
442 }
443
444 @end
445 // }}}
446 // -[NSString wb$themedPath] {{{
447 @interface NSString (WinterBoard)
448 - (NSString *) wb$themedPath;
449 @end
450
451 @implementation NSString (WinterBoard)
452
453 - (NSString *) wb$themedPath {
454 if ([self hasPrefix:@"/Library/Themes/"])
455 return self;
456
457 if (Debug_)
458 NSLog(@"WB:Debug:Bypass(\"%@\")", self);
459
460 if (NSBundle *bundle = [NSBundle wb$bundleWithFile:self]) {
461 NSString *file([self stringByResolvingSymlinksInPath]);
462 NSString *prefix([[bundle bundlePath] stringByResolvingSymlinksInPath]);
463 if ([file hasPrefix:prefix]) {
464 NSUInteger length([prefix length]);
465 if (length != [file length])
466 if (NSString *path = $pathForFile$inBundle$([file substringFromIndex:(length + 1)], bundle, false))
467 return path;
468 }
469 }
470
471 return self;
472 }
473
474 @end
475 // }}}
476
477 void WBLogRect(const char *tag, struct CGRect rect) {
478 NSLog(@"%s:{%f,%f+%f,%f}", tag, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
479 }
480
481 void WBLogHierarchy(UIView *view, unsigned index = 0, unsigned indent = 0) {
482 CGRect frame([view frame]);
483 NSLog(@"%*s|%2d:%p:%s : {%f,%f+%f,%f} (%@)", indent * 3, "", index, view, class_getName([view class]), frame.origin.x, frame.origin.y, frame.size.width, frame.size.height, [view backgroundColor]);
484 index = 0;
485 for (UIView *child in [view subviews])
486 WBLogHierarchy(child, index++, indent + 1);
487 }
488
489 UIImage *$cacheForImage$(UIImage *image) {
490 CGColorSpaceRef space(CGColorSpaceCreateDeviceRGB());
491 CGRect rect = {CGPointMake(1, 1), [image size]};
492 CGSize size = {rect.size.width + 2, rect.size.height + 2};
493
494 CGContextRef context(CGBitmapContextCreate(NULL, size.width, size.height, 8, 4 * size.width, space, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst));
495 CGColorSpaceRelease(space);
496
497 CGContextDrawImage(context, rect, [image CGImage]);
498 CGImageRef ref(CGBitmapContextCreateImage(context));
499 CGContextRelease(context);
500
501 UIImage *cache([UIImage imageWithCGImage:ref]);
502 CGImageRelease(ref);
503
504 return cache;
505 }
506
507 /*MSHook(id, SBImageCache$initWithName$forImageWidth$imageHeight$initialCapacity$, SBImageCache *self, SEL sel, NSString *name, unsigned width, unsigned height, unsigned capacity) {
508 //if ([name isEqualToString:@"icons"]) return nil;
509 return _SBImageCache$initWithName$forImageWidth$imageHeight$initialCapacity$(self, sel, name, width, height, capacity);
510 }*/
511
512 MSHook(void, SBIconModel$cacheImageForIcon$, SBIconModel *self, SEL sel, SBIcon *icon) {
513 NSString *key([icon displayIdentifier]);
514
515 if (UIImage *image = [icon icon]) {
516 CGSize size = [image size];
517 if (size.width != 59 || size.height != 60) {
518 UIImage *cache($cacheForImage$(image));
519 [Cache_ setObject:cache forKey:key];
520 return;
521 }
522 }
523
524 _SBIconModel$cacheImageForIcon$(self, sel, icon);
525 }
526
527 MSHook(void, SBIconModel$cacheImagesForIcon$, SBIconModel *self, SEL sel, SBIcon *icon) {
528 /* XXX: do I /really/ have to do this? figure out how to cache the small icon! */
529 _SBIconModel$cacheImagesForIcon$(self, sel, icon);
530
531 NSString *key([icon displayIdentifier]);
532
533 if (UIImage *image = [icon icon]) {
534 CGSize size = [image size];
535 if (size.width != 59 || size.height != 60) {
536 UIImage *cache($cacheForImage$(image));
537 [Cache_ setObject:cache forKey:key];
538 return;
539 }
540 }
541 }
542
543 MSHook(UIImage *, SBIconModel$getCachedImagedForIcon$, SBIconModel *self, SEL sel, SBIcon *icon) {
544 NSString *key([icon displayIdentifier]);
545 if (UIImage *image = [Cache_ objectForKey:key])
546 return image;
547 else
548 return _SBIconModel$getCachedImagedForIcon$(self, sel, icon);
549 }
550
551 MSHook(UIImage *, SBIconModel$getCachedImagedForIcon$smallIcon$, SBIconModel *self, SEL sel, SBIcon *icon, BOOL small) {
552 if (small)
553 return _SBIconModel$getCachedImagedForIcon$smallIcon$(self, sel, icon, small);
554 NSString *key([icon displayIdentifier]);
555 if (UIImage *image = [Cache_ objectForKey:key])
556 return image;
557 else
558 return _SBIconModel$getCachedImagedForIcon$smallIcon$(self, sel, icon, small);
559 }
560
561 MSHook(id, SBSearchView$initWithFrame$, id /* XXX: SBSearchView */ self, SEL sel, struct CGRect frame) {
562 if ((self = _SBSearchView$initWithFrame$(self, sel, frame)) != nil) {
563 [self setBackgroundColor:[UIColor clearColor]];
564 for (UIView *child in [self subviews])
565 [child setBackgroundColor:[UIColor clearColor]];
566 } return self;
567 }
568
569 MSHook(id, SBSearchTableViewCell$initWithStyle$reuseIdentifier$, SBSearchTableViewCell *self, SEL sel, int style, NSString *reuse) {
570 if ((self = _SBSearchTableViewCell$initWithStyle$reuseIdentifier$(self, sel, style, reuse)) != nil) {
571 [self setBackgroundColor:[UIColor clearColor]];
572 } return self;
573 }
574
575 MSHook(void, SBSearchTableViewCell$drawRect$, SBSearchTableViewCell *self, SEL sel, struct CGRect rect, BOOL selected) {
576 _SBSearchTableViewCell$drawRect$(self, sel, rect, selected);
577 float inset([self edgeInset]);
578 [[UIColor clearColor] set];
579 UIRectFill(CGRectMake(0, 0, inset, rect.size.height));
580 UIRectFill(CGRectMake(rect.size.width - inset, 0, inset, rect.size.height));
581 }
582
583 MSHook(UIImage *, SBApplicationIcon$icon, SBApplicationIcon *self, SEL sel) {
584 if (![Info_ wb$boolForKey:@"ComposeStoreIcons"])
585 if (NSString *path = $pathForIcon$([self application]))
586 return [UIImage imageWithContentsOfFile:path];
587 return _SBApplicationIcon$icon(self, sel);
588 }
589
590 MSHook(UIImage *, SBApplicationIcon$generateIconImage$, SBApplicationIcon *self, SEL sel, int type) {
591 if (type == 2)
592 if (![Info_ wb$boolForKey:@"ComposeStoreIcons"]) {
593 if (IsWild_ && false) // XXX: delete this code, it should not be supported
594 if (NSString *path72 = $pathForIcon$([self application], @"-72"))
595 return [UIImage imageWithContentsOfFile:path72];
596 if (NSString *path = $pathForIcon$([self application]))
597 if (UIImage *image = [UIImage imageWithContentsOfFile:path]) {
598 float width;
599 if ([$SBIcon respondsToSelector:@selector(defaultIconImageSize)])
600 width = [$SBIcon defaultIconImageSize].width;
601 else
602 width = 59;
603 return width == 59 ? image : [image _imageScaledToProportion:(width / 59.0) interpolationQuality:5];
604 }
605 }
606 return _SBApplicationIcon$generateIconImage$(self, sel, type);
607 }
608
609 MSHook(UIImage *, SBWidgetApplicationIcon$icon, SBWidgetApplicationIcon *self, SEL sel) {
610 if (Debug_)
611 NSLog(@"WB:Debug:Widget(%@:%@)", [self displayIdentifier], [self displayName]);
612 if (NSString *path = $getTheme$([NSArray arrayWithObject:[NSString stringWithFormat:@"Icons/%@.png", [self displayName]]]))
613 return [UIImage imageWithContentsOfFile:path];
614 return _SBWidgetApplicationIcon$icon(self, sel);
615 }
616
617 MSHook(UIImage *, SBBookmarkIcon$icon, SBBookmarkIcon *self, SEL sel) {
618 if (Debug_)
619 NSLog(@"WB:Debug:Bookmark(%@:%@)", [self displayIdentifier], [self displayName]);
620 if (NSString *path = $getTheme$([NSArray arrayWithObject:[NSString stringWithFormat:@"Icons/%@.png", [self displayName]]]))
621 return [UIImage imageWithContentsOfFile:path];
622 return _SBBookmarkIcon$icon(self, sel);
623 }
624
625 MSHook(NSString *, SBApplication$pathForIcon, SBApplication *self, SEL sel) {
626 if (NSString *path = $pathForIcon$(self))
627 return path;
628 return _SBApplication$pathForIcon(self, sel);
629 }
630
631 static UIImage *CachedImageAtPath(NSString *path) {
632 path = [path stringByResolvingSymlinksInPath];
633 UIImage *image = [PathImages_ objectForKey:path];
634 if (image != nil)
635 return reinterpret_cast<id>(image) == [NSNull null] ? nil : image;
636 image = [[UIImage alloc] initWithContentsOfFile:path cache:true];
637 if (image != nil)
638 image = [image autorelease];
639 [PathImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:path];
640 return image;
641 }
642
643 MSHook(UIImage *, _UIApplicationImageWithName, NSString *name) {
644 NSBundle *bundle = [NSBundle mainBundle];
645 if (Debug_)
646 NSLog(@"WB:Debug: _UIApplicationImageWithName(\"%@\", %@)", name, bundle);
647 if (NSString *path = $pathForFile$inBundle$(name, bundle, false))
648 return CachedImageAtPath(path);
649 return __UIApplicationImageWithName(name);
650 }
651
652 #define WBDelegate(delegate) \
653 - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel { \
654 if (Engineer_) \
655 NSLog(@"WB:MS:%s:(%s)", class_getName([self class]), sel_getName(sel)); \
656 if (NSMethodSignature *sig = [delegate methodSignatureForSelector:sel]) \
657 return sig; \
658 NSLog(@"WB:Error: [%s methodSignatureForSelector:(%s)]", class_getName([self class]), sel_getName(sel)); \
659 return nil; \
660 } \
661 \
662 - (void) forwardInvocation:(NSInvocation*)inv { \
663 SEL sel = [inv selector]; \
664 if ([delegate respondsToSelector:sel]) \
665 [inv invokeWithTarget:delegate]; \
666 else \
667 NSLog(@"WB:Error: [%s forwardInvocation:(%s)]", class_getName([self class]), sel_getName(sel)); \
668 }
669
670 // %hook -[NSBundle pathForResource:ofType:] {{{
671 MSInstanceMessageHook2(NSString *, NSBundle, pathForResource,ofType, NSString *, resource, NSString *, type) {
672 NSString *file = type == nil ? resource : [NSString stringWithFormat:@"%@.%@", resource, type];
673 if (Debug_)
674 NSLog(@"WB:Debug: [NSBundle(%@) pathForResource:\"%@\"]", [self bundleIdentifier], file);
675 if (NSString *path = $pathForFile$inBundle$(file, self, false))
676 return path;
677 return MSOldCall(resource, type);
678 }
679 // }}}
680
681 static void $drawLabel$(NSString *label, CGRect rect, NSString *style, NSString *custom) {
682 bool ellipsis(false);
683 float max = rect.size.width - 11, width;
684 width:
685 width = [(ellipsis ? [label stringByAppendingString:@"..."] : label) sizeWithStyle:style forWidth:320].width;
686
687 if (width > max) {
688 size_t length([label length]);
689 float spacing((width - max) / (length - 1));
690
691 if (spacing > 1.25) {
692 ellipsis = true;
693 label = [label substringToIndex:(length - 1)];
694 goto width;
695 }
696
697 style = [style stringByAppendingString:[NSString stringWithFormat:@"letter-spacing: -%f; ", spacing]];
698 }
699
700 if (ellipsis)
701 label = [label stringByAppendingString:@"..."];
702
703 if (custom != nil)
704 style = [style stringByAppendingString:custom];
705
706 CGSize size = [label sizeWithStyle:style forWidth:rect.size.width];
707 [label drawAtPoint:CGPointMake((rect.size.width - size.width) / 2 + rect.origin.x, rect.origin.y) withStyle:style];
708 }
709
710 static struct WBStringDrawingState {
711 WBStringDrawingState *next_;
712 unsigned count_;
713 NSString *base_;
714 NSString *info_;
715 } *stringDrawingState_;
716
717 MSInstanceMessageHook6(CGSize, NSString, drawAtPoint,forWidth,withFont,lineBreakMode,letterSpacing,includeEmoji, CGPoint, point, float, width, UIFont *, font, UILineBreakMode, mode, float, spacing, BOOL, emoji) {
718 //NSLog(@"XXX: @\"%@\" %g", self, spacing);
719
720 WBStringDrawingState *state(stringDrawingState_);
721 if (state == NULL)
722 return MSOldCall(point, width, font, mode, spacing, emoji);
723
724 if (--state->count_ == 0)
725 stringDrawingState_ = state->next_;
726 if (state->info_ == nil)
727 return MSOldCall(point, width, font, mode, spacing, emoji);
728
729 NSString *info([Info_ objectForKey:state->info_]);
730 if (info == nil)
731 return MSOldCall(point, width, font, mode, spacing, emoji);
732
733 NSString *base(state->base_ ?: @"");
734 NSString *extra([NSString stringWithFormat:@"letter-spacing: %gpx", spacing]);
735 [self drawAtPoint:point withStyle:[NSString stringWithFormat:@"%@;%@;%@;%@", [font markupDescription], extra, base, info]];
736 return CGSizeZero;
737 }
738
739 extern "C" NSString *NSStringFromCGRect(CGRect rect);
740
741 MSInstanceMessageHook7(CGSize, NSString, _drawInRect,withFont,lineBreakMode,alignment,lineSpacing,includeEmoji,truncationRect, CGRect, rect, UIFont *, font, UILineBreakMode, mode, UITextAlignment, alignment, float, spacing, BOOL, emoji, CGRect, truncation) {
742 //NSLog(@"XXX: &\"%@\" %@ \"%@\" %u %u %g %u %@", self, NSStringFromCGRect(rect), font, mode, alignment, spacing, emoji, NSStringFromCGRect(truncation));
743
744 WBStringDrawingState *state(stringDrawingState_);
745 if (state == NULL)
746 return MSOldCall(rect, font, mode, alignment, spacing, emoji, truncation);
747
748 if (--state->count_ == 0)
749 stringDrawingState_ = state->next_;
750 if (state->info_ == nil)
751 return MSOldCall(rect, font, mode, alignment, spacing, emoji, truncation);
752
753 NSString *info([Info_ objectForKey:state->info_]);
754 if (info == nil)
755 return MSOldCall(rect, font, mode, alignment, spacing, emoji, truncation);
756
757 NSString *textAlign;
758 switch (alignment) {
759 default:
760 case UITextAlignmentLeft:
761 textAlign = @"left";
762 break;
763 case UITextAlignmentCenter:
764 textAlign = @"center";
765 break;
766 case UITextAlignmentRight:
767 textAlign = @"right";
768 break;
769 }
770
771 NSString *base(state->base_ ?: @"");
772 NSString *extra([NSString stringWithFormat:@"text-align: %@", textAlign]);
773
774 if (true)
775 $drawLabel$(self, rect, [NSString stringWithFormat:@"%@;%@", [font markupDescription], base], info);
776 else
777 [self drawInRect:rect withStyle:[NSString stringWithFormat:@"%@;%@;%@;%@", [font markupDescription], extra, base, info]];
778
779 return CGSizeZero;
780 }
781
782 MSInstanceMessageHook4(CGSize, NSString, sizeWithFont,forWidth,lineBreakMode,letterSpacing, UIFont *, font, float, width, UILineBreakMode, mode, float, spacing) {
783 //NSLog(@"XXX: #\"%@\" \"%@\" %g %u %g", self, font, width, mode, spacing);
784
785 WBStringDrawingState *state(stringDrawingState_);
786 if (state == NULL)
787 return MSOldCall(font, width, mode, spacing);
788
789 if (--state->count_ == 0)
790 stringDrawingState_ = state->next_;
791 if (state->info_ == nil)
792 return MSOldCall(font, width, mode, spacing);
793
794 NSString *info([Info_ objectForKey:state->info_]);
795 if (info == nil)
796 return MSOldCall(font, width, mode, spacing);
797
798 NSString *base(state->base_ ?: @"");
799 NSString *extra([NSString stringWithFormat:@"letter-spacing: %gpx", spacing]);
800 return [self sizeWithStyle:[NSString stringWithFormat:@"%@;%@;%@;%@", [font markupDescription], extra, base, info] forWidth:width];
801 }
802
803 MSInstanceMessageHook1(CGSize, NSString, sizeWithFont, UIFont *, font) {
804 //NSLog(@"XXX: ?\"%@\"", self);
805
806 WBStringDrawingState *state(stringDrawingState_);
807 if (state == NULL)
808 return MSOldCall(font);
809
810 if (--state->count_ == 0)
811 stringDrawingState_ = state->next_;
812 if (state->info_ == nil)
813 return MSOldCall(font);
814
815 NSString *info([Info_ objectForKey:state->info_]);
816 if (info == nil)
817 return MSOldCall(font);
818
819 NSString *base(state->base_ ?: @"");
820 return [self sizeWithStyle:[NSString stringWithFormat:@"%@;%@;%@", [font markupDescription], base, info] forWidth:65535];
821 }
822
823 MSInstanceMessageHook1(UIImage *, SBIconBadgeFactory, checkoutBadgeImageForText, NSString *, text) {
824 WBStringDrawingState badgeState = {NULL, 1, @""
825 "color: white;"
826 , @"BadgeStyle"};
827
828 stringDrawingState_ = &badgeState;
829
830 UIImage *image(MSOldCall(text));
831
832 stringDrawingState_ = NULL;
833 return image;
834 }
835
836 MSInstanceMessageHook1(UIImage *, SBCalendarApplicationIcon, generateIconImage, int, type) {
837 WBStringDrawingState dayState = {NULL, 2, @""
838 "color: white;"
839 // XXX: this is only correct on an iPod dock
840 "text-shadow: rgba(0, 0, 0, 0.2) -1px -1px 2px;"
841 , @"CalendarIconDayStyle"};
842
843 WBStringDrawingState sizeState = {&dayState, 7, nil, nil};
844
845 WBStringDrawingState dateState = {&sizeState, 2, @""
846 "color: #333333;"
847 , @"CalendarIconDateStyle"};
848
849 stringDrawingState_ = &dateState;
850
851 UIImage *image(MSOldCall(type));
852
853 stringDrawingState_ = NULL;
854 return image;
855 }
856
857 MSHook(void, SBCalendarIconContentsView$drawRect$, SBCalendarIconContentsView *self, SEL sel, CGRect rect) {
858 NSBundle *bundle([NSBundle mainBundle]);
859
860 CFLocaleRef locale(CFLocaleCopyCurrent());
861 CFDateFormatterRef formatter(CFDateFormatterCreate(NULL, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle));
862 CFRelease(locale);
863
864 CFDateRef now(CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()));
865
866 CFDateFormatterSetFormat(formatter, (CFStringRef) [bundle localizedStringForKey:@"CALENDAR_ICON_DAY_NUMBER_FORMAT" value:@"d" table:@"SpringBoard"]);
867 CFStringRef date(CFDateFormatterCreateStringWithDate(NULL, formatter, now));
868 CFDateFormatterSetFormat(formatter, (CFStringRef) [bundle localizedStringForKey:@"CALENDAR_ICON_DAY_NAME_FORMAT" value:@"cccc" table:@"SpringBoard"]);
869 CFStringRef day(CFDateFormatterCreateStringWithDate(NULL, formatter, now));
870
871 CFRelease(now);
872
873 CFRelease(formatter);
874
875 NSString *datestyle([@""
876 "font-family: Helvetica; "
877 "font-weight: bold; "
878 "color: #333333; "
879 "alpha: 1.0; "
880 "" stringByAppendingString:(IsWild_
881 ? @"font-size: 54px; "
882 : @"font-size: 39px; "
883 )]);
884
885 NSString *daystyle([@""
886 "font-family: Helvetica; "
887 "font-weight: bold; "
888 "color: white; "
889 "text-shadow: rgba(0, 0, 0, 0.2) -1px -1px 2px; "
890 "" stringByAppendingString:(IsWild_
891 ? @"font-size: 11px; "
892 : @"font-size: 9px; "
893 )]);
894
895 if (NSString *style = [Info_ objectForKey:@"CalendarIconDateStyle"])
896 datestyle = [datestyle stringByAppendingString:style];
897 if (NSString *style = [Info_ objectForKey:@"CalendarIconDayStyle"])
898 daystyle = [daystyle stringByAppendingString:style];
899
900 float width([self bounds].size.width);
901 float leeway(10);
902 CGSize datesize = [(NSString *)date sizeWithStyle:datestyle forWidth:(width + leeway)];
903 CGSize daysize = [(NSString *)day sizeWithStyle:daystyle forWidth:(width + leeway)];
904
905 unsigned base0(IsWild_ ? 89 : 70);
906 if ($GSFontGetUseLegacyFontMetrics())
907 base0 = base0 + 1;
908 unsigned base1(IsWild_ ? 18 : 16);
909
910 if (Four_) {
911 ++base0;
912 ++base1;
913 }
914
915 [(NSString *)date drawAtPoint:CGPointMake(
916 (width + 1 - datesize.width) / 2, (base0 - datesize.height) / 2
917 ) withStyle:datestyle];
918
919 [(NSString *)day drawAtPoint:CGPointMake(
920 (width + 1 - daysize.width) / 2, (base1 - daysize.height) / 2
921 ) withStyle:daystyle];
922
923 CFRelease(date);
924 CFRelease(day);
925 }
926
927 // %hook -[{NavigationBar,Toolbar} setBarStyle:] {{{
928 void $setBarStyle$_(NSString *name, int &style) {
929 if (Debug_)
930 NSLog(@"WB:Debug:%@Style:%d", name, style);
931 NSNumber *number = nil;
932 if (number == nil)
933 number = [Info_ objectForKey:[NSString stringWithFormat:@"%@Style-%d", name, style]];
934 if (number == nil)
935 number = [Info_ objectForKey:[NSString stringWithFormat:@"%@Style", name]];
936 if (number != nil) {
937 style = [number intValue];
938 if (Debug_)
939 NSLog(@"WB:Debug:%@Style=%d", name, style);
940 }
941 }
942
943 MSInstanceMessageHook1(void, UIToolbar, setBarStyle, int, style) {
944 $setBarStyle$_(@"Toolbar", style);
945 return MSOldCall(style);
946 }
947
948 MSInstanceMessageHook1(void, UINavigationBar, setBarStyle, int, style) {
949 $setBarStyle$_(@"NavigationBar", style);
950 return MSOldCall(style);
951 }
952 // }}}
953
954 MSHook(void, SBButtonBar$didMoveToSuperview, UIView *self, SEL sel) {
955 [[self superview] setBackgroundColor:[UIColor clearColor]];
956 _SBButtonBar$didMoveToSuperview(self, sel);
957 }
958
959 MSHook(void, SBStatusBarContentsView$didMoveToSuperview, UIView *self, SEL sel) {
960 [[self superview] setBackgroundColor:[UIColor clearColor]];
961 _SBStatusBarContentsView$didMoveToSuperview(self, sel);
962 }
963
964 static NSArray *Wallpapers_;
965 static bool Papered_;
966 static bool Docked_;
967 static bool SMSBackgrounded_;
968 static NSString *WallpaperFile_;
969 static UIImageView *WallpaperImage_;
970 static UIWebDocumentView *WallpaperPage_;
971 static NSURL *WallpaperURL_;
972
973 #define _release(object) \
974 do if (object != nil) { \
975 [object release]; \
976 object = nil; \
977 } while (false)
978
979 static UIImage *$getImage$(NSString *path) {
980 UIImage *image([UIImage imageWithContentsOfFile:path]);
981
982 unsigned scale($getScale$(path));
983 if (scale != 1 && [image respondsToSelector:@selector(setScale)])
984 [image setScale:scale];
985
986 return image;
987 }
988
989 static UIImage *$getDefaultDesktopImage$() {
990 if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObjects:@"LockBackground.png", @"LockBackground.jpg", nil])))
991 return $getImage$(path);
992 return nil;
993 }
994
995 MSClassMessageHook0(UIImage *, UIImage, defaultDesktopImage) {
996 return $getDefaultDesktopImage$() ?: MSOldCall();
997 }
998
999 MSInstanceMessageHook0(UIImage *, SBSlidingAlertDisplay, _defaultDesktopImage) {
1000 return $getDefaultDesktopImage$() ?: MSOldCall();
1001 }
1002
1003 MSInstanceMessageHook0(void, SBWallpaperView, resetCurrentImageToWallpaper) {
1004 for (UIView *parent([self superview]); parent != nil; parent = [parent superview])
1005 if ([parent isKindOfClass:$SBSlidingAlertDisplay]) {
1006 if (UIImage *image = $getDefaultDesktopImage$()) {
1007 [self setImage:image];
1008 return;
1009 }
1010
1011 break;
1012 }
1013
1014 MSOldCall();
1015 }
1016
1017 // %hook -[SBUIController init] {{{
1018 MSInstanceMessageHook0(id, SBUIController, init) {
1019 self = MSOldCall();
1020 if (self == nil)
1021 return nil;
1022
1023 NSString *paper($getTheme$(Wallpapers_));
1024 if (paper != nil)
1025 paper = [paper stringByDeletingLastPathComponent];
1026
1027 {
1028 size_t size;
1029 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
1030 char *machine = new char[size];
1031
1032 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1) {
1033 perror("sysctlbyname(\"hw.machine\", ?)");
1034 delete [] machine;
1035 machine = NULL;
1036 }
1037
1038 IsWild_ = machine != NULL && strncmp(machine, "iPad", 4) == 0;
1039 }
1040
1041 if (Debug_)
1042 NSLog(@"WB:Debug:Info = %@", [Info_ description]);
1043
1044 if (paper != nil) {
1045 UIImageView *&_wallpaperView(MSHookIvar<UIImageView *>(self, "_wallpaperView"));
1046 if (&_wallpaperView != NULL) {
1047 [_wallpaperView removeFromSuperview];
1048 [_wallpaperView release];
1049 _wallpaperView = nil;
1050 }
1051 }
1052
1053 UIView *&_contentLayer(MSHookIvar<UIView *>(self, "_contentLayer"));
1054 UIView *&_contentView(MSHookIvar<UIView *>(self, "_contentView"));
1055
1056 UIView **player;
1057 if (&_contentLayer != NULL)
1058 player = &_contentLayer;
1059 else if (&_contentView != NULL)
1060 player = &_contentView;
1061 else
1062 player = NULL;
1063 UIView *layer(player == NULL ? nil : *player);
1064
1065 UIWindow *window([[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]);
1066 UIView *content([[[UIView alloc] initWithFrame:[window frame]] autorelease]);
1067 [window setContentView:content];
1068
1069 UIWindow *&_window(MSHookIvar<UIWindow *>(self, "_window"));
1070 [window setBackgroundColor:[_window backgroundColor]];
1071 [_window setBackgroundColor:[UIColor clearColor]];
1072
1073 [window setLevel:-1000];
1074 [window setHidden:NO];
1075
1076 /*if (player != NULL)
1077 *player = content;*/
1078
1079 [content setBackgroundColor:[layer backgroundColor]];
1080 [layer setBackgroundColor:[UIColor clearColor]];
1081
1082 UIView *indirect;
1083 if (!SummerBoard_ || !IsWild_)
1084 indirect = content;
1085 else {
1086 CGRect bounds([content bounds]);
1087 bounds.origin.y = -30;
1088 indirect = [[[UIView alloc] initWithFrame:bounds] autorelease];
1089 [content addSubview:indirect];
1090 [indirect zoomToScale:2.4];
1091 }
1092
1093 _release(WallpaperFile_);
1094 _release(WallpaperImage_);
1095 _release(WallpaperPage_);
1096 _release(WallpaperURL_);
1097
1098 if (paper != nil) {
1099 NSArray *themes([NSArray arrayWithObject:paper]);
1100
1101 if (NSString *path = $getTheme$([NSArray arrayWithObject:@"Wallpaper.mp4"], themes)) {
1102 #if UseAVController
1103 NSError *error;
1104
1105 static AVController *controller_(nil);
1106 if (controller_ == nil) {
1107 AVQueue *queue([AVQueue avQueue]);
1108 controller_ = [[AVController avControllerWithQueue:queue error:&error] retain];
1109 }
1110
1111 AVQueue *queue([controller_ queue]);
1112
1113 UIView *video([[[UIView alloc] initWithFrame:[indirect bounds]] autorelease]);
1114 [controller_ setLayer:[video _layer]];
1115
1116 AVItem *item([[[AVItem alloc] initWithPath:path error:&error] autorelease]);
1117 [queue appendItem:item error:&error];
1118
1119 [controller_ play:&error];
1120 #elif UseMPMoviePlayerController
1121 NSURL *url([NSURL fileURLWithPath:path]);
1122 MPMoviePlayerController *controller = [[$MPMoviePlayerController alloc] initWithContentURL:url];
1123 controller.movieControlMode = MPMovieControlModeHidden;
1124 [controller play];
1125 #else
1126 MPVideoView *video = [[[$MPVideoView alloc] initWithFrame:[indirect bounds]] autorelease];
1127 [video setMovieWithPath:path];
1128 [video setRepeatMode:1];
1129 [video setRepeatGap:-1];
1130 [video playFromBeginning];;
1131 #endif
1132
1133 [indirect addSubview:video];
1134 }
1135
1136 if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObjects:@"Wallpaper.png", @"Wallpaper.jpg", nil]), themes)) {
1137 if (UIImage *image = $getImage$(path)) {
1138 WallpaperFile_ = [path retain];
1139 WallpaperImage_ = [[UIImageView alloc] initWithImage:image];
1140 if (NSNumber *number = [Info_ objectForKey:@"WallpaperAlpha"])
1141 [WallpaperImage_ setAlpha:[number floatValue]];
1142 [indirect addSubview:WallpaperImage_];
1143 }
1144 }
1145
1146 if (NSString *path = $getTheme$([NSArray arrayWithObject:@"Wallpaper.html"], themes)) {
1147 CGRect bounds = [indirect bounds];
1148
1149 UIWebDocumentView *view([[[UIWebDocumentView alloc] initWithFrame:bounds] autorelease]);
1150 [view setAutoresizes:true];
1151
1152 WallpaperPage_ = [view retain];
1153 WallpaperURL_ = [[NSURL fileURLWithPath:path] retain];
1154
1155 [WallpaperPage_ loadRequest:[NSURLRequest requestWithURL:WallpaperURL_]];
1156
1157 [view setBackgroundColor:[UIColor clearColor]];
1158 if ([view respondsToSelector:@selector(setDrawsBackground:)])
1159 [view setDrawsBackground:NO];
1160 [[view webView] setDrawsBackground:NO];
1161
1162 [indirect addSubview:view];
1163 }
1164 }
1165
1166 for (size_t i(0), e([Themes_ count]); i != e; ++i) {
1167 NSString *theme = [Themes_ objectAtIndex:(e - i - 1)];
1168 NSString *html = [theme stringByAppendingPathComponent:@"Widget.html"];
1169 if ([Manager_ fileExistsAtPath:html]) {
1170 CGRect bounds = [indirect bounds];
1171
1172 UIWebDocumentView *view([[[UIWebDocumentView alloc] initWithFrame:bounds] autorelease]);
1173 [view setAutoresizes:true];
1174
1175 NSURL *url = [NSURL fileURLWithPath:html];
1176 [view loadRequest:[NSURLRequest requestWithURL:url]];
1177
1178 [view setBackgroundColor:[UIColor clearColor]];
1179 if ([view respondsToSelector:@selector(setDrawsBackground:)])
1180 [view setDrawsBackground:NO];
1181 [[view webView] setDrawsBackground:NO];
1182
1183 [indirect addSubview:view];
1184 }
1185 }
1186
1187 return self;
1188 }
1189 // }}}
1190
1191 MSHook(void, SBAwayView$updateDesktopImage$, SBAwayView *self, SEL sel, UIImage *image) {
1192 NSString *path = $getTheme$([NSArray arrayWithObject:@"LockBackground.html"]);
1193 UIView *&_backgroundView(MSHookIvar<UIView *>(self, "_backgroundView"));
1194
1195 if (path != nil && _backgroundView != nil)
1196 path = nil;
1197
1198 _SBAwayView$updateDesktopImage$(self, sel, image);
1199
1200 if (path != nil) {
1201 CGRect bounds = [self bounds];
1202
1203 UIWebDocumentView *view([[[UIWebDocumentView alloc] initWithFrame:bounds] autorelease]);
1204 [view setAutoresizes:true];
1205
1206 if (WallpaperPage_ != nil)
1207 [WallpaperPage_ release];
1208 WallpaperPage_ = [view retain];
1209
1210 if (WallpaperURL_ != nil)
1211 [WallpaperURL_ release];
1212 WallpaperURL_ = [[NSURL fileURLWithPath:path] retain];
1213
1214 [WallpaperPage_ loadRequest:[NSURLRequest requestWithURL:WallpaperURL_]];
1215
1216 [[view webView] setDrawsBackground:false];
1217 [view setBackgroundColor:[UIColor clearColor]];
1218
1219 [self insertSubview:view aboveSubview:_backgroundView];
1220 }
1221 }
1222
1223 /*extern "C" CGColorRef CGGStateGetSystemColor(void *);
1224 extern "C" CGColorRef CGGStateGetFillColor(void *);
1225 extern "C" CGColorRef CGGStateGetStrokeColor(void *);
1226 extern "C" NSString *UIStyleStringFromColor(CGColorRef);*/
1227
1228 /* WBTimeLabel {{{ */
1229 @interface WBTimeLabel : NSProxy {
1230 NSString *time_;
1231 _transient SBStatusBarTimeView *view_;
1232 }
1233
1234 - (id) initWithTime:(NSString *)time view:(SBStatusBarTimeView *)view;
1235
1236 @end
1237
1238 @implementation WBTimeLabel
1239
1240 - (void) dealloc {
1241 [time_ release];
1242 [super dealloc];
1243 }
1244
1245 - (id) initWithTime:(NSString *)time view:(SBStatusBarTimeView *)view {
1246 time_ = [time retain];
1247 view_ = view;
1248 return self;
1249 }
1250
1251 - (NSString *) description {
1252 return time_;
1253 }
1254
1255 WBDelegate(time_)
1256
1257 - (CGSize) drawAtPoint:(CGPoint)point forWidth:(float)width withFont:(UIFont *)font lineBreakMode:(UILineBreakMode)mode {
1258 if (NSString *custom = [Info_ objectForKey:@"TimeStyle"]) {
1259 BOOL &_mode(MSHookIvar<BOOL>(view_, "_mode"));;
1260
1261 [time_ drawAtPoint:point withStyle:[NSString stringWithFormat:@""
1262 "font-family: Helvetica; "
1263 "font-weight: bold; "
1264 "font-size: 14px; "
1265 "color: %@; "
1266 "%@", _mode ? @"white" : @"black", custom]];
1267
1268 return CGSizeZero;
1269 }
1270
1271 return [time_ drawAtPoint:point forWidth:width withFont:font lineBreakMode:mode];
1272 }
1273
1274 @end
1275 /* }}} */
1276 /* WBBadgeLabel {{{ */
1277 @interface WBBadgeLabel : NSProxy {
1278 NSString *badge_;
1279 }
1280
1281 - (id) initWithBadge:(NSString *)badge;
1282 - (NSString *) description;
1283
1284 @end
1285
1286 @implementation WBBadgeLabel
1287
1288 - (void) dealloc {
1289 [badge_ release];
1290 [super dealloc];
1291 }
1292
1293 - (id) initWithBadge:(NSString *)badge {
1294 badge_ = [badge retain];
1295 return self;
1296 }
1297
1298 - (NSString *) description {
1299 return [badge_ description];
1300 }
1301
1302 WBDelegate(badge_)
1303
1304 - (CGSize) drawAtPoint:(CGPoint)point forWidth:(float)width withFont:(UIFont *)font lineBreakMode:(UILineBreakMode)mode {
1305 if (NSString *custom = [Info_ objectForKey:@"BadgeStyle"]) {
1306 [badge_ drawAtPoint:point withStyle:[NSString stringWithFormat:@""
1307 "font-family: Helvetica; "
1308 "font-weight: bold; "
1309 "font-size: 17px; "
1310 "color: white; "
1311 "%@", custom]];
1312
1313 return CGSizeZero;
1314 }
1315
1316 return [badge_ drawAtPoint:point forWidth:width withFont:font lineBreakMode:mode];
1317 }
1318
1319 @end
1320 /* }}} */
1321
1322 // IconAlpha {{{
1323 MSInstanceMessageHook1(void, SBIcon, setIconImageAlpha, float, alpha) {
1324 if (NSNumber *number = [Info_ objectForKey:@"IconAlpha"])
1325 alpha = [number floatValue];
1326 return MSOldCall(alpha);
1327 }
1328
1329 MSInstanceMessageHook1(void, SBIcon, setIconLabelAlpha, float, alpha) {
1330 if (NSNumber *number = [Info_ objectForKey:@"IconAlpha"])
1331 alpha = [number floatValue];
1332 return MSOldCall(alpha);
1333 }
1334
1335 MSInstanceMessageHook0(id, SBIcon, initWithDefaultSize) {
1336 if ((self = MSOldCall()) != nil) {
1337 if (NSNumber *number = [Info_ objectForKey:@"IconAlpha"]) {
1338 // XXX: note: this is overridden above, which is silly
1339 float alpha([number floatValue]);
1340 if ([self respondsToSelector:@selector(setIconImageAlpha:)])
1341 [self setIconImageAlpha:alpha];
1342 if ([self respondsToSelector:@selector(setIconLabelAlpha:)])
1343 [self setIconLabelAlpha:alpha];
1344 if ([self respondsToSelector:@selector(setAlpha:)])
1345 [self setAlpha:alpha];
1346 }
1347 } return self;
1348 }
1349
1350 MSInstanceMessageHook1(void, SBIcon, setAlpha, float, alpha) {
1351 if (NSNumber *number = [Info_ objectForKey:@"IconAlpha"])
1352 alpha = [number floatValue];
1353 return MSOldCall(alpha);
1354 }
1355 // }}}
1356
1357 MSHook(id, SBIconBadge$initWithBadge$, SBIconBadge *self, SEL sel, NSString *badge) {
1358 if ((self = _SBIconBadge$initWithBadge$(self, sel, badge)) != nil) {
1359 id &_badge(MSHookIvar<id>(self, "_badge"));
1360 if (_badge != nil)
1361 if (id label = [[WBBadgeLabel alloc] initWithBadge:[_badge autorelease]])
1362 _badge = label;
1363 } return self;
1364 }
1365
1366 void SBStatusBarController$setStatusBarMode(int &mode) {
1367 if (Debug_)
1368 NSLog(@"WB:Debug:setStatusBarMode:%d", mode);
1369 if (mode < 100) // 104:hidden 105:glowing
1370 if (NSNumber *number = [Info_ objectForKey:@"StatusBarMode"])
1371 mode = [number intValue];
1372 }
1373
1374 /*MSHook(void, SBStatusBarController$setStatusBarMode$orientation$duration$animation$, SBStatusBarController *self, SEL sel, int mode, int orientation, double duration, int animation) {
1375 NSLog(@"mode:%d orientation:%d duration:%f animation:%d", mode, orientation, duration, animation);
1376 SBStatusBarController$setStatusBarMode(mode);
1377 return _SBStatusBarController$setStatusBarMode$orientation$duration$animation$(self, sel, mode, orientation, duration, animation);
1378 }*/
1379
1380 MSHook(void, SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$, SBStatusBarController *self, SEL sel, int mode, int orientation, float duration, int fenceID, int animation) {
1381 //NSLog(@"mode:%d orientation:%d duration:%f fenceID:%d animation:%d", mode, orientation, duration, fenceID, animation);
1382 SBStatusBarController$setStatusBarMode(mode);
1383 return _SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$(self, sel, mode, orientation, duration, fenceID, animation);
1384 }
1385
1386 MSHook(void, SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$startTime$, SBStatusBarController *self, SEL sel, int mode, int orientation, double duration, int fenceID, int animation, double startTime) {
1387 //NSLog(@"mode:%d orientation:%d duration:%f fenceID:%d animation:%d startTime:%f", mode, orientation, duration, fenceID, animation, startTime);
1388 SBStatusBarController$setStatusBarMode(mode);
1389 //NSLog(@"mode=%u", mode);
1390 return _SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$startTime$(self, sel, mode, orientation, duration, fenceID, animation, startTime);
1391 }
1392
1393 /*MSHook(id, SBStatusBarContentsView$initWithStatusBar$mode$, SBStatusBarContentsView *self, SEL sel, id bar, int mode) {
1394 if (NSNumber *number = [Info_ objectForKey:@"StatusBarContentsMode"])
1395 mode = [number intValue];
1396 return _SBStatusBarContentsView$initWithStatusBar$mode$(self, sel, bar, mode);
1397 }*/
1398
1399 MSHook(NSString *, SBStatusBarOperatorNameView$operatorNameStyle, SBStatusBarOperatorNameView *self, SEL sel) {
1400 NSString *style(_SBStatusBarOperatorNameView$operatorNameStyle(self, sel));
1401 if (Debug_)
1402 NSLog(@"operatorNameStyle= %@", style);
1403 if (NSString *custom = [Info_ objectForKey:@"OperatorNameStyle"])
1404 style = [NSString stringWithFormat:@"%@; %@", style, custom];
1405 return style;
1406 }
1407
1408 MSHook(void, SBStatusBarOperatorNameView$setOperatorName$fullSize$, SBStatusBarOperatorNameView *self, SEL sel, NSString *name, BOOL full) {
1409 if (Debug_)
1410 NSLog(@"setOperatorName:\"%@\" fullSize:%u", name, full);
1411 return _SBStatusBarOperatorNameView$setOperatorName$fullSize$(self, sel, name, NO);
1412 }
1413
1414 // XXX: replace this with [SBStatusBarTimeView tile]
1415 MSHook(void, SBStatusBarTimeView$drawRect$, SBStatusBarTimeView *self, SEL sel, CGRect rect) {
1416 id &_time(MSHookIvar<id>(self, "_time"));
1417 if (_time != nil && [_time class] != [WBTimeLabel class])
1418 object_setInstanceVariable(self, "_time", reinterpret_cast<void *>([[WBTimeLabel alloc] initWithTime:[_time autorelease] view:self]));
1419 return _SBStatusBarTimeView$drawRect$(self, sel, rect);
1420 }
1421
1422 @interface UIView (WinterBoard)
1423 - (bool) wb$isWBImageView;
1424 - (void) wb$logHierarchy;
1425 - (void) wb$setBackgroundColor:(UIColor *)color;
1426 @end
1427
1428 @implementation UIView (WinterBoard)
1429
1430 - (bool) wb$isWBImageView {
1431 return false;
1432 }
1433
1434 - (void) wb$logHierarchy {
1435 WBLogHierarchy(self);
1436 }
1437
1438 - (void) wb$setBackgroundColor:(UIColor *)color {
1439 [self setBackgroundColor:color];
1440 for (UIView *child in [self subviews])
1441 [child wb$setBackgroundColor:color];
1442 }
1443
1444 @end
1445
1446 @interface WBImageView : UIImageView {
1447 }
1448
1449 - (bool) wb$isWBImageView;
1450 - (void) wb$updateFrame;
1451 @end
1452
1453 @implementation WBImageView
1454
1455 - (bool) wb$isWBImageView {
1456 return true;
1457 }
1458
1459 - (void) wb$updateFrame {
1460 CGRect frame([self frame]);
1461 frame.origin.y = 0;
1462
1463 for (UIView *view(self); ; ) {
1464 view = [view superview];
1465 if (view == nil)
1466 break;
1467 frame.origin.y -= [view frame].origin.y;
1468 }
1469
1470 [self setFrame:frame];
1471 }
1472
1473 @end
1474
1475 MSHook(void, SBIconList$setFrame$, SBIconList *self, SEL sel, CGRect frame) {
1476 NSArray *subviews([self subviews]);
1477 WBImageView *view([subviews count] == 0 ? nil : [subviews objectAtIndex:0]);
1478 if (view != nil && [view wb$isWBImageView])
1479 [view wb$updateFrame];
1480 _SBIconList$setFrame$(self, sel, frame);
1481 }
1482
1483 static void $addPerPageView$(unsigned i, UIView *list) {
1484 NSString *path($getTheme$([NSArray arrayWithObject:[NSString stringWithFormat:@"Page%u.png", i]]));
1485 if (path == nil)
1486 return;
1487
1488 NSArray *subviews([list subviews]);
1489
1490 WBImageView *view([subviews count] == 0 ? nil : [subviews objectAtIndex:0]);
1491 if (view == nil || ![view wb$isWBImageView]) {
1492 view = [[[WBImageView alloc] init] autorelease];
1493 [list insertSubview:view atIndex:0];
1494 }
1495
1496 UIImage *image([UIImage imageWithContentsOfFile:path]);
1497
1498 CGRect frame([view frame]);
1499 frame.size = [image size];
1500 [view setFrame:frame];
1501
1502 [view setImage:image];
1503 [view wb$updateFrame];
1504 }
1505
1506 static void $addPerPageViews$(NSArray *lists) {
1507 for (unsigned i(0), e([lists count]); i != e; ++i)
1508 $addPerPageView$(i, [lists objectAtIndex:i]);
1509 }
1510
1511 MSInstanceMessageHook0(void, SBIconController, updateNumberOfRootIconLists) {
1512 NSArray *&_rootIconLists(MSHookIvar<NSArray *>(self, "_rootIconLists"));
1513 $addPerPageViews$(_rootIconLists);
1514 return MSOldCall();
1515 }
1516
1517 MSInstanceMessageHook0(void, SBIconContentView, layoutSubviews) {
1518 MSOldCall();
1519
1520 if (SBIconController *controller = [$SBIconController sharedInstance]) {
1521 UIView *&_dockContainerView(MSHookIvar<UIView *>(controller, "_dockContainerView"));
1522 if (&_dockContainerView != NULL)
1523 [[_dockContainerView superview] bringSubviewToFront:_dockContainerView];
1524 }
1525 }
1526
1527 MSHook(void, SBIconController$noteNumberOfIconListsChanged, SBIconController *self, SEL sel) {
1528 SBIconModel *&_iconModel(MSHookIvar<SBIconModel *>(self, "_iconModel"));
1529 $addPerPageViews$([_iconModel iconLists]);
1530 return _SBIconController$noteNumberOfIconListsChanged(self, sel);
1531 }
1532
1533 MSHook(id, SBIconLabel$initWithSize$label$, SBIconLabel *self, SEL sel, CGSize size, NSString *label) {
1534 self = _SBIconLabel$initWithSize$label$(self, sel, size, label);
1535 if (self != nil)
1536 [self setClipsToBounds:NO];
1537 return self;
1538 }
1539
1540 MSHook(void, SBIconLabel$setInDock$, SBIconLabel *self, SEL sel, BOOL docked) {
1541 static bool gssc(false);
1542 if (!gssc) {
1543 BOOL (*GSSystemHasCapability)(CFStringRef) = reinterpret_cast<BOOL (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, "GSSystemHasCapability"));
1544 Papered_ |= GSSystemHasCapability != NULL && GSSystemHasCapability(CFSTR("homescreen-wallpaper"));
1545 gssc = true;
1546
1547 if ([Info_ objectForKey:@"UndockedIconLabels"] == nil)
1548 [Info_ setObject:[NSNumber numberWithBool:(
1549 !Papered_ ||
1550 [Info_ objectForKey:@"DockedIconLabelStyle"] != nil ||
1551 [Info_ objectForKey:@"UndockedIconLabelStyle"] != nil
1552 )] forKey:@"UndockedIconLabels"];
1553 }
1554
1555 id &_label(MSHookIvar<id>(self, "_label"));
1556 if (![Info_ wb$boolForKey:@"UndockedIconLabels"])
1557 docked = true;
1558
1559 if (_label != nil && [_label respondsToSelector:@selector(setInDock:)])
1560 [_label setInDock:docked];
1561
1562 _SBIconLabel$setInDock$(self, sel, docked);
1563 [self setNeedsDisplay];
1564 }
1565
1566 MSHook(BOOL, SBDockIconListView$shouldShowNewDock, id self, SEL sel) {
1567 return SummerBoard_ && Docked_ ? NO : _SBDockIconListView$shouldShowNewDock(self, sel);
1568 }
1569
1570 MSHook(void, SBDockIconListView$setFrame$, id self, SEL sel, CGRect frame) {
1571 _SBDockIconListView$setFrame$(self, sel, frame);
1572 }
1573
1574 // %hook -[NSBundle localizedStringForKey:value:table:] {{{
1575 MSInstanceMessageHook3(NSString *, NSBundle, localizedStringForKey,value,table, NSString *, key, NSString *, value, NSString *, table) {
1576 NSString *identifier = [self bundleIdentifier];
1577 NSLocale *locale = [NSLocale currentLocale];
1578 NSString *language = [locale objectForKey:NSLocaleLanguageCode];
1579 if (Debug_)
1580 NSLog(@"WB:Debug:[NSBundle(%@) localizedStringForKey:\"%@\" value:\"%@\" table:\"%@\"] (%@)", identifier, key, value, table, language);
1581 NSString *file = table == nil ? @"Localizable" : table;
1582 NSString *name = [NSString stringWithFormat:@"%@:%@", identifier, file];
1583 NSDictionary *strings;
1584 if ((strings = [Strings_ objectForKey:name]) != nil) {
1585 if (static_cast<id>(strings) != [NSNull null]) strings:
1586 if (NSString *value = [strings objectForKey:key])
1587 return value;
1588 } else if (NSString *path = $pathForFile$inBundle$([NSString stringWithFormat:@"%@.lproj/%@.strings",
1589 language, file
1590 ], self, false)) {
1591 if ((strings = [[NSDictionary alloc] initWithContentsOfFile:path]) != nil) {
1592 [Strings_ setObject:[strings autorelease] forKey:name];
1593 goto strings;
1594 } else goto null;
1595 } else null:
1596 [Strings_ setObject:[NSNull null] forKey:name];
1597 return MSOldCall(key, value, table);
1598 }
1599 // }}}
1600 // %hook -[WebCoreFrameBridge renderedSizeOfNode:constrainedToWidth:] {{{
1601 MSClassHook(WebCoreFrameBridge)
1602
1603 MSInstanceMessageHook2(CGSize, WebCoreFrameBridge, renderedSizeOfNode,constrainedToWidth, id, node, float, width) {
1604 if (node == nil)
1605 return CGSizeZero;
1606 void **core(reinterpret_cast<void **>([node _node]));
1607 if (core == NULL || core[6] == NULL)
1608 return CGSizeZero;
1609 return MSOldCall(node, width);
1610 }
1611 // }}}
1612
1613 MSInstanceMessage1(void, SBIconLabel, drawRect, CGRect, rect) {
1614 static Ivar drawMoreLegibly = object_getInstanceVariable(self, "_drawMoreLegibly", NULL);
1615
1616 int docked;
1617 Ivar ivar = object_getInstanceVariable(self, "_inDock", reinterpret_cast<void **>(&docked));
1618 docked = (docked & (ivar_getOffset(ivar) == ivar_getOffset(drawMoreLegibly) ? 0x2 : 0x1)) != 0;
1619
1620 NSString *label(MSHookIvar<NSString *>(self, "_label"));
1621
1622 NSString *style = [NSString stringWithFormat:@""
1623 "font-family: Helvetica; "
1624 "font-weight: bold; "
1625 "color: %@; %@"
1626 "", (docked || !SummerBoard_ ? @"white" : @"#b3b3b3"), (IsWild_
1627 ? @"font-size: 12px; "
1628 : @"font-size: 11px; "
1629 )];
1630
1631 if (IsWild_)
1632 style = [style stringByAppendingString:@"text-shadow: rgba(0, 0, 0, 0.5) 0px 1px 0px; "];
1633 else if (docked)
1634 style = [style stringByAppendingString:@"text-shadow: rgba(0, 0, 0, 0.5) 0px -1px 0px; "];
1635
1636 NSString *custom([Info_ objectForKey:(docked ? @"DockedIconLabelStyle" : @"UndockedIconLabelStyle")]);
1637
1638 $drawLabel$(label, [self bounds], style, custom);
1639 }
1640
1641 MSInstanceMessage0(CGImageRef, SBIconLabel, buildLabelImage) {
1642 bool docked((MSHookIvar<unsigned>(self, "_inDock") & 0x2) != 0);
1643
1644 WBStringDrawingState labelState = {NULL, 0, @""
1645 "color: white;"
1646 , docked ? @"DockedIconLabelStyle" : @"UndockedIconLabelStyle"};
1647
1648 stringDrawingState_ = &labelState;
1649
1650 //NSLog(@"XXX: +");
1651 CGImageRef image(MSOldCall());
1652 //NSLog(@"XXX: -");
1653
1654 stringDrawingState_ = NULL;
1655 return image;
1656 }
1657
1658 // ChatKit {{{
1659 MSInstanceMessageHook2(id, CKBalloonView, initWithFrame,delegate, CGRect, frame, id, delegate) {
1660 if ((self = MSOldCall(frame, delegate)) != nil) {
1661 [self setBackgroundColor:[UIColor clearColor]];
1662 } return self;
1663 }
1664
1665 MSInstanceMessageHook0(BOOL, CKBalloonView, _canUseLayerBackedBalloon) {
1666 return SMSBackgrounded_ ? NO : MSOldCall();
1667 }
1668
1669 MSInstanceMessageHook0(void, CKTranscriptHeaderView, layoutSubviews) {
1670 [self wb$setBackgroundColor:[UIColor clearColor]];
1671 return MSOldCall();
1672 }
1673
1674 MSInstanceMessageHook1(void, CKMessageCell, addBalloonView, CKBalloonView *, balloon) {
1675 MSOldCall(balloon);
1676 [balloon setBackgroundColor:[UIColor clearColor]];
1677 }
1678
1679 MSInstanceMessageHook1(void, CKTranscriptCell, setBackgroundColor, UIColor *, color) {
1680 MSOldCall([UIColor clearColor]);
1681 [[self contentView] wb$setBackgroundColor:[UIColor clearColor]];
1682 }
1683
1684 // iOS >= 5.0
1685 MSInstanceMessageHook2(id, CKTranscriptCell, initWithStyle,reuseIdentifier, int, style, NSString *, reuse) {
1686 if ((self = MSOldCall(style, reuse)) != nil) {
1687 [self setBackgroundColor:[UIColor clearColor]];
1688 [[self contentView] wb$setBackgroundColor:[UIColor clearColor]];
1689 } return self;
1690 }
1691
1692 // iOS << 5.0
1693 MSInstanceMessageHook2(id, CKMessageCell, initWithStyle,reuseIdentifier, int, style, NSString *, reuse) {
1694 if ((self = MSOldCall(style, reuse)) != nil) {
1695 [self setBackgroundColor:[UIColor clearColor]];
1696 [[self contentView] setBackgroundColor:[UIColor clearColor]];
1697 } return self;
1698 }
1699
1700 MSInstanceMessageHook2(id, CKTimestampView, initWithStyle,reuseIdentifier, int, style, NSString *, reuse) {
1701 if ((self = MSOldCall(style, reuse)) != nil) {
1702 UILabel *&_label(MSHookIvar<UILabel *>(self, "_label"));
1703 [_label setBackgroundColor:[UIColor clearColor]];
1704 } return self;
1705 }
1706
1707 MSInstanceMessageHook1(void, CKTranscriptTableView, setSeparatorStyle, int, style) {
1708 MSOldCall(UITableViewCellSeparatorStyleNone);
1709 }
1710
1711 MSInstanceMessageHook2(id, CKTranscriptTableView, initWithFrame,style, CGRect, frame, int, style) {
1712 if ((self = MSOldCall(frame, style)) != nil) {
1713 [self setSeparatorStyle:UITableViewCellSeparatorStyleNone];
1714 } return self;
1715 }
1716
1717 MSInstanceMessageHook0(void, CKTranscriptController, loadView) {
1718 MSOldCall();
1719
1720 if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObjects:@"SMSBackground.png", @"SMSBackground.jpg", nil])))
1721 if (UIImage *image = $getImage$(path)) {
1722 SMSBackgrounded_ = true;
1723
1724 UIView *&_transcriptTable(MSHookIvar<UIView *>(self, "_transcriptTable"));
1725 UIView *&_transcriptLayer(MSHookIvar<UIView *>(self, "_transcriptLayer"));
1726 UIView *table;
1727 if (&_transcriptTable != NULL)
1728 table = _transcriptTable;
1729 else if (&_transcriptLayer != NULL)
1730 table = _transcriptLayer;
1731 else
1732 table = nil;
1733
1734 UIView *placard(table != nil ? [table superview] : MSHookIvar<UIView *>(self, "_backPlacard"));
1735 UIImageView *background([[[UIImageView alloc] initWithImage:image] autorelease]);
1736
1737 if (table == nil)
1738 [placard insertSubview:background atIndex:0];
1739 else {
1740 [table setBackgroundColor:[UIColor clearColor]];
1741 [placard insertSubview:background belowSubview:table];
1742 }
1743 }
1744 }
1745 // }}}
1746
1747 // %hook _UIImageWithName() {{{
1748 MSHook(UIImage *, _UIImageWithName, NSString *name) {
1749 if (Debug_)
1750 NSLog(@"WB:Debug: _UIImageWithName(\"%@\")", name);
1751 if (name == nil)
1752 return nil;
1753
1754 int identifier;
1755 bool packed;
1756
1757 if (_UIPackedImageTableGetIdentifierForName != NULL)
1758 packed = _UIPackedImageTableGetIdentifierForName(name, &identifier);
1759 else if (_UISharedImageNameGetIdentifier != NULL) {
1760 identifier = _UISharedImageNameGetIdentifier(name);
1761 packed = identifier != -1;
1762 } else {
1763 identifier = -1;
1764 packed = false;
1765 }
1766
1767 if (Debug_)
1768 NSLog(@"WB:Debug: _UISharedImageNameGetIdentifier(\"%@\") = %d", name, identifier);
1769
1770 if (!packed)
1771 return __UIImageWithName(name);
1772 else {
1773 NSNumber *key([NSNumber numberWithInt:identifier]);
1774 UIImage *image([UIImages_ objectForKey:key]);
1775 if (image != nil)
1776 return reinterpret_cast<id>(image) == [NSNull null] ? __UIImageWithName(name) : image;
1777 if (NSString *path = $pathForFile$inBundle$(name, _UIKitBundle(), true))
1778 image = $getImage$(path);
1779 [UIImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:key];
1780 if (image != nil)
1781 return image;
1782
1783 image = __UIImageWithName(name);
1784
1785 if (UIDebug_) {
1786 NSString *path([@"/tmp/UIImages/" stringByAppendingString:name]);
1787 if (![Manager_ fileExistsAtPath:path])
1788 [UIImagePNGRepresentation(image) writeToFile:path atomically:YES];
1789 }
1790
1791 return image;
1792 }
1793 }
1794 // }}}
1795 // %hook _UIImageWithNameInDomain() {{{
1796 MSHook(UIImage *, _UIImageWithNameInDomain, NSString *name, NSString *domain) {
1797 NSString *key([NSString stringWithFormat:@"D:%zu%@%@", [domain length], domain, name]);
1798 UIImage *image([PathImages_ objectForKey:key]);
1799 if (image != nil)
1800 return reinterpret_cast<id>(image) == [NSNull null] ? __UIImageWithNameInDomain(name, domain) : image;
1801 if (Debug_)
1802 NSLog(@"WB:Debug: UIImageWithNameInDomain(\"%@\", \"%@\")", name, domain);
1803 if (NSString *path = $getTheme$($useScale$([NSArray arrayWithObject:[NSString stringWithFormat:@"Domains/%@/%@", domain, name]])))
1804 image = $getImage$(path);
1805 [PathImages_ setObject:(image == nil ? [NSNull null] : reinterpret_cast<id>(image)) forKey:key];
1806 return image == nil ? __UIImageWithNameInDomain(name, domain) : image;
1807 }
1808 // }}}
1809
1810 // %hook GSFontCreateWithName() {{{
1811 MSHook(GSFontRef, GSFontCreateWithName, const char *name, GSFontSymbolicTraits traits, float size) {
1812 if (Debug_)
1813 NSLog(@"WB:Debug: GSFontCreateWithName(\"%s\", %f)", name, size);
1814 if (NSString *font = [Info_ objectForKey:[NSString stringWithFormat:@"FontName-%s", name]])
1815 name = [font UTF8String];
1816 //if (NSString *scale = [Info_ objectForKey:[NSString stringWithFormat:@"FontScale-%s", name]])
1817 // size *= [scale floatValue];
1818 return _GSFontCreateWithName(name, traits, size);
1819 }
1820 // }}}
1821
1822 #define AudioToolbox "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"
1823 #define UIKit "/System/Library/Frameworks/UIKit.framework/UIKit"
1824
1825 bool (*_Z24GetFileNameForThisActionmPcRb)(unsigned long a0, char *a1, bool &a2);
1826
1827 MSHook(bool, _Z24GetFileNameForThisActionmPcRb, unsigned long a0, char *a1, bool &a2) {
1828 if (Debug_)
1829 NSLog(@"WB:Debug:GetFileNameForThisAction(%u, %p, %u)", a0, a1, a2);
1830 bool value = __Z24GetFileNameForThisActionmPcRb(a0, a1, a2);
1831 if (Debug_)
1832 NSLog(@"WB:Debug:GetFileNameForThisAction(%u, %s, %u) = %u", a0, value ? a1 : NULL, a2, value);
1833
1834 if (value) {
1835 NSString *path = [NSString stringWithUTF8String:a1];
1836 if ([path hasPrefix:@"/System/Library/Audio/UISounds/"]) {
1837 NSString *file = [path substringFromIndex:31];
1838 for (NSString *theme in Themes_) {
1839 NSString *path([NSString stringWithFormat:@"%@/UISounds/%@", theme, file]);
1840 if ([Manager_ fileExistsAtPath:path]) {
1841 strcpy(a1, [path UTF8String]);
1842 break;
1843 }
1844 }
1845 }
1846 }
1847 return value;
1848 }
1849
1850 static void ChangeWallpaper(
1851 CFNotificationCenterRef center,
1852 void *observer,
1853 CFStringRef name,
1854 const void *object,
1855 CFDictionaryRef info
1856 ) {
1857 if (Debug_)
1858 NSLog(@"WB:Debug:ChangeWallpaper!");
1859
1860 UIImage *image;
1861 if (WallpaperFile_ != nil) {
1862 image = [[UIImage alloc] initWithContentsOfFile:WallpaperFile_];
1863 if (image != nil)
1864 image = [image autorelease];
1865 } else image = nil;
1866
1867 if (WallpaperImage_ != nil)
1868 [WallpaperImage_ setImage:image];
1869 if (WallpaperPage_ != nil)
1870 [WallpaperPage_ loadRequest:[NSURLRequest requestWithURL:WallpaperURL_]];
1871
1872 }
1873
1874 #define WBRename(name, sel, imp) \
1875 _ ## name ## $ ## imp = MSHookMessage($ ## name, @selector(sel), &$ ## name ## $ ## imp)
1876
1877 template <typename Type_>
1878 static void msset(Type_ &function, MSImageRef image, const char *name) {
1879 function = reinterpret_cast<Type_>(MSFindSymbol(image, name));
1880 }
1881
1882 template <typename Type_>
1883 static void nlset(Type_ &function, struct nlist *nl, size_t index) {
1884 struct nlist &name(nl[index]);
1885 uintptr_t value(name.n_value);
1886 if ((name.n_desc & N_ARM_THUMB_DEF) != 0)
1887 value |= 0x00000001;
1888 function = reinterpret_cast<Type_>(value);
1889 }
1890
1891 template <typename Type_>
1892 static void dlset(Type_ &function, const char *name) {
1893 function = reinterpret_cast<Type_>(dlsym(RTLD_DEFAULT, name));
1894 }
1895
1896 // %hook CGImageReadCreateWithFile() {{{
1897 MSHook(void *, CGImageReadCreateWithFile, NSString *path, int flag) {
1898 if (Debug_)
1899 NSLog(@"WB:Debug: CGImageReadCreateWithFile(%@, %d)", path, flag);
1900 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
1901 void *value(_CGImageReadCreateWithFile([path wb$themedPath], flag));
1902 [pool release];
1903 return value;
1904 }
1905 // }}}
1906
1907 static void NSString$drawAtPoint$withStyle$(NSString *self, SEL _cmd, CGPoint point, NSString *style) {
1908 WKSetCurrentGraphicsContext(UIGraphicsGetCurrentContext());
1909 if (style == nil || [style length] == 0)
1910 style = @"font-family: Helvetica; font-size: 12px";
1911 //NSLog(@"XXX:draw(%@)", [style stringByReplacingOccurrencesOfString:@"\n" withString:@" "]);
1912 return [[WBMarkup sharedMarkup] drawString:self atPoint:point withStyle:style];
1913 }
1914
1915 static void NSString$drawInRect$withStyle$(NSString *self, SEL _cmd, CGRect rect, NSString *style) {
1916 WKSetCurrentGraphicsContext(UIGraphicsGetCurrentContext());
1917 if (style == nil || [style length] == 0)
1918 style = @"font-family: Helvetica; font-size: 12px";
1919 return [[WBMarkup sharedMarkup] drawString:self inRect:rect withStyle:style];
1920 }
1921
1922 static CGSize NSString$sizeWithStyle$forWidth$(NSString *self, SEL _cmd, NSString *style, float width) {
1923 if (style == nil || [style length] == 0)
1924 style = @"font-family: Helvetica; font-size: 12px";
1925 //NSLog(@"XXX:size(%@)", [style stringByReplacingOccurrencesOfString:@"\n" withString:@" "]);
1926 return [[WBMarkup sharedMarkup] sizeOfString:self withStyle:style forWidth:width];
1927 }
1928
1929 static void SBInitialize() {
1930 class_addMethod($NSString, @selector(drawAtPoint:withStyle:), (IMP) &NSString$drawAtPoint$withStyle$, "v20@0:4{CGPoint=ff}8@16");
1931 class_addMethod($NSString, @selector(drawInRect:withStyle:), (IMP) &NSString$drawInRect$withStyle$, "v28@0:4{CGRect={CGSize=ff}{CGSize=ff}}8@24");
1932 class_addMethod($NSString, @selector(sizeWithStyle:forWidth:), (IMP) &NSString$sizeWithStyle$forWidth$, "{CGSize=ff}16@0:4@8f12");
1933
1934 if (SummerBoard_) {
1935 WBRename(SBApplication, pathForIcon, pathForIcon);
1936 WBRename(SBApplicationIcon, icon, icon);
1937 WBRename(SBApplicationIcon, generateIconImage:, generateIconImage$);
1938 }
1939
1940 WBRename(SBBookmarkIcon, icon, icon);
1941 WBRename(SBButtonBar, didMoveToSuperview, didMoveToSuperview);
1942 WBRename(SBCalendarIconContentsView, drawRect:, drawRect$);
1943 WBRename(SBIconBadge, initWithBadge:, initWithBadge$);
1944 WBRename(SBIconController, noteNumberOfIconListsChanged, noteNumberOfIconListsChanged);
1945
1946 WBRename(SBWidgetApplicationIcon, icon, icon);
1947
1948 WBRename(SBDockIconListView, setFrame:, setFrame$);
1949 MSHookMessage(object_getClass($SBDockIconListView), @selector(shouldShowNewDock), &$SBDockIconListView$shouldShowNewDock, &_SBDockIconListView$shouldShowNewDock);
1950
1951 if (kCFCoreFoundationVersionNumber < 600 || SummerBoard_)
1952 WBRename(SBIconLabel, drawRect:, drawRect$);
1953 else
1954 WBRename(SBIconLabel, buildLabelImage, buildLabelImage);
1955
1956 WBRename(SBIconLabel, initWithSize:label:, initWithSize$label$);
1957 WBRename(SBIconLabel, setInDock:, setInDock$);
1958
1959 WBRename(SBIconList, setFrame:, setFrame$);
1960
1961 WBRename(SBIconModel, cacheImageForIcon:, cacheImageForIcon$);
1962 WBRename(SBIconModel, cacheImagesForIcon:, cacheImagesForIcon$);
1963 WBRename(SBIconModel, getCachedImagedForIcon:, getCachedImagedForIcon$);
1964 WBRename(SBIconModel, getCachedImagedForIcon:smallIcon:, getCachedImagedForIcon$smallIcon$);
1965
1966 WBRename(SBSearchView, initWithFrame:, initWithFrame$);
1967 WBRename(SBSearchTableViewCell, drawRect:, drawRect$);
1968 WBRename(SBSearchTableViewCell, initWithStyle:reuseIdentifier:, initWithStyle$reuseIdentifier$);
1969
1970 //WBRename(SBImageCache, initWithName:forImageWidth:imageHeight:initialCapacity:, initWithName$forImageWidth$imageHeight$initialCapacity$);
1971
1972 WBRename(SBAwayView, updateDesktopImage:, updateDesktopImage$);
1973 WBRename(SBStatusBarContentsView, didMoveToSuperview, didMoveToSuperview);
1974 //WBRename(SBStatusBarContentsView, initWithStatusBar:mode:, initWithStatusBar$mode$);
1975 //WBRename(SBStatusBarController, setStatusBarMode:orientation:duration:animation:, setStatusBarMode$orientation$duration$animation$);
1976 WBRename(SBStatusBarController, setStatusBarMode:orientation:duration:fenceID:animation:, setStatusBarMode$orientation$duration$fenceID$animation$);
1977 WBRename(SBStatusBarController, setStatusBarMode:orientation:duration:fenceID:animation:startTime:, setStatusBarMode$orientation$duration$fenceID$animation$startTime$);
1978 WBRename(SBStatusBarOperatorNameView, operatorNameStyle, operatorNameStyle);
1979 WBRename(SBStatusBarOperatorNameView, setOperatorName:fullSize:, setOperatorName$fullSize$);
1980 WBRename(SBStatusBarTimeView, drawRect:, drawRect$);
1981
1982 if (SummerBoard_)
1983 English_ = [[NSDictionary alloc] initWithContentsOfFile:@"/System/Library/CoreServices/SpringBoard.app/English.lproj/LocalizedApplicationNames.strings"];
1984 }
1985
1986 MSInitialize {
1987 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
1988
1989 NSString *identifier([[NSBundle mainBundle] bundleIdentifier]);
1990 SpringBoard_ = [identifier isEqualToString:@"com.apple.springboard"];
1991
1992 Manager_ = [[NSFileManager defaultManager] retain];
1993 Themes_ = [[NSMutableArray alloc] initWithCapacity:8];
1994
1995 dlset(_GSFontGetUseLegacyFontMetrics, "GSFontGetUseLegacyFontMetrics");
1996
1997 // Load Settings.plist {{{
1998 if (NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"/User/Library/Preferences/com.saurik.WinterBoard.plist"]]) {
1999 if (NSNumber *value = [settings objectForKey:@"SummerBoard"])
2000 SummerBoard_ = [value boolValue];
2001 else
2002 SummerBoard_ = true;
2003
2004 if (NSNumber *value = [settings objectForKey:@"Debug"])
2005 Debug_ = [value boolValue];
2006 if (NSNumber *value = [settings objectForKey:@"RecordUI"])
2007 UIDebug_ = [value boolValue];
2008
2009 NSArray *themes([settings objectForKey:@"Themes"]);
2010 if (themes == nil)
2011 if (NSString *theme = [settings objectForKey:@"Theme"])
2012 themes = [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:
2013 theme, @"Name",
2014 [NSNumber numberWithBool:true], @"Active",
2015 nil]];
2016
2017 if (themes != nil)
2018 for (NSDictionary *theme in themes) {
2019 NSNumber *active([theme objectForKey:@"Active"]);
2020 if (![active boolValue])
2021 continue;
2022
2023 NSString *name([theme objectForKey:@"Name"]);
2024 if (name == nil)
2025 continue;
2026
2027 NSString *theme(nil);
2028
2029 #define testForTheme(format...) \
2030 if (theme == nil) { \
2031 NSString *path = [NSString stringWithFormat:format]; \
2032 if ([Manager_ fileExistsAtPath:path]) { \
2033 [Themes_ addObject:path]; \
2034 continue; \
2035 } \
2036 }
2037
2038 testForTheme(@"/Library/Themes/%@.theme", name)
2039 testForTheme(@"/Library/Themes/%@", name)
2040 testForTheme(@"%@/Library/SummerBoard/Themes/%@", NSHomeDirectory(), name)
2041
2042 }
2043 }
2044 // }}}
2045 // Merge Info.plist {{{
2046 Info_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2047
2048 for (NSString *theme in Themes_)
2049 if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", theme]])
2050 for (NSString *key in [info allKeys])
2051 if ([Info_ objectForKey:key] == nil)
2052 [Info_ setObject:[info objectForKey:key] forKey:key];
2053 // }}}
2054
2055 // AudioToolbox {{{
2056 if (MSImageRef image = MSGetImageByName(AudioToolbox)) {
2057 msset(_Z24GetFileNameForThisActionmPcRb, image, "__Z24GetFileNameForThisActionmPcRb");
2058 MSHookFunction(_Z24GetFileNameForThisActionmPcRb, &$_Z24GetFileNameForThisActionmPcRb, &__Z24GetFileNameForThisActionmPcRb);
2059 }
2060 // }}}
2061 // GraphicsServices {{{
2062 if (true) {
2063 MSHookFunction(&GSFontCreateWithName, &$GSFontCreateWithName, &_GSFontCreateWithName);
2064 }
2065 // }}}
2066 // ImageIO {{{
2067 if (MSImageRef image = MSGetImageByName("/System/Library/Frameworks/ImageIO.framework/ImageIO")) {
2068 void *(*CGImageReadCreateWithFile)(NSString *, int);
2069 msset(CGImageReadCreateWithFile, image, "_CGImageReadCreateWithFile");
2070 MSHookFunction(CGImageReadCreateWithFile, MSHake(CGImageReadCreateWithFile));
2071 }
2072 // }}}
2073 // SpringBoard {{{
2074 if (SpringBoard_) {
2075 Wallpapers_ = [[NSArray arrayWithObjects:@"Wallpaper.mp4", @"Wallpaper@2x.png", @"Wallpaper@2x.jpg", @"Wallpaper.png", @"Wallpaper.jpg", @"Wallpaper.html", nil] retain];
2076 Papered_ = $getTheme$(Wallpapers_) != nil;
2077 Docked_ = $getTheme$([NSArray arrayWithObjects:@"Dock.png", nil]) != nil;
2078
2079 CFNotificationCenterAddObserver(
2080 CFNotificationCenterGetDarwinNotifyCenter(),
2081 NULL, &ChangeWallpaper, (CFStringRef) @"com.saurik.winterboard.lockbackground", NULL, 0
2082 );
2083
2084 if ($getTheme$([NSArray arrayWithObject:@"Wallpaper.mp4"]) != nil) {
2085 NSBundle *MediaPlayer([NSBundle bundleWithPath:@"/System/Library/Frameworks/MediaPlayer.framework"]);
2086 if (MediaPlayer != nil)
2087 [MediaPlayer load];
2088
2089 $MPMoviePlayerController = objc_getClass("MPMoviePlayerController");
2090 $MPVideoView = objc_getClass("MPVideoView");
2091 }
2092
2093 SBInitialize();
2094 }
2095 // }}}
2096 // UIKit {{{
2097 if ([NSBundle bundleWithIdentifier:@"com.apple.UIKit"] != nil) {
2098 struct nlist nl[6];
2099 memset(nl, 0, sizeof(nl));
2100 nl[0].n_un.n_name = (char *) "__UIApplicationImageWithName";
2101 nl[1].n_un.n_name = (char *) "__UIImageWithNameInDomain";
2102 nl[2].n_un.n_name = (char *) "__UIKitBundle";
2103 nl[3].n_un.n_name = (char *) "__UIPackedImageTableGetIdentifierForName";
2104 nl[4].n_un.n_name = (char *) "__UISharedImageNameGetIdentifier";
2105 nlist(UIKit, nl);
2106
2107 nlset(_UIApplicationImageWithName, nl, 0);
2108 nlset(_UIImageWithNameInDomain, nl, 1);
2109 nlset(_UIKitBundle, nl, 2);
2110 nlset(_UIPackedImageTableGetIdentifierForName, nl, 3);
2111 nlset(_UISharedImageNameGetIdentifier, nl, 4);
2112
2113 MSHookFunction(_UIApplicationImageWithName, &$_UIApplicationImageWithName, &__UIApplicationImageWithName);
2114 MSHookFunction(_UIImageWithName, &$_UIImageWithName, &__UIImageWithName);
2115 MSHookFunction(_UIImageWithNameInDomain, &$_UIImageWithNameInDomain, &__UIImageWithNameInDomain);
2116 }
2117 // }}}
2118
2119 if (UIDebug_ && ![Manager_ fileExistsAtPath:@"/tmp/UIImages"]) {
2120 NSError *error(nil);
2121 if (![Manager_ createDirectoryAtPath:@"/tmp/UIImages" withIntermediateDirectories:NO attributes:[NSDictionary dictionaryWithObjectsAndKeys:
2122 [NSNumber numberWithShort:0777], NSFilePosixPermissions,
2123 nil] error:&error])
2124 NSLog(@"WB:Error: cannot create /tmp/UIImages (%@)", error);
2125 }
2126
2127 [pool release];
2128 }