// // ViewController.swift // External Display // // Created by Thomas Carroll on 9/20/21. // import Cocoa let observatory = NotificationCenter.default class ViewController: NSViewController { var connectedDisplay: NSScreen? var connectedDisplayWindow: NSWindow? var connectedDisplayView: NSView? var connectedDisplayCount: Int = 0 var connectedDisplayID: UInt32 = 0 override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. setupObservatory() if NSScreen.screens.count > 1 { handleDisplayConnectionChange(notification: nil) } } override var representedObject: Any? { didSet { // Update the view, if already loaded. } } override func viewWillDisappear() { connectedDisplayWindow?.close() } func setupObservatory() { observatory.addObserver(self, selector: #selector(handleDisplayConnectionChange), name: NSApplication.didChangeScreenParametersNotification, object: nil) observatory.addObserver(forName: .setupConnectedDisplayWindow, object: nil, queue: nil, using: setupConnectedDisplayWindow) } @objc func handleDisplayConnectionChange(notification: Notification?) { if connectedDisplayCount != NSScreen.screens.count { if connectedDisplayCount < NSScreen.screens.count { print("There is a connected display.") connectedDisplayCount = NSScreen.screens.count if let _ = NSScreen.screens.last { if connectedDisplay != NSScreen.screens.last { connectedDisplayID = NSScreen.screens.last!.displayID! connectedDisplay = NSScreen.screens.last! } } else { connectedDisplayID = 0 } if connectedDisplayID != 0 && !connectedDisplayIsActive { observatory.post(name: .setupConnectedDisplayWindow, object: nil) } } else if connectedDisplayCount > NSScreen.screens.count { print("A connected display was removed.") connectedDisplayCount = NSScreen.screens.count connectedDisplayIsActive = false connectedDisplayWindow?.close() //connectedDisplayView = nil <- causes error @main in AppDelegate //connectedDisplayWindow = nil <- causes error @main in AppDelegate connectedDisplay = nil connectedDisplayID = 0 } } } func setupConnectedDisplayWindow(notification: Notification) { if NSScreen.screens.count > 1 && !connectedDisplayIsActive { connectedDisplay = NSScreen.screens.last let mask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable] connectedDisplayWindow = NSWindow(contentRect: connectedDisplay!.frame, styleMask: mask, backing: .buffered, defer: true, screen: connectedDisplay) // <- causes error on subsequent connection connectedDisplayWindow?.level = .normal connectedDisplayWindow?.isOpaque = false connectedDisplayWindow?.backgroundColor = .clear connectedDisplayWindow?.hidesOnDeactivate = false let viewRect = NSRect(x: 0, y: 0, width: connectedDisplay!.frame.width, height: connectedDisplay!.frame.height) connectedDisplayView = ConnectedDisplayView(frame: viewRect) connectedDisplayWindow?.contentView = connectedDisplayView connectedDisplayWindow?.orderFront(nil) connectedDisplayView?.window?.toggleFullScreen(self) connectedDisplayIsActive = true observatory.post(name: .setupConnectedDisplayView, object: nil) } } } extension Notification.Name { static var setupConnectedDisplayWindow: Notification.Name { return .init(rawValue: "ViewController.setupConnectedDisplayView") } static var setupConnectedDisplayView: Notification.Name { return .init(rawValue: "ConnectedDisplayView.setupConnectedDisplayView") } } extension NSScreen { var displayID: CGDirectDisplayID? { return deviceDescription[NSDeviceDescriptionKey(rawValue: "NSScreenNumber")] as? CGDirectDisplayID } }