Return to Snippet

Revision: 4034
at February 14, 2008 04:52 by 0xced


Updated Code
// 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)"

#import <AppKit/AppKit.h>
#import <Carbon/Carbon.h>

// Generate this header with
// sdef "/Applications/System Preferences.app" | sdp -fh --basename SystemPreferences
#import "SystemPreferences.h"

@interface TerminationListener : NSObject
{
    const char *prefPaneIdentifier;
    pid_t parentProcessId;
}

- (void) reload;
- (void) exit;

@end

@implementation TerminationListener

- (id) initWithPrefPaneIdentifier:(const char *)paneId parentProcessId:(pid_t)ppid
{
    self = [super init];
    if (self != nil) {
        prefPaneIdentifier = paneId ;
        parentProcessId = ppid;
        
        // This adds the input source required by the run loop
        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
        if (getppid() == 1) {
            // ppid is launchd (1) => parent terminated already
            [self reload];
        }
    }
    return self;
}

- (void) applicationDidTerminate:(NSNotification *)notification
{
    if (parentProcessId == [[[notification userInfo] valueForKey:@"NSApplicationProcessIdentifier"] intValue]) {
        // parent just terminated
        [self reload];
    }
}

- (void) applicationDidLaunch:(NSNotification *)notification
{
    NSDictionary *notificationInfo = [notification userInfo];
    if ([[notificationInfo valueForKey:@"NSApplicationName"] isEqualToString:@"System Preferences"]) {
        // System Preferences just relaunched
        ProcessSerialNumber psn = {[[notificationInfo valueForKey:@"NSApplicationProcessSerialNumberHigh"] intValue], [[notificationInfo valueForKey:@"NSApplicationProcessSerialNumberLow"] intValue]};
        SetFrontProcess(&psn);
        
        // Load the pref pane by sending the appropriate apple event
        AppleEvent event = {typeNull, NULL};
        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];
        AEBuildAppleEvent(kAECoreSuite, kAESetData, typeProcessSerialNumber, &psn, sizeof(psn), kAutoGenerateReturnID, kAnyTransactionID, &event, NULL, [gizmo UTF8String]);
        AESend(&event, NULL, kAENoReply, kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
        AEDisposeDesc(&event);
        
        [self exit];
    }
}

- (void) reload
{
    if (NSClassFromString(@"SBApplication")) {
        SystemPreferencesApplication *SystemPreferences = [SBApplication applicationWithBundleIdentifier:@"com.apple.systempreferences"];
        
        @try {
            [SystemPreferences activate];
            SystemPreferences.currentPane = [SystemPreferences.panes objectWithID:[NSString stringWithCString:prefPaneIdentifier encoding:NSUTF8StringEncoding]];
        } @catch (NSException *exception) {
            NSLog(@"%@", [exception description]);
        }
        [self exit];
    } else {
        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
        if (![[NSWorkspace sharedWorkspace] launchApplication:@"System Preferences"]) {
            [self exit];
        }
    }
}

- (void) exit
{
    /* As it is impossible to get the right combination of 
       {[NSApp stop:self] call, [NSApp abortModal] call, [NSApp terminate:self] call, on Tiger, on Leopard, from Terminal, from NSTask} 
       to work (that is, exit the run loop), just call the more radical exit() function. */ 
    exit(0);
}

@end

int main (int argc, const char * argv[])
{
    if (argc != 3) return EXIT_FAILURE;
    
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    [[[TerminationListener alloc] initWithPrefPaneIdentifier:argv[1] parentProcessId:atoi(argv[2])] autorelease];
    [[NSApplication sharedApplication] run];
    
    [pool release];
    
    return EXIT_SUCCESS;
}

Revision: 4033
at January 24, 2008 16:56 by 0xced


Updated Code
// 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)"

#import <AppKit/AppKit.h>
#import <Carbon/Carbon.h>

// Generate this header with
// sdef "/Applications/System Preferences.app" | sdp -fh --basename SystemPreferences
#import "SystemPreferences.h"

@interface TerminationListener : NSObject
{
	const char *prefPaneIdentifier;
	pid_t parentProcessId;
}

- (void) reload;
- (void) exit;

@end

@implementation TerminationListener

- (id) initWithPrefPaneIdentifier:(const char *)paneId parentProcessId:(pid_t)ppid
{
	self = [super init];
	if (self != nil) {
		prefPaneIdentifier = paneId ;
		parentProcessId = ppid;
		
		[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
		if (getppid() == 1) {
			// ppid is launchd (1) => parent terminated already
			[self reload];
		}
	}
	return self;
}

- (void) applicationDidTerminate:(NSNotification *)notification
{
	if (parentProcessId == [[[notification userInfo] valueForKey:@"NSApplicationProcessIdentifier"] intValue]) {
		// parent just terminated
		[self reload];
	}
}

- (void) applicationDidLaunch:(NSNotification *)notification
{
	NSDictionary *notificationInfo = [notification userInfo];
	if ([[notificationInfo valueForKey:@"NSApplicationName"] isEqualToString:@"System Preferences"]) {
		// System Preferences just relaunched
		ProcessSerialNumber psn = {[[notificationInfo valueForKey:@"NSApplicationProcessSerialNumberHigh"] intValue], [[notificationInfo valueForKey:@"NSApplicationProcessSerialNumberLow"] intValue]};
		SetFrontProcess(&psn);
		
		// Load the pref pane by sending the appropriate apple event
		AppleEvent event = {typeNull, NULL};
		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];
		AEBuildAppleEvent(kAECoreSuite, kAESetData, typeProcessSerialNumber, &psn, sizeof(psn), kAutoGenerateReturnID, kAnyTransactionID, &event, NULL, [gizmo UTF8String]);
		AESend(&event, NULL, kAENoReply, kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
		AEDisposeDesc(&event);
		
		[self exit];
	}
}

- (void) reload
{
	if (NSClassFromString(@"SBApplication")) {
		SystemPreferencesApplication *SystemPreferences = [SBApplication applicationWithBundleIdentifier:@"com.apple.systempreferences"];
		
		@try {
			[SystemPreferences activate];
			SystemPreferences.currentPane = [SystemPreferences.panes objectWithID:[NSString stringWithCString:prefPaneIdentifier encoding:NSUTF8StringEncoding]];
		} @catch (NSException *exception) {
			NSLog(@"%@", [exception description]);
		}
		[self exit];
	} else {
		[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
		if (![[NSWorkspace sharedWorkspace] launchApplication:@"System Preferences"]) {
			[self exit];
		}
	}
}

- (void) exit
{
	[NSApp abortModal];
	[NSApp stop:self];
}

@end

int main (int argc, const char * argv[])
{
	if (argc != 3) return EXIT_FAILURE;
	
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	
	[[[TerminationListener alloc] initWithPrefPaneIdentifier:argv[1] parentProcessId:atoi(argv[2])] autorelease];
	[[NSApplication sharedApplication] run];
	
	[pool release];
	
	return EXIT_SUCCESS;
}

Revision: 4032
at November 13, 2007 05:57 by 0xced


Updated Code
// gcc -Wall -arch i386 -arch ppc -Os -s -framework AppKit -o reload reload.m

#import <AppKit/AppKit.h>
#import <unistd.h>

@interface TerminationListener : NSObject
{
	const char *prefPaneIdentifier;
	pid_t parentProcessId;
}

- (void) reload;

@end

@implementation TerminationListener

- (id) initWithPrefPaneIdentifier:(const char *)paneId parentProcessId:(pid_t)ppid
{
	self = [super init];
	if (self != nil) {
		prefPaneIdentifier = paneId ;
		parentProcessId = ppid;
		
		[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
		if (getppid() == 1) {
			// ppid is launchd (1) => parent terminated already
			[self reload];
		}
	}
	return self;
}

- (void) applicationDidTerminate:(NSNotification *)notification
{
	if (parentProcessId == [[[notification userInfo] valueForKey:@"NSApplicationProcessIdentifier"] intValue]) {
		// parent just terminated
		[self reload];
	}
}

- (void) reload
{
	NSAppleEventDescriptor *returnDescriptor = NULL;
	
	NSString *scriptSource = [NSString stringWithFormat:
@"\
tell application \"System Preferences\"\n\
	activate\n\
	set current pane to pane id \"%s\"\n\
end tell", prefPaneIdentifier];
	
	NSAppleScript *scriptObject = [[NSAppleScript alloc] initWithSource:scriptSource];
	returnDescriptor = [scriptObject executeAndReturnError:nil];
	[scriptObject release];
	
	if (returnDescriptor == NULL) {
		// unsuccessful script execution, just launch System Preferences
		[[NSWorkspace sharedWorkspace] launchApplication:@"System Preferences"];
	}
	
	[NSApp stop:self];
}

@end

int main (int argc, const char * argv[])
{
	if (argc != 3) return EXIT_FAILURE;
	
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	
	[[[TerminationListener alloc] initWithPrefPaneIdentifier:argv[1] parentProcessId:atoi(argv[2])] autorelease];
	[[NSApplication sharedApplication] run];
	
	[pool release];
	
	return EXIT_SUCCESS;
}

Revision: 4031
at November 13, 2007 05:47 by 0xced


Updated Code
// gcc -Wall -arch i386 -arch ppc -Os -s -framework AppKit -o reload reload.m

#import <AppKit/AppKit.h>
#import <unistd.h>

@interface TerminationListener : NSObject
{
	const char *prefPaneIdentifier;
	pid_t parentProcessId;
}

- (void) reload;

@end

@implementation TerminationListener

- (id) initWithPrefPaneIdentifier:(const char *)paneId parentProcessId:(pid_t)ppid
{
	self = [super init];
	if (self != nil) {
		prefPaneIdentifier = paneId ;
		parentProcessId = ppid;
		
		[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
		if (getppid() == 1) {
			// ppid is launchd (1) => parent terminated already
			[self reload];
		}
	}
	return self;
}

- (void) applicationDidTerminate:(NSNotification *)notification
{
	if (parentProcessId == [[[notification userInfo] valueForKey:@"NSApplicationProcessIdentifier"] intValue]) {
		// parent just terminated
		[self reload];
	}
}

- (void) reload
{
	NSAppleEventDescriptor *returnDescriptor = NULL;
	
	NSString *scriptSource = [NSString stringWithFormat:
@"\
tell application \"System Preferences\"\n\
	activate\n\
	set current pane to pane id \"%s\"\n\
end tell", prefPaneIdentifier];
	
	NSAppleScript *scriptObject = [[NSAppleScript alloc] initWithSource:scriptSource];
	returnDescriptor = [scriptObject executeAndReturnError:nil];
	[scriptObject release];
	
	if (returnDescriptor == NULL) {
		// unsuccessful script execution, just launch System Preferences
		[[NSWorkspace sharedWorkspace] launchApplication:@"System Preferences"];
	}
	
	[NSApp terminate:self];
}

@end

int main (int argc, const char * argv[])
{
	if (argc != 3) return EXIT_FAILURE;
	
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	
	[[[TerminationListener alloc] initWithPrefPaneIdentifier:argv[1] parentProcessId:atoi(argv[2])] autorelease];
	[[NSApplication sharedApplication] run];
	
	// This will not be executed because -[NSApp terminate:] is called
	[pool release];
	
	return EXIT_SUCCESS;
}

Revision: 4030
at October 18, 2007 15:16 by 0xced


Initial Code
// gcc -Wall -arch i386 -arch ppc -Os -s -framework AppKit -o reload reload.m

#import <AppKit/AppKit.h>
#import <unistd.h>

@interface TerminationListener : NSObject
{
	const char *prefPaneIdentifier;
	pid_t parentProcessId;
}

- (void) reload;

@end

@implementation TerminationListener

- (id) initWithPrefPaneIdentifier:(const char *)paneId parentProcessId:(pid_t)ppid
{
	self = [super init];
	if (self != nil) {
		prefPaneIdentifier = paneId ;
		parentProcessId = ppid;
		
		[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
		if (getppid() == 1) {
			// ppid is launchd (1) => parent terminated already
			[self reload];
		}
	}
	return self;
}

- (void) applicationDidTerminate:(NSNotification *)notification
{
	if (parentProcessId == [[[notification userInfo] valueForKey:@"NSApplicationProcessIdentifier"] intValue]) {
		// parent just terminated
		[self reload];
	}
}

- (void) reload
{
	NSAppleEventDescriptor *returnDescriptor = NULL;
	
	NSString *scriptSource = [NSString stringWithFormat:
@"\
tell application \"System Preferences\"\n\
	activate\n\
	set current pane to pane id \"%s\"\n\
end tell", prefPaneIdentifier];
	
	NSAppleScript *scriptObject = [[NSAppleScript alloc] initWithSource:scriptSource];
	returnDescriptor = [scriptObject executeAndReturnError:nil];
	[scriptObject release];
	
	if (returnDescriptor == NULL) {
		// unsuccessful script execution, just launch System Preferences
		[[NSWorkspace sharedWorkspace] launchApplication:@"System Preferences"];
	}
	
	[NSApp terminate:self];
}

@end

int main (int argc, const char * argv[])
{
	if (argc != 3) return EXIT_FAILURE;
	
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	
	[[[TerminationListener alloc] initWithPrefPaneIdentifier:argv[1] parentProcessId:atoi(argv[2])] autorelease];
	[NSApp run];
	
	// This will not be executed because -[NSApp terminate:] is called
	[pool release];
	
	return EXIT_SUCCESS;
}

Initial URL


Initial Description
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().

Initial Title
Reload a preference pane

Initial Tags


Initial Language
Objective C