From 14cf105c38883750e24e2094e46462555a61fab7 Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 11:43:21 +0200 Subject: [PATCH 01/29] Add Legacy Login Item methods These methods use the LSSharedFileList APIs --- Postgres.xcodeproj/project.pbxproj | 10 +++++-- Postgres/LegacyLoginItemRegistration.h | 13 +++++++++ Postgres/LegacyLoginItemRegistration.m | 37 ++++++++++++++++++++++++++ Postgres/Postgres-Bridging-Header.h | 1 + 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 Postgres/LegacyLoginItemRegistration.h create mode 100644 Postgres/LegacyLoginItemRegistration.m diff --git a/Postgres.xcodeproj/project.pbxproj b/Postgres.xcodeproj/project.pbxproj index b5a73dbb0..928e09816 100644 --- a/Postgres.xcodeproj/project.pbxproj +++ b/Postgres.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 360E961C1DC20D5E005D8C69 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */; }; 360E961D1DC20EF2005D8C69 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */; }; 360E961E1DC20EF3005D8C69 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */; }; + 3623DDD12FAA229F00A33CB0 /* LegacyLoginItemRegistration.m in Sources */ = {isa = PBXBuildFile; fileRef = 3623DDD02FAA229F00A33CB0 /* LegacyLoginItemRegistration.m */; }; + 3623DDD32FAA22A800A33CB0 /* LegacyLoginItemRegistration.h in Sources */ = {isa = PBXBuildFile; fileRef = 3623DDD22FAA22A600A33CB0 /* LegacyLoginItemRegistration.h */; }; 3625F43E2DF5B24D001708B6 /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3625F43D2DF5B24D001708B6 /* InternetAccessPolicy.plist */; }; 3630E17C2B839A7E007875A9 /* PreferencesClientCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3630E17B2B839A7E007875A9 /* PreferencesClientCellView.swift */; }; 3634D7F62954BB2C005CE01C /* BinaryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3634D7F52954BB2C005CE01C /* BinaryManager.swift */; }; @@ -132,6 +134,8 @@ 3600796F2A1B891000CF88B7 /* PostgresPermissionDialog */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PostgresPermissionDialog; sourceTree = BUILT_PRODUCTS_DIR; }; 360079712A1B891000CF88B7 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = ""; }; + 3623DDD02FAA229F00A33CB0 /* LegacyLoginItemRegistration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LegacyLoginItemRegistration.m; sourceTree = ""; }; + 3623DDD22FAA22A600A33CB0 /* LegacyLoginItemRegistration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LegacyLoginItemRegistration.h; sourceTree = ""; }; 3625F43D2DF5B24D001708B6 /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = ""; }; 3630E17B2B839A7E007875A9 /* PreferencesClientCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesClientCellView.swift; sourceTree = ""; }; 3634D7F52954BB2C005CE01C /* BinaryManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryManager.swift; sourceTree = ""; }; @@ -340,6 +344,8 @@ 83BB11591DB680DA00772E8D /* DnDArrayController.swift */, 83543E261D2136E800F764CB /* ErrorRecoveryAttempter.h */, 83543E271D2136E800F764CB /* ErrorRecoveryAttempter.m */, + 3623DDD02FAA229F00A33CB0 /* LegacyLoginItemRegistration.m */, + 3623DDD22FAA22A600A33CB0 /* LegacyLoginItemRegistration.h */, 83969DC21D36A3EA005B9F1C /* ClientLauncher.applescript */, 835954271E4A301F00D1F0DF /* PostgresAppScriptable.sdef */, 835954291E4A30FD00D1F0DF /* ScriptCommands.swift */, @@ -618,7 +624,9 @@ 83A544E31D6682C700603068 /* KeyValueObserver.swift in Sources */, 36CC815829E6FF990080B0B4 /* ChangePasswordViewController.swift in Sources */, 830D62CC1D77297800570BFC /* Preferences.swift in Sources */, + 3623DDD12FAA229F00A33CB0 /* LegacyLoginItemRegistration.m in Sources */, 8358D53C1D26EBE7007B7904 /* Server.swift in Sources */, + 3623DDD32FAA22A800A33CB0 /* LegacyLoginItemRegistration.h in Sources */, 3634D7F82954CD69005CE01C /* ServerManagerExtras.swift in Sources */, 83543E281D2136E800F764CB /* ErrorRecoveryAttempter.m in Sources */, 3634D7F62954BB2C005CE01C /* BinaryManager.swift in Sources */, @@ -849,7 +857,6 @@ "DEBUG=1", "$(inherited)", ); - GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -912,7 +919,6 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; diff --git a/Postgres/LegacyLoginItemRegistration.h b/Postgres/LegacyLoginItemRegistration.h new file mode 100644 index 000000000..bed8b8733 --- /dev/null +++ b/Postgres/LegacyLoginItemRegistration.h @@ -0,0 +1,13 @@ +// +// LegacyLoginItemRegistration.h +// Postgres +// +// +// Created by Jakob Egger on 05.05.26. +// This code is released under the terms of the PostgreSQL License. +// + + +void RegisterLegacyLoginItem(CFURLRef url); +void UnregisterLegacyLoginItem(CFURLRef url); +BOOL IsLoginItemRegistered(CFURLRef url); diff --git a/Postgres/LegacyLoginItemRegistration.m b/Postgres/LegacyLoginItemRegistration.m new file mode 100644 index 000000000..9ea6d0063 --- /dev/null +++ b/Postgres/LegacyLoginItemRegistration.m @@ -0,0 +1,37 @@ +// +// LegacyLoginItemRegistration.m +// Postgres +// +// +// Created by Jakob Egger on 05.05.26. +// This code is released under the terms of the PostgreSQL License. +// + + +#import + +void RegisterLegacyLoginItem(CFURLRef url) { + LSSharedFileListRef loginItemsList = LSSharedFileListCreate( nil, kLSSharedFileListSessionLoginItems, nil); + LSSharedFileListInsertItemURL(loginItemsList, kLSSharedFileListItemLast, nil, nil, url, nil, nil); + CFRelease(loginItemsList); +} + +BOOL IsLoginItemRegistered(CFURLRef url) { + LSSharedFileListRef loginItemsList = LSSharedFileListCreate( nil, kLSSharedFileListSessionLoginItems, nil); + CFArrayRef loginItems = LSSharedFileListCopySnapshot(loginItemsList, nil); + CFIndex count = CFArrayGetCount(loginItems); + BOOL foundMatch = NO; + for (CFIndex i=0; i #import "libproc.h" #import "ErrorRecoveryAttempter.h" +#import "LegacyLoginItemRegistration.h" From 30d9044ef281439213395b1783078facaaa90c5f Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 12:33:36 +0200 Subject: [PATCH 02/29] Show main window explicitly This change will allow us to launch the app without displaying UI --- Postgres/AppDelegate.swift | 9 +++++++++ Postgres/Base.lproj/Main.storyboard | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index edb8c9cc1..616d1f2d6 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -18,6 +18,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe @IBOutlet var sparkleUpdater: SUUpdater! @IBOutlet var preferencesMenuItem: NSMenuItem! + @IBOutlet var mainWindowMenuItem: NSMenuItem! func isFirstLaunch() -> Bool { if UserDefaults.standard.bool(forKey: "alreadyLaunched") == false { @@ -33,6 +34,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe func applicationDidFinishLaunching(_ notification: Notification) { CrashLogCollector.shared.scanInBackground() + showMainWindow() let clientAppPath = UserDefaults.standard.string(forKey: "PreferredClientApplicationPath") ?? "" if clientAppPath.isEmpty { // Read the old setting, reverting to Terminal as default @@ -125,11 +127,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe } func applicationDidBecomeActive(_ notification: Notification) { + showMainWindow() DispatchQueue.main.async { self.serverManager.refreshServerStatuses() } } + func showMainWindow() { + // This is a workaround to trigger a storyboard segue programmatically + // If you come up with a better solution please let me know :) + NSApp.sendAction(mainWindowMenuItem.action!, to: mainWindowMenuItem.target, from: mainWindowMenuItem) + } + func showPreferences() { // The preference window is displayed by a storyboard segue hooked up to a menu item // This seems to be the easiest way to trigger that segue programmatically diff --git a/Postgres/Base.lproj/Main.storyboard b/Postgres/Base.lproj/Main.storyboard index 6441b1843..2f9a36795 100644 --- a/Postgres/Base.lproj/Main.storyboard +++ b/Postgres/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -453,6 +453,12 @@ + + + + + + @@ -581,6 +587,7 @@ + From 662436c7089eda4d640999a40ffc02c1b3c1916a Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 12:21:56 +0200 Subject: [PATCH 03/29] Allow app to start as login item without UI --- Postgres/AppDelegate.swift | 11 +++++++++-- Postgres/MainWindow.swift | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index 616d1f2d6..db9206997 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -33,8 +33,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe } func applicationDidFinishLaunching(_ notification: Notification) { - CrashLogCollector.shared.scanInBackground() - showMainWindow() + if NSAppleEventManager.shared().currentAppleEvent?.paramDescriptor(forKeyword: keyAEPropData)?.enumCodeValue == keyAELaunchedAsLogInItem { + NSApp.setActivationPolicy(.accessory) + } else { + NSApp.setActivationPolicy(.regular) + NSApp.activate(ignoringOtherApps: true) + showMainWindow() + CrashLogCollector.shared.scanInBackground() + } let clientAppPath = UserDefaults.standard.string(forKey: "PreferredClientApplicationPath") ?? "" if clientAppPath.isEmpty { // Read the old setting, reverting to Terminal as default @@ -127,6 +133,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe } func applicationDidBecomeActive(_ notification: Notification) { + NSApp.setActivationPolicy(.regular) showMainWindow() DispatchQueue.main.async { self.serverManager.refreshServerStatuses() diff --git a/Postgres/MainWindow.swift b/Postgres/MainWindow.swift index ff1ce5cb6..bee6202c5 100644 --- a/Postgres/MainWindow.swift +++ b/Postgres/MainWindow.swift @@ -40,6 +40,6 @@ class MainWindowController: NSWindowController, NSWindowDelegate { func windowWillClose(_ notification: Notification) { - NSApp.terminate(nil) + NSApp.setActivationPolicy(.accessory) } } From f79b1b7589c94b2cc87e9fc57753eea6a5e4301c Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 12:23:59 +0200 Subject: [PATCH 04/29] Hide dock icon by default This prevents dock icon bounce during app startup. Not sure what the right choice is here: - we want no bounce when started as login item - we would like bounce when started from Finder We probably can't have it both ways. --- Postgres/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Postgres/Info.plist b/Postgres/Info.plist index b51e09c3e..96ffcbafd 100644 --- a/Postgres/Info.plist +++ b/Postgres/Info.plist @@ -40,5 +40,7 @@ dsa_pub.pem NSAppleEventsUsageDescription Postgres.app uses Apple Script so you can automatically connect to a database with a double click. + LSUIElement + From 753f9803f70de04d0b0fd645091a01be78537083 Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 12:47:12 +0200 Subject: [PATCH 05/29] Login Helper just opens main app --- Postgres.xcodeproj/project.pbxproj | 6 +++ PostgresLoginHelper/OpenURLAsLoginItem.h | 10 +++++ PostgresLoginHelper/OpenURLAsLoginItem.m | 32 +++++++++++++++ .../PostgresLoginHelper-Bridging-Header.h | 1 + PostgresLoginHelper/main.swift | 40 +++---------------- 5 files changed, 55 insertions(+), 34 deletions(-) create mode 100644 PostgresLoginHelper/OpenURLAsLoginItem.h create mode 100644 PostgresLoginHelper/OpenURLAsLoginItem.m diff --git a/Postgres.xcodeproj/project.pbxproj b/Postgres.xcodeproj/project.pbxproj index 928e09816..9147fbfea 100644 --- a/Postgres.xcodeproj/project.pbxproj +++ b/Postgres.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 360E961C1DC20D5E005D8C69 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */; }; 360E961D1DC20EF2005D8C69 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */; }; 360E961E1DC20EF3005D8C69 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */; }; + 361CAF8B2FAB551F00DCF309 /* OpenURLAsLoginItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 361CAF8A2FAB551C00DCF309 /* OpenURLAsLoginItem.m */; }; 3623DDD12FAA229F00A33CB0 /* LegacyLoginItemRegistration.m in Sources */ = {isa = PBXBuildFile; fileRef = 3623DDD02FAA229F00A33CB0 /* LegacyLoginItemRegistration.m */; }; 3623DDD32FAA22A800A33CB0 /* LegacyLoginItemRegistration.h in Sources */ = {isa = PBXBuildFile; fileRef = 3623DDD22FAA22A600A33CB0 /* LegacyLoginItemRegistration.h */; }; 3625F43E2DF5B24D001708B6 /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3625F43D2DF5B24D001708B6 /* InternetAccessPolicy.plist */; }; @@ -134,6 +135,8 @@ 3600796F2A1B891000CF88B7 /* PostgresPermissionDialog */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PostgresPermissionDialog; sourceTree = BUILT_PRODUCTS_DIR; }; 360079712A1B891000CF88B7 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = ""; }; + 361CAF892FAB550800DCF309 /* OpenURLAsLoginItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenURLAsLoginItem.h; sourceTree = ""; }; + 361CAF8A2FAB551C00DCF309 /* OpenURLAsLoginItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OpenURLAsLoginItem.m; sourceTree = ""; }; 3623DDD02FAA229F00A33CB0 /* LegacyLoginItemRegistration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LegacyLoginItemRegistration.m; sourceTree = ""; }; 3623DDD22FAA22A600A33CB0 /* LegacyLoginItemRegistration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LegacyLoginItemRegistration.h; sourceTree = ""; }; 3625F43D2DF5B24D001708B6 /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = ""; }; @@ -265,6 +268,8 @@ 83C19C2B1D7D977000F4862D /* PostgresLoginHelper */ = { isa = PBXGroup; children = ( + 361CAF892FAB550800DCF309 /* OpenURLAsLoginItem.h */, + 361CAF8A2FAB551C00DCF309 /* OpenURLAsLoginItem.m */, 83C19C2C1D7D977000F4862D /* main.swift */, 83C19C2E1D7D977000F4862D /* Assets.xcassets */, 83C19C331D7D977000F4862D /* Info.plist */, @@ -581,6 +586,7 @@ 36EB76A72AC1E77400580683 /* BundleExtensions.swift in Sources */, 83999C6B1D7DA8E300B8A1D6 /* ServerManager.swift in Sources */, 83C19C2D1D7D977000F4862D /* main.swift in Sources */, + 361CAF8B2FAB551F00DCF309 /* OpenURLAsLoginItem.m in Sources */, 83999C6C1D7DA8EA00B8A1D6 /* Extensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/PostgresLoginHelper/OpenURLAsLoginItem.h b/PostgresLoginHelper/OpenURLAsLoginItem.h new file mode 100644 index 000000000..0a021acd9 --- /dev/null +++ b/PostgresLoginHelper/OpenURLAsLoginItem.h @@ -0,0 +1,10 @@ +// +// OpenURLAsLoginItem.h +// Postgres +// +// +// Created by Jakob Egger on 06.05.26. +// This code is released under the terms of the PostgreSQL License. +// + +BOOL OpenURLAsLoginItem(CFURLRef url); diff --git a/PostgresLoginHelper/OpenURLAsLoginItem.m b/PostgresLoginHelper/OpenURLAsLoginItem.m new file mode 100644 index 000000000..c6ebd5eef --- /dev/null +++ b/PostgresLoginHelper/OpenURLAsLoginItem.m @@ -0,0 +1,32 @@ +// +// OpenURLAsLoginItem.m +// Postgres +// +// +// Created by Jakob Egger on 06.05.26. +// This code is released under the terms of the PostgreSQL License. +// + +#import + +BOOL OpenURLAsLoginItem(CFURLRef url) { + struct LSLaunchURLSpec launchSpec = {}; + launchSpec.appURL = url; + struct AEDesc launchAsLoginItem = {}; + AEKeyword launchAsLoginItemKeyword = keyAELaunchedAsLogInItem; + OSErr err = AECreateDesc(typeEnumerated, &launchAsLoginItemKeyword, 4, &launchAsLoginItem); + if (err != noErr) { + NSLog(@"AECreateDesc() return OSErr %d", (int)err); + return NO; + } + NSLog(@"AECreateDesc: %d", (int)err); + launchSpec.passThruParams = &launchAsLoginItem; + CFURLRef launchedURL = NULL; + OSStatus status = LSOpenFromURLSpec(&launchSpec, &launchedURL); + AEDisposeDesc(&launchAsLoginItem); + if (status != noErr) { + NSLog(@"LSOpenFromURLSpec() return OSStatus %d", (int)status); + return NO; + } + return YES; +} diff --git a/PostgresLoginHelper/PostgresLoginHelper-Bridging-Header.h b/PostgresLoginHelper/PostgresLoginHelper-Bridging-Header.h index 7d07554cb..36b2cfd8f 100644 --- a/PostgresLoginHelper/PostgresLoginHelper-Bridging-Header.h +++ b/PostgresLoginHelper/PostgresLoginHelper-Bridging-Header.h @@ -5,3 +5,4 @@ #import #import "libproc.h" #import "ErrorRecoveryAttempter.h" +#import "OpenURLAsLoginItem.h" diff --git a/PostgresLoginHelper/main.swift b/PostgresLoginHelper/main.swift index 0f7dde68e..b1dd5cdf4 100644 --- a/PostgresLoginHelper/main.swift +++ b/PostgresLoginHelper/main.swift @@ -1,45 +1,17 @@ // -// maine.swift +// main.swift // PostgresLoginHelper // -// Created by Chris on 05/09/2016. +// Created by jakob on May 6, 2026 // This code is released under the terms of the PostgreSQL License. // import Cocoa -if #available(macOS 13, *) { - // we only need the else clause -} else { - // on old macOS autostart only works when the app is in /Applications/Postgres.app - if !Bundle.main.bundlePath.hasPrefix("/Applications/Postgres.app") { - NSLog("Not in /Applications/Postgres.app") - exit(0) - } +guard let mainAppURL = Bundle.mainApp?.bundleURL else { + fatalError("Main app not found") } -// Launch the menu bar helper -if UserDefaults.shared.bool(forKey: "HideMenuHelperApp") == false { - if let menuHelperURL = Bundle.mainApp?.url(forAuxiliaryExecutable: "PostgresMenuHelper.app") { - if !NSWorkspace.shared.launchApplication(menuHelperURL.path) { - NSLog("Failed to launch PostgresMenuHelper.app") - } - } else { - NSLog("PostgresMenuHelper.app not found") - } -} - -// Start PostgreSQL servers -// This may take a few seconds, so we do this after launching the menu bar helper -let serverManager = ServerManager.shared -serverManager.loadServers() -for server in serverManager.servers { - if server.startOnLogin { - do { - try server.startSync() - } - catch let error as NSError { - Swift.print("Failed to start server \(server.name): \(error.localizedDescription)") - } - } +guard OpenURLAsLoginItem(mainAppURL as CFURL) else { + fatalError("Could not open main app as login item") } From b3b76cdf7fec03982a45072cb6721675ee19da4c Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 14:04:45 +0200 Subject: [PATCH 06/29] Do not try to launch main app if it is already running --- PostgresLoginHelper/main.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/PostgresLoginHelper/main.swift b/PostgresLoginHelper/main.swift index b1dd5cdf4..5e49992b6 100644 --- a/PostgresLoginHelper/main.swift +++ b/PostgresLoginHelper/main.swift @@ -12,6 +12,14 @@ guard let mainAppURL = Bundle.mainApp?.bundleURL else { fatalError("Main app not found") } +if let bundleIdentifier = Bundle.mainApp?.bundleIdentifier { + let matchingApplications = NSRunningApplication.runningApplications(withBundleIdentifier: bundleIdentifier) + guard matchingApplications.isEmpty else { + print("Main app is already running") + exit(0) + } +} + guard OpenURLAsLoginItem(mainAppURL as CFURL) else { fatalError("Could not open main app as login item") } From d282dc10034074b175b3c98a2eb78f272c43f1ce Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 15:20:16 +0200 Subject: [PATCH 07/29] Move status menu into main application --- Postgres.xcodeproj/project.pbxproj | 175 +----------------- Postgres/AppDelegate.swift | 98 +++++++--- .../icon-caution.imageset/Contents.json | 0 .../icon-caution.imageset/caution-icon.pdf | Bin .../statusicon.imageset/Contents.json | 0 .../statusicon.imageset/statusicon.png | Bin .../statusicon.imageset/statusicon@2x.png | Bin .../MenuItemView.swift | 0 .../MenuItemView.xib | 29 +-- PostgresMenuHelper/AppDelegate.swift | 131 ------------- .../AppIcon.appiconset/Contents.json | 58 ------ .../Assets.xcassets/Contents.json | 6 - .../icon-running.imageset/Contents.json | 21 --- .../icon-running.imageset/icon_running.pdf | Bin 5496 -> 0 bytes .../icon-stopped.imageset/Contents.json | 21 --- .../icon-stopped.imageset/icon_stopped.pdf | Bin 5474 -> 0 bytes PostgresMenuHelper/Base.lproj/MainMenu.xib | 54 ------ PostgresMenuHelper/Info.plist | 34 ---- PostgresMenuHelper/PostgresApplication.h | 17 -- .../PostgresMenuHelper-Bridging-Header.h | 7 - 20 files changed, 98 insertions(+), 553 deletions(-) rename {PostgresMenuHelper => Postgres}/Assets.xcassets/icon-caution.imageset/Contents.json (100%) rename {PostgresMenuHelper => Postgres}/Assets.xcassets/icon-caution.imageset/caution-icon.pdf (100%) rename {PostgresMenuHelper => Postgres}/Assets.xcassets/statusicon.imageset/Contents.json (100%) rename {PostgresMenuHelper => Postgres}/Assets.xcassets/statusicon.imageset/statusicon.png (100%) rename {PostgresMenuHelper => Postgres}/Assets.xcassets/statusicon.imageset/statusicon@2x.png (100%) rename {PostgresMenuHelper => Postgres}/MenuItemView.swift (100%) rename {PostgresMenuHelper => Postgres}/MenuItemView.xib (92%) delete mode 100644 PostgresMenuHelper/AppDelegate.swift delete mode 100644 PostgresMenuHelper/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 PostgresMenuHelper/Assets.xcassets/Contents.json delete mode 100644 PostgresMenuHelper/Assets.xcassets/icon-running.imageset/Contents.json delete mode 100644 PostgresMenuHelper/Assets.xcassets/icon-running.imageset/icon_running.pdf delete mode 100644 PostgresMenuHelper/Assets.xcassets/icon-stopped.imageset/Contents.json delete mode 100644 PostgresMenuHelper/Assets.xcassets/icon-stopped.imageset/icon_stopped.pdf delete mode 100644 PostgresMenuHelper/Base.lproj/MainMenu.xib delete mode 100644 PostgresMenuHelper/Info.plist delete mode 100644 PostgresMenuHelper/PostgresApplication.h delete mode 100644 PostgresMenuHelper/PostgresMenuHelper-Bridging-Header.h diff --git a/Postgres.xcodeproj/project.pbxproj b/Postgres.xcodeproj/project.pbxproj index 9147fbfea..38dcd2ac7 100644 --- a/Postgres.xcodeproj/project.pbxproj +++ b/Postgres.xcodeproj/project.pbxproj @@ -8,9 +8,10 @@ /* Begin PBXBuildFile section */ 360079722A1B891000CF88B7 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360079712A1B891000CF88B7 /* main.swift */; }; + 3603EF172FAB6BBA00BDA576 /* MenuItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83C19C611D7D9C2500F4862D /* MenuItemView.xib */; }; + 3603EF182FAB6C2900BDA576 /* MenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C19C5B1D7D9AAE00F4862D /* MenuItemView.swift */; }; 360E961C1DC20D5E005D8C69 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */; }; 360E961D1DC20EF2005D8C69 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */; }; - 360E961E1DC20EF3005D8C69 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */; }; 361CAF8B2FAB551F00DCF309 /* OpenURLAsLoginItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 361CAF8A2FAB551C00DCF309 /* OpenURLAsLoginItem.m */; }; 3623DDD12FAA229F00A33CB0 /* LegacyLoginItemRegistration.m in Sources */ = {isa = PBXBuildFile; fileRef = 3623DDD02FAA229F00A33CB0 /* LegacyLoginItemRegistration.m */; }; 3623DDD32FAA22A800A33CB0 /* LegacyLoginItemRegistration.h in Sources */ = {isa = PBXBuildFile; fileRef = 3623DDD22FAA22A600A33CB0 /* LegacyLoginItemRegistration.h */; }; @@ -23,7 +24,6 @@ 3641083B296D7C2C00F79014 /* libpgport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3641083A296D7C2C00F79014 /* libpgport.a */; }; 36447A62282568BA00F5BB7A /* InstallHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36447A61282568BA00F5BB7A /* InstallHistory.swift */; }; 36447A63282568BA00F5BB7A /* InstallHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36447A61282568BA00F5BB7A /* InstallHistory.swift */; }; - 36447A64282568BA00F5BB7A /* InstallHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36447A61282568BA00F5BB7A /* InstallHistory.swift */; }; 3686D3362A963787003389DD /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 3686D3352A963787003389DD /* Credits.rtf */; }; 369189BA2A1E3D5F000A3C97 /* UnixProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 369189B92A1E3D5F000A3C97 /* UnixProcessInfo.swift */; }; 369189BC2A1E5BE0000A3C97 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360E961B1DC20D5E005D8C69 /* UserDefaults.swift */; }; @@ -33,20 +33,16 @@ 36CC815829E6FF990080B0B4 /* ChangePasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36CC815729E6FF990080B0B4 /* ChangePasswordViewController.swift */; }; 36D767B02729EFDE00161CF2 /* ProcessExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36D767AF2729EFDE00161CF2 /* ProcessExtensions.swift */; }; 36D767B32729F1AD00161CF2 /* ProcessExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36D767AF2729EFDE00161CF2 /* ProcessExtensions.swift */; }; - 36D767B62729F1AD00161CF2 /* ProcessExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36D767AF2729EFDE00161CF2 /* ProcessExtensions.swift */; }; 36EB76A62AC1E55A00580683 /* BundleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36EB76A52AC1E55A00580683 /* BundleExtensions.swift */; }; 36EB76A72AC1E77400580683 /* BundleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36EB76A52AC1E55A00580683 /* BundleExtensions.swift */; }; - 36EB76A82AC1E77400580683 /* BundleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36EB76A52AC1E55A00580683 /* BundleExtensions.swift */; }; 830658E51D536F2E00BA1752 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830658E41D536F2E00BA1752 /* Extensions.swift */; }; 830B4B261D1C132300F16762 /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830B4B251D1C132300F16762 /* ServerManager.swift */; }; 830B4B281D1C289A00F16762 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830B4B271D1C289A00F16762 /* Sidebar.swift */; }; 830D62CC1D77297800570BFC /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830D62CB1D77297800570BFC /* Preferences.swift */; }; 8324D9AA1D9402A10066DCDB /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 831C84AC1D771B4800FA5F59 /* Sparkle.framework */; }; 8324D9AB1D9402A10066DCDB /* Sparkle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 831C84AC1D771B4800FA5F59 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 834355B11D7D9DC8007CD457 /* ErrorRecoveryAttempter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83543E271D2136E800F764CB /* ErrorRecoveryAttempter.m */; }; 8348A1ED1DB4ED5E0039A024 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 8348A1EC1DB4ED5E0039A024 /* dsa_pub.pem */; }; 8348A1F11DB52E9A0039A024 /* ValueTransformers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8348A1F01DB52E9A0039A024 /* ValueTransformers.swift */; }; - 8348A1F21DB52E9A0039A024 /* ValueTransformers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8348A1F01DB52E9A0039A024 /* ValueTransformers.swift */; }; 8353FBDE1D1D57C8002F77E7 /* ServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8353FBDD1D1D57C8002F77E7 /* ServerView.swift */; }; 8353FBE01D1D5E4C002F77E7 /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8353FBDF1D1D5E4C002F77E7 /* SplitView.swift */; }; 83543E281D2136E800F764CB /* ErrorRecoveryAttempter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83543E271D2136E800F764CB /* ErrorRecoveryAttempter.m */; }; @@ -59,7 +55,6 @@ 83969DBB1D369BD1005B9F1C /* ClientLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83969DBA1D369BD1005B9F1C /* ClientLauncher.swift */; }; 83969DC31D36A3EA005B9F1C /* ClientLauncher.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 83969DC21D36A3EA005B9F1C /* ClientLauncher.applescript */; }; 83999C671D7DA3C300B8A1D6 /* PostgresLoginHelper.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83C19C2A1D7D977000F4862D /* PostgresLoginHelper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 83999C691D7DA42B00B8A1D6 /* PostgresMenuHelper.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83C19C4E1D7D9A7D00F4862D /* PostgresMenuHelper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 83999C6A1D7DA8DF00B8A1D6 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8358D53B1D26EBE7007B7904 /* Server.swift */; }; 83999C6B1D7DA8E300B8A1D6 /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830B4B251D1C132300F16762 /* ServerManager.swift */; }; 83999C6C1D7DA8EA00B8A1D6 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830658E41D536F2E00BA1752 /* Extensions.swift */; }; @@ -70,15 +65,6 @@ 83BB115A1DB680DA00772E8D /* DnDArrayController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83BB11591DB680DA00772E8D /* DnDArrayController.swift */; }; 83C19C2D1D7D977000F4862D /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C19C2C1D7D977000F4862D /* main.swift */; }; 83C19C2F1D7D977000F4862D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83C19C2E1D7D977000F4862D /* Assets.xcassets */; }; - 83C19C511D7D9A7D00F4862D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C19C501D7D9A7D00F4862D /* AppDelegate.swift */; }; - 83C19C561D7D9A7D00F4862D /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83C19C541D7D9A7D00F4862D /* MainMenu.xib */; }; - 83C19C5C1D7D9AAE00F4862D /* MenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C19C5B1D7D9AAE00F4862D /* MenuItemView.swift */; }; - 83C19C621D7D9C2500F4862D /* MenuItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83C19C611D7D9C2500F4862D /* MenuItemView.xib */; }; - 83C19C671D7D9CCF00F4862D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83C19C661D7D9CCF00F4862D /* Assets.xcassets */; }; - 83C19C681D7D9D1900F4862D /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8358D53B1D26EBE7007B7904 /* Server.swift */; }; - 83C19C691D7D9D1E00F4862D /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830B4B251D1C132300F16762 /* ServerManager.swift */; }; - 83C19C6A1D7D9D2300F4862D /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830658E41D536F2E00BA1752 /* Extensions.swift */; }; - 83C19C6C1D7D9D2E00F4862D /* KeyValueObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A544E21D6682C700603068 /* KeyValueObserver.swift */; }; 83F3647E1D5204D30096F1F5 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F3647D1D5204D30096F1F5 /* SettingsView.swift */; }; 83FB571C1D1B05F000A903A3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FB571B1D1B05F000A903A3 /* AppDelegate.swift */; }; 83FB57201D1B05F000A903A3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83FB571F1D1B05F000A903A3 /* Assets.xcassets */; }; @@ -124,7 +110,6 @@ dstPath = ""; dstSubfolderSpec = 6; files = ( - 83999C691D7DA42B00B8A1D6 /* PostgresMenuHelper.app in CopyFiles */, 369189BD2A1E7DAD000A3C97 /* PostgresPermissionDialog in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; @@ -156,7 +141,6 @@ 36CC815729E6FF990080B0B4 /* ChangePasswordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangePasswordViewController.swift; sourceTree = ""; }; 36D767AF2729EFDE00161CF2 /* ProcessExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessExtensions.swift; sourceTree = ""; }; 36EB76A52AC1E55A00580683 /* BundleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleExtensions.swift; sourceTree = ""; }; - 830377B71E4C820A008E6896 /* PostgresApplication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PostgresApplication.h; sourceTree = ""; }; 830658E41D536F2E00BA1752 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 830B4B251D1C132300F16762 /* ServerManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerManager.swift; sourceTree = ""; }; 830B4B271D1C289A00F16762 /* Sidebar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Sidebar.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -187,14 +171,8 @@ 83C19C2C1D7D977000F4862D /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 83C19C2E1D7D977000F4862D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 83C19C331D7D977000F4862D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 83C19C4E1D7D9A7D00F4862D /* PostgresMenuHelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PostgresMenuHelper.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 83C19C501D7D9A7D00F4862D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 83C19C551D7D9A7D00F4862D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 83C19C571D7D9A7D00F4862D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 83C19C5B1D7D9AAE00F4862D /* MenuItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuItemView.swift; sourceTree = ""; }; 83C19C611D7D9C2500F4862D /* MenuItemView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MenuItemView.xib; sourceTree = ""; }; - 83C19C631D7D9C6300F4862D /* PostgresMenuHelper-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PostgresMenuHelper-Bridging-Header.h"; sourceTree = ""; }; - 83C19C661D7D9CCF00F4862D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 83F3647D1D5204D30096F1F5 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; 83FB57181D1B05F000A903A3 /* Postgres.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Postgres.app; sourceTree = BUILT_PRODUCTS_DIR; }; 83FB571B1D1B05F000A903A3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -220,13 +198,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 83C19C4B1D7D9A7D00F4862D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 83FB57151D1B05F000A903A3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -278,27 +249,11 @@ path = PostgresLoginHelper; sourceTree = ""; }; - 83C19C4F1D7D9A7D00F4862D /* PostgresMenuHelper */ = { - isa = PBXGroup; - children = ( - 83C19C501D7D9A7D00F4862D /* AppDelegate.swift */, - 83C19C5B1D7D9AAE00F4862D /* MenuItemView.swift */, - 83C19C661D7D9CCF00F4862D /* Assets.xcassets */, - 83C19C541D7D9A7D00F4862D /* MainMenu.xib */, - 83C19C611D7D9C2500F4862D /* MenuItemView.xib */, - 83C19C571D7D9A7D00F4862D /* Info.plist */, - 83C19C631D7D9C6300F4862D /* PostgresMenuHelper-Bridging-Header.h */, - 830377B71E4C820A008E6896 /* PostgresApplication.h */, - ); - path = PostgresMenuHelper; - sourceTree = ""; - }; 83FB570F1D1B05F000A903A3 = { isa = PBXGroup; children = ( 83FB571A1D1B05F000A903A3 /* Postgres */, 83C19C2B1D7D977000F4862D /* PostgresLoginHelper */, - 83C19C4F1D7D9A7D00F4862D /* PostgresMenuHelper */, 360079702A1B891000CF88B7 /* PostgresPermissionDialog */, 83FB57191D1B05F000A903A3 /* Products */, 836CC37F1D2D6A0D000D0C13 /* Frameworks */, @@ -310,7 +265,6 @@ children = ( 83FB57181D1B05F000A903A3 /* Postgres.app */, 83C19C2A1D7D977000F4862D /* PostgresLoginHelper.app */, - 83C19C4E1D7D9A7D00F4862D /* PostgresMenuHelper.app */, 3600796F2A1B891000CF88B7 /* PostgresPermissionDialog */, ); name = Products; @@ -356,6 +310,8 @@ 835954291E4A30FD00D1F0DF /* ScriptCommands.swift */, 83FB571F1D1B05F000A903A3 /* Assets.xcassets */, 83FB57211D1B05F000A903A3 /* Main.storyboard */, + 83C19C5B1D7D9AAE00F4862D /* MenuItemView.swift */, + 83C19C611D7D9C2500F4862D /* MenuItemView.xib */, 83FB57241D1B05F000A903A3 /* Info.plist */, 3625F43D2DF5B24D001708B6 /* InternetAccessPolicy.plist */, 3686D3352A963787003389DD /* Credits.rtf */, @@ -404,23 +360,6 @@ productReference = 83C19C2A1D7D977000F4862D /* PostgresLoginHelper.app */; productType = "com.apple.product-type.application"; }; - 83C19C4D1D7D9A7D00F4862D /* PostgresMenuHelper */ = { - isa = PBXNativeTarget; - buildConfigurationList = 83C19C581D7D9A7D00F4862D /* Build configuration list for PBXNativeTarget "PostgresMenuHelper" */; - buildPhases = ( - 83C19C4A1D7D9A7D00F4862D /* Sources */, - 83C19C4B1D7D9A7D00F4862D /* Frameworks */, - 83C19C4C1D7D9A7D00F4862D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = PostgresMenuHelper; - productName = PostgresMenuHelper; - productReference = 83C19C4E1D7D9A7D00F4862D /* PostgresMenuHelper.app */; - productType = "com.apple.product-type.application"; - }; 83FB57171D1B05F000A903A3 /* Postgres */ = { isa = PBXNativeTarget; buildConfigurationList = 83FB57271D1B05F000A903A3 /* Build configuration list for PBXNativeTarget "Postgres" */; @@ -461,11 +400,6 @@ LastSwiftMigration = 1130; ProvisioningStyle = Manual; }; - 83C19C4D1D7D9A7D00F4862D = { - CreatedOnToolsVersion = 8.0; - LastSwiftMigration = 1130; - ProvisioningStyle = Manual; - }; 83FB57171D1B05F000A903A3 = { CreatedOnToolsVersion = 8.0; LastSwiftMigration = 1130; @@ -488,7 +422,6 @@ targets = ( 83FB57171D1B05F000A903A3 /* Postgres */, 83C19C291D7D977000F4862D /* PostgresLoginHelper */, - 83C19C4D1D7D9A7D00F4862D /* PostgresMenuHelper */, 3600796E2A1B891000CF88B7 /* PostgresPermissionDialog */, ); }; @@ -503,20 +436,11 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 83C19C4C1D7D9A7D00F4862D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 83C19C561D7D9A7D00F4862D /* MainMenu.xib in Resources */, - 83C19C671D7D9CCF00F4862D /* Assets.xcassets in Resources */, - 83C19C621D7D9C2500F4862D /* MenuItemView.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 83FB57161D1B05F000A903A3 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3603EF172FAB6BBA00BDA576 /* MenuItemView.xib in Resources */, 8348A1ED1DB4ED5E0039A024 /* dsa_pub.pem in Resources */, 835954281E4A301F00D1F0DF /* PostgresAppScriptable.sdef in Resources */, 83FB57201D1B05F000A903A3 /* Assets.xcassets in Resources */, @@ -591,25 +515,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 83C19C4A1D7D9A7D00F4862D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 83C19C6A1D7D9D2300F4862D /* Extensions.swift in Sources */, - 834355B11D7D9DC8007CD457 /* ErrorRecoveryAttempter.m in Sources */, - 83C19C691D7D9D1E00F4862D /* ServerManager.swift in Sources */, - 36D767B62729F1AD00161CF2 /* ProcessExtensions.swift in Sources */, - 36447A64282568BA00F5BB7A /* InstallHistory.swift in Sources */, - 83C19C5C1D7D9AAE00F4862D /* MenuItemView.swift in Sources */, - 83C19C681D7D9D1900F4862D /* Server.swift in Sources */, - 360E961E1DC20EF3005D8C69 /* UserDefaults.swift in Sources */, - 8348A1F21DB52E9A0039A024 /* ValueTransformers.swift in Sources */, - 83C19C6C1D7D9D2E00F4862D /* KeyValueObserver.swift in Sources */, - 83C19C511D7D9A7D00F4862D /* AppDelegate.swift in Sources */, - 36EB76A82AC1E77400580683 /* BundleExtensions.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 83FB57141D1B05F000A903A3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -638,6 +543,7 @@ 3634D7F62954BB2C005CE01C /* BinaryManager.swift in Sources */, 36447A62282568BA00F5BB7A /* InstallHistory.swift in Sources */, 36C9D0482CC69746006ABA02 /* ConnectionDialog.swift in Sources */, + 3603EF182FAB6C2900BDA576 /* MenuItemView.swift in Sources */, D7A2F0242DF044AA00F28AD0 /* CrashLogCollector.swift in Sources */, 8348A1F11DB52E9A0039A024 /* ValueTransformers.swift in Sources */, 83969DBB1D369BD1005B9F1C /* ClientLauncher.swift in Sources */, @@ -653,14 +559,6 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ - 83C19C541D7D9A7D00F4862D /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 83C19C551D7D9A7D00F4862D /* Base */, - ); - name = MainMenu.xib; - sourceTree = ""; - }; 83FB57211D1B05F000A903A3 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -767,58 +665,6 @@ }; name = Release; }; - 83C19C591D7D9A7D00F4862D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_SUSPICIOUS_MOVES = YES; - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = PostgresMenuHelper/Info.plist; - LATEST_STABLE_PG_VERSION = 15; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@executable_path/../../../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.postgresapp.Postgres2MenuHelper; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SWIFT_OBJC_BRIDGING_HEADER = "PostgresMenuHelper/PostgresMenuHelper-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 83C19C5A1D7D9A7D00F4862D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_SUSPICIOUS_MOVES = YES; - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = PostgresMenuHelper/Info.plist; - LATEST_STABLE_PG_VERSION = 15; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../../../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.postgresapp.Postgres2MenuHelper; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SWIFT_OBJC_BRIDGING_HEADER = "PostgresMenuHelper/PostgresMenuHelper-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; 83FB57251D1B05F000A903A3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1032,15 +878,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 83C19C581D7D9A7D00F4862D /* Build configuration list for PBXNativeTarget "PostgresMenuHelper" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83C19C591D7D9A7D00F4862D /* Debug */, - 83C19C5A1D7D9A7D00F4862D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 83FB57131D1B05F000A903A3 /* Build configuration list for PBXProject "Postgres" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index db9206997..4f06b0a47 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -10,12 +10,15 @@ import Cocoa import Sparkle import ServiceManagement -class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDelegate { +class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDelegate, NSMenuDelegate { let serverManager: ServerManager = ServerManager.shared var hideMenuHelperApp = UserDefaults.standard.bool(forKey: "HideMenuHelperApp") var startLoginHelper = UserDefaults.standard.bool(forKey: "StartLoginHelper") + let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) + let statusIcon = NSImage(named: "statusicon")! + @IBOutlet var sparkleUpdater: SUUpdater! @IBOutlet var preferencesMenuItem: NSMenuItem! @IBOutlet var mainWindowMenuItem: NSMenuItem! @@ -80,16 +83,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe let hideMenuHelperApp = UserDefaults.standard.bool(forKey: "HideMenuHelperApp") if self.hideMenuHelperApp != hideMenuHelperApp { self.hideMenuHelperApp = hideMenuHelperApp - - if self.hideMenuHelperApp { - let runningMenuHelperApps = NSRunningApplication.runningApplications(withBundleIdentifier: "com.postgresapp.Postgres2MenuHelper") - for app in runningMenuHelperApps where app.bundleURL!.path == Bundle.main.url(forAuxiliaryExecutable: "PostgresMenuHelper.app")!.path { - app.terminate() - } - } else { - let url = Bundle.main.url(forAuxiliaryExecutable: "PostgresMenuHelper.app")! - NSWorkspace.shared.launchApplication(url.path) - } + self.statusItem.isVisible = !hideMenuHelperApp } if #available(macOS 13, *) { // this setting was removed in macOS 13 @@ -119,34 +113,96 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe destroyLaunchAgent() } } - - if UserDefaults.standard.bool(forKey: "HideMenuHelperApp") == false { - let url = Bundle.main.url(forAuxiliaryExecutable: "PostgresMenuHelper.app")! - NSWorkspace.shared.launchApplication(url.path) - NSApp.activate(ignoringOtherApps: true) - } } for server in serverManager.servers where server.startOnLogin && server.serverStatus == .Startable { server.start { _ in } } + + statusMenu.addItem(withTitle: "Open Postgres", action: #selector(showMainWindow), keyEquivalent: "") + statusMenu.addItem(withTitle: "Settings…", action: #selector(showPreferences), keyEquivalent: "") + statusMenu.addItem(withTitle: "Check for Updates…", action: #selector(SUUpdater.checkForUpdates), keyEquivalent: "") + statusMenu.items.last?.target = sparkleUpdater + statusMenu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate), keyEquivalent: "") + statusMenu.addItem(.separator()) + statusMenu.delegate = self + + statusItem.button!.image = statusIcon + statusItem.isVisible = !hideMenuHelperApp + statusItem.menu = statusMenu } + let statusMenu = NSMenu() + var menuItemViewControllers: [MenuItemViewController] = [] + + func menuNeedsUpdate(_ menu: NSMenu) { + guard menu == statusMenu else { return } + +// serverManager.loadServers() + serverManager.refreshServerStatuses() + + menuItemViewControllers.removeAll() + + for item in statusMenu.items where item.view is MenuItemView { + statusMenu.removeItem(item) + } + + var maxStringWidth = CGFloat(0) + for server in serverManager.servers { + let stringWidth = (server.name as NSString).size(withAttributes: [NSAttributedString.Key.font: NSFont.systemFont(ofSize: 12)]).width + maxStringWidth = max(stringWidth, maxStringWidth) + } + + for server in serverManager.servers { + guard let menuItemViewController = MenuItemViewController(server) else { return } + menuItemViewControllers.append(menuItemViewController) + + let menuItem = NSMenuItem() + + menuItemViewController.view.setFrameSize(NSSize(width: min(max(150+maxStringWidth, 200), 300), height: 32)) + menuItem.view = menuItemViewController.view + + statusMenu.addItem(menuItem) + } + } + func applicationDidBecomeActive(_ notification: Notification) { NSApp.setActivationPolicy(.regular) - showMainWindow() + if !NSApp.windows.contains(where: { $0.canBecomeMain }) { + showMainWindow() + } DispatchQueue.main.async { self.serverManager.refreshServerStatuses() } } - func showMainWindow() { + func applicationWillTerminate(_ notification: Notification) { + for server in serverManager.servers where server.running { + try? server.stopSync() + } + } + + @IBAction func showMainWindow(_ sender: Any? = nil) { + if #available(macOS 14.0, *) { + // this seems to help with getting the app to activate + // i have no idea how the window server decides to allow postgres.app to activate + NSApp.yieldActivation(toApplicationWithBundleIdentifier: Bundle.main.bundleIdentifier!) + } + NSApp.setActivationPolicy(.regular) + NSApp.activate(ignoringOtherApps: true) // This is a workaround to trigger a storyboard segue programmatically // If you come up with a better solution please let me know :) NSApp.sendAction(mainWindowMenuItem.action!, to: mainWindowMenuItem.target, from: mainWindowMenuItem) - } +} - func showPreferences() { + @IBAction func showPreferences(_ sender: Any? = nil) { + if #available(macOS 14.0, *) { + // this seems to help with getting the app to activate + // i have no idea how the window server decides to allow postgres.app to activate + NSApp.yieldActivation(toApplicationWithBundleIdentifier: Bundle.main.bundleIdentifier!) + } + NSApp.setActivationPolicy(.regular) + NSApp.activate(ignoringOtherApps: true) // The preference window is displayed by a storyboard segue hooked up to a menu item // This seems to be the easiest way to trigger that segue programmatically NSApp.sendAction(preferencesMenuItem.action!, to: preferencesMenuItem.target, from: preferencesMenuItem) diff --git a/PostgresMenuHelper/Assets.xcassets/icon-caution.imageset/Contents.json b/Postgres/Assets.xcassets/icon-caution.imageset/Contents.json similarity index 100% rename from PostgresMenuHelper/Assets.xcassets/icon-caution.imageset/Contents.json rename to Postgres/Assets.xcassets/icon-caution.imageset/Contents.json diff --git a/PostgresMenuHelper/Assets.xcassets/icon-caution.imageset/caution-icon.pdf b/Postgres/Assets.xcassets/icon-caution.imageset/caution-icon.pdf similarity index 100% rename from PostgresMenuHelper/Assets.xcassets/icon-caution.imageset/caution-icon.pdf rename to Postgres/Assets.xcassets/icon-caution.imageset/caution-icon.pdf diff --git a/PostgresMenuHelper/Assets.xcassets/statusicon.imageset/Contents.json b/Postgres/Assets.xcassets/statusicon.imageset/Contents.json similarity index 100% rename from PostgresMenuHelper/Assets.xcassets/statusicon.imageset/Contents.json rename to Postgres/Assets.xcassets/statusicon.imageset/Contents.json diff --git a/PostgresMenuHelper/Assets.xcassets/statusicon.imageset/statusicon.png b/Postgres/Assets.xcassets/statusicon.imageset/statusicon.png similarity index 100% rename from PostgresMenuHelper/Assets.xcassets/statusicon.imageset/statusicon.png rename to Postgres/Assets.xcassets/statusicon.imageset/statusicon.png diff --git a/PostgresMenuHelper/Assets.xcassets/statusicon.imageset/statusicon@2x.png b/Postgres/Assets.xcassets/statusicon.imageset/statusicon@2x.png similarity index 100% rename from PostgresMenuHelper/Assets.xcassets/statusicon.imageset/statusicon@2x.png rename to Postgres/Assets.xcassets/statusicon.imageset/statusicon@2x.png diff --git a/PostgresMenuHelper/MenuItemView.swift b/Postgres/MenuItemView.swift similarity index 100% rename from PostgresMenuHelper/MenuItemView.swift rename to Postgres/MenuItemView.swift diff --git a/PostgresMenuHelper/MenuItemView.xib b/Postgres/MenuItemView.xib similarity index 92% rename from PostgresMenuHelper/MenuItemView.xib rename to Postgres/MenuItemView.xib index 1203f4b70..cdc7487ff 100644 --- a/PostgresMenuHelper/MenuItemView.xib +++ b/Postgres/MenuItemView.xib @@ -1,23 +1,24 @@ - + - - + + + - + - - + + - + @@ -29,16 +30,16 @@ - PostgresMenuHelper.ServerStatusImageTransformer + Postgres.ServerStatusImageTransformer - + - - + + @@ -144,7 +145,7 @@ - + diff --git a/PostgresMenuHelper/AppDelegate.swift b/PostgresMenuHelper/AppDelegate.swift deleted file mode 100644 index c4fa3ebba..000000000 --- a/PostgresMenuHelper/AppDelegate.swift +++ /dev/null @@ -1,131 +0,0 @@ -// -// AppDelegate.swift -// PostgresMenuHelper -// -// Created by Chris on 05/09/2016. -// This code is released under the terms of the PostgreSQL License. -// - -import Cocoa - -@NSApplicationMain -class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate { - - let serverManager = ServerManager.shared - - let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) - let statusIcon = NSImage(named: "statusicon")! - - var menuItemViewControllers: [MenuItemViewController] = [] - - var mainApp: SBApplication { - let mainAppURL = Bundle.mainApp!.bundleURL - let mainApp = SBApplication(url: mainAppURL)! - return mainApp - } - - @IBOutlet var statusMenu: NSMenu! - - func applicationWillFinishLaunching(_ notification: Notification) { - if let myBundleVersion = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String { - for app in NSRunningApplication.runningApplications(withBundleIdentifier: Bundle.main.bundleIdentifier!) { - if app.bundleURL == Bundle.main.bundleURL { - continue - } - guard let bundleURL = app.bundleURL, - let bundle = Bundle(url: bundleURL), - let bundleVersion = bundle.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String - else { - print("Detected broken menu helper. Trying to quit it.") - app.terminate() - continue - } - - switch myBundleVersion.compare(bundleVersion, options: .numeric) { - case .orderedAscending: - // other app is newer - print("Detected newer menu helper is already running. Quitting.") - exit(1) - case .orderedDescending: - print("Detected older menu helper. Trying to quit it.") - app.terminate() - case .orderedSame: - print("Detected identical menu helper. Trying to quit it.") - app.terminate() - } - } - } - } - - func applicationDidFinishLaunching(_ notification: Notification) { - statusItem.menu = statusMenu - statusItem.button!.image = statusIcon - } - - - func menuNeedsUpdate(_ menu: NSMenu) { - guard menu == statusMenu else { return } - - serverManager.loadServers() - serverManager.refreshServerStatuses() - - menuItemViewControllers.removeAll() - - for item in statusMenu.items where item.view is MenuItemView { - statusMenu.removeItem(item) - } - - var maxStringWidth = CGFloat(0) - for server in serverManager.servers { - let stringWidth = (server.name as NSString).size(withAttributes: [NSAttributedString.Key.font: NSFont.systemFont(ofSize: 12)]).width - maxStringWidth = max(stringWidth, maxStringWidth) - } - - for server in serverManager.servers { - guard let menuItemViewController = MenuItemViewController(server) else { return } - menuItemViewControllers.append(menuItemViewController) - - let menuItem = NSMenuItem() - - menuItemViewController.view.setFrameSize(NSSize(width: min(max(150+maxStringWidth, 200), 300), height: 32)) - menuItem.view = menuItemViewController.view - - statusMenu.addItem(menuItem) - } - } - - - @IBAction func openPostgresApp(_ sender: AnyObject?) { - if #available(macOS 14.0, *) { - NSApp.yieldActivation(toApplicationWithBundleIdentifier: Bundle.mainApp!.bundleIdentifier!) - } - mainApp.activate() - } - - @IBAction func openPreferences(_ sender: AnyObject?) { - if #available(macOS 14.0, *) { - NSApp.yieldActivation(toApplicationWithBundleIdentifier: Bundle.mainApp!.bundleIdentifier!) - } - mainApp.openPreferences() - } - - @IBAction func checkForUpdates(_ sender: AnyObject?) { - if #available(macOS 14.0, *) { - NSApp.yieldActivation(toApplicationWithBundleIdentifier: Bundle.mainApp!.bundleIdentifier!) - } - mainApp.checkForUpdates() - } - - @IBAction func quitPostgresMenuHelper(_ sender: AnyObject?) { - for server in serverManager.servers where server.running { - try? server.stopSync() - } - - for app in NSRunningApplication.runningApplications(withBundleIdentifier: "com.postgresapp.Postgres2") { - app.terminate() - } - - NSApp.terminate(nil) - } - -} diff --git a/PostgresMenuHelper/Assets.xcassets/AppIcon.appiconset/Contents.json b/PostgresMenuHelper/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 2db2b1c7c..000000000 --- a/PostgresMenuHelper/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "images" : [ - { - "idiom" : "mac", - "size" : "16x16", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "16x16", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "32x32", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "32x32", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "128x128", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "128x128", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "256x256", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "256x256", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "512x512", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "512x512", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/PostgresMenuHelper/Assets.xcassets/Contents.json b/PostgresMenuHelper/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164c9..000000000 --- a/PostgresMenuHelper/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/PostgresMenuHelper/Assets.xcassets/icon-running.imageset/Contents.json b/PostgresMenuHelper/Assets.xcassets/icon-running.imageset/Contents.json deleted file mode 100644 index 8be281874..000000000 --- a/PostgresMenuHelper/Assets.xcassets/icon-running.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "icon_running.pdf", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/PostgresMenuHelper/Assets.xcassets/icon-running.imageset/icon_running.pdf b/PostgresMenuHelper/Assets.xcassets/icon-running.imageset/icon_running.pdf deleted file mode 100644 index 2c70439dab795660e6d4f87bc3143a17f828c60a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5496 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 EKT)-%5dZ)H diff --git a/PostgresMenuHelper/Base.lproj/MainMenu.xib b/PostgresMenuHelper/Base.lproj/MainMenu.xib deleted file mode 100644 index 7e5bc4826..000000000 --- a/PostgresMenuHelper/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PostgresMenuHelper/Info.plist b/PostgresMenuHelper/Info.plist deleted file mode 100644 index 3a6d2a456..000000000 --- a/PostgresMenuHelper/Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(POSTGRESAPP_SHORT_VERSION) - CFBundleVersion - $(POSTGRESAPP_BUILD_VERSION) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - LSUIElement - - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - NSAppleEventsUsageDescription - Postgres.app Menu Bar Helper uses Apple Script to control Postgres.app - - diff --git a/PostgresMenuHelper/PostgresApplication.h b/PostgresMenuHelper/PostgresApplication.h deleted file mode 100644 index c2dc97151..000000000 --- a/PostgresMenuHelper/PostgresApplication.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * PostgresApplication.h - */ - -#import -#import - -/* - * Standard Suite - */ - -@interface SBApplication(PostgresApplication) - -- (BOOL) openPreferences; // Opens the Preferences. -- (BOOL) checkForUpdates; // Checks for updates. - -@end diff --git a/PostgresMenuHelper/PostgresMenuHelper-Bridging-Header.h b/PostgresMenuHelper/PostgresMenuHelper-Bridging-Header.h deleted file mode 100644 index dbe7c03bb..000000000 --- a/PostgresMenuHelper/PostgresMenuHelper-Bridging-Header.h +++ /dev/null @@ -1,7 +0,0 @@ -// -// Use this file to import your target's public headers that you would like to expose to Swift. -// - -#import "libproc.h" -#import "ErrorRecoveryAttempter.h" -#import "PostgresApplication.h" From 006f5b6eb899f57b30e8bdb2293a62e8ec52ac20 Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 15:28:12 +0200 Subject: [PATCH 08/29] Handle closing settings window --- Postgres/AppDelegate.swift | 5 +++++ Postgres/MainWindow.swift | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index 4f06b0a47..6fdc4a1ce 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -182,6 +182,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe } } + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + NSApp.setActivationPolicy(.accessory) + return false; + } + @IBAction func showMainWindow(_ sender: Any? = nil) { if #available(macOS 14.0, *) { // this seems to help with getting the app to activate diff --git a/Postgres/MainWindow.swift b/Postgres/MainWindow.swift index bee6202c5..fdb62d153 100644 --- a/Postgres/MainWindow.swift +++ b/Postgres/MainWindow.swift @@ -37,9 +37,4 @@ class MainWindowController: NSWindowController, NSWindowDelegate { let model = MainWindowModel() mainWindowModel = model } - - - func windowWillClose(_ notification: Notification) { - NSApp.setActivationPolicy(.accessory) - } } From 300c1177d170471d57a56c9e6a8c7bc7f8fb4a02 Mon Sep 17 00:00:00 2001 From: Tobias Bussmann Date: Wed, 6 May 2026 15:52:37 +0200 Subject: [PATCH 09/29] No longer need to code signed removed helper --- buildscripts/01-build.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/buildscripts/01-build.sh b/buildscripts/01-build.sh index ed04e5aea..5fe1d8dd1 100755 --- a/buildscripts/01-build.sh +++ b/buildscripts/01-build.sh @@ -119,7 +119,6 @@ done codesign --force --timestamp --options runtime --sign "$CODE_SIGN_IDENTITY" \ "$APP"/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate \ "$APP"/Contents/Frameworks/Sparkle.framework/Versions/A/Sparkle \ - "$APP"/Contents/MacOS/PostgresMenuHelper.app \ "$APP"/Contents/Library/LoginItems/PostgresLoginHelper.app \ >>"$LOG_DIR/04-codesign.log" 2>&1 From cf5b1756899a0ce454d716e14284141f96223e5b Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 16:06:59 +0200 Subject: [PATCH 10/29] Allow hiding menu bar item by dragging it with cmd --- Postgres/AppDelegate.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index 6fdc4a1ce..a12194700 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -128,6 +128,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe statusMenu.delegate = self statusItem.button!.image = statusIcon + statusItem.behavior = .removalAllowed // if we don't allow this, macOS Tahoe will disable the icon in system settings if the user drags the icon from the status bar + // TODO: detect when the user removes the icon and toggle the user default accordingly statusItem.isVisible = !hideMenuHelperApp statusItem.menu = statusMenu } From f889762c899dc70911266e01e2a1df41be0c8fac Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 17:13:47 +0200 Subject: [PATCH 11/29] Hide dock icon when app moves to background This avoids weird window server behavior where all windows from the next app are moved to foreground --- Postgres/AppDelegate.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index a12194700..354e0f022 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -184,9 +184,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe } } - func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - NSApp.setActivationPolicy(.accessory) - return false; + func applicationDidResignActive(_ notification: Notification) { + if !NSApp.windows.contains(where: { $0.canBecomeMain }) { + NSApp.setActivationPolicy(.accessory) + } } @IBAction func showMainWindow(_ sender: Any? = nil) { From d19e253d2ba3e3661dd1f8777dc6cd2e3f4e720e Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 6 May 2026 17:15:17 +0200 Subject: [PATCH 12/29] Preferences: Do not disable menu item checkbox when translocated --- Postgres/Base.lproj/Main.storyboard | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Postgres/Base.lproj/Main.storyboard b/Postgres/Base.lproj/Main.storyboard index 2f9a36795..f3ddf77c4 100644 --- a/Postgres/Base.lproj/Main.storyboard +++ b/Postgres/Base.lproj/Main.storyboard @@ -297,11 +297,6 @@ - - - NSNegateBoolean - - NSNegateBoolean From e9cb2922bb5e2ee2a75dccec953097f48a944104 Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Thu, 7 May 2026 10:09:32 +0200 Subject: [PATCH 13/29] Remove unnecessary log statement --- Postgres/LegacyLoginItemRegistration.m | 1 - 1 file changed, 1 deletion(-) diff --git a/Postgres/LegacyLoginItemRegistration.m b/Postgres/LegacyLoginItemRegistration.m index 9ea6d0063..c6b667dac 100644 --- a/Postgres/LegacyLoginItemRegistration.m +++ b/Postgres/LegacyLoginItemRegistration.m @@ -24,7 +24,6 @@ BOOL IsLoginItemRegistered(CFURLRef url) { for (CFIndex i=0; i Date: Fri, 8 May 2026 13:29:57 +0200 Subject: [PATCH 14/29] Change app version in Xcode file to 3alpha This will be overwritten by build scripts but it is useful for dev builds --- Postgres.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Postgres.xcodeproj/project.pbxproj b/Postgres.xcodeproj/project.pbxproj index 38dcd2ac7..001c5b107 100644 --- a/Postgres.xcodeproj/project.pbxproj +++ b/Postgres.xcodeproj/project.pbxproj @@ -726,7 +726,7 @@ PG_BINARIES_DIR = $HOME/PostgresApp/Binaries; PG_BINARIES_VERSIONS = $LATEST_STABLE_PG_VERSION; POSTGRESAPP_BUILD_VERSION = dev; - POSTGRESAPP_SHORT_VERSION = 2.9; + POSTGRESAPP_SHORT_VERSION = 3alpha; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -787,7 +787,7 @@ PG_BINARIES_DIR = $HOME/PostgresApp/Binaries; PG_BINARIES_VERSIONS = $LATEST_STABLE_PG_VERSION; POSTGRESAPP_BUILD_VERSION = dev; - POSTGRESAPP_SHORT_VERSION = 2.9; + POSTGRESAPP_SHORT_VERSION = 3alpha; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; From 9c10eea0b346f9afd639971208cd69a984ea7d14 Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Fri, 8 May 2026 13:31:54 +0200 Subject: [PATCH 15/29] Rewrote app activation code --- Postgres/AppDelegate.swift | 61 +++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index 354e0f022..d06cc9484 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -39,8 +39,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe if NSAppleEventManager.shared().currentAppleEvent?.paramDescriptor(forKeyword: keyAEPropData)?.enumCodeValue == keyAELaunchedAsLogInItem { NSApp.setActivationPolicy(.accessory) } else { - NSApp.setActivationPolicy(.regular) - NSApp.activate(ignoringOtherApps: true) showMainWindow() CrashLogCollector.shared.scanInBackground() } @@ -140,7 +138,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe func menuNeedsUpdate(_ menu: NSMenu) { guard menu == statusMenu else { return } -// serverManager.loadServers() serverManager.refreshServerStatuses() menuItemViewControllers.removeAll() @@ -168,11 +165,44 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe } } - func applicationDidBecomeActive(_ notification: Notification) { - NSApp.setActivationPolicy(.regular) - if !NSApp.windows.contains(where: { $0.canBecomeMain }) { + var hasVisibleWindowsThatCanBecomeKey: Bool { + return NSApp.windows.contains { $0.isVisible && $0.canBecomeKey } + } + + func applicationWillBecomeActive(_ notification: Notification) { + if #available(macOS 13, *) { + NSApp.setActivationPolicy(.regular) + } else { + // This is a workaround for a macOS 12 bug + // When the app is reopened (by double clicking in the Finder or Dock icon) while it is in .accessory mode, + // the menu bar becomes unresponsive. The workaround is to move the app to background, + // change the activation policy, then show it again + if NSApp.activationPolicy() == .accessory { + DispatchQueue.main.async { + NSApp.hide(nil) + DispatchQueue.main.async { + self.showMainWindow() + } + } + return + } + } + } + + func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows: Bool) -> Bool { + if #available(macOS 13, *) { showMainWindow() + } else { + // This is a workaround for a macOS 12 bug + // See comment in applicationWillBecomeActive + if NSApp.activationPolicy() == .regular { + showMainWindow() + } } + return false + } + + func applicationDidBecomeActive(_ notification: Notification) { DispatchQueue.main.async { self.serverManager.refreshServerStatuses() } @@ -184,8 +214,25 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe } } + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + // this function ignores the about window + // we don't want to suddenly hide the app when the about window is still visible + // so we double check that no windows are visible + if !hasVisibleWindowsThatCanBecomeKey { + DispatchQueue.main.async { + // This is a workaround in a macOS bug (last verified macOS 26) + // when setting the activation policy of the frontmost app to .accessory + // macOS brings all windows of the next app to the foreground + // Hiding the app does not trigger this behavior + // The activation policy will later be changed in applicationDidResignActive + NSApp.hide(nil) + } + } + return false + } + func applicationDidResignActive(_ notification: Notification) { - if !NSApp.windows.contains(where: { $0.canBecomeMain }) { + if !hasVisibleWindowsThatCanBecomeKey { NSApp.setActivationPolicy(.accessory) } } From 469f77d88eae224b08dd3a82a922e395cf2da8d9 Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 20 May 2026 14:58:59 +0200 Subject: [PATCH 16/29] Add confirmation before quitting --- Postgres/AppDelegate.swift | 36 +++++++++++++++++++++-------- Postgres/Base.lproj/Main.storyboard | 2 +- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index d06cc9484..0a3b0833b 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -214,6 +214,33 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe } } + @IBAction func quitWithConfirmation(_ sender: AnyObject?) { + let alert = NSAlert() + alert.messageText = "Do you want to completely quit Postgres.app?" + alert.informativeText = "This will stop servers and hide the menu bar icon.\n\nIf you want to continue using PostgreSQL servers, Postgres.app can move to the background instead." + alert.addButton(withTitle: "Quit") + alert.addButton(withTitle: "Move to background") + alert.addButton(withTitle: "Cancel") + switch alert.runModal() { + case .alertFirstButtonReturn: + NSApp.terminate(nil) + case .alertSecondButtonReturn: + var didClose = false + for window in NSApp.windows where window.isVisible && window.canBecomeKey { + // app will hide after last window was closed + window.performClose(nil) + didClose = true + } + if !didClose { + // no windows were open + // just hide the app + NSApp.hide(nil) + } + default: + break + } + } + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { // this function ignores the about window // we don't want to suddenly hide the app when the about window is still visible @@ -325,13 +352,4 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe menuApp.terminate() } } - - func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { - if isTranslocated() { - for server in serverManager.servers where server.running && server.binPath.hasPrefix(Bundle.main.bundlePath) { - try? server.stopSync() - } - } - return .terminateNow - } } diff --git a/Postgres/Base.lproj/Main.storyboard b/Postgres/Base.lproj/Main.storyboard index f3ddf77c4..1ff61bc4d 100644 --- a/Postgres/Base.lproj/Main.storyboard +++ b/Postgres/Base.lproj/Main.storyboard @@ -438,7 +438,7 @@ - + From 30688cc36299bb36998bf7b24cc08139c321fed1 Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Wed, 20 May 2026 17:12:07 +0200 Subject: [PATCH 17/29] Refactor handling of "launch at login" and "show menu bar icon" setting --- Postgres/AppDelegate.swift | 150 +++++++++++++------------ Postgres/Base.lproj/Main.storyboard | 3 +- Postgres/LegacyLoginItemRegistration.m | 20 ++++ Postgres/Preferences.swift | 50 ++++++++- Postgres/UserDefaults.swift | 1 + 5 files changed, 143 insertions(+), 81 deletions(-) diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index 0a3b0833b..ebcfb935c 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -13,8 +13,6 @@ import ServiceManagement class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDelegate, NSMenuDelegate { let serverManager: ServerManager = ServerManager.shared - var hideMenuHelperApp = UserDefaults.standard.bool(forKey: "HideMenuHelperApp") - var startLoginHelper = UserDefaults.standard.bool(forKey: "StartLoginHelper") let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) let statusIcon = NSImage(named: "statusicon")! @@ -76,42 +74,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe DistributedNotificationCenter.default.addObserver(forName: Server.StatusChangedNotification, object: nil, queue: OperationQueue.main) { _ in self.serverManager.refreshServerStatuses() } - - NotificationCenter.default.addObserver(forName: UserDefaults.didChangeNotification, object: nil, queue: OperationQueue.main) { _ in - let hideMenuHelperApp = UserDefaults.standard.bool(forKey: "HideMenuHelperApp") - if self.hideMenuHelperApp != hideMenuHelperApp { - self.hideMenuHelperApp = hideMenuHelperApp - self.statusItem.isVisible = !hideMenuHelperApp - } - if #available(macOS 13, *) { - // this setting was removed in macOS 13 - // since we try to register the login item in any case - // user can enable / disable login item in system settings - } else { - let startLoginHelper = UserDefaults.standard.bool(forKey: "StartLoginHelper") - if self.startLoginHelper != startLoginHelper { - self.startLoginHelper = startLoginHelper - if self.startLoginHelper { - self.createLaunchAgent() - } else { - self.destroyLaunchAgent() - } - } - } - } - - if !isTranslocated() { - if #available(macOS 13, *) { - destroyLaunchAgent() - registerLoginItem() - } else { - if startLoginHelper { - createLaunchAgent() - } else { - destroyLaunchAgent() - } - } - } + + configureLoginItem() for server in serverManager.servers where server.startOnLogin && server.serverStatus == .Startable { server.start { _ in } @@ -127,11 +91,34 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe statusItem.button!.image = statusIcon statusItem.behavior = .removalAllowed // if we don't allow this, macOS Tahoe will disable the icon in system settings if the user drags the icon from the status bar - // TODO: detect when the user removes the icon and toggle the user default accordingly - statusItem.isVisible = !hideMenuHelperApp + statusItem.isVisible = !UserDefaults.standard.bool(forKey: "HideMenuHelperApp") + UserDefaults.standard.addObserver(self, forKeyPath: "HideMenuHelperApp", context: nil) + statusItem.addObserver(self, forKeyPath: "isVisible", context: nil) statusItem.menu = statusMenu } + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if let object = object as? AnyObject { + if object === statusItem && keyPath == "isVisible" { + // detect when the user hides icon by dragging away from menu bar + let hideMenuItem = !statusItem.isVisible + if UserDefaults.standard.bool(forKey: "HideMenuHelperApp") != hideMenuItem { + UserDefaults.standard.set(hideMenuItem, forKey: "HideMenuHelperApp") + } + return + } + if object is UserDefaults && keyPath == "HideMenuHelperApp" { + // detect when the user hides icon by changing user defaults + let statusItemIsVisible = !UserDefaults.standard.bool(forKey: "HideMenuHelperApp") + if statusItem.isVisible != statusItemIsVisible { + statusItem.isVisible = statusItemIsVisible + } + return + } + } + super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) + } + let statusMenu = NSMenu() var menuItemViewControllers: [MenuItemViewController] = [] @@ -295,54 +282,69 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe } - @available(macOS 13, *) private func registerLoginItem() { - let loginHelper = SMAppService.loginItem(identifier:"com.postgresapp.Postgres2LoginHelper") - do { - try loginHelper.register() - } catch let error { - // This most likely means that the user disabled the login item in system setting - // We ignore the error, but print it to stdout for easier debugging - print("Failed to register login item because: \(error)") + private func configureLoginItem() { + if isTranslocated() { + return } - } - - private func createLaunchAgent() { - let laPath = NSHomeDirectory().appending("/Library/LaunchAgents") - let laName = "com.postgresapp.Postgres2LoginHelper" - if !FileManager.default.fileExists(atPath: laPath) { + + let laPath = NSHomeDirectory() + "/Library/LaunchAgents/com.postgresapp.Postgres2LoginHelper.plist" + if FileManager.default.fileExists(atPath: laPath) { + // found a legacy launch agent + // migrate it to the new system do { - try FileManager.default.createDirectory(atPath: laPath, withIntermediateDirectories: true, attributes: nil) + try FileManager.default.removeItem(atPath: laPath) + UserDefaults.standard.set(false, forKey: "StartLoginHelper") // prevent legacy postgres.app from re-adding the launch agent } catch let error as NSError { - NSLog("Could not create directory at \(laPath): \(error)") - return + NSLog("Could not delete launch agent \(laPath): \(error)") } + registerLoginItem() + return } - let plistPath = laPath+"/"+laName+".plist" - let attributes: [FileAttributeKey: Any] = [.posixPermissions: 0o600] - do { - let data = try Data(contentsOf: Bundle.main.url(forResource: laName, withExtension: "plist")!) - if !FileManager.default.createFile(atPath: plistPath, contents: data, attributes: attributes) { - NSLog("Could not create plist file at \(plistPath)") + if UserDefaults.standard.bool(forKey: UserDefaults.LoginItemWasRegisteredKey) { + // we don't want to add it back if it was removed by the user + return + } + + if #available(macOS 13, *) { + let loginItemStatus = SMAppService.loginItem(identifier:"com.postgresapp.Postgres2LoginHelper").status + switch loginItemStatus { + case .enabled, .requiresApproval: + // just leave it like it is + UserDefaults.standard.set(true, forKey: UserDefaults.LoginItemWasRegisteredKey) + return + case .notFound, .notRegistered: + // we can still register it + break + @unknown default: + // not sure what we should do here + NSLog("Unknown login item status: \(loginItemStatus)") + break } - } catch let error as NSError { - NSLog("Error getting data of original plist file: \(error)") } + + if UserDefaults.standard.bool(forKey: "StartLoginHelper") == false { + // this must have been set by a previous version of Postgres.app + // don't auto-add the login item + return + } + + registerLoginItem() } - private func destroyLaunchAgent() { - let laPath = NSHomeDirectory() + "/Library/LaunchAgents/com.postgresapp.Postgres2LoginHelper.plist" - if FileManager.default.fileExists(atPath: laPath) { - do { - try FileManager.default.removeItem(atPath: laPath) - } catch let error as NSError { - NSLog("Could not delete launch agent \(laPath): \(error)") + private func registerLoginItem() { + do { + if #available(macOS 13, *) { + try SMAppService.mainApp.register() + } else { + RegisterLegacyLoginItem(Bundle.main.bundleURL as CFURL) } + UserDefaults.standard.set(true, forKey: UserDefaults.LoginItemWasRegisteredKey) + } catch let error as NSError { + NSLog("Could not add app to login items: \(error)") } } - - // SUUpdater delegate methods func updater(_ updater: SUUpdater, willInstallUpdate item: SUAppcastItem) { for server in serverManager.servers where server.running { diff --git a/Postgres/Base.lproj/Main.storyboard b/Postgres/Base.lproj/Main.storyboard index 1ff61bc4d..b1c1de1cc 100644 --- a/Postgres/Base.lproj/Main.storyboard +++ b/Postgres/Base.lproj/Main.storyboard @@ -316,8 +316,7 @@ NSNegateBoolean - - + diff --git a/Postgres/LegacyLoginItemRegistration.m b/Postgres/LegacyLoginItemRegistration.m index c6b667dac..125bc5447 100644 --- a/Postgres/LegacyLoginItemRegistration.m +++ b/Postgres/LegacyLoginItemRegistration.m @@ -34,3 +34,23 @@ BOOL IsLoginItemRegistered(CFURLRef url) { CFRelease(loginItemsList); return foundMatch; } + +void UnregisterLegacyLoginItem(CFURLRef url) { + LSSharedFileListRef loginItemsList = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems, nil); + CFArrayRef loginItems = LSSharedFileListCopySnapshot(loginItemsList, nil); + CFIndex count = CFArrayGetCount(loginItems); + for (CFIndex i=0; i Date: Wed, 20 May 2026 17:16:37 +0200 Subject: [PATCH 18/29] Rename "Preferences" window to "Postgres App Settings" --- Postgres/Base.lproj/Main.storyboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Postgres/Base.lproj/Main.storyboard b/Postgres/Base.lproj/Main.storyboard index b1c1de1cc..457e4ea6e 100644 --- a/Postgres/Base.lproj/Main.storyboard +++ b/Postgres/Base.lproj/Main.storyboard @@ -10,7 +10,7 @@ - + From f55b2c6470bfd2c7daff4a2e94e6d0585d7e86f5 Mon Sep 17 00:00:00 2001 From: Tobias Bussmann Date: Wed, 3 Jun 2026 11:29:28 +0200 Subject: [PATCH 19/29] Rename settings window --- Postgres/Base.lproj/Main.storyboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Postgres/Base.lproj/Main.storyboard b/Postgres/Base.lproj/Main.storyboard index 457e4ea6e..6f0394ed0 100644 --- a/Postgres/Base.lproj/Main.storyboard +++ b/Postgres/Base.lproj/Main.storyboard @@ -10,7 +10,7 @@ - + From 53cefeff819c44a71c6b83d25312bcec60cae055 Mon Sep 17 00:00:00 2001 From: Tobias Bussmann Date: Wed, 3 Jun 2026 11:45:00 +0200 Subject: [PATCH 20/29] unregister old loginItem unconditionally on >=macOS13 --- Postgres/Preferences.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Postgres/Preferences.swift b/Postgres/Preferences.swift index fcb3d5e1f..77f95b0de 100644 --- a/Postgres/Preferences.swift +++ b/Postgres/Preferences.swift @@ -49,6 +49,7 @@ class PreferencesViewController: NSViewController { } else { // unregister login item if SMAppService.mainApp.status == .enabled { + try? SMAppService.loginItem(identifier:"com.postgresapp.Postgres2LoginHelper").unregister() try SMAppService.mainApp.unregister() } else { try SMAppService.loginItem(identifier:"com.postgresapp.Postgres2LoginHelper").unregister() From b2e58f97f757de3a151c8bfda34eea45fe1d98c5 Mon Sep 17 00:00:00 2001 From: Tobias Bussmann Date: Thu, 4 Jun 2026 02:27:53 +0200 Subject: [PATCH 21/29] increase deployment target to macOS 11 remove 10.13 swift fix from dcefa8b that was obsolote since 37040ae --- Postgres.xcodeproj/project.pbxproj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Postgres.xcodeproj/project.pbxproj b/Postgres.xcodeproj/project.pbxproj index 001c5b107..80fb3858d 100644 --- a/Postgres.xcodeproj/project.pbxproj +++ b/Postgres.xcodeproj/project.pbxproj @@ -718,11 +718,10 @@ HEADER_SEARCH_PATHS = "\"$(SRCROOT)/src-staticlibs\""; IGNORE_MISSING_BINARIES = 1; LATEST_STABLE_PG_VERSION = ""; - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_CODE_SIGN_FLAGS = "--prefix com.postgresapp."; - OTHER_LDFLAGS = "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/macosx/libswiftAppKit.dylib"; PG_BINARIES_DIR = $HOME/PostgresApp/Binaries; PG_BINARIES_VERSIONS = $LATEST_STABLE_PG_VERSION; POSTGRESAPP_BUILD_VERSION = dev; @@ -780,10 +779,9 @@ HEADER_SEARCH_PATHS = "\"$(SRCROOT)/src-staticlibs\""; IGNORE_MISSING_BINARIES = ""; LATEST_STABLE_PG_VERSION = ""; - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11; MTL_ENABLE_DEBUG_INFO = NO; OTHER_CODE_SIGN_FLAGS = "--prefix com.postgresapp."; - OTHER_LDFLAGS = "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/macosx/libswiftAppKit.dylib"; PG_BINARIES_DIR = $HOME/PostgresApp/Binaries; PG_BINARIES_VERSIONS = $LATEST_STABLE_PG_VERSION; POSTGRESAPP_BUILD_VERSION = dev; From 7f2d8efe6a93b3529fe3cc3b303dcd00abfc0dd2 Mon Sep 17 00:00:00 2001 From: Tobias Bussmann Date: Thu, 4 Jun 2026 02:49:04 +0200 Subject: [PATCH 22/29] remove compatibility for older macOS this also reverts 1262da3 and 42f8249 --- Postgres/Base.lproj/Main.storyboard | 5 ++--- Postgres/ClientLauncher.swift | 14 +++----------- Postgres/PreferencesClientCellView.swift | 12 ------------ Postgres/Server.swift | 13 ++----------- 4 files changed, 7 insertions(+), 37 deletions(-) diff --git a/Postgres/Base.lproj/Main.storyboard b/Postgres/Base.lproj/Main.storyboard index 6f0394ed0..94c3ce182 100644 --- a/Postgres/Base.lproj/Main.storyboard +++ b/Postgres/Base.lproj/Main.storyboard @@ -108,7 +108,7 @@ - + @@ -212,7 +212,7 @@ @@ -333,6 +329,21 @@ + + + + + + + + + + + NSNegateBoolean + + + + @@ -345,6 +356,7 @@ + @@ -354,6 +366,7 @@ + diff --git a/Postgres/LaunchAtLoginManager.swift b/Postgres/LaunchAtLoginManager.swift index 7b0a62c4e..c4b976b62 100644 --- a/Postgres/LaunchAtLoginManager.swift +++ b/Postgres/LaunchAtLoginManager.swift @@ -16,6 +16,18 @@ extension UserDefaults { class LaunchAtLoginManager { static var shared = LaunchAtLoginManager() + var requiresApproval : Bool { + if #available(macOS 13.0, *) { + if SMAppService.mainApp.status == .enabled { + return false + } + if SMAppService.loginItem(identifier:"com.postgresapp.Postgres2LoginHelper").status == .requiresApproval { + return true + } + } + return false + } + var isLaunchAtLoginEnabled : Bool { if #available(macOS 13, *) { if SMAppService.mainApp.status == .enabled { @@ -32,15 +44,10 @@ class LaunchAtLoginManager { func registerLoginItem() throws { UserDefaults.standard.set(true, forKey: UserDefaults.LoginItemWasRegisteredKey) - do { - if #available(macOS 13, *) { - // clean up login item if necessary - try? SMAppService.loginItem(identifier:"com.postgresapp.Postgres2LoginHelper").unregister() - // register main app as login item - try SMAppService.mainApp.register() - } else { - RegisterLegacyLoginItem(Bundle.main.bundleURL as CFURL) - } + if #available(macOS 13, *) { + try SMAppService.loginItem(identifier:"com.postgresapp.Postgres2LoginHelper").register() + } else { + RegisterLegacyLoginItem(Bundle.main.bundleURL as CFURL) } guard isLaunchAtLoginEnabled else { throw LaunchAtLoginManagerError(errorDescription: "Registering login item failed", recoverySuggestion: "Make sure Postgres.app has permission to run in the background in system settings.") diff --git a/Postgres/Preferences.swift b/Postgres/Preferences.swift index 333fafb90..c3d0ff923 100644 --- a/Postgres/Preferences.swift +++ b/Postgres/Preferences.swift @@ -52,6 +52,14 @@ class PreferencesViewController: NSViewController { Bundle.main.bundlePath.contains("/AppTranslocation/") } + @objc dynamic var launchAtLoginRequiresApproval: Bool { + LaunchAtLoginManager.shared.requiresApproval + } + + @objc dynamic var isLaunchAtLoginCheckboxEnabled: Bool { + !isTranslocated && !launchAtLoginRequiresApproval + } + @IBAction func resetAppPermissions(_ sender: Any?) { UserDefaults.shared.set(nil, forKey: "ClientApplicationPermissions") } From 03c94dfb8f8075b6798358bb934f5a15e567c89f Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Thu, 25 Jun 2026 10:25:43 +0200 Subject: [PATCH 27/29] Enable update checks by default Since Sparkle now runs on login, we don't want to annoy the user when no update is available --- Postgres/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Postgres/Info.plist b/Postgres/Info.plist index 96ffcbafd..2a5b09fd0 100644 --- a/Postgres/Info.plist +++ b/Postgres/Info.plist @@ -38,6 +38,8 @@ https://postgresapp.com/sparkle/updates_$(PG_BINARIES_VERSIONS).xml SUPublicDSAKeyFile dsa_pub.pem + SUEnableAutomaticChecks + NSAppleEventsUsageDescription Postgres.app uses Apple Script so you can automatically connect to a database with a double click. LSUIElement From 8b1dda9264d25228b028971b273372e8cb38cf64 Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Thu, 25 Jun 2026 13:39:50 +0200 Subject: [PATCH 28/29] Hide app only when main window is closed The -applicationShouldTerminateAfterLastWindowClosed caused unexpected behavior for Sparkle: It was called when the progress window was closed, and caused the app to hide just before Sparkle showed the result of the update check. --- Postgres/AppDelegate.swift | 12 +++++------- Postgres/MainWindow.swift | 5 +++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index eddacbb07..ee30bba97 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -223,12 +223,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe } } - func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - // this function ignores the about window - // we don't want to suddenly hide the app when the about window is still visible - // so we double check that no windows are visible - if !hasVisibleWindowsThatCanBecomeKey { - DispatchQueue.main.async { + func mainWindowWillClose(_ notification: Notification) { + DispatchQueue.main.async { + // we don't want to suddenly hide the app when other windows are still open (about window, sparkle etc) + // so we check that no windows are visible + if !self.hasVisibleWindowsThatCanBecomeKey { // This is a workaround in a macOS bug (last verified macOS 26) // when setting the activation policy of the frontmost app to .accessory // macOS brings all windows of the next app to the foreground @@ -237,7 +236,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe NSApp.hide(nil) } } - return false } func applicationDidResignActive(_ notification: Notification) { diff --git a/Postgres/MainWindow.swift b/Postgres/MainWindow.swift index fdb62d153..cc3322e10 100644 --- a/Postgres/MainWindow.swift +++ b/Postgres/MainWindow.swift @@ -37,4 +37,9 @@ class MainWindowController: NSWindowController, NSWindowDelegate { let model = MainWindowModel() mainWindowModel = model } + + func windowWillClose(_ notification: Notification) { + let delegate = NSApp.delegate as! AppDelegate + delegate.mainWindowWillClose(notification) + } } From 823ff0177248e1ab8f923112db0c319022121089 Mon Sep 17 00:00:00 2001 From: Jakob Egger Date: Thu, 25 Jun 2026 13:50:19 +0200 Subject: [PATCH 29/29] Make sure "Move to Background" always hides the app --- Postgres/AppDelegate.swift | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Postgres/AppDelegate.swift b/Postgres/AppDelegate.swift index ee30bba97..f56ec472f 100644 --- a/Postgres/AppDelegate.swift +++ b/Postgres/AppDelegate.swift @@ -207,17 +207,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, SUUpdaterDelegate, NSAlertDe case .alertFirstButtonReturn: NSApp.terminate(nil) case .alertSecondButtonReturn: - var didClose = false for window in NSApp.windows where window.isVisible && window.canBecomeKey { - // app will hide after last window was closed window.performClose(nil) - didClose = true - } - if !didClose { - // no windows were open - // just hide the app - NSApp.hide(nil) } + NSApp.hide(nil) default: break }