]>
Commit | Line | Data |
---|---|---|
36228fbc JF |
1 | /* CydgetScript - open-source lock screen multiplexer |
2 | * Copyright (C) 2009-2011 Jay Freeman (saurik) | |
545370d8 JF |
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 | #import <Foundation/Foundation.h> | |
39 | #import <UIKit/UIKit.h> | |
2140e557 JF |
40 | #import <Preferences/PSRootController.h> |
41 | #import <Preferences/PSViewController.h> | |
545370d8 JF |
42 | #import <Preferences/PSListController.h> |
43 | #import <Preferences/PSSpecifier.h> | |
44 | #import <Preferences/PSTableCell.h> | |
45 | #import <UIKit/UINavigationButton.h> | |
46 | ||
2140e557 JF |
47 | #include <dlfcn.h> |
48 | #include <objc/runtime.h> | |
49 | ||
50 | static BOOL (*IsIconHiddenDisplayId)(NSString *); | |
51 | static BOOL (*HideIconViaDisplayId)(NSString *); | |
52 | static BOOL (*UnHideIconViaDisplayId)(NSString *); | |
53 | ||
545370d8 JF |
54 | extern NSString *PSTableCellKey; |
55 | extern "C" UIImage *_UIImageWithName(NSString *); | |
56 | ||
57 | static UIImage *checkImage; | |
58 | static UIImage *uncheckedImage; | |
59 | ||
60 | static BOOL settingsChanged; | |
61 | static NSMutableDictionary *_settings; | |
62 | static NSString *_plist; | |
63 | ||
2140e557 JF |
64 | /* [NSObject yieldToSelector:(withObject:)] {{{*/ |
65 | @interface NSObject (wb$yieldToSelector) | |
66 | - (id) wb$yieldToSelector:(SEL)selector withObject:(id)object; | |
67 | - (id) wb$yieldToSelector:(SEL)selector; | |
68 | @end | |
69 | ||
70 | @implementation NSObject (Cydia) | |
71 | ||
72 | - (void) wb$doNothing { | |
73 | } | |
74 | ||
75 | - (void) wb$_yieldToContext:(NSMutableArray *)context { | |
76 | NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); | |
77 | ||
78 | SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue])); | |
79 | id object([[context objectAtIndex:1] nonretainedObjectValue]); | |
80 | volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue])); | |
81 | ||
82 | /* XXX: deal with exceptions */ | |
83 | id value([self performSelector:selector withObject:object]); | |
84 | ||
85 | NSMethodSignature *signature([self methodSignatureForSelector:selector]); | |
86 | [context removeAllObjects]; | |
87 | if ([signature methodReturnLength] != 0 && value != nil) | |
88 | [context addObject:value]; | |
89 | ||
90 | stopped = true; | |
91 | ||
92 | [self | |
93 | performSelectorOnMainThread:@selector(wb$doNothing) | |
94 | withObject:nil | |
95 | waitUntilDone:NO | |
96 | ]; | |
97 | ||
98 | [pool release]; | |
99 | } | |
100 | ||
101 | - (id) wb$yieldToSelector:(SEL)selector withObject:(id)object { | |
102 | /*return [self performSelector:selector withObject:object];*/ | |
103 | ||
104 | volatile bool stopped(false); | |
105 | ||
106 | NSMutableArray *context([NSMutableArray arrayWithObjects: | |
107 | [NSValue valueWithPointer:selector], | |
108 | [NSValue valueWithNonretainedObject:object], | |
109 | [NSValue valueWithPointer:const_cast<bool *>(&stopped)], | |
110 | nil]); | |
111 | ||
112 | NSThread *thread([[[NSThread alloc] | |
113 | initWithTarget:self | |
114 | selector:@selector(wb$_yieldToContext:) | |
115 | object:context | |
116 | ] autorelease]); | |
117 | ||
118 | [thread start]; | |
119 | ||
120 | NSRunLoop *loop([NSRunLoop currentRunLoop]); | |
121 | NSDate *future([NSDate distantFuture]); | |
122 | ||
123 | while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); | |
124 | ||
125 | return [context count] == 0 ? nil : [context objectAtIndex:0]; | |
126 | } | |
127 | ||
128 | - (id) wb$yieldToSelector:(SEL)selector { | |
129 | return [self wb$yieldToSelector:selector withObject:nil]; | |
130 | } | |
131 | ||
132 | @end | |
133 | /* }}} */ | |
134 | ||
545370d8 | 135 | /* Theme Settings Controller {{{ */ |
3a0081b3 | 136 | @interface CydgetOrderController: PSViewController <UITableViewDelegate, UITableViewDataSource> { |
545370d8 JF |
137 | UITableView *_tableView; |
138 | NSMutableArray *_themes; | |
139 | } | |
140 | ||
141 | @property (nonatomic, retain) NSMutableArray *themes; | |
142 | ||
143 | + (void) load; | |
144 | ||
145 | - (id) initForContentSize:(CGSize)size; | |
146 | - (id) view; | |
147 | - (id) navigationTitle; | |
148 | - (void) themesChanged; | |
149 | ||
150 | - (int) numberOfSectionsInTableView:(UITableView *)tableView; | |
151 | - (id) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section; | |
152 | - (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section; | |
153 | - (id) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; | |
154 | - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; | |
155 | - (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath; | |
156 | - (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath; | |
157 | - (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath; | |
158 | - (BOOL) tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath; | |
159 | ||
160 | @end | |
161 | ||
3a0081b3 | 162 | @implementation CydgetOrderController |
545370d8 JF |
163 | |
164 | @synthesize themes = _themes; | |
165 | ||
166 | + (void) load { | |
167 | NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); | |
168 | checkImage = [_UIImageWithName(@"UIPreferencesBlueCheck.png") retain]; | |
3a0081b3 | 169 | uncheckedImage = [[UIImage imageWithContentsOfFile:@"/System/Library/PreferenceBundles/CydgetSettings.bundle/SearchResultsCheckmarkClear.png"] retain]; |
545370d8 JF |
170 | [pool release]; |
171 | } | |
172 | ||
173 | - (id) initForContentSize:(CGSize)size { | |
174 | if ((self = [super initForContentSize:size]) != nil) { | |
3a0081b3 | 175 | self.themes = [_settings objectForKey:@"LockCydgets"]; |
545370d8 | 176 | if (!_themes) { |
3a0081b3 | 177 | self.themes = [NSMutableArray arrayWithObjects:[NSMutableDictionary dictionaryWithObjectsAndKeys: |
3aa346e5 | 178 | @"Welcome", @"Name", [NSNumber numberWithBool:YES], @"Active", nil |
3a0081b3 | 179 | ], [NSMutableDictionary dictionaryWithObjectsAndKeys: |
3aa346e5 | 180 | @"AwayView", @"Name", [NSNumber numberWithBool:YES], @"Active", nil |
3a0081b3 | 181 | ], nil]; |
545370d8 | 182 | |
3a0081b3 | 183 | [_settings setObject:_themes forKey:@"LockCydgets"]; |
545370d8 JF |
184 | } |
185 | ||
3a0081b3 JF |
186 | NSMutableArray *themesOnDisk([NSMutableArray arrayWithCapacity:4]); |
187 | for (NSString *theme in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/System/Library/LockCydgets" error:NULL]) | |
188 | if ([theme hasSuffix:@".cydget"]) | |
189 | [themesOnDisk addObject:[theme stringByDeletingPathExtension]]; | |
190 | ||
545370d8 JF |
191 | NSMutableSet *themesSet([NSMutableSet set]); |
192 | ||
193 | for (int i = 0, count = [_themes count]; i < count; i++) { | |
194 | NSDictionary *theme([_themes objectAtIndex:i]); | |
195 | NSString *name([theme objectForKey:@"Name"]); | |
196 | ||
197 | if (!name || ![themesOnDisk containsObject:name]) { | |
198 | [_themes removeObjectAtIndex:i]; | |
199 | i--; | |
200 | count--; | |
201 | } else { | |
202 | [themesSet addObject:name]; | |
203 | } | |
204 | } | |
205 | ||
206 | for (NSString *theme in themesOnDisk) { | |
207 | if ([themesSet containsObject:theme]) | |
208 | continue; | |
209 | [themesSet addObject:theme]; | |
210 | ||
211 | [_themes insertObject:[NSMutableDictionary dictionaryWithObjectsAndKeys: | |
212 | theme, @"Name", | |
213 | [NSNumber numberWithBool:NO], @"Active", | |
214 | nil] atIndex:0]; | |
215 | } | |
216 | ||
217 | _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 480-64) style:UITableViewStyleGrouped]; | |
218 | [_tableView setDataSource:self]; | |
219 | [_tableView setDelegate:self]; | |
220 | [_tableView setEditing:YES]; | |
221 | [_tableView setAllowsSelectionDuringEditing:YES]; | |
2140e557 JF |
222 | if ([self respondsToSelector:@selector(setView:)]) |
223 | [self setView:_tableView]; | |
545370d8 JF |
224 | } |
225 | return self; | |
226 | } | |
227 | ||
228 | - (void) dealloc { | |
229 | [_tableView release]; | |
230 | [_themes release]; | |
231 | [super dealloc]; | |
232 | } | |
233 | ||
234 | - (id) navigationTitle { | |
3a0081b3 | 235 | return @"Lock Cydgets"; |
545370d8 JF |
236 | } |
237 | ||
238 | - (id) view { | |
239 | return _tableView; | |
240 | } | |
241 | ||
242 | - (void) themesChanged { | |
243 | settingsChanged = YES; | |
244 | } | |
245 | ||
246 | /* UITableViewDelegate / UITableViewDataSource Methods {{{ */ | |
247 | - (int) numberOfSectionsInTableView:(UITableView *)tableView { | |
248 | return 1; | |
249 | } | |
250 | ||
251 | - (id) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section { | |
252 | return nil; | |
253 | } | |
254 | ||
255 | - (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section { | |
256 | return _themes.count; | |
257 | } | |
258 | ||
259 | - (id) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { | |
260 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ThemeCell"]; | |
261 | if (!cell) { | |
262 | cell = [[[UITableViewCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100) reuseIdentifier:@"ThemeCell"] autorelease]; | |
263 | //[cell setTableViewStyle:UITableViewCellStyleDefault]; | |
264 | } | |
265 | ||
266 | NSDictionary *theme([_themes objectAtIndex:indexPath.row]); | |
267 | cell.text = [theme objectForKey:@"Name"]; | |
268 | cell.hidesAccessoryWhenEditing = NO; | |
269 | NSNumber *active([theme objectForKey:@"Active"]); | |
270 | BOOL inactive(active == nil || ![active boolValue]); | |
271 | [cell setImage:(inactive ? uncheckedImage : checkImage)]; | |
272 | return cell; | |
273 | } | |
274 | ||
275 | - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { | |
276 | UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; | |
277 | NSMutableDictionary *theme = [_themes objectAtIndex:indexPath.row]; | |
278 | NSNumber *active = [theme objectForKey:@"Active"]; | |
279 | BOOL inactive = active == nil || ![active boolValue]; | |
280 | [theme setObject:[NSNumber numberWithBool:inactive] forKey:@"Active"]; | |
281 | [cell setImage:(!inactive ? uncheckedImage : checkImage)]; | |
282 | [tableView deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:YES]; | |
283 | [self themesChanged]; | |
284 | } | |
285 | ||
286 | - (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { | |
287 | NSUInteger fromIndex = [fromIndexPath row]; | |
288 | NSUInteger toIndex = [toIndexPath row]; | |
289 | if (fromIndex == toIndex) | |
290 | return; | |
291 | NSMutableDictionary *theme = [[[_themes objectAtIndex:fromIndex] retain] autorelease]; | |
292 | [_themes removeObjectAtIndex:fromIndex]; | |
293 | [_themes insertObject:theme atIndex:toIndex]; | |
294 | [self themesChanged]; | |
295 | } | |
296 | ||
297 | - (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { | |
298 | return UITableViewCellEditingStyleNone; | |
299 | } | |
300 | ||
301 | - (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath { | |
302 | return NO; | |
303 | } | |
304 | ||
305 | - (BOOL) tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { | |
306 | return YES; | |
307 | } | |
308 | /* }}} */ | |
309 | @end | |
310 | /* }}} */ | |
311 | ||
3a0081b3 | 312 | @interface CydgetSettingsController: PSListController { |
545370d8 JF |
313 | } |
314 | ||
315 | - (id) initForContentSize:(CGSize)size; | |
316 | - (void) dealloc; | |
317 | - (void) suspend; | |
318 | - (void) navigationBarButtonClicked:(int)buttonIndex; | |
319 | - (void) viewWillRedisplay; | |
320 | - (void) pushController:(id)controller; | |
321 | - (id) specifiers; | |
322 | - (void) settingsChanged; | |
323 | - (NSString *) title; | |
324 | - (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec; | |
325 | - (id) readPreferenceValue:(PSSpecifier *)spec; | |
326 | ||
327 | @end | |
328 | ||
3a0081b3 | 329 | @implementation CydgetSettingsController |
545370d8 JF |
330 | |
331 | - (id) initForContentSize:(CGSize)size { | |
332 | if ((self = [super initForContentSize:size]) != nil) { | |
3a0081b3 | 333 | _plist = [[NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()] retain]; |
545370d8 JF |
334 | _settings = [([NSMutableDictionary dictionaryWithContentsOfFile:_plist] ?: [NSMutableDictionary dictionary]) retain]; |
335 | } return self; | |
336 | } | |
337 | ||
338 | - (void) dealloc { | |
339 | [_settings release]; | |
340 | [_plist release]; | |
341 | [super dealloc]; | |
342 | } | |
343 | ||
344 | - (void) suspend { | |
345 | if (!settingsChanged) | |
346 | return; | |
347 | ||
348 | NSData *data([NSPropertyListSerialization dataFromPropertyList:_settings format:NSPropertyListBinaryFormat_v1_0 errorDescription:NULL]); | |
349 | if (!data) | |
350 | return; | |
351 | if (![data writeToFile:_plist options:NSAtomicWrite error:NULL]) | |
352 | return; | |
545370d8 JF |
353 | system("killall SpringBoard"); |
354 | } | |
355 | ||
2140e557 JF |
356 | - (void) cancelChanges { |
357 | [_settings release]; | |
358 | [_plist release]; | |
359 | _plist = [[NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()] retain]; | |
360 | _settings = [([NSMutableDictionary dictionaryWithContentsOfFile:_plist] ?: [NSMutableDictionary dictionary]) retain]; | |
361 | ||
362 | [self reloadSpecifiers]; | |
363 | if (![[PSViewController class] instancesRespondToSelector:@selector(showLeftButton:withStyle:rightButton:withStyle:)]) { | |
364 | [[self navigationItem] setLeftBarButtonItem:nil]; | |
365 | [[self navigationItem] setRightBarButtonItem:nil]; | |
366 | } else { | |
367 | [self showLeftButton:nil withStyle:0 rightButton:nil withStyle:0]; | |
368 | } | |
369 | settingsChanged = NO; | |
370 | } | |
371 | ||
545370d8 JF |
372 | - (void) navigationBarButtonClicked:(int)buttonIndex { |
373 | if (!settingsChanged) { | |
374 | [super navigationBarButtonClicked:buttonIndex]; | |
375 | return; | |
376 | } | |
377 | ||
2140e557 JF |
378 | if (buttonIndex == 0) { |
379 | [self cancelChanges]; | |
380 | return; | |
381 | } | |
545370d8 JF |
382 | |
383 | [self suspend]; | |
384 | [self.rootController popController]; | |
385 | } | |
386 | ||
2140e557 JF |
387 | - (void) settingsConfirmButtonClicked:(UIBarButtonItem *)button { |
388 | [self navigationBarButtonClicked:button.tag]; | |
389 | } | |
390 | ||
545370d8 JF |
391 | - (void) viewWillRedisplay { |
392 | if (settingsChanged) | |
393 | [self settingsChanged]; | |
394 | [super viewWillRedisplay]; | |
395 | } | |
396 | ||
2140e557 JF |
397 | - (void) viewWillAppear:(BOOL)animated { |
398 | if (settingsChanged) | |
399 | [self settingsChanged]; | |
400 | if ([super respondsToSelector:@selector(viewWillAppear:)]) | |
401 | [super viewWillAppear:animated]; | |
402 | } | |
403 | ||
545370d8 JF |
404 | - (void) pushController:(id)controller { |
405 | [self hideNavigationBarButtons]; | |
406 | [super pushController:controller]; | |
407 | } | |
408 | ||
409 | - (id) specifiers { | |
410 | if (!_specifiers) | |
3a0081b3 | 411 | _specifiers = [[self loadSpecifiersFromPlistName:@"Cydget" target:self] retain]; |
545370d8 JF |
412 | return _specifiers; |
413 | } | |
414 | ||
415 | - (void) settingsChanged { | |
2140e557 JF |
416 | if (![[PSViewController class] instancesRespondToSelector:@selector(showLeftButton:withStyle:rightButton:withStyle:)]) { |
417 | UIBarButtonItem *respringButton([[UIBarButtonItem alloc] initWithTitle:@"Respring" style:UIBarButtonItemStyleDone target:self action:@selector(settingsConfirmButtonClicked:)]); | |
418 | UIBarButtonItem *cancelButton([[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(settingsConfirmButtonClicked:)]); | |
419 | cancelButton.tag = 0; | |
420 | respringButton.tag = 1; | |
421 | [[self navigationItem] setLeftBarButtonItem:respringButton]; | |
422 | [[self navigationItem] setRightBarButtonItem:cancelButton]; | |
423 | [respringButton release]; | |
424 | [cancelButton release]; | |
425 | } else { | |
426 | [self showLeftButton:@"Respring" withStyle:2 rightButton:@"Cancel" withStyle:0]; | |
427 | } | |
545370d8 JF |
428 | settingsChanged = YES; |
429 | } | |
430 | ||
431 | - (NSString *) title { | |
3a0081b3 | 432 | return @"Cydget"; |
545370d8 JF |
433 | } |
434 | ||
435 | - (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec { | |
2140e557 | 436 | NSString *key([spec propertyForKey:@"key"]); |
545370d8 JF |
437 | if ([[spec propertyForKey:@"negate"] boolValue]) |
438 | value = [NSNumber numberWithBool:(![value boolValue])]; | |
2140e557 | 439 | [_settings setValue:value forKey:key]; |
545370d8 JF |
440 | [self settingsChanged]; |
441 | } | |
442 | ||
443 | - (id) readPreferenceValue:(PSSpecifier *)spec { | |
444 | NSString *key([spec propertyForKey:@"key"]); | |
445 | id defaultValue([spec propertyForKey:@"default"]); | |
446 | id plistValue([_settings objectForKey:key]); | |
447 | if (!plistValue) | |
448 | return defaultValue; | |
449 | if ([[spec propertyForKey:@"negate"] boolValue]) | |
450 | plistValue = [NSNumber numberWithBool:(![plistValue boolValue])]; | |
451 | return plistValue; | |
452 | } | |
453 | ||
454 | @end | |
2140e557 JF |
455 | |
456 | #define WBSAddMethod(_class, _sel, _imp, _type) \ | |
457 | if (![[_class class] instancesRespondToSelector:@selector(_sel)]) \ | |
458 | class_addMethod([_class class], @selector(_sel), (IMP)_imp, _type) | |
459 | void $PSRootController$popController(PSRootController *self, SEL _cmd) { | |
460 | [self popViewControllerAnimated:YES]; | |
461 | } | |
462 | ||
463 | void $PSViewController$hideNavigationBarButtons(PSRootController *self, SEL _cmd) { | |
464 | } | |
465 | ||
466 | id $PSViewController$initForContentSize$(PSRootController *self, SEL _cmd, CGRect contentSize) { | |
467 | return [self init]; | |
468 | } | |
469 | ||
470 | static __attribute__((constructor)) void __wbsInit() { | |
471 | WBSAddMethod(PSRootController, popController, $PSRootController$popController, "v@:"); | |
472 | WBSAddMethod(PSViewController, hideNavigationBarButtons, $PSViewController$hideNavigationBarButtons, "v@:"); | |
473 | WBSAddMethod(PSViewController, initForContentSize:, $PSViewController$initForContentSize$, "@@:{ff}"); | |
474 | } |