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