diff options
Diffstat (limited to 'src/gui/osutils/macutils/AppKitImpl.mm')
-rw-r--r-- | src/gui/osutils/macutils/AppKitImpl.mm | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/src/gui/osutils/macutils/AppKitImpl.mm b/src/gui/osutils/macutils/AppKitImpl.mm new file mode 100644 index 000000000..077dd71a6 --- /dev/null +++ b/src/gui/osutils/macutils/AppKitImpl.mm @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2016 Lennart Glauer <mail@lennart-glauer.de> + * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#import "AppKitImpl.h" + +#import <AppKit/NSWorkspace.h> +#import <CoreVideo/CVPixelBuffer.h> + +@implementation AppKitImpl + +- (id) initWithObject:(AppKit*)appkit +{ + self = [super init]; + if (self) { + m_appkit = appkit; + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast<id>(self) + selector:@selector(didDeactivateApplicationObserver:) + name:NSWorkspaceDidDeactivateApplicationNotification + object:nil]; + + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast<id>(self) + selector:@selector(userSwitchHandler:) + name:NSWorkspaceSessionDidResignActiveNotification + object:nil]; + } + return self; +} + +// +// Update last active application property +// +- (void) didDeactivateApplicationObserver:(NSNotification*) notification +{ + NSDictionary* userInfo = notification.userInfo; + NSRunningApplication* app = userInfo[NSWorkspaceApplicationKey]; + + if (app.processIdentifier != [self ownProcessId]) { + self.lastActiveApplication = app; + } +} + +// +// Get process id of frontmost application (-> keyboard input) +// +- (pid_t) activeProcessId +{ + return [NSWorkspace sharedWorkspace].frontmostApplication.processIdentifier; +} + +// +// Get process id of own process +// +- (pid_t) ownProcessId +{ + return [NSProcessInfo processInfo].processIdentifier; +} + +// +// Activate application by process id +// +- (bool) activateProcess:(pid_t) pid +{ + NSRunningApplication* app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid]; + return [app activateWithOptions:NSApplicationActivateIgnoringOtherApps]; +} + +// +// Hide application by process id +// +- (bool) hideProcess:(pid_t) pid +{ + NSRunningApplication* app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid]; + return [app hide]; +} + +// +// Get application hidden state by process id +// +- (bool) isHidden:(pid_t) pid +{ + NSRunningApplication* app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid]; + return [app isHidden]; +} + +// +// Get state of macOS Dark Mode color scheme +// +- (bool) isDarkMode +{ + NSDictionary* dict = [[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain]; + id style = [dict objectForKey:@"AppleInterfaceStyle"]; + return ( style && [style isKindOfClass:[NSString class]] + && NSOrderedSame == [style caseInsensitiveCompare:@"dark"] ); +} + +// +// Notification for user switch +// +- (void) userSwitchHandler:(NSNotification*) notification +{ + if ([[notification name] isEqualToString:NSWorkspaceSessionDidResignActiveNotification] && m_appkit) + { + emit m_appkit->lockDatabases(); + } +} + +// +// Check if accessibility is enabled, may show an popup asking for permissions +// +- (bool) enableAccessibility +{ + // Request accessibility permissions for Auto-Type type on behalf of the user + NSDictionary* opts = @{static_cast<id>(kAXTrustedCheckOptionPrompt): @YES}; + return AXIsProcessTrustedWithOptions(static_cast<CFDictionaryRef>(opts)); +} + +// +// Check if screen recording is enabled, may show an popup asking for permissions +// +- (bool) enableScreenRecording +{ + if (@available(macOS 10.15, *)) { + // Request screen recording permission on macOS 10.15+ + // This is necessary to get the current window title + CGDisplayStreamRef stream = CGDisplayStreamCreate(CGMainDisplayID(), 1, 1, kCVPixelFormatType_32BGRA, nil, + ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, + IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) { + Q_UNUSED(status); + Q_UNUSED(displayTime); + Q_UNUSED(frameSurface); + Q_UNUSED(updateRef); + }); + if (stream) { + CFRelease(stream); + } else { + return NO; + } + } + return YES; +} + +- (void) toggleForegroundApp:(bool) foreground +{ + ProcessSerialNumber psn = {0, kCurrentProcess}; + if (foreground) { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + } else { + TransformProcessType(&psn, kProcessTransformToUIElementApplication); + } +} + +@end + +// +// ------------------------- C++ Trampolines ------------------------- +// + +AppKit::AppKit(QObject* parent) : QObject(parent) +{ + self = [[AppKitImpl alloc] initWithObject:this]; +} + +AppKit::~AppKit() +{ + [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:static_cast<id>(self)]; + [static_cast<id>(self) dealloc]; +} + +pid_t AppKit::lastActiveProcessId() +{ + return [static_cast<id>(self) lastActiveApplication].processIdentifier; +} + +pid_t AppKit::activeProcessId() +{ + return [static_cast<id>(self) activeProcessId]; +} + +pid_t AppKit::ownProcessId() +{ + return [static_cast<id>(self) ownProcessId]; +} + +bool AppKit::activateProcess(pid_t pid) +{ + return [static_cast<id>(self) activateProcess:pid]; +} + +bool AppKit::hideProcess(pid_t pid) +{ + return [static_cast<id>(self) hideProcess:pid]; +} + +bool AppKit::isHidden(pid_t pid) +{ + return [static_cast<id>(self) isHidden:pid]; +} + +bool AppKit::isDarkMode() +{ + return [static_cast<id>(self) isDarkMode]; +} + +bool AppKit::enableAccessibility() +{ + return [static_cast<id>(self) enableAccessibility]; +} + +bool AppKit::enableScreenRecording() +{ + return [static_cast<id>(self) enableScreenRecording]; +} + +void AppKit::toggleForegroundApp(bool foreground) +{ + [static_cast<id>(self) toggleForegroundApp:foreground]; +} |