From 22ec62e2941be4fc3b48ec079e1c9e3e8dea59e9 Mon Sep 17 00:00:00 2001 From: Guillaume Louel Date: Thu, 9 May 2019 16:00:10 +0200 Subject: [PATCH 1/7] WIP on screen detection --- Aerial.xcodeproj/project.pbxproj | 14 ++ .../PreferencesWindowController.swift | 3 + Aerial/Source/Models/DisplayDetection.swift | 130 ++++++++++++++++++ Aerial/Source/Views/AerialView.swift | 2 +- Aerial/Source/Views/DisplayView.swift | 62 +++++++++ Resources/Info.plist | 4 +- Resources/PreferencesWindow.xib | 36 ++++- 7 files changed, 244 insertions(+), 7 deletions(-) create mode 100644 Aerial/Source/Models/DisplayDetection.swift create mode 100644 Aerial/Source/Views/DisplayView.swift diff --git a/Aerial.xcodeproj/project.pbxproj b/Aerial.xcodeproj/project.pbxproj index fe62e0e0..06ee3947 100644 --- a/Aerial.xcodeproj/project.pbxproj +++ b/Aerial.xcodeproj/project.pbxproj @@ -49,6 +49,11 @@ 0395835621807D1F008E8F9C /* thumbnail.png in Resources */ = {isa = PBXBuildFile; fileRef = 0395835221807D1F008E8F9C /* thumbnail.png */; }; 03A2CB9C216BA9AF0061E8E8 /* VideoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A2CB9B216BA9AF0061E8E8 /* VideoManager.swift */; }; 03A2CB9D216BB1490061E8E8 /* VideoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A2CB9B216BA9AF0061E8E8 /* VideoManager.swift */; }; + 03D1E78722842FB300D10CF7 /* DisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D1E78622842FB300D10CF7 /* DisplayView.swift */; }; + 03D1E7882284367200D10CF7 /* DisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D1E78622842FB300D10CF7 /* DisplayView.swift */; }; + 03D1E78A2284471A00D10CF7 /* DisplayDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D1E7892284471A00D10CF7 /* DisplayDetection.swift */; }; + 03D1E78B22844AFD00D10CF7 /* DisplayDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D1E7892284471A00D10CF7 /* DisplayDetection.swift */; }; + 03D1E78C22844AFE00D10CF7 /* DisplayDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D1E7892284471A00D10CF7 /* DisplayDetection.swift */; }; 03D37FD922145487005A146F /* es.json in Resources */ = {isa = PBXBuildFile; fileRef = 03D37FD722145487005A146F /* es.json */; }; 03D37FDA22145487005A146F /* es.json in Resources */ = {isa = PBXBuildFile; fileRef = 03D37FD722145487005A146F /* es.json */; }; 03D37FDB22145487005A146F /* fr.json in Resources */ = {isa = PBXBuildFile; fileRef = 03D37FD822145487005A146F /* fr.json */; }; @@ -128,6 +133,8 @@ 0395835121807D1F008E8F9C /* thumbnail@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumbnail@2x.png"; sourceTree = ""; }; 0395835221807D1F008E8F9C /* thumbnail.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = thumbnail.png; sourceTree = ""; }; 03A2CB9B216BA9AF0061E8E8 /* VideoManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoManager.swift; sourceTree = ""; }; + 03D1E78622842FB300D10CF7 /* DisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DisplayView.swift; path = Aerial/Source/Views/DisplayView.swift; sourceTree = SOURCE_ROOT; }; + 03D1E7892284471A00D10CF7 /* DisplayDetection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayDetection.swift; sourceTree = ""; }; 03D37FD722145487005A146F /* es.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = es.json; sourceTree = ""; }; 03D37FD822145487005A146F /* fr.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = fr.json; sourceTree = ""; }; 03D3DAC3221F286700BDA52F /* pl.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = pl.json; sourceTree = ""; }; @@ -340,6 +347,7 @@ FAC36F401BE1756D007F2A20 /* AerialVideo.swift */, FAC36F661BE1778C007F2A20 /* ManifestLoader.swift */, 03893CB2217749F0008E7125 /* ErrorLog.swift */, + 03D1E7892284471A00D10CF7 /* DisplayDetection.swift */, ); path = Models; sourceTree = ""; @@ -362,6 +370,7 @@ children = ( FAC36F431BE1756D007F2A20 /* AerialView.swift */, FAC36F441BE1756D007F2A20 /* CheckCellView.swift */, + 03D1E78622842FB300D10CF7 /* DisplayView.swift */, AA7E2E5D1FC62E8B00E5F320 /* AerialPlayerItem.swift */, ); path = Views; @@ -720,9 +729,11 @@ 03233B692172762C0077D3F9 /* PoiStringProvider.swift in Sources */, 03A2CB9D216BB1490061E8E8 /* VideoManager.swift in Sources */, 03E87314216760B7002B469B /* TimeManagement.swift in Sources */, + 03D1E78B22844AFD00D10CF7 /* DisplayDetection.swift in Sources */, 03E8731021662AEB002B469B /* DownloadManager.swift in Sources */, 03E8731121662AEB002B469B /* AsynchronousOperation.swift in Sources */, 03510C7121834FC7008F74F2 /* IOBridge.m in Sources */, + 03D1E7882284367200D10CF7 /* DisplayView.swift in Sources */, 030D9B7C21551A8D00961E95 /* AerialPlayerItem.swift in Sources */, FAC36F5E1BE1756D007F2A20 /* CheckCellView.swift in Sources */, FAC36F5C1BE1756D007F2A20 /* AerialView.swift in Sources */, @@ -744,6 +755,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 03D1E78C22844AFE00D10CF7 /* DisplayDetection.swift in Sources */, 0393857C2175D4B80040B850 /* AVPlayerViewExtension.swift in Sources */, FA7199711D94EC5A00FBC99B /* PreferencesTests.swift in Sources */, ); @@ -753,6 +765,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 03D1E78A2284471A00D10CF7 /* DisplayDetection.swift in Sources */, FAC36F5D1BE1756D007F2A20 /* CheckCellView.swift in Sources */, FAC36F5B1BE1756D007F2A20 /* AerialView.swift in Sources */, 0393857A2175D4B80040B850 /* AVPlayerViewExtension.swift in Sources */, @@ -768,6 +781,7 @@ 03A2CB9C216BA9AF0061E8E8 /* VideoManager.swift in Sources */, FAF450211BE2B45D00C1F98A /* VideoLoader.swift in Sources */, 03E8731321675FE0002B469B /* TimeManagement.swift in Sources */, + 03D1E78722842FB300D10CF7 /* DisplayView.swift in Sources */, FAB22A7E1BE17D7D0065C0F5 /* AssetLoaderDelegate.swift in Sources */, 03958349217F4416008E8F9C /* Solar.swift in Sources */, 03233B68217272640077D3F9 /* PoiStringProvider.swift in Sources */, diff --git a/Aerial/Source/Controllers/PreferencesWindowController.swift b/Aerial/Source/Controllers/PreferencesWindowController.swift index c407d4ca..1b279b59 100644 --- a/Aerial/Source/Controllers/PreferencesWindowController.swift +++ b/Aerial/Source/Controllers/PreferencesWindowController.swift @@ -226,6 +226,7 @@ final class PreferencesWindowController: NSWindowController, NSOutlineViewDataSo var locationManager: CLLocationManager? var sparkleUpdater: SUUpdater? + @IBOutlet var displayView: DisplayView! public var appMode: Bool = false private lazy var timeFormatter: DateFormatter = { @@ -308,6 +309,8 @@ final class PreferencesWindowController: NSWindowController, NSOutlineViewDataSo logTableView.delegate = self logTableView.dataSource = self + + //displayView = new DisplayView() if let version = Bundle(identifier: "com.johncoates.Aerial-Test")?.infoDictionary?["CFBundleShortVersionString"] as? String { versionLabel.stringValue = version diff --git a/Aerial/Source/Models/DisplayDetection.swift b/Aerial/Source/Models/DisplayDetection.swift new file mode 100644 index 00000000..6120e942 --- /dev/null +++ b/Aerial/Source/Models/DisplayDetection.swift @@ -0,0 +1,130 @@ +// +// DisplayDetection.swift +// Aerial +// +// Created by Guillaume Louel on 09/05/2019. +// Copyright © 2019 John Coates. All rights reserved. +// + +import Foundation +import Cocoa + +class Screen { + var id: CGDirectDisplayID + var width: Int + var height: Int + var bottomLeftFrame: CGRect + var topRightCorner: CGPoint + var isMain: Bool + var backingScaleFactor: CGFloat + + init(id: CGDirectDisplayID, width: Int, height: Int, bottomLeftFrame: CGRect, isMain: Bool) { + self.id = id + self.width = width + self.height = height + self.bottomLeftFrame = bottomLeftFrame + // We precalculate the right corner too, as we will need this ! + self.topRightCorner = CGPoint(x: bottomLeftFrame.origin.x + CGFloat(width), + y: bottomLeftFrame.origin.y + CGFloat(height)) + self.isMain = isMain + self.backingScaleFactor = 1 + } + + var description: String { + //swiftlint:disable:next line_length + return "[id=\(self.id), width=\(self.width), height=\(self.height), bottomLeftFrame=\(self.bottomLeftFrame), topRightCorner=\(self.topRightCorner), isMain=\(self.isMain), backingScaleFactor=\(self.backingScaleFactor)]" + } +} + +final class DisplayDetection: NSObject { + static let sharedInstance = DisplayDetection() + + var screens = [Screen]() + + // MARK: - Lifecycle + override init() { + super.init() + debugLog("Display Detection initialized") + _ = detectDisplays() + } + + // MARK: - Detection + func detectDisplays() { + // Display detection is done in two passes : + // - Through CGDisplay, we grab all online screens (connected, but + // may or may not be powered on !) and get most information needed + // - Through NSScreen to get the backingScaleFactor (retinaness of a screen) + + debugLog("***Display Detection***") + // First pass + let maxDisplays: UInt32 = 32 + var onlineDisplays = [CGDirectDisplayID](repeating: 0, count: Int(maxDisplays)) + var displayCount: UInt32 = 0 + + _ = CGGetOnlineDisplayList(maxDisplays, &onlineDisplays, &displayCount) + debugLog("\(displayCount) display(s) detected") + + for currentDisplay in onlineDisplays[0.. Screen? { + for screen in screens where frame == screen.bottomLeftFrame { + return screen + } + + return nil + } + + func findScreenWith(id: CGDirectDisplayID) -> Screen? { + for screen in screens where screen.id == id { + return screen + } + + return nil + } +/* + func getGlobalScreenRect -> CGRect { + var minX, minY, maxX, maxY = 0 + for screen in screens { + if screen.origin.x + } + }*/ + // NSScreen coordinates are with a bottom left origin, whereas CGDisplay + // coordinates are top left origin, this function converts the origin.y value + func convertTopLeftToBottomLeft(rect: CGRect) -> CGRect { + let screenFrame = (NSScreen.main?.frame)! + let newY = 0 - (rect.origin.y - screenFrame.size.height + rect.height) + return CGRect(x: rect.origin.x, y: newY, width: rect.width, height: rect.height) + } + +} diff --git a/Aerial/Source/Views/AerialView.swift b/Aerial/Source/Views/AerialView.swift index bccd7772..549eb6b6 100644 --- a/Aerial/Source/Views/AerialView.swift +++ b/Aerial/Source/Views/AerialView.swift @@ -110,7 +110,7 @@ final class AerialView: ScreenSaverView { // This is the one used by System Preferences override init?(frame: NSRect, isPreview: Bool) { super.init(frame: frame, isPreview: isPreview) - debugLog("avInit1") + debugLog("avInit1 \(frame)") self.animationTimeInterval = 1.0 / 30.0 setup() } diff --git a/Aerial/Source/Views/DisplayView.swift b/Aerial/Source/Views/DisplayView.swift new file mode 100644 index 00000000..26a3e897 --- /dev/null +++ b/Aerial/Source/Views/DisplayView.swift @@ -0,0 +1,62 @@ +// +// DisplayView.swift +// Aerial +// +// Created by Guillaume Louel on 09/05/2019. +// Copyright © 2019 John Coates. All rights reserved. +// + +import Foundation +import Cocoa + +class DisplayView: NSView { + /*override init() { + debugLog("************************DisplayView init") + super.init() + }*/ + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + override func draw(_ dirtyRect: NSRect) { + super.draw(dirtyRect) + + // We need to handle dark mode + var backgroundColor = NSColor.init(white: 0.9, alpha: 1.0) + var borderColor = NSColor.init(white: 0.8, alpha: 1.0) + + let screenColor = NSColor.init(red: 0.44, green: 0.60, blue: 0.82, alpha: 1.0) + let screenBorderColor = NSColor.black + + let timeManagement = TimeManagement.sharedInstance + if timeManagement.isDarkModeEnabled() { + backgroundColor = NSColor.init(white: 0.2, alpha: 1.0) + borderColor = NSColor.init(white: 0.6, alpha: 1.0) + } + + // Draw background with a 1pt border + borderColor.setFill() + __NSRectFill(dirtyRect) + + let path = NSBezierPath(rect: dirtyRect.insetBy(dx: 1, dy: 1)) + backgroundColor.setFill() + path.fill() + + let displayDetection = DisplayDetection.sharedInstance + + let sRect = NSRect(x: 20, y: 20, width: + 150, height: 50) + let sPath = NSBezierPath(rect: sRect) + screenBorderColor.setFill() + sPath.fill() + + let sInPath = NSBezierPath(rect: sRect.insetBy(dx: 1, dy: 1)) + screenColor.setFill() + sInPath.fill() + } +} diff --git a/Resources/Info.plist b/Resources/Info.plist index 9cf15a28..94920653 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.10beta3 + 1.5.0alpha0 CFBundleSignature ???? CFBundleVersion - 1.4.10beta3 + 1.5.0alpha0 LSApplicationCategoryType LSMinimumSystemVersion diff --git a/Resources/PreferencesWindow.xib b/Resources/PreferencesWindow.xib index 00c7b8b2..36ff8ffd 100644 --- a/Resources/PreferencesWindow.xib +++ b/Resources/PreferencesWindow.xib @@ -44,6 +44,7 @@ + @@ -565,6 +566,32 @@ is disabled + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1523,7 +1550,7 @@ Shift, but macOS 10.12.4 or above and a compatible Mac are required) - + @@ -2278,7 +2305,7 @@ You can enable it when on battery, or only when your battery reaches 20%. - + @@ -470,7 +439,7 @@ is disabled - + @@ -491,7 +460,7 @@ is disabled - + @@ -523,7 +492,7 @@ is disabled