Skip to content

Commit d313af1

Browse files
committed
✨ add "Launch at login" item for macOS 13+
1 parent 44c880d commit d313af1

File tree

5 files changed

+75
-20
lines changed

5 files changed

+75
-20
lines changed

BeezyLight.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
965A05CF29C5224000559377 /* AudioSystemObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965A05CE29C5224000559377 /* AudioSystemObject.swift */; };
1414
965A05D129C522C000559377 /* AudioObjectProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965A05D029C522C000559377 /* AudioObjectProperty.swift */; };
1515
965A05D329C524F100559377 /* AudioBufferListWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965A05D229C524F100559377 /* AudioBufferListWrapper.swift */; };
16+
966385472A2BF0420092FC62 /* LaunchAtLogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966385462A2BF0420092FC62 /* LaunchAtLogin.swift */; };
1617
96B8901227F4E78D00C39B4A /* AboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96B8901127F4E78D00C39B4A /* AboutWindow.swift */; };
1718
96C2A1E227C5632D00768B18 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C2A1E127C5632D00768B18 /* AppDelegate.swift */; };
1819
96C2A1E627C5632E00768B18 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96C2A1E527C5632E00768B18 /* Assets.xcassets */; };
@@ -30,6 +31,7 @@
3031
965A05CE29C5224000559377 /* AudioSystemObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSystemObject.swift; sourceTree = "<group>"; };
3132
965A05D029C522C000559377 /* AudioObjectProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioObjectProperty.swift; sourceTree = "<group>"; };
3233
965A05D229C524F100559377 /* AudioBufferListWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioBufferListWrapper.swift; sourceTree = "<group>"; };
34+
966385462A2BF0420092FC62 /* LaunchAtLogin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAtLogin.swift; sourceTree = "<group>"; };
3335
96A53DDC27C572B9002DA809 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3436
96B8901127F4E78D00C39B4A /* AboutWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutWindow.swift; sourceTree = "<group>"; };
3537
96B9AA4828774C1A00175C2A /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
@@ -77,6 +79,7 @@
7779
96C2A1E127C5632D00768B18 /* AppDelegate.swift */,
7880
962377FD27F4ECD8002D718F /* StatusItem.swift */,
7981
96B8901127F4E78D00C39B4A /* AboutWindow.swift */,
82+
966385462A2BF0420092FC62 /* LaunchAtLogin.swift */,
8083
96C2A1F327C567C300768B18 /* BlinkStick.swift */,
8184
96D5B44727C57CE300C4FFCA /* Debouncer.swift */,
8285
96D07DC529D7974B009C2C23 /* AudioInput.swift */,
@@ -209,6 +212,7 @@
209212
965A05CF29C5224000559377 /* AudioSystemObject.swift in Sources */,
210213
96C2A1F527C567C300768B18 /* BlinkStick.swift in Sources */,
211214
96C2A1E227C5632D00768B18 /* AppDelegate.swift in Sources */,
215+
966385472A2BF0420092FC62 /* LaunchAtLogin.swift in Sources */,
212216
965A05CB29C5212400559377 /* AudioDevice.swift in Sources */,
213217
965A05CD29C5214000559377 /* AudioObject.swift in Sources */,
214218
962377FE27F4ECD8002D718F /* StatusItem.swift in Sources */,

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,3 @@
1717
#### &ensp;install:
1818
1. download lastest version [**here**](https://github.com/leonardodino/BeezyLight/releases/latest/download/BeezyLight.zip)
1919
2. unzip and move `BeezyLight.app` to `/Applications`
20-
3. add to [**Login Items**](https://support.apple.com/en-gb/guide/mac-help/mh15189/mac) to launch it automatically after login in **(optional)**

Sources/AboutWindow.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ extension NSApplication.AboutPanelOptionKey {
2828

2929
class AboutWindow {
3030
static let shared = AboutWindow()
31+
let menuItem: NSMenuItem
32+
private init() {
33+
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") ?? ""
34+
menuItem = NSMenuItem(title: "About \(appName)", action: #selector(AboutWindow.show), keyEquivalent: "")
35+
menuItem.target = self
36+
}
3137
@objc func show() {
3238
NSApp.orderFrontStandardAboutPanel(options: [
3339
.copyright: "",

Sources/LaunchAtLogin.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import Cocoa
2+
import ServiceManagement
3+
4+
@available(macOS 13.0, *)
5+
final class LaunchAtLogin {
6+
static let shared = LaunchAtLogin()
7+
private(set) var menuItem: NSMenuItem
8+
9+
private init() {
10+
menuItem = NSMenuItem(title: "Launch at login", action: #selector(LaunchAtLogin.toggle), keyEquivalent: "")
11+
menuItem.target = self
12+
menuItem.state = state
13+
}
14+
15+
private func set(_ launch: Bool) {
16+
if launch {
17+
if SMAppService.mainApp.status == .enabled {
18+
try? SMAppService.mainApp.unregister()
19+
}
20+
try? SMAppService.mainApp.register()
21+
} else {
22+
try? SMAppService.mainApp.unregister()
23+
}
24+
}
25+
26+
private var state: NSControl.StateValue {
27+
switch SMAppService.mainApp.status {
28+
case .notRegistered: return .off
29+
case .enabled: return .on
30+
case .requiresApproval: return .mixed
31+
case .notFound: return .off
32+
@unknown default: return .off
33+
}
34+
}
35+
36+
@objc
37+
func toggle() {
38+
switch state {
39+
case .on: set(false)
40+
case .off: set(true)
41+
default: SMAppService.openSystemSettingsLoginItems()
42+
}
43+
menuItem.state = state
44+
}
45+
}
46+

Sources/StatusItem.swift

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
import Cocoa
2+
import ServiceManagement
3+
4+
fileprivate extension NSApplication {
5+
var quitMenuItem: NSMenuItem {
6+
NSMenuItem(title: "Quit", action: #selector(terminate), keyEquivalent: "q")
7+
}
8+
}
29

310
class StatusItem {
411
enum StateIcon {
@@ -17,29 +24,22 @@ class StatusItem {
1724

1825
private let instance = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
1926

27+
private lazy var menu: NSMenu = {
28+
let menu = NSMenu()
29+
menu.addItem(AboutWindow.shared.menuItem)
30+
if #available(macOS 13.0, *) {
31+
menu.addItem(LaunchAtLogin.shared.menuItem)
32+
menu.addItem(NSMenuItem.separator())
33+
}
34+
menu.addItem(NSApplication.shared.quitMenuItem)
35+
return menu
36+
}()
37+
2038
init() {
21-
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") ?? ""
22-
instance.menu = NSMenu([
23-
NSMenuItem("About \(appName)", action: #selector(AboutWindow.show), target: AboutWindow.shared),
24-
NSMenuItem("Quit", action: #selector(NSApp.terminate), keyEquivalent: "q"),
25-
])
39+
instance.menu = menu
2640
}
2741

2842
func setIcon(_ icon: StateIcon) {
2943
instance.button?.image = icon.image
3044
}
3145
}
32-
33-
fileprivate extension NSMenuItem {
34-
convenience init(_ title: String, action: Selector, target: AnyObject? = nil, keyEquivalent: String = "") {
35-
self.init(title: title, action: action, keyEquivalent: keyEquivalent)
36-
self.target = target
37-
}
38-
}
39-
40-
fileprivate extension NSMenu {
41-
convenience init(_ items: [NSMenuItem]) {
42-
self.init()
43-
items.forEach { self.addItem($0) }
44-
}
45-
}

0 commit comments

Comments
 (0)