diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 4ee9c91..407bddd 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -16,6 +16,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa let unlockRSSIMenu = NSMenu() let timeoutMenu = NSMenu() let lockDelayMenu = NSMenu() + let externalDisplayMenu = NSMenu() var deviceDict: [UUID: NSMenuItem] = [:] var monitorMenuItem : NSMenuItem? let prefs = UserDefaults.standard @@ -30,6 +31,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa var unlockedAt = 0.0 var inScreensaver = false var lastRSSI: Int? = nil + var externalDisplayModelOnly = false func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { @@ -66,6 +68,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa item.state = .off } } + } else if menu == externalDisplayMenu { + updateExternalMonitor() } } @@ -277,6 +281,60 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa return false } + func getDisplayUUIDs() -> [(localizedName: String, uuidString: String)] { + var displayInfoArray: [(localizedName: String, uuidString: String)] = [] + + let screens = NSScreen.screens + for screen in screens { + let deviceDescription = screen.deviceDescription + if let screenNumber = deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID { + let uuidRef = CGDisplayCreateUUIDFromDisplayID(screenNumber)?.takeRetainedValue() + if let uuidRef = uuidRef { + let uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef) as String + if #available(macOS 10.15, *) { + displayInfoArray.append((localizedName: screen.localizedName, uuidString: uuidString)) + } else { + displayInfoArray.append((localizedName: uuidString, uuidString: uuidString)) + } + } + } + } + return displayInfoArray + } + + func updateExternalMonitor() { + externalDisplayMenu.removeAllItems() + let selectedDisplayUUIDs = prefs.array(forKey: "externalDisplays") as? [String] ?? [] + let displays = getDisplayUUIDs() + + for display in displays { + let menuItem = NSMenuItem(title: display.localizedName, action: nil, keyEquivalent: "") + menuItem.representedObject = display.uuidString + + if display.localizedName.contains("Built-in") { + menuItem.isEnabled = false + } else { + menuItem.action = #selector(setExternalDisplays(_:)) + menuItem.target = self + menuItem.state = selectedDisplayUUIDs.contains(display.uuidString) ? .on : .off + } + + externalDisplayMenu.addItem(menuItem) + } + } + + func isExternalDisplayConnected() -> Bool { + let selectedDisplayUUIDs = prefs.array(forKey: "externalDisplays") as? [String] ?? [] + + if selectedDisplayUUIDs.isEmpty { + return false + } + + let connectedDisplayUUIDs = getDisplayUUIDs().map { $0.uuidString } + + return selectedDisplayUUIDs.allSatisfy { connectedDisplayUUIDs.contains($0) } + } + func tryUnlockScreen() { guard !manualLock else { return } guard ble.presence else { return } @@ -291,6 +349,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa CGEvent(keyboardEventSource: src, virtualKey: 0x35, keyDown: true)?.post(tap: .cghidEventTap) CGEvent(keyboardEventSource: src, virtualKey: 0x35, keyDown: false)?.post(tap: .cghidEventTap) } + + if self.prefs.bool(forKey: "externalDisplayModelOnly") && !isExternalDisplayConnected() { + print("External Display is not connected") + return + } guard !self.prefs.bool(forKey: "wakeWithoutUnlocking") else { return } @@ -489,6 +552,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa menuItem.state = value ? .on : .off prefs.set(value, forKey: "wakeOnProximity") } + + @objc func toggleExternalDisplayModeOnly(_ menuItem: NSMenuItem) { + let value = !prefs.bool(forKey: "externalDisplayModelOnly") + menuItem.state = value ? .on : .off + prefs.set(value, forKey: "externalDisplayModelOnly") + } @objc func setLockRSSI(_ menuItem: NSMenuItem) { let value = menuItem.tag @@ -551,6 +620,20 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa prefs.set(wakeWithoutUnlocking, forKey: "wakeWithoutUnlocking") menuItem.state = wakeWithoutUnlocking ? .on : .off } + + @objc func setExternalDisplays(_ menuItem: NSMenuItem) { + guard let uuidString = menuItem.representedObject as? String else { return } + + menuItem.state = (menuItem.state == .on) ? .off : .on + + var selectedDisplayUUIDs = prefs.array(forKey: "externalDisplays") as? [String] ?? [] + if menuItem.state == .on { + selectedDisplayUUIDs.append(uuidString) + } else { + selectedDisplayUUIDs.removeAll { $0 == uuidString } + } + prefs.set(selectedDisplayUUIDs, forKey: "externalDisplays") + } @objc func lockNow() { guard !isScreenLocked() else { return } @@ -622,6 +705,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa if prefs.bool(forKey: "wakeOnProximity") { item.state = .on } + + mainMenu.addItem(NSMenuItem.separator()) + item = mainMenu.addItem(withTitle: t("external_display_mode_only"), action: #selector(toggleExternalDisplayModeOnly), keyEquivalent: "") + if prefs.bool(forKey: "externalDisplayModelOnly") { + item.state = .on + } + + item = mainMenu.addItem(withTitle: t("external_displays"), action: nil, keyEquivalent: "") + item.submenu = externalDisplayMenu + externalDisplayMenu.delegate = self + mainMenu.addItem(NSMenuItem.separator()) item = mainMenu.addItem(withTitle: t("wake_without_unlocking"), action: #selector(toggleWakeWithoutUnlocking), keyEquivalent: "") if prefs.bool(forKey: "wakeWithoutUnlocking") { diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 4e75488..ff9be37 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -36,3 +36,5 @@ "use_screensaver_to_lock" = "Use Screensaver to Lock"; "wake_on_proximity" = "Wake on Proximity"; "wake_without_unlocking" = "Wake without Unlocking"; +"external_display_mode_only" = "External Display Detection"; +"external_displays" = "External Displays"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index a871345..678853a 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.12.2 CFBundleVersion - 796 + 847 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/de.lproj/Localizable.strings b/BLEUnlock/de.lproj/Localizable.strings index c9d978e..3ed36bb 100644 --- a/BLEUnlock/de.lproj/Localizable.strings +++ b/BLEUnlock/de.lproj/Localizable.strings @@ -36,3 +36,5 @@ "use_screensaver_to_lock" = "Den Bildschirmschoner verwenden zum Sperren"; "wake_on_proximity" = "Aufwachen bei Annäherung"; "wake_without_unlocking" = "Aufwachen ohne Entsperren"; +"external_display_mode_only" = "Externer Bildschirm Erkennung"; +"external_displays" = "Externe Bildschirme";