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