Posted By

0xced on 10/18/07


Tagged

cocoa reload preference pane prefpane


Versions (?)

Reload a preference pane


 / Published in: Objective C
 

The preference pane that need to be reloaded must fork 'reload' with two arguments, then terminate System Preferences. In Cocoa, the fork is easily achieved with a NSTask. The first argument must be the bundle identifier of the preference pane to reload. The second argument must be the process identifier of the terminating application, i.e. System Preferences. In Cocoa, you can get it with [[NSProcessInfo processInfo] processIdentifier], which is equivalent to getpid().

  1. // gcc -Wall -arch i386 -arch ppc -mmacosx-version-min=10.4 -framework AppKit -framework Carbon -weak_framework ScriptingBridge -o reload reload.m 2>&1 | egrep -v "(In file included from reload.m:8:)|(Mac OS X version 10.5 or later is needed for use of property)"
  2.  
  3. #import <AppKit/AppKit.h>
  4. #import <Carbon/Carbon.h>
  5.  
  6. // Generate this header with
  7. // sdef "/Applications/System Preferences.app" | sdp -fh --basename SystemPreferences
  8. #import "SystemPreferences.h"
  9.  
  10. @interface TerminationListener : NSObject
  11. {
  12. const char *prefPaneIdentifier;
  13. pid_t parentProcessId;
  14. }
  15.  
  16. - (void) reload;
  17. - (void) exit;
  18.  
  19. @end
  20.  
  21. @implementation TerminationListener
  22.  
  23. - (id) initWithPrefPaneIdentifier:(const char *)paneId parentProcessId:(pid_t)ppid
  24. {
  25. self = [super init];
  26. if (self != nil) {
  27. prefPaneIdentifier = paneId ;
  28. parentProcessId = ppid;
  29.  
  30. // This adds the input source required by the run loop
  31. [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
  32. if (getppid() == 1) {
  33. // ppid is launchd (1) => parent terminated already
  34. [self reload];
  35. }
  36. }
  37. return self;
  38. }
  39.  
  40. - (void) applicationDidTerminate:(NSNotification *)notification
  41. {
  42. if (parentProcessId == [[[notification userInfo] valueForKey:@"NSApplicationProcessIdentifier"] intValue]) {
  43. // parent just terminated
  44. [self reload];
  45. }
  46. }
  47.  
  48. - (void) applicationDidLaunch:(NSNotification *)notification
  49. {
  50. NSDictionary *notificationInfo = [notification userInfo];
  51. if ([[notificationInfo valueForKey:@"NSApplicationName"] isEqualToString:@"System Preferences"]) {
  52. // System Preferences just relaunched
  53. ProcessSerialNumber psn = {[[notificationInfo valueForKey:@"NSApplicationProcessSerialNumberHigh"] intValue], [[notificationInfo valueForKey:@"NSApplicationProcessSerialNumberLow"] intValue]};
  54. SetFrontProcess(&psn);
  55.  
  56. // Load the pref pane by sending the appropriate apple event
  57. AppleEvent event = {typeNull, NULL};
  58. NSString *gizmo = [NSString stringWithFormat:@"'data':obj{'form':enum('ID '), 'want':type('xppb'), 'seld':\"%s\", 'from':'null'()}, '----':obj{'form':enum('prop'), 'want':type('prop'), 'seld':type('xpcp'), 'from':'null'()}", prefPaneIdentifier];
  59. AEBuildAppleEvent(kAECoreSuite, kAESetData, typeProcessSerialNumber, &psn, sizeof(psn), kAutoGenerateReturnID, kAnyTransactionID, &event, NULL, [gizmo UTF8String]);
  60. AESend(&event, NULL, kAENoReply, kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
  61. AEDisposeDesc(&event);
  62.  
  63. [self exit];
  64. }
  65. }
  66.  
  67. - (void) reload
  68. {
  69. if (NSClassFromString(@"SBApplication")) {
  70. SystemPreferencesApplication *SystemPreferences = [SBApplication applicationWithBundleIdentifier:@"com.apple.systempreferences"];
  71.  
  72. @try {
  73. [SystemPreferences activate];
  74. SystemPreferences.currentPane = [SystemPreferences.panes objectWithID:[NSString stringWithCString:prefPaneIdentifier encoding:NSUTF8StringEncoding]];
  75. } @catch (NSException *exception) {
  76. NSLog(@"%@", [exception description]);
  77. }
  78. [self exit];
  79. } else {
  80. [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
  81. if (![[NSWorkspace sharedWorkspace] launchApplication:@"System Preferences"]) {
  82. [self exit];
  83. }
  84. }
  85. }
  86.  
  87. - (void) exit
  88. {
  89. /* As it is impossible to get the right combination of
  90.   {[NSApp stop:self] call, [NSApp abortModal] call, [NSApp terminate:self] call, on Tiger, on Leopard, from Terminal, from NSTask}
  91.   to work (that is, exit the run loop), just call the more radical exit() function. */
  92. exit(0);
  93. }
  94.  
  95. @end
  96.  
  97. int main (int argc, const char * argv[])
  98. {
  99. if (argc != 3) return EXIT_FAILURE;
  100.  
  101. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  102.  
  103. [[[TerminationListener alloc] initWithPrefPaneIdentifier:argv[1] parentProcessId:atoi(argv[2])] autorelease];
  104. [[NSApplication sharedApplication] run];
  105.  
  106. [pool release];
  107.  
  108. return EXIT_SUCCESS;
  109. }

Report this snippet  

You need to login to post a comment.