Skip to content

Commit

Permalink
✨ add "Launch at login" item for macOS 13+
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardodino committed Jun 4, 2023
1 parent 44c880d commit d313af1
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 20 deletions.
4 changes: 4 additions & 0 deletions BeezyLight.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
965A05CF29C5224000559377 /* AudioSystemObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965A05CE29C5224000559377 /* AudioSystemObject.swift */; };
965A05D129C522C000559377 /* AudioObjectProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965A05D029C522C000559377 /* AudioObjectProperty.swift */; };
965A05D329C524F100559377 /* AudioBufferListWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965A05D229C524F100559377 /* AudioBufferListWrapper.swift */; };
966385472A2BF0420092FC62 /* LaunchAtLogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966385462A2BF0420092FC62 /* LaunchAtLogin.swift */; };
96B8901227F4E78D00C39B4A /* AboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96B8901127F4E78D00C39B4A /* AboutWindow.swift */; };
96C2A1E227C5632D00768B18 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C2A1E127C5632D00768B18 /* AppDelegate.swift */; };
96C2A1E627C5632E00768B18 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96C2A1E527C5632E00768B18 /* Assets.xcassets */; };
Expand All @@ -30,6 +31,7 @@
965A05CE29C5224000559377 /* AudioSystemObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSystemObject.swift; sourceTree = "<group>"; };
965A05D029C522C000559377 /* AudioObjectProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioObjectProperty.swift; sourceTree = "<group>"; };
965A05D229C524F100559377 /* AudioBufferListWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioBufferListWrapper.swift; sourceTree = "<group>"; };
966385462A2BF0420092FC62 /* LaunchAtLogin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAtLogin.swift; sourceTree = "<group>"; };
96A53DDC27C572B9002DA809 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
96B8901127F4E78D00C39B4A /* AboutWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutWindow.swift; sourceTree = "<group>"; };
96B9AA4828774C1A00175C2A /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
Expand Down Expand Up @@ -77,6 +79,7 @@
96C2A1E127C5632D00768B18 /* AppDelegate.swift */,
962377FD27F4ECD8002D718F /* StatusItem.swift */,
96B8901127F4E78D00C39B4A /* AboutWindow.swift */,
966385462A2BF0420092FC62 /* LaunchAtLogin.swift */,
96C2A1F327C567C300768B18 /* BlinkStick.swift */,
96D5B44727C57CE300C4FFCA /* Debouncer.swift */,
96D07DC529D7974B009C2C23 /* AudioInput.swift */,
Expand Down Expand Up @@ -209,6 +212,7 @@
965A05CF29C5224000559377 /* AudioSystemObject.swift in Sources */,
96C2A1F527C567C300768B18 /* BlinkStick.swift in Sources */,
96C2A1E227C5632D00768B18 /* AppDelegate.swift in Sources */,
966385472A2BF0420092FC62 /* LaunchAtLogin.swift in Sources */,
965A05CB29C5212400559377 /* AudioDevice.swift in Sources */,
965A05CD29C5214000559377 /* AudioObject.swift in Sources */,
962377FE27F4ECD8002D718F /* StatusItem.swift in Sources */,
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,3 @@
#### &ensp;install:
1. download lastest version [**here**](https://github.com/leonardodino/BeezyLight/releases/latest/download/BeezyLight.zip)
2. unzip and move `BeezyLight.app` to `/Applications`
3. add to [**Login Items**](https://support.apple.com/en-gb/guide/mac-help/mh15189/mac) to launch it automatically after login in **(optional)**
6 changes: 6 additions & 0 deletions Sources/AboutWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ extension NSApplication.AboutPanelOptionKey {

class AboutWindow {
static let shared = AboutWindow()
let menuItem: NSMenuItem
private init() {
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") ?? ""
menuItem = NSMenuItem(title: "About \(appName)", action: #selector(AboutWindow.show), keyEquivalent: "")
menuItem.target = self
}
@objc func show() {
NSApp.orderFrontStandardAboutPanel(options: [
.copyright: "",
Expand Down
46 changes: 46 additions & 0 deletions Sources/LaunchAtLogin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Cocoa
import ServiceManagement

@available(macOS 13.0, *)
final class LaunchAtLogin {
static let shared = LaunchAtLogin()
private(set) var menuItem: NSMenuItem

private init() {
menuItem = NSMenuItem(title: "Launch at login", action: #selector(LaunchAtLogin.toggle), keyEquivalent: "")
menuItem.target = self
menuItem.state = state
}

private func set(_ launch: Bool) {
if launch {
if SMAppService.mainApp.status == .enabled {
try? SMAppService.mainApp.unregister()
}
try? SMAppService.mainApp.register()
} else {
try? SMAppService.mainApp.unregister()
}
}

private var state: NSControl.StateValue {
switch SMAppService.mainApp.status {
case .notRegistered: return .off
case .enabled: return .on
case .requiresApproval: return .mixed
case .notFound: return .off
@unknown default: return .off
}
}

@objc
func toggle() {
switch state {
case .on: set(false)
case .off: set(true)
default: SMAppService.openSystemSettingsLoginItems()
}
menuItem.state = state
}
}

38 changes: 19 additions & 19 deletions Sources/StatusItem.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import Cocoa
import ServiceManagement

fileprivate extension NSApplication {
var quitMenuItem: NSMenuItem {
NSMenuItem(title: "Quit", action: #selector(terminate), keyEquivalent: "q")
}
}

class StatusItem {
enum StateIcon {
Expand All @@ -17,29 +24,22 @@ class StatusItem {

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

private lazy var menu: NSMenu = {
let menu = NSMenu()
menu.addItem(AboutWindow.shared.menuItem)
if #available(macOS 13.0, *) {
menu.addItem(LaunchAtLogin.shared.menuItem)
menu.addItem(NSMenuItem.separator())
}
menu.addItem(NSApplication.shared.quitMenuItem)
return menu
}()

init() {
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") ?? ""
instance.menu = NSMenu([
NSMenuItem("About \(appName)", action: #selector(AboutWindow.show), target: AboutWindow.shared),
NSMenuItem("Quit", action: #selector(NSApp.terminate), keyEquivalent: "q"),
])
instance.menu = menu
}

func setIcon(_ icon: StateIcon) {
instance.button?.image = icon.image
}
}

fileprivate extension NSMenuItem {
convenience init(_ title: String, action: Selector, target: AnyObject? = nil, keyEquivalent: String = "") {
self.init(title: title, action: action, keyEquivalent: keyEquivalent)
self.target = target
}
}

fileprivate extension NSMenu {
convenience init(_ items: [NSMenuItem]) {
self.init()
items.forEach { self.addItem($0) }
}
}

0 comments on commit d313af1

Please sign in to comment.