mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2025-01-16 20:01:33 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
738 lines
26 KiB
Objective-C
738 lines
26 KiB
Objective-C
//
|
|
// CloverPrefpane.m
|
|
// CloverPrefpane
|
|
//
|
|
// Created by JrCs on 03/05/13.
|
|
// Copyright (c) 2013 ProjectOSX. All rights reserved.
|
|
//
|
|
|
|
#import "CloverPrefpane.h"
|
|
#include <mach/mach_error.h>
|
|
|
|
#define kCloverInstaller @"com.projectosx.clover.installer"
|
|
|
|
// Global Variables
|
|
static const CFStringRef agentIdentifier=CFSTR("com.projectosx.Clover.Updater");
|
|
static const CFStringRef agentExecutable=CFSTR("/Library/Application Support/Clover/CloverUpdaterUtility");
|
|
static const CFStringRef checkIntervalKey=CFSTR("ScheduledCheckInterval");
|
|
static const CFStringRef lastCheckTimestampKey=CFSTR("LastCheckTimestamp");
|
|
static const CFStringRef efiDirPathKey=CFSTR("EFI Directory Path");
|
|
|
|
static NSString* kLogLineCount = @"LogLineCount";
|
|
static NSString* kLogEveryBoot = @"LogEveryBoot";
|
|
static NSString* kBackupDirOnDestVol = @"BackupDirOnDestVol";
|
|
static NSString* kKeepBackupLimit = @"KeepBackupLimit";
|
|
static NSString* kMountEFI = @"MountEFI";
|
|
static NSString* kNVRamDisk = @"NVRamDisk";
|
|
|
|
@implementation CloverPrefpane
|
|
|
|
@synthesize cloverLogEveryBootEnabled; // = _cloverLogEveryBootEnabled;
|
|
@synthesize cloverLogEveryBootLimit; // = _cloverLogEveryBootLimit;
|
|
|
|
@synthesize diskutilList; // = _diskutilList;
|
|
@synthesize efiPartitions; // = _efiPartitions;
|
|
@synthesize nvRamPartitions; // = _nvRamPartitions;
|
|
|
|
@synthesize nvram; // = _nvram;
|
|
@synthesize themeInfo; // = _themeInfo;
|
|
|
|
#pragma mark Properties
|
|
|
|
- (NSDictionary *)diskutilList {
|
|
if (diskutilList == nil) {
|
|
// Get diskutil list -plist output
|
|
NSTask *task = [[NSTask alloc] init];
|
|
|
|
[task setLaunchPath: @"/usr/sbin/diskutil"];
|
|
[task setArguments:[NSArray arrayWithObjects: @"list", @"-plist", nil]];
|
|
|
|
NSPipe *pipe = [NSPipe pipe];
|
|
|
|
[task setStandardOutput: pipe];
|
|
|
|
NSFileHandle *file = [pipe fileHandleForReading];
|
|
|
|
[task launch];
|
|
|
|
NSData *data = [file readDataToEndOfFile];
|
|
|
|
diskutilList = CFBridgingRelease(CFPropertyListCreateWithData(kCFAllocatorDefault,
|
|
(__bridge CFDataRef)(data),
|
|
kCFPropertyListImmutable,
|
|
NULL, NULL));
|
|
}
|
|
return diskutilList;
|
|
}
|
|
|
|
// Hack to avoid warning generated by genstrings
|
|
#define GetLocalized\
|
|
String(key, comment) NSLocalized\
|
|
StringFromTableInBundle(key, nil, self.bundle, comment)
|
|
|
|
#define AddKeyValueToList(list, title, value) \
|
|
[list addObject:[NSDictionary dictionaryWithObjectsAndKeys: \
|
|
(title), @"Title", \
|
|
(value ? value : [NSNull null]), @"Value", nil]]
|
|
|
|
- (NSArray*)efiPartitions {
|
|
if (efiPartitions == nil) {
|
|
NSMutableArray *list = [[NSMutableArray alloc] init];
|
|
|
|
AddKeyValueToList(list,
|
|
GetLocalizedString(@"No",@"Not mounting EFI partition"),
|
|
nil);
|
|
AddKeyValueToList(list,
|
|
GetLocalizedString(@"Boot Volume",@"EFI partition from boot volume"),
|
|
[NSNumber numberWithBool:YES]);
|
|
|
|
NSArray *disksAndPartitions = [[self diskutilList] objectForKey:@"AllDisksAndPartitions"];
|
|
if (disksAndPartitions != nil) {
|
|
for (NSDictionary *diskEntry in disksAndPartitions) {
|
|
|
|
NSString *content = [diskEntry objectForKey:@"Content"];
|
|
|
|
if (content != nil) {
|
|
// Disk has partitions
|
|
if ([content isEqualToString:@"GUID_partition_scheme"] ||
|
|
[content isEqualToString:@"FDisk_partition_scheme"]) {
|
|
|
|
NSString *diskIdentifier = [diskEntry objectForKey:@"DeviceIdentifier"];
|
|
NSArray *partitions = [diskEntry objectForKey:@"Partitions"];
|
|
|
|
if (diskIdentifier != nil && partitions != nil) {
|
|
NSMutableArray *volumeNames = [[NSMutableArray alloc] init];
|
|
NSString *espIdentifier = nil;
|
|
|
|
for (NSDictionary *partitionEntry in partitions) {
|
|
|
|
NSString *content = [partitionEntry objectForKey:@"Content"];
|
|
|
|
if (content != nil && [content isEqualToString:@"EFI"]) {
|
|
|
|
NSString *identifier = [partitionEntry objectForKey:@"DeviceIdentifier"];
|
|
|
|
if (identifier != nil) {
|
|
espIdentifier = [[self getPartitionProperties:identifier] objectForKey:@"UUID"];
|
|
|
|
if (!espIdentifier) {
|
|
espIdentifier = identifier;
|
|
}
|
|
}
|
|
}
|
|
|
|
NSString *volumeName = [partitionEntry objectForKey:@"VolumeName"];
|
|
|
|
if (volumeName) {
|
|
[volumeNames addObject:volumeName];
|
|
}
|
|
}
|
|
|
|
if (espIdentifier) {
|
|
NSString *name = [NSString stringWithFormat:GetLocalizedString(@"ESP on [%@] %@", nil),
|
|
diskIdentifier, [volumeNames componentsJoinedByString:@", "]];
|
|
AddKeyValueToList(list, name, espIdentifier);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
efiPartitions = [NSArray arrayWithArray:list];
|
|
}
|
|
return efiPartitions;
|
|
}
|
|
|
|
- (NSArray*)nvRamPartitions {
|
|
if (nil == nvRamPartitions) {
|
|
NSMutableArray *list = [[NSMutableArray alloc] init];
|
|
AddKeyValueToList(list, GetLocalizedString(@"Never",nil), [NSNumber numberWithBool:NO]);
|
|
AddKeyValueToList(list, GetLocalizedString(@"Auto",nil), [NSNull null]);
|
|
AddKeyValueToList(list, GetLocalizedString(@"Always",nil), [NSNumber numberWithBool:YES]);
|
|
|
|
nvRamPartitions = [NSArray arrayWithArray:list];
|
|
}
|
|
|
|
return nvRamPartitions;
|
|
}
|
|
|
|
- (IBAction)updateCloverLogEveryBoot:(id)sender {
|
|
NSString* logEveryBoot;
|
|
if ([self.cloverLogEveryBootEnabled boolValue] == YES) {
|
|
unsigned int logEveryBootLimit = [self.cloverLogEveryBootLimit unsignedIntValue];
|
|
if (logEveryBootLimit > 0) {
|
|
logEveryBoot = [NSString stringWithFormat:@"%d", logEveryBootLimit];
|
|
} else {
|
|
logEveryBoot=@"Yes";
|
|
}
|
|
}
|
|
else
|
|
logEveryBoot=@"";
|
|
|
|
[self.nvram setValue:logEveryBoot forKey:kLogEveryBoot];
|
|
[self updateCloverNVRamVariables:nil];
|
|
}
|
|
|
|
- (IBAction)updateCloverNVRamVariables:(id)sender {
|
|
Class boolClass = [[NSNumber numberWithBool:YES] class];
|
|
Class nullClass = [NSNull class];
|
|
|
|
for (id key in [self.nvram allKeys]) {
|
|
id value = [self.nvram valueForKey:key];
|
|
if ([value isKindOfClass:boolClass]) {
|
|
value = [value boolValue] ? @"Yes" : @"No";
|
|
} else if ([value isKindOfClass:nullClass]) {
|
|
value = @"";
|
|
} else {
|
|
value = [NSString stringWithFormat:@"%@",value];
|
|
}
|
|
[self setNVRamKey:[NSString stringWithFormat:@"Clover.%@", key] Value:value];
|
|
}
|
|
}
|
|
|
|
#pragma mark Methods
|
|
|
|
- (NSDictionary*)getPartitionProperties:(NSString*)bsdName {
|
|
CFMutableDictionaryRef matchingDict;
|
|
io_service_t service;
|
|
NSDictionary *result = nil;
|
|
|
|
matchingDict = IOBSDNameMatching(kIOMasterPortDefault, 0, [bsdName UTF8String]);
|
|
|
|
if (matchingDict == NULL) {
|
|
NSLog(@"IOBSDNameMatching returned a NULL dictionary");
|
|
} else {
|
|
// Fetch the object with the matching BSD node name.
|
|
// Note that there should only be one match, so IOServiceGetMatchingService is used instead of
|
|
// IOServiceGetMatchingServices to simplify the code.
|
|
service = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
|
|
|
|
if (IO_OBJECT_NULL == service) {
|
|
NSLog(@"IOServiceGetMatchingService returned IO_OBJECT_NULL");
|
|
} else {
|
|
if (IOObjectConformsTo(service, "IOMedia")) {
|
|
CFMutableDictionaryRef properties;
|
|
IORegistryEntryCreateCFProperties(service,&properties, kCFAllocatorDefault, 0);
|
|
result = (__bridge_transfer id)properties;
|
|
}
|
|
IOObjectRelease(service);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
#pragma mark Events
|
|
|
|
// System Preferences calls this method when the pane is initialized.
|
|
- (id)initWithBundle:(NSBundle *)bundle {
|
|
if ( ( self = [super initWithBundle:bundle] ) != nil ) {
|
|
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
|
|
NSString *agentsFolder = [[searchPaths objectAtIndex:0] stringByAppendingPathComponent:@"LaunchAgents"];
|
|
|
|
[[NSFileManager defaultManager]
|
|
createDirectoryAtPath:agentsFolder
|
|
withIntermediateDirectories:YES
|
|
attributes:nil
|
|
error:nil];
|
|
|
|
agentPlistPath = [[NSString alloc]
|
|
initWithString:[[agentsFolder stringByAppendingPathComponent:(NSString *)CFBridgingRelease(agentIdentifier)]
|
|
stringByAppendingPathExtension:@"plist"]];
|
|
|
|
// Allocate object for accessing IORegistry
|
|
mach_port_t masterPort;
|
|
kern_return_t result = IOMasterPort(bootstrap_port, &masterPort);
|
|
if (result != KERN_SUCCESS) {
|
|
NSLog(@"Error getting the IOMaster port: %s", mach_error_string(result));
|
|
exit(1);
|
|
}
|
|
|
|
_ioRegEntryRef = IORegistryEntryFromPath(masterPort, "IODeviceTree:/options");
|
|
if (_ioRegEntryRef == 0) {
|
|
NSLog(@"nvram is not supported on this system");
|
|
}
|
|
|
|
diskutilList = nil;
|
|
|
|
// Init NVRam variables fields
|
|
[self initNVRamVariableFields];
|
|
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)mainViewDidLoad {
|
|
BOOL plistExists;
|
|
|
|
// Initialize revision fields
|
|
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSLocalDomainMask, YES);
|
|
NSString *preferenceFolder = [[searchPaths objectAtIndex:0] stringByAppendingPathComponent:@"Preferences"];
|
|
NSString *cloverInstallerPlist = [[preferenceFolder stringByAppendingPathComponent:kCloverInstaller] stringByAppendingPathExtension:@"plist"];
|
|
plistExists = [[NSFileManager defaultManager] fileExistsAtPath:cloverInstallerPlist];
|
|
NSString* installedRevision = @"-";
|
|
if (plistExists) {
|
|
NSDictionary *dict = [[NSDictionary alloc]
|
|
initWithContentsOfFile:cloverInstallerPlist];
|
|
NSNumber* revision = [dict objectForKey:@"CloverRevision"];
|
|
if (revision)
|
|
installedRevision = [revision stringValue];
|
|
}
|
|
[lastInstalledRevision setStringValue:installedRevision];
|
|
|
|
NSString* bootedRevision = @"-";
|
|
io_registry_entry_t ioRegistryEFI = IORegistryEntryFromPath(kIOMasterPortDefault,
|
|
"IODeviceTree:/efi/platform");
|
|
if (ioRegistryEFI) {
|
|
CFStringRef nameRef = CFStringCreateWithCString(kCFAllocatorDefault,
|
|
"clovergui-revision",
|
|
kCFStringEncodingUTF8);
|
|
if (nameRef) {
|
|
CFTypeRef valueRef = IORegistryEntryCreateCFProperty(ioRegistryEFI, nameRef, 0, 0);
|
|
CFRelease(nameRef);
|
|
if (valueRef) {
|
|
// Get the OF variable's type.
|
|
CFTypeID typeID = CFGetTypeID(valueRef);
|
|
if (typeID == CFDataGetTypeID())
|
|
bootedRevision = [NSString stringWithFormat:@"%u",*((uint32_t*)CFDataGetBytePtr(valueRef))];
|
|
CFRelease(valueRef);
|
|
}
|
|
}
|
|
IOObjectRelease(ioRegistryEFI);
|
|
}
|
|
[lastBootedRevision setStringValue:bootedRevision];
|
|
|
|
// Initialize popUpCheckInterval
|
|
unsigned int checkInterval =
|
|
[self getUIntPreferenceKey:checkIntervalKey forAppID:agentIdentifier withDefault:0];
|
|
[popUpCheckInterval selectItemWithTag:checkInterval];
|
|
|
|
// Initialize LastRunDate
|
|
unsigned int lastCheckTimestamp =
|
|
[self getUIntPreferenceKey:lastCheckTimestampKey forAppID:agentIdentifier withDefault:0];
|
|
if (lastCheckTimestamp == 0) {
|
|
[LastRunDate setStringValue:@"-"];
|
|
} else {
|
|
NSDate *date = [NSDate dateWithTimeIntervalSince1970:lastCheckTimestamp];
|
|
[LastRunDate setStringValue:[LastRunDate.formatter stringFromDate:date]];
|
|
|
|
}
|
|
// Enable the checkNowButton if executable is present
|
|
[checkNowButton setEnabled:
|
|
[[NSFileManager defaultManager] fileExistsAtPath:(NSString*)CFBridgingRelease(agentExecutable)]];
|
|
|
|
// Get EFI Path
|
|
NSString* efiDir=[self getStringPreferenceKey:efiDirPathKey
|
|
forAppID:(CFStringRef)[self.bundle bundleIdentifier]
|
|
withDefault:CFSTR("/")];
|
|
[_EFIPathControl setURL:[NSURL fileURLWithPath:efiDir]];
|
|
[self initThemeTab:efiDir];
|
|
|
|
// Setup security.
|
|
AuthorizationItem items = {kAuthorizationRightExecute, 0, NULL, 0};
|
|
AuthorizationRights rights = {1, &items};
|
|
[authView setAuthorizationRights:&rights];
|
|
authView.delegate = self;
|
|
[authView updateStatus:nil];
|
|
|
|
plistExists = [[NSFileManager defaultManager] fileExistsAtPath:agentPlistPath];
|
|
if (plistExists && checkInterval == 0) {
|
|
[[NSFileManager defaultManager] removeItemAtPath:agentPlistPath error:nil];
|
|
}
|
|
}
|
|
|
|
|
|
- (void) initNVRamVariableFields {
|
|
NSString *value;
|
|
self.nvram = [NSMutableDictionary dictionaryWithCapacity:16]; // 16 entries for the moment
|
|
|
|
value = [self getCloverNVRam:kLogLineCount];
|
|
[self.nvram setValue:value forKey:kLogLineCount];
|
|
|
|
value = [self getCloverNVRam:kLogEveryBoot];
|
|
[self.nvram setValue:value
|
|
forKey:kLogEveryBoot];
|
|
|
|
if ([value length] == 0) {
|
|
self.cloverLogEveryBootEnabled = [NSNumber numberWithBool:NO];
|
|
self.cloverLogEveryBootLimit = [NSNumber numberWithInt:0];
|
|
} else {
|
|
self.cloverLogEveryBootEnabled = [value boolValue] ?
|
|
[NSNumber numberWithBool:YES] :
|
|
[NSNumber numberWithBool:NO];
|
|
self.cloverLogEveryBootLimit = [NSNumber numberWithInteger:[value integerValue]];
|
|
}
|
|
|
|
value = [self getCloverNVRam:kBackupDirOnDestVol];
|
|
[self.nvram setValue:value
|
|
forKey:kBackupDirOnDestVol];
|
|
|
|
value = [self getCloverNVRam:kKeepBackupLimit];
|
|
if ([value length] == 0)
|
|
value = @"0";
|
|
[self.nvram setValue:value
|
|
forKey:kKeepBackupLimit];
|
|
|
|
value = [self getCloverNVRam:kMountEFI];
|
|
[self.nvram setValue:value forKey:kMountEFI];
|
|
|
|
value = [self getCloverNVRam:kNVRamDisk];
|
|
[self.nvram setValue:value forKey:kNVRamDisk];
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark General Tab Methods
|
|
- (IBAction) configureAutomaticUpdates:(id)sender {
|
|
CFDictionaryRef launchInfo = SMJobCopyDictionary(kSMDomainUserLaunchd, agentIdentifier);
|
|
if (launchInfo != NULL) {
|
|
CFRelease(launchInfo);
|
|
CFErrorRef error = NULL;
|
|
if (!SMJobRemove(kSMDomainUserLaunchd, agentIdentifier, NULL, YES, &error))
|
|
NSLog(@"Error in SMJobRemove: %@", error);
|
|
if (error)
|
|
CFRelease(error);
|
|
}
|
|
|
|
NSInteger checkInterval = [sender tag];
|
|
[self setPreferenceKey:checkIntervalKey forAppID:agentIdentifier fromInt:(int)checkInterval];
|
|
|
|
|
|
if (checkInterval > 0 && [[NSFileManager defaultManager] fileExistsAtPath:(NSString*)CFBridgingRelease(agentExecutable)]) {
|
|
// Create a new plist
|
|
NSArray* call = [NSArray arrayWithObjects:
|
|
(NSString *)CFBridgingRelease(agentExecutable),
|
|
@"startup",
|
|
nil];
|
|
NSDictionary *plist = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
(NSString *)CFBridgingRelease(agentIdentifier), @"Label",
|
|
[NSNumber numberWithInteger:checkInterval], @"StartInterval",
|
|
[NSNumber numberWithBool:YES], @"RunAtLoad",
|
|
(NSString *)CFBridgingRelease(agentExecutable), @"Program",
|
|
call, @"ProgramArguments",
|
|
nil];
|
|
[plist writeToFile:agentPlistPath atomically:YES];
|
|
|
|
CFErrorRef error = NULL;
|
|
if (!SMJobSubmit(kSMDomainUserLaunchd, (__bridge CFDictionaryRef)(plist), NULL, &error)) {
|
|
if (error) {
|
|
NSLog(@"Error in SMJobSubmit: %@", error);
|
|
} else
|
|
NSLog(@"Error in SMJobSubmit without details. Check /var/db/launchd.db/com.apple.launchd.peruser.NNN/overrides.plist for %@ set to disabled.", agentIdentifier);
|
|
}
|
|
if (error)
|
|
CFRelease(error);
|
|
} else {
|
|
// Remove the plist
|
|
[[NSFileManager defaultManager] removeItemAtPath:agentPlistPath error:nil];
|
|
}
|
|
CFPreferencesAppSynchronize(agentIdentifier); // Force the preferences to be save to disk
|
|
}
|
|
|
|
- (IBAction)checkNow:(id)sender {
|
|
[[NSWorkspace sharedWorkspace] launchApplication:(NSString*)CFBridgingRelease(agentExecutable)];
|
|
}
|
|
|
|
|
|
- (BOOL)isUnlocked {
|
|
return [authView authorizationState] == SFAuthorizationViewUnlockedState;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Theme Tab Methods
|
|
|
|
- (void) initThemeTab:(NSString*) efiDir {
|
|
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
BOOL isDir = NO;
|
|
|
|
[_cloverThemeComboBox removeAllItems]; // Remove all theme names if exists
|
|
|
|
NSString *themesDir = [efiDir stringByAppendingPathComponent:@"CLOVER/Themes"];
|
|
[fileManager fileExistsAtPath:themesDir isDirectory:(&isDir)];
|
|
[_themeWarning setHidden:isDir];
|
|
|
|
// get the list of all files and directories
|
|
NSMutableArray *themes = [[NSMutableArray alloc] init];
|
|
|
|
NSArray *fileList = [fileManager contentsOfDirectoryAtPath:themesDir error:nil];
|
|
if (fileList) {
|
|
for(NSString *file in fileList) {
|
|
NSString *path = [[themesDir stringByAppendingPathComponent:file]
|
|
stringByAppendingPathComponent:@"theme.plist"];
|
|
if ([fileManager fileExistsAtPath:path]) {
|
|
[themes addObject:file];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Try to get installed themes
|
|
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSLocalDomainMask, YES);
|
|
NSString *preferenceFolder = [[searchPaths objectAtIndex:0] stringByAppendingPathComponent:@"Preferences"];
|
|
NSString *cloverInstallerPlist = [[preferenceFolder stringByAppendingPathComponent:kCloverInstaller] stringByAppendingPathExtension:@"plist"];
|
|
NSDictionary *dict = [[NSDictionary alloc]
|
|
initWithContentsOfFile:cloverInstallerPlist];
|
|
if (dict) {
|
|
NSArray *installedThemes = [dict objectForKey:@"InstalledThemes"];
|
|
[themes addObjectsFromArray:installedThemes];
|
|
}
|
|
else {
|
|
// Get default themes from bundle
|
|
NSString* defaultThemePlistPath = [self.bundle pathForResource:@"DefaultThemes.plist" ofType:@"plist"];
|
|
NSDictionary *dict = [[NSDictionary alloc]
|
|
initWithContentsOfFile:defaultThemePlistPath];
|
|
if (dict) {
|
|
NSArray *defaultThemes = [dict objectForKey:@"Default Themes"];
|
|
if (defaultThemes)
|
|
[themes addObjectsFromArray:defaultThemes];
|
|
}
|
|
}
|
|
}
|
|
|
|
NSString* currentTheme = [self getNVRamKey:@"Clover.Theme"];
|
|
if (currentTheme && [themes indexOfObject:currentTheme] == NSNotFound)
|
|
[themes addObject:currentTheme];
|
|
|
|
NSArray *sortedThemes = [themes sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
|
|
[_cloverThemeComboBox addItemsWithObjectValues:sortedThemes];
|
|
if (currentTheme) {
|
|
[_cloverThemeComboBox selectItemWithObjectValue:currentTheme];
|
|
[self updateThemeTab:currentTheme];
|
|
}
|
|
}
|
|
|
|
- (void) updateThemeTab:(NSString*) themeName {
|
|
NSString *efiDir = [[_EFIPathControl URL] path];
|
|
NSString *themeDir = [[efiDir stringByAppendingPathComponent:@"CLOVER/Themes"]
|
|
stringByAppendingPathComponent:themeName];
|
|
// Load the theme.plist file
|
|
NSString *themePlistPath = [themeDir stringByAppendingPathComponent:@"theme.plist"];
|
|
|
|
NSMutableDictionary *newThemeInfo = [NSMutableDictionary dictionaryWithContentsOfFile:themePlistPath];
|
|
if (newThemeInfo) {
|
|
self.themeInfo = newThemeInfo;
|
|
} else {
|
|
self.themeInfo = [NSMutableDictionary dictionaryWithCapacity:16]; // 16 entries for the moment
|
|
}
|
|
|
|
NSString *imagePath = [themeDir stringByAppendingPathComponent:@"screenshot.png"];
|
|
NSNumber *previewAvailable = [NSNumber numberWithBool:YES];
|
|
if (![[NSFileManager defaultManager] fileExistsAtPath:imagePath]) {
|
|
imagePath = [self.bundle pathForResource:@"NoPreview" ofType:@"png"];
|
|
previewAvailable = [NSNumber numberWithBool:NO];
|
|
}
|
|
[self.themeInfo setObject:previewAvailable forKey:@"PreviewAvailable"];
|
|
|
|
NSImage* preview = [[NSImage alloc] initWithContentsOfFile:imagePath];
|
|
[self.themeInfo setObject:preview forKey:@"Preview"];
|
|
|
|
if (![self.themeInfo objectForKey:@"Author"]) {
|
|
[self.themeInfo setObject:GetLocalizedString(@"Unknown",nil) forKey:@"Author"];
|
|
}
|
|
|
|
if (![self.themeInfo objectForKey:@"Year"]) {
|
|
[self.themeInfo setObject:GetLocalizedString(@"Unknown",nil) forKey:@"Year"];
|
|
}
|
|
|
|
if (![self.themeInfo objectForKey:@"Description"]) {
|
|
[self.themeInfo setObject:GetLocalizedString(@"No description available",nil) forKey:@"Description"];
|
|
}
|
|
|
|
// Update NVRam
|
|
NSString *oldValue = [self getNVRamKey:@"Clover.Theme"];
|
|
if ((oldValue.length != 0 || themeName.length != 0) &&
|
|
![oldValue isEqualToString:themeName]) {
|
|
if ([self setNVRamKey:@"Clover.Theme" Value:themeName] != 0) {
|
|
[_cloverThemeComboBox setStringValue:oldValue];
|
|
[self updateThemeTab:oldValue];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (IBAction)showPathOpenPanel:(id)sender {
|
|
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
|
[panel setAllowsMultipleSelection:NO];
|
|
[panel setCanChooseDirectories:YES];
|
|
[panel setCanChooseFiles:NO];
|
|
[panel setResolvesAliases:YES];
|
|
|
|
NSString *panelTitle = GetLocalizedString(@"Select the EFI directory", @"Title for the open panel");
|
|
[panel setTitle:panelTitle];
|
|
|
|
NSString *promptString = GetLocalizedString(@"Set EFI directory", @"Prompt for the open panel prompt");
|
|
[panel setPrompt:promptString];
|
|
|
|
[panel beginSheetModalForWindow:[sender window] completionHandler:^(NSInteger result) {
|
|
|
|
// Hide the open panel.
|
|
[panel orderOut:self];
|
|
|
|
// If the return code wasn't OK, don't do anything.
|
|
if (result != NSOKButton) {
|
|
return;
|
|
}
|
|
// Get the first URL returned from the Open Panel and set it at the first path component of the control.
|
|
NSURL *url = [[panel URLs] objectAtIndex:0];
|
|
[self->_EFIPathControl setURL:url];
|
|
|
|
NSString *efiDir = [url path];
|
|
[self setPreferenceKey:efiDirPathKey
|
|
forAppID:(CFStringRef)[self.bundle bundleIdentifier]
|
|
fromString:(CFStringRef)efiDir];
|
|
CFPreferencesAppSynchronize((CFStringRef)[self.bundle bundleIdentifier]); // Force the preferences to be save to disk
|
|
|
|
[self initThemeTab:efiDir];
|
|
}];
|
|
}
|
|
|
|
- (IBAction)themeComboBox:(NSComboBox*)sender {
|
|
NSString *themeName = [sender stringValue];
|
|
[self updateThemeTab:themeName];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Authorization delegates
|
|
|
|
//
|
|
// SFAuthorization delegates
|
|
//
|
|
- (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view {
|
|
}
|
|
|
|
- (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view {
|
|
}
|
|
|
|
|
|
//
|
|
// NVRAM methods
|
|
//
|
|
#pragma mark -
|
|
#pragma mark NVRam methods
|
|
|
|
// Get Clover NVRAM variable
|
|
-(NSString*) getCloverNVRam:(const NSString*)key {
|
|
return [self getNVRamKey:[NSString stringWithFormat:@"Clover.%@",key]];
|
|
}
|
|
|
|
// Get NVRAM value
|
|
-(NSString*) getNVRamKey:(const NSString*)key {
|
|
NSString* result = @"";
|
|
|
|
CFTypeRef valueRef = IORegistryEntryCreateCFProperty(_ioRegEntryRef, (__bridge CFStringRef)(key), 0, 0);
|
|
if (valueRef == 0) {
|
|
return result;
|
|
}
|
|
|
|
// Get the OF variable's type.
|
|
CFTypeID typeID = CFGetTypeID(valueRef);
|
|
|
|
if (typeID == CFDataGetTypeID()) {
|
|
result = [NSString stringWithUTF8String:(const char*)CFDataGetBytePtr(valueRef)];
|
|
}
|
|
|
|
CFRelease(valueRef);
|
|
|
|
return result;
|
|
}
|
|
|
|
// Set NVRAM key/value pair
|
|
- (OSErr) setNVRamKey:(const NSString*)key Value:(NSString*)value {
|
|
|
|
OSErr processError = 0;
|
|
|
|
if (key) {
|
|
if (!value) {
|
|
value=@"";
|
|
}
|
|
NSString *oldValue = [self getNVRamKey:key];
|
|
if ((oldValue.length != 0 || value.length != 0) &&
|
|
![oldValue isEqualToString:value]) {
|
|
|
|
// Size for key=value + null terminal char
|
|
size_t len = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1
|
|
+ [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
|
char* nvram_arg=(char*) malloc(sizeof(char) * len);
|
|
snprintf(nvram_arg, len, "%s=%s", [key UTF8String], [value UTF8String]);
|
|
|
|
// Need 2 parameters: key=value and NULL
|
|
const char **argv = (const char **)malloc(sizeof(char *) * 2);
|
|
argv[0] = nvram_arg;
|
|
argv[1] = NULL;
|
|
|
|
processError = AuthorizationExecuteWithPrivileges([[authView authorization] authorizationRef],
|
|
[@"/usr/sbin/nvram" UTF8String],
|
|
kAuthorizationFlagDefaults,
|
|
(char *const *)argv,
|
|
nil);
|
|
|
|
if (processError != errAuthorizationSuccess)
|
|
NSLog(@"Error trying to set nvram %s:%d", nvram_arg, processError);
|
|
|
|
free(argv);
|
|
free(nvram_arg);
|
|
}
|
|
}
|
|
return processError;
|
|
}
|
|
|
|
//
|
|
// Preferences methods
|
|
//
|
|
#pragma mark -
|
|
#pragma mark Preferences methods
|
|
|
|
// get and set preference keys functions idea taken from:
|
|
// http://svn.perian.org/branches/perian-1.1/CPFPerianPrefPaneController.m
|
|
- (unsigned int)getUIntPreferenceKey:(CFStringRef)key
|
|
forAppID:(CFStringRef)appID
|
|
withDefault:(unsigned int)defaultValue {
|
|
CFPropertyListRef value;
|
|
unsigned int ret = defaultValue;
|
|
|
|
value = CFPreferencesCopyAppValue(key, appID);
|
|
if (value && CFGetTypeID(value) == CFNumberGetTypeID())
|
|
CFNumberGetValue(value, kCFNumberIntType, &ret);
|
|
|
|
if (value) {
|
|
CFRelease(value);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
- (void)setPreferenceKey:(CFStringRef)key
|
|
forAppID:(CFStringRef)appID
|
|
fromInt:(int)value {
|
|
CFNumberRef numRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
|
|
CFPreferencesSetAppValue(key, numRef, appID);
|
|
CFRelease(numRef);
|
|
}
|
|
|
|
- (NSString *)getStringPreferenceKey:(CFStringRef)key
|
|
forAppID:(CFStringRef)appID
|
|
withDefault:(CFStringRef)defaultValue {
|
|
CFPropertyListRef value;
|
|
NSString *ret = nil;
|
|
|
|
value = CFPreferencesCopyAppValue(key, appID);
|
|
if(value && CFGetTypeID(value) == CFStringGetTypeID())
|
|
ret = [NSString stringWithString:(__bridge NSString * _Nonnull)(value)];
|
|
else
|
|
ret = [NSString stringWithString:(__bridge NSString * _Nonnull)(defaultValue)];
|
|
|
|
if(value) {
|
|
CFRelease(value);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
- (void)setPreferenceKey:(CFStringRef)key
|
|
forAppID:(CFStringRef)appID
|
|
fromString:(CFStringRef)value {
|
|
CFPreferencesSetAppValue(key, value, appID);
|
|
}
|
|
@end
|