diff --git a/External/Pearl b/External/Pearl
index e50cad14..a3f33c5f 160000
--- a/External/Pearl
+++ b/External/Pearl
@@ -1 +1 @@
-Subproject commit e50cad1411aaafe8083b3863e48b53a1f722c239
+Subproject commit a3f33c5f0cbafd6910ae55fb313764c18b48ad69
diff --git a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m
index e7a194f0..4b2ed8a3 100644
--- a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m
+++ b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m
@@ -26,7 +26,7 @@
@property(nonatomic, strong) NSWindowController *initialWindow;
@end
-@implementation MPMacAppDelegate { NSWindow *_window; }
+@implementation MPMacAppDelegate
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfour-char-constants"
@@ -66,71 +66,157 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
return eventNotHandledErr;
}
-- (void)updateUsers {
+#pragma mark - Life
- [[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- if (idx > 2)
- [[self.usersItem submenu] removeItem:obj];
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+
+ // Setup delegates and listeners.
+ [MPConfig get].delegate = self;
+ __weak id weakSelf = self;
+ [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
+ dispatch_async( dispatch_get_main_queue(), ^{
+ [weakSelf updateMenuItems];
+ } );
+ } forKeyPath:@"key" options:0 context:nil];
+ [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
+ dispatch_async( dispatch_get_main_queue(), ^{
+ [weakSelf updateMenuItems];
+ } );
+ } forKeyPath:@"activeUser" options:0 context:nil];
+ [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
+ dispatch_async( dispatch_get_main_queue(), ^{
+ [weakSelf updateMenuItems];
+ } );
+ } forKeyPath:@"storeManager.cloudAvailable" options:0 context:nil];
+
+ // Status item.
+ self.statusView = [[RHStatusItemView alloc] initWithStatusBarItem:
+ [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]];
+ self.statusView.image = [NSImage imageNamed:@"menu-icon"];
+ self.statusView.menu = self.statusMenu;
+ self.statusView.target = self;
+ self.statusView.action = @selector( showMenu );
+
+ [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil
+ queue:[NSOperationQueue mainQueue] usingBlock:
+ ^(NSNotification *note) {
+ [self updateUsers];
}];
+ [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidImportChangesNotification object:nil
+ queue:[NSOperationQueue mainQueue] usingBlock:
+ ^(NSNotification *note) {
+ [self updateUsers];
+ }];
+ [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil
+ queue:[NSOperationQueue mainQueue] usingBlock:
+ ^(NSNotification *note) {
+ NSString *key = note.object;
+ if (!key || [key isEqualToString:NSStringFromSelector( @selector( rememberLogin ) )])
+ self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
+ if (!key || [key isEqualToString:NSStringFromSelector( @selector( dialogStyleHUD ) )]) {
+ self.dialogStyleRegular.state = ![[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState;
+ self.dialogStyleHUD.state = [[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState;
+ if (![self.passwordWindow.window isVisible])
+ self.passwordWindow = nil;
+ else {
+ [self.passwordWindow close];
+ self.passwordWindow = nil;
+ [self showPasswordWindow:nil];
+ }
+ }
+ }];
+ [self updateUsers];
+
+ // Global hotkey.
+ EventHotKeyRef hotKeyRef;
+ EventTypeSpec hotKeyEvents[1] = { { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed } };
+ OSStatus status = InstallApplicationEventHandler( NewEventHandlerUPP( MPHotKeyHander ), GetEventTypeCount( hotKeyEvents ),
+ hotKeyEvents, (__bridge void *)self, NULL );
+ if (status != noErr)
+ err( @"Error installing application event handler: %i", (int)status );
+ status = RegisterEventHotKey( 35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
+ if (status != noErr)
+ err( @"Error registering 'show' hotkey: %i", (int)status );
+ status = RegisterEventHotKey( 35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
+ if (status != noErr)
+ err( @"Error registering 'lock' hotkey: %i", (int)status );
+
+ // Initial display.
+ [NSApp activateIgnoringOtherApps:YES];
+ if ([[MPMacConfig get].firstRun boolValue]) {
+ self.initialWindow = [[NSWindowController alloc] initWithWindowNibName:@"MPInitialWindow" owner:self];
+ [self.initialWindow.window setLevel:NSFloatingWindowLevel];
+ [self.initialWindow showWindow:self];
+ }
+}
+
+- (void)applicationWillResignActive:(NSNotification *)notification {
+
+ if (![[MPConfig get].rememberLogin boolValue])
+ [self lock:nil];
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
+ // Save changes in the application's managed object context before the application terminates.
NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
- if (!context) {
- self.createUserItem.title = @"New User (Not ready)";
- self.createUserItem.enabled = NO;
- self.createUserItem.toolTip = @"Please wait until the app is fully loaded.";
- self.deleteUserItem.title = @"Delete User (Not ready)";
- self.deleteUserItem.enabled = NO;
- self.deleteUserItem.toolTip = @"Please wait until the app is fully loaded.";
- [self.usersItem.submenu addItemWithTitle:@"Loading..." action:NULL keyEquivalent:@""].enabled = NO;
+ if (!context)
+ return NSTerminateNow;
- return;
- }
+ if (![context commitEditing])
+ return NSTerminateCancel;
- MPUserEntity *activeUser = [self activeUserInContext:context];
+ if (![context hasChanges])
+ return NSTerminateNow;
- self.createUserItem.title = @"New User";
- self.createUserItem.enabled = YES;
- self.createUserItem.toolTip = nil;
-
- self.deleteUserItem.title = activeUser? @"Delete User": @"Delete User (None Selected)";
- self.deleteUserItem.enabled = activeUser != nil;
- self.deleteUserItem.toolTip = activeUser? nil: @"First select the user to delete.";
-
- NSError *error = nil;
- NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
- fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
- NSArray *users = [context executeFetchRequest:fetchRequest error:&error];
- if (!users)
- err( @"Failed to load users: %@", error );
-
- if (![users count]) {
- NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
- noUsersItem.enabled = NO;
- noUsersItem.toolTip = @"Use the iOS app to create users and make sure iCloud is enabled in its preferences as well. "
- @"Then give iCloud some time to sync the new user to your Mac.";
- }
-
- self.usersItem.state = NSMixedState;
- for (MPUserEntity *user in users) {
- NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""];
- [userItem setTarget:self];
- [userItem setRepresentedObject:[user objectID]];
- [[self.usersItem submenu] addItem:userItem];
-
- if (!activeUser && [user.name isEqualToString:[MPMacConfig get].usedUserName])
- [super setActiveUser:activeUser = user];
-
- if ([activeUser isEqual:user]) {
- userItem.state = NSOnState;
- self.usersItem.state = NSOffState;
- }
- else
- userItem.state = NSOffState;
- }
-
- [self updateMenuItems];
+ [context saveToStore];
+ return NSTerminateNow;
}
+#pragma mark - State
+
+- (void)setActiveUser:(MPUserEntity *)activeUser {
+
+ [super setActiveUser:activeUser];
+
+ [MPMacConfig get].usedUserName = activeUser.name;
+
+ PearlMainQueue( ^{
+ [self updateUsers];
+ } );
+}
+
+- (void)setLoginItemEnabled:(BOOL)enabled {
+
+ BOOL loginItemEnabled = [self loginItemEnabled];
+ if (loginItemEnabled != enabled) {
+ if (SMLoginItemSetEnabled( (__bridge CFStringRef)LOGIN_HELPER_BUNDLE_ID, (Boolean)enabled ) == true)
+ loginItemEnabled = enabled;
+ else
+ wrn( @"Failed to set login item." );
+ }
+
+ self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
+ self.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
+}
+
+- (BOOL)loginItemEnabled {
+
+ // The easy and sane method (SMJobCopyDictionary) can pose problems when the app is sandboxed. -_-
+ NSArray *jobs = (__bridge_transfer NSArray *)SMCopyAllJobDictionaries( kSMDomainUserLaunchd );
+
+ for (NSDictionary *job in jobs)
+ if ([LOGIN_HELPER_BUNDLE_ID isEqualToString:[job objectForKey:@"Label"]]) {
+ dbg( @"loginItemEnabled: %@", @([[job objectForKey:@"OnDemand"] boolValue]) );
+ return [[job objectForKey:@"OnDemand"] boolValue];
+ }
+
+ dbg( @"loginItemEnabled: not found" );
+ return NO;
+}
+
+#pragma mark - Actions
+
- (void)selectUser:(NSMenuItem *)item {
[self signOutAnimated:NO];
@@ -143,13 +229,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
err( @"While looking up selected user: %@", error );
}
-- (void)showMenu {
-
- [self updateMenuItems];
-
- [self.statusView popUpMenu];
-}
-
- (IBAction)togglePreference:(id)sender {
if (sender == self.enableCloudButton) {
@@ -278,104 +357,99 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
self.initialWindow = nil;
}
-- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
+- (IBAction)showPasswordWindow:(id)sender {
- [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey )];
-}
-
-#pragma mark - NSApplicationDelegate
-
-- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
-
- // Setup delegates and listeners.
- [MPConfig get].delegate = self;
- __weak id weakSelf = self;
- [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
- dispatch_async( dispatch_get_main_queue(), ^{
- [weakSelf updateMenuItems];
- } );
- } forKeyPath:@"key" options:0 context:nil];
- [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
- dispatch_async( dispatch_get_main_queue(), ^{
- [weakSelf updateMenuItems];
- } );
- } forKeyPath:@"activeUser" options:0 context:nil];
- [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
- dispatch_async( dispatch_get_main_queue(), ^{
- [weakSelf updateMenuItems];
- } );
- } forKeyPath:@"storeManager.cloudAvailable" options:0 context:nil];
-
- // Status item.
- self.statusView = [[RHStatusItemView alloc] initWithStatusBarItem:
- [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]];
- self.statusView.image = [NSImage imageNamed:@"menu-icon"];
- self.statusView.menu = self.statusMenu;
- self.statusView.target = self;
- self.statusView.action = @selector( showMenu );
-
- [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil
- queue:[NSOperationQueue mainQueue] usingBlock:
- ^(NSNotification *note) {
- [self updateUsers];
- }];
- [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidImportChangesNotification object:nil
- queue:[NSOperationQueue mainQueue] usingBlock:
- ^(NSNotification *note) {
- [self updateUsers];
- }];
- [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil
- queue:[NSOperationQueue mainQueue] usingBlock:
- ^(NSNotification *note) {
- NSString *key = note.object;
- if (!key || [key isEqualToString:NSStringFromSelector( @selector( rememberLogin ) )])
- self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
- if (!key || [key isEqualToString:NSStringFromSelector( @selector( dialogStyleHUD ) )]) {
- self.dialogStyleRegular.state = ![[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState;
- self.dialogStyleHUD.state = [[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState;
- if (![self.passwordWindow.window isVisible])
- self.passwordWindow = nil;
- else {
- [self.passwordWindow close];
- self.passwordWindow = nil;
- [self showPasswordWindow:nil];
- }
- }
- }];
- [self updateUsers];
-
- // Global hotkey.
- EventHotKeyRef hotKeyRef;
- EventTypeSpec hotKeyEvents[1] = { { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed } };
- OSStatus status = InstallApplicationEventHandler( NewEventHandlerUPP( MPHotKeyHander ), GetEventTypeCount( hotKeyEvents ),
- hotKeyEvents, (__bridge void *)self, NULL );
- if (status != noErr)
- err( @"Error installing application event handler: %i", (int)status );
- status = RegisterEventHotKey( 35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
- if (status != noErr)
- err( @"Error registering 'show' hotkey: %i", (int)status );
- status = RegisterEventHotKey( 35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
- if (status != noErr)
- err( @"Error registering 'lock' hotkey: %i", (int)status );
-
- // Initial display.
[NSApp activateIgnoringOtherApps:YES];
- if ([[MPMacConfig get].firstRun boolValue]) {
- self.initialWindow = [[NSWindowController alloc] initWithWindowNibName:@"MPInitialWindow" owner:self];
- [self.initialWindow.window setLevel:NSFloatingWindowLevel];
- [self.initialWindow showWindow:self];
+
+ // If no user, can't activate.
+ if (![self activeUserForMainThread]) {
+ [[NSAlert alertWithMessageText:@"No User Selected" defaultButton:[PearlStrings get].commonButtonOkay alternateButton:nil
+ otherButton:nil informativeTextWithFormat:
+ @"Begin by selecting or creating your user from the status menu (●●●|) next to the clock."]
+ runModal];
+ [self.statusView popUpMenu];
+ return;
}
+
+ // Don't show window if we weren't already running (ie. if we haven't been activated before).
+ if (!self.passwordWindow)
+ self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
+
+ [self.passwordWindow showWindow:self];
}
-- (void)setActiveUser:(MPUserEntity *)activeUser {
+#pragma mark - Private
- [super setActiveUser:activeUser];
+- (void)updateUsers {
- [MPMacConfig get].usedUserName = activeUser.name;
+ [[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ if (idx > 2)
+ [[self.usersItem submenu] removeItem:obj];
+ }];
- PearlMainQueue( ^{
- [self updateUsers];
- } );
+ NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
+ if (!context) {
+ self.createUserItem.title = @"New User (Not ready)";
+ self.createUserItem.enabled = NO;
+ self.createUserItem.toolTip = @"Please wait until the app is fully loaded.";
+ self.deleteUserItem.title = @"Delete User (Not ready)";
+ self.deleteUserItem.enabled = NO;
+ self.deleteUserItem.toolTip = @"Please wait until the app is fully loaded.";
+ [self.usersItem.submenu addItemWithTitle:@"Loading..." action:NULL keyEquivalent:@""].enabled = NO;
+
+ return;
+ }
+
+ MPUserEntity *activeUser = [self activeUserInContext:context];
+
+ self.createUserItem.title = @"New User";
+ self.createUserItem.enabled = YES;
+ self.createUserItem.toolTip = nil;
+
+ self.deleteUserItem.title = activeUser? @"Delete User": @"Delete User (None Selected)";
+ self.deleteUserItem.enabled = activeUser != nil;
+ self.deleteUserItem.toolTip = activeUser? nil: @"First select the user to delete.";
+
+ NSError *error = nil;
+ NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
+ fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
+ NSArray *users = [context executeFetchRequest:fetchRequest error:&error];
+ if (!users)
+ err( @"Failed to load users: %@", error );
+
+ if (![users count]) {
+ NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
+ noUsersItem.enabled = NO;
+ noUsersItem.toolTip = @"Use the iOS app to create users and make sure iCloud is enabled in its preferences as well. "
+ @"Then give iCloud some time to sync the new user to your Mac.";
+ }
+
+ self.usersItem.state = NSMixedState;
+ for (MPUserEntity *user in users) {
+ NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""];
+ [userItem setTarget:self];
+ [userItem setRepresentedObject:[user objectID]];
+ [[self.usersItem submenu] addItem:userItem];
+
+ if (!activeUser && [user.name isEqualToString:[MPMacConfig get].usedUserName])
+ [super setActiveUser:activeUser = user];
+
+ if ([activeUser isEqual:user]) {
+ userItem.state = NSOnState;
+ self.usersItem.state = NSOffState;
+ }
+ else
+ userItem.state = NSOffState;
+ }
+
+ [self updateMenuItems];
+}
+
+- (void)showMenu {
+
+ [self updateMenuItems];
+
+ [self.statusView popUpMenu];
}
- (void)updateMenuItems {
@@ -440,77 +514,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
}
}
-- (IBAction)showPasswordWindow:(id)sender {
+#pragma mark - PearlConfigDelegate
- [NSApp activateIgnoringOtherApps:YES];
+- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
- // If no user, can't activate.
- if (![self activeUserForMainThread]) {
- [[NSAlert alertWithMessageText:@"No User Selected" defaultButton:[PearlStrings get].commonButtonOkay alternateButton:nil
- otherButton:nil informativeTextWithFormat:
- @"Begin by selecting or creating your user from the status menu (●●●|) next to the clock."]
- runModal];
- [self.statusView popUpMenu];
- return;
- }
-
- // Don't show window if we weren't already running (ie. if we haven't been activated before).
- if (!self.passwordWindow)
- self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
-
- [self.passwordWindow showWindow:self];
-}
-
-- (void)setLoginItemEnabled:(BOOL)enabled {
-
- BOOL loginItemEnabled = [self loginItemEnabled];
- if (loginItemEnabled != enabled) {
- if (SMLoginItemSetEnabled( (__bridge CFStringRef)LOGIN_HELPER_BUNDLE_ID, (Boolean)enabled ) == true)
- loginItemEnabled = enabled;
- else
- wrn( @"Failed to set login item." );
- }
-
- self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
- self.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
-}
-
-- (BOOL)loginItemEnabled {
-
- // The easy and sane method (SMJobCopyDictionary) can pose problems when the app is sandboxed. -_-
- NSArray *jobs = (__bridge_transfer NSArray *)SMCopyAllJobDictionaries( kSMDomainUserLaunchd );
-
- for (NSDictionary *job in jobs)
- if ([LOGIN_HELPER_BUNDLE_ID isEqualToString:[job objectForKey:@"Label"]]) {
- dbg( @"loginItemEnabled: %@", @([[job objectForKey:@"OnDemand"] boolValue]) );
- return [[job objectForKey:@"OnDemand"] boolValue];
- }
-
- dbg( @"loginItemEnabled: not found" );
- return NO;
-}
-
-- (void)applicationWillResignActive:(NSNotification *)notification {
-
- if (![[MPConfig get].rememberLogin boolValue])
- [self lock:nil];
-}
-
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
- // Save changes in the application's managed object context before the application terminates.
-
- NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
- if (!context)
- return NSTerminateNow;
-
- if (![context commitEditing])
- return NSTerminateCancel;
-
- if (![context hasChanges])
- return NSTerminateNow;
-
- [context saveToStore];
- return NSTerminateNow;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey )];
}
@end
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
index a1b69ef5..2a1d9724 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
@@ -22,6 +22,7 @@
#import "MPAppDelegate_Store.h"
#import "MPElementModel.h"
#import "MPAppDelegate_Key.h"
+#import "PearlProfiler.h"
#define MPAlertIncorrectMP @"MPAlertIncorrectMP"
#define MPAlertCreateSite @"MPAlertCreateSite"
@@ -357,12 +358,15 @@
if ([self.window isOnActiveSpace] && self.window.alphaValue)
return;
+ PearlProfiler *profiler = [PearlProfiler new];
CGWindowID windowID = (CGWindowID)[self.window windowNumber];
CGImageRef capturedImage = CGWindowListCreateImage( CGRectInfinite, kCGWindowListOptionOnScreenBelowWindow, windowID,
kCGWindowImageBoundsIgnoreFraming );
+ [profiler finishJob:@"captured window: %d, on screen: %@", windowID, self.window.screen];
NSImage *screenImage = [[NSImage alloc] initWithCGImage:capturedImage size:NSMakeSize(
CGImageGetWidth( capturedImage ) / self.window.backingScaleFactor,
CGImageGetHeight( capturedImage ) / self.window.backingScaleFactor )];
+ [profiler finishJob:@"image size: %@, bytes: %ld", NSStringFromSize( screenImage.size ), screenImage.TIFFRepresentation.length ];
NSImage *smallImage = [[NSImage alloc] initWithSize:NSMakeSize(
CGImageGetWidth( capturedImage ) / 20,
@@ -373,12 +377,16 @@
operation:NSCompositeSourceOver
fraction:1.0];
[smallImage unlockFocus];
+ [profiler finishJob:@"small image size: %@, bytes: %ld", NSStringFromSize( screenImage.size ), screenImage.TIFFRepresentation.length];
self.blurView.image = smallImage;
+ [profiler finishJob:@"assigned image"];
[self.window setFrame:self.window.screen.frame display:YES];
+ [profiler finishJob:@"assigned frame"];
[[NSAnimationContext currentContext] setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
[[self.window animator] setAlphaValue:1.0];
+ [profiler finishJob:@"animating window"];
}
- (void)fadeOut {
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
index 4ca07c0c..5fb88038 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
@@ -42,25 +42,15 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
+
@@ -113,9 +103,9 @@
-
+
-
+
@@ -170,9 +160,9 @@
-
+
-
+
@@ -186,9 +176,9 @@
-
+
-
+
@@ -328,6 +318,19 @@
+
+
+
+YnBsaXN0MDDUAQIDBAUGICFYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKcHCA8Q
+CRUbVSRudWxs0wkKCwwNDlR0eXBlViRjbGFzc18QEl9fQ0FDb2RpbmdDb250ZW50c4ACgAaAA1RmYWRl
+0hEKEhRaTlMub2JqZWN0c6ETgASABdIWFxgZWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNBcnJheaIYGlhO
+U09iamVjdNIWFxwdXENBVHJhbnNpdGlvbqMeHxpcQ0FUcmFuc2l0aW9uW0NBQW5pbWF0aW9uXxAPTlNL
+ZXllZEFyY2hpdmVy0SIjVHJvb3SAAQAIABEAGgAjAC0AMgA3AD8ARQBMAFEAWABtAG8AcQBzAHgAfQCI
+AIoAjACOAJMAngCnAK8AsgC7AMAAzQDRAN4A6gD8AP8BBAAAAAAAAAIBAAAAAAAAACQAAAAAAAAAAAAA
+AAAAAAEGA
+
+
+
diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
index 02b6b7a5..4c4a46d8 100644
--- a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
+++ b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
@@ -10,9 +10,11 @@
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; };
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
+ 93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */; };
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */; };
93D39C7C2BE7C0E0763B0177 /* MPElementCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */; };
+ 93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */; };
DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */; };
@@ -309,12 +311,14 @@
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = ""; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = ""; };
93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementCollectionView.m; sourceTree = ""; };
+ 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PearlProfiler.h; path = ../../../External/Pearl/Pearl/PearlProfiler.h; sourceTree = ""; };
93D3960D320FF8A072B092E3 /* MPElementCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementCollectionView.h; sourceTree = ""; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = ""; };
93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = ""; };
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = ""; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = ""; };
93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = ""; };
+ 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PearlProfiler.m; path = ../../../External/Pearl/Pearl/PearlProfiler.m; sourceTree = ""; };
93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementModel.m; sourceTree = ""; };
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = ""; };
DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = ""; };
@@ -702,6 +706,8 @@
DACA22121705DDC5002C6C22 /* External */,
DA5BFA47147E415C00F98B1E /* Frameworks */,
DA5BFA45147E415C00F98B1E /* Products */,
+ 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */,
+ 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */,
);
sourceTree = "";
};
@@ -1343,6 +1349,7 @@
DAEB93E718AB0FFD000490CC /* crypto.h in Headers */,
DAEB941318AB0FFD000490CC /* ssl2.h in Headers */,
DAEB940D18AB0FFD000490CC /* ripemd.h in Headers */,
+ 93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1715,6 +1722,7 @@
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */,
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */,
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */,
+ 93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};