diff --git a/Aerial.xcodeproj/project.pbxproj b/Aerial.xcodeproj/project.pbxproj index ebcbb5da..af4e176c 100644 --- a/Aerial.xcodeproj/project.pbxproj +++ b/Aerial.xcodeproj/project.pbxproj @@ -698,7 +698,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if which swiftlint >/dev/null; then\n if [ -z \"$CI\" ]; then\n make --directory=${SRCROOT} xcode-lint\n fi\nelse\n echo \"warning: SwiftLint not installed, install using `brew install swiftlint`\"\nfi\n"; + shellScript = "#if which swiftlint >/dev/null; then\n# if [ -z \"$CI\" ]; then\n# make --directory=${SRCROOT} xcode-lint\n# fi\n#else\n# echo \"warning: SwiftLint not installed, install using `brew install swiftlint`\"\n#fi\nif which swiftlint >/dev/null; then\n swiftlint autocorrect\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; 910394578D35CCEBAEE0D456 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -748,7 +748,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if which swiftlint >/dev/null; then\n if [ -z \"$CI\" ]; then\n make --directory=${SRCROOT} xcode-lint\n fi\nelse\n echo \"warning: SwiftLint not installed, install using `brew install swiftlint`\"\nfi"; + shellScript = "#if which swiftlint >/dev/null; then\n# if [ -z \"$CI\" ]; then\n# make --directory=${SRCROOT} xcode-lint\n# fi\n#else\n# echo \"warning: SwiftLint not installed, install using `brew install swiftlint`\"\n#fi\nif which swiftlint >/dev/null; then\n swiftlint autocorrect\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/Aerial/Source/Controllers/Preferences.swift b/Aerial/Source/Controllers/Preferences.swift index d93c2cfc..01841ec5 100644 --- a/Aerial/Source/Controllers/Preferences.swift +++ b/Aerial/Source/Controllers/Preferences.swift @@ -77,6 +77,8 @@ final class Preferences { case newDisplayMode = "newDisplayMode" case newViewingMode = "newViewingMode" case newDisplayDict = "newDisplayDict" + case logMilliseconds = "logMilliseconds" + case horizontalMargin = "horizontalMargin" } enum NewDisplayMode: Int { @@ -84,7 +86,7 @@ final class Preferences { } enum NewViewingMode: Int { - case independant, mirrored, spanned + case independent, mirrored, spanned } enum BetaCheckFrequency: Int { @@ -211,8 +213,10 @@ final class Preferences { defaultValues[.allowBetas] = false defaultValues[.betaCheckFrequency] = BetaCheckFrequency.daily defaultValues[.newDisplayMode] = NewDisplayMode.allDisplays - defaultValues[.newViewingMode] = NewViewingMode.independant + defaultValues[.newViewingMode] = NewViewingMode.independent defaultValues[.newDisplayDict] = [String: Bool]() + defaultValues[.logMilliseconds] = false + defaultValues[.horizontalMargin] = 0 // Set today's date as default let dateFormatter = DateFormatter() @@ -249,6 +253,15 @@ final class Preferences { } } + var horizontalMargin: Double? { + get { + return optionalValue(forIdentifier: .horizontalMargin) + } + set { + setValue(forIdentifier: .horizontalMargin, value: newValue) + } + } + var newDisplayMode: Int? { get { return optionalValue(forIdentifier: .newDisplayMode) @@ -303,6 +316,15 @@ final class Preferences { } } + var logMilliseconds: Bool { + get { + return value(forIdentifier: .logMilliseconds) + } + set { + setValue(forIdentifier: .logMilliseconds, value: newValue) + } + } + var allowBetas: Bool { get { return value(forIdentifier: .allowBetas) diff --git a/Aerial/Source/Controllers/PreferencesWindowController.swift b/Aerial/Source/Controllers/PreferencesWindowController.swift index 63f160d0..e7229585 100644 --- a/Aerial/Source/Controllers/PreferencesWindowController.swift +++ b/Aerial/Source/Controllers/PreferencesWindowController.swift @@ -210,7 +210,11 @@ final class PreferencesWindowController: NSWindowController, NSOutlineViewDataSo @IBOutlet var newDisplayModePopup: NSPopUpButton! @IBOutlet var newViewingModePopup: NSPopUpButton! @IBOutlet var displayInstructionLabel: NSTextField! + @IBOutlet var quitConfirmationPanel: NSPanel! + @IBOutlet var logMillisecondsButton: NSButton! + @IBOutlet var displayMarginBox: NSBox! + @IBOutlet var horizontalDisplayMarginTextfield: NSTextField! var player: AVPlayer = AVPlayer() var videos: [AerialVideo]? @@ -419,14 +423,18 @@ final class PreferencesWindowController: NSWindowController, NSOutlineViewDataSo if !preferences.allowSkips { rightArrowKeyPlaysNextCheckbox.state = .off } + horizontalDisplayMarginTextfield.doubleValue = preferences.horizontalMargin! - // Aerial panel + // Advanced panel if preferences.debugMode { debugModeCheckbox.state = .on } if preferences.logToDisk { logToDiskCheckbox.state = .on } + if preferences.logMilliseconds { + logMillisecondsButton.state = .on + } // Text panel if preferences.showClock { @@ -660,7 +668,23 @@ final class PreferencesWindowController: NSWindowController, NSOutlineViewDataSo } @IBAction func close(_ sender: AnyObject?) { - // This seems needed for screensavers as our lifecycle is different from a regular app + // We ask for confirmation in case downloads are ongoing + if !downloadProgressIndicator.isHidden { + quitConfirmationPanel.makeKeyAndOrderFront(self) + } else { + // This seems needed for screensavers as our lifecycle is different from a regular app + preferences.synchronize() + logPanel.close() + if appMode { + NSApplication.shared.terminate(nil) + } else { + window?.sheetParent?.endSheet(window!) + } + } + } + + @IBAction func confirmQuitClick(_ sender: Any) { + quitConfirmationPanel.close() preferences.synchronize() logPanel.close() if appMode { @@ -670,6 +694,10 @@ final class PreferencesWindowController: NSWindowController, NSOutlineViewDataSo } } + @IBAction func cancelQuitClick(_ sender: Any) { + quitConfirmationPanel.close() + } + // MARK: Video playback // Rewind preview video when reaching end @@ -861,6 +889,17 @@ final class PreferencesWindowController: NSWindowController, NSOutlineViewDataSo debugLog("UI newViewingModeClick: \(sender.indexOfSelectedItem)") preferences.newViewingMode = sender.indexOfSelectedItem displayView.needsDisplay = true + + if preferences.newViewingMode == Preferences.NewViewingMode.spanned.rawValue { + displayMarginBox.isHidden = false + } else { + displayMarginBox.isHidden = true + } + } + + @IBAction func horizontalDisplayMarginChange(_ sender: NSTextField) { + debugLog("UI horizontalDisplayMarginChange \(sender.stringValue)") + preferences.horizontalMargin = sender.doubleValue } // MARK: - Text panel @@ -1459,6 +1498,13 @@ final class PreferencesWindowController: NSWindowController, NSOutlineViewDataSo } // MARK: - Advanced panel + + @IBAction func logMillisecondsClick(_ button: NSButton) { + let onState = button.state == .on + preferences.logMilliseconds = onState + debugLog("UI logMilliseconds: \(onState)") + } + @IBAction func logButtonClick(_ sender: NSButton) { logTableView.reloadData() if logPanel.isVisible { diff --git a/Aerial/Source/Models/DisplayDetection.swift b/Aerial/Source/Models/DisplayDetection.swift index 8cb06ac7..1cc8795a 100644 --- a/Aerial/Source/Models/DisplayDetection.swift +++ b/Aerial/Source/Models/DisplayDetection.swift @@ -42,6 +42,9 @@ final class DisplayDetection: NSObject { static let sharedInstance = DisplayDetection() var screens = [Screen]() + var cmInPoints: CGFloat = 40 + var maxLeftScreens: CGFloat = 0 + var maxBelowScreens: CGFloat = 0 // MARK: - Lifecycle override init() { @@ -72,8 +75,13 @@ final class DisplayDetection: NSObject { var rect = CGDisplayBounds(currentDisplay) if isMain == 0 { rect = convertTopLeftToBottomLeft(rect: rect) + } else { + // We calculate the equivalent of a centimeter in points on the main screen as a reference + let mmsize = CGDisplayScreenSize(currentDisplay) + let wide = CGDisplayPixelsWide(currentDisplay) + cmInPoints = CGFloat(wide) / CGFloat(mmsize.width) * 10 + debugLog("1cm = \(cmInPoints) points") } - screens.append(Screen(id: currentDisplay, width: CGDisplayPixelsWide(currentDisplay), height: CGDisplayPixelsHigh(currentDisplay), @@ -97,6 +105,7 @@ final class DisplayDetection: NSObject { for screen in screens { debugLog("\(screen)") } + debugLog("\(getGlobalScreenRect())") debugLog("***Display Detection Done***") } @@ -106,12 +115,33 @@ final class DisplayDetection: NSObject { func calculateZeroedOrigins() { let orect = getGlobalScreenRect() + // First we check for the screen relative position and calculate how many screens we have horizontally + // TODO Vertical + H/V mix for screen in screens { - screen.zeroedOrigin = CGPoint(x: screen.bottomLeftFrame.origin.x - orect.origin.x, + debugLog("src orig : \(screen.bottomLeftFrame.origin)") + var leftScreens: CGFloat = 0 + // Very rough, horizontal spans only + for otherScreen in screens { + if otherScreen.bottomLeftFrame.origin.x != screen.bottomLeftFrame.origin.x && + otherScreen.bottomLeftFrame.origin.x < screen.bottomLeftFrame.origin.x { + leftScreens += 1 + } + } + + if leftScreens > maxLeftScreens { + maxLeftScreens = leftScreens + } + + screen.zeroedOrigin = CGPoint(x: screen.bottomLeftFrame.origin.x - orect.origin.x + (leftScreens * leftMargin()), y: screen.bottomLeftFrame.origin.y - orect.origin.y) } } + func leftMargin() -> CGFloat { + let preferences = Preferences.sharedInstance + return cmInPoints * CGFloat(preferences.horizontalMargin!) + } + func findScreenWith(frame: CGRect) -> Screen? { for screen in screens where frame == screen.bottomLeftFrame { return screen @@ -146,7 +176,7 @@ final class DisplayDetection: NSObject { } } - return CGRect(x: minX, y: minY, width: maxX-minX, height: maxY-minY) + return CGRect(x: minX, y: minY, width: maxX-minX+(maxLeftScreens*leftMargin()), height: maxY-minY) } func getZeroedActiveSpannedRect() -> CGRect { @@ -172,7 +202,7 @@ final class DisplayDetection: NSObject { let orect = getGlobalScreenRect() minX -= orect.origin.x minY -= orect.origin.y - return CGRect(x: minX, y: minY, width: width, height: height) + return CGRect(x: minX, y: minY, width: width+(maxLeftScreens*leftMargin()), height: height) } // NSScreen coordinates are with a bottom left origin, whereas CGDisplay diff --git a/Aerial/Source/Models/ErrorLog.swift b/Aerial/Source/Models/ErrorLog.swift index 1730bb31..b34d288a 100644 --- a/Aerial/Source/Models/ErrorLog.swift +++ b/Aerial/Source/Models/ErrorLog.swift @@ -77,7 +77,11 @@ func Log(level: ErrorLevel, message: String) { if preferences.logToDisk { DispatchQueue.main.async { let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy-MM-dd' 'HH:mm:ss" + if preferences.logMilliseconds { + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" + } else { + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + } let string = dateFormatter.string(from: Date()) + " : " + message + "\n" // tmpOverride diff --git a/Aerial/Source/Views/AerialView.swift b/Aerial/Source/Views/AerialView.swift index 3d682a97..a931a610 100644 --- a/Aerial/Source/Views/AerialView.swift +++ b/Aerial/Source/Views/AerialView.swift @@ -269,9 +269,9 @@ final class AerialView: ScreenSaverView { } } else { // If we don't know this screen, we disable - debugLog("This is an unknown display, disabling") - isDisabled = true - return + //debugLog("This is an unknown display, disabling") + //isDisabled = false + //return } } else { AerialView.previewView = self @@ -356,6 +356,7 @@ final class AerialView: ScreenSaverView { debugLog("\(self.description) setting up player layer with bounds/frame: \(layer.bounds) / \(layer.frame)") playerLayer = AVPlayerLayer(player: player) + if #available(OSX 10.10, *) { playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill } @@ -370,7 +371,9 @@ final class AerialView: ScreenSaverView { y: zRect.origin.y - scr.zeroedOrigin.y, width: zRect.width, height: zRect.height) + debugLog("tRect : \(tRect)") playerLayer.frame = tRect + //playerLayer.bounds = layer.bounds } else { errorLog("This is an unknown screen in span mode, this is not good") playerLayer.frame = layer.bounds @@ -378,7 +381,6 @@ final class AerialView: ScreenSaverView { } else { playerLayer.frame = layer.bounds } - layer.addSublayer(playerLayer) textLayer = CATextLayer() @@ -630,10 +632,14 @@ final class AerialView: ScreenSaverView { if preferences.allowSkips { if event.keyCode == 124 { - //playNextVideo() - // We need to skip forward all our views - for view in AerialView.instanciatedViews { - view.playNextVideo() + // If we share, just call this on our main view + if AerialView.sharingPlayers { + playNextVideo() + } else { + // If we do independant playback we have to skip all views + for view in AerialView.instanciatedViews { + view.playNextVideo() + } } } else { self.nextResponder!.keyDown(with: event) diff --git a/Aerial/Source/Views/DisplayView.swift b/Aerial/Source/Views/DisplayView.swift index bbe7a48a..316d7b94 100644 --- a/Aerial/Source/Views/DisplayView.swift +++ b/Aerial/Source/Views/DisplayView.swift @@ -115,7 +115,7 @@ class DisplayView: NSView { let sInRect = sRect.insetBy(dx: 1, dy: 1) - if preferences.newViewingMode == Preferences.NewViewingMode.independant.rawValue || + if preferences.newViewingMode == Preferences.NewViewingMode.independent.rawValue || preferences.newViewingMode == Preferences.NewViewingMode.mirrored.rawValue { if displayDetection.isScreenActive(id: screen.id) { let bundle = Bundle(for: PreferencesWindowController.self) @@ -128,7 +128,7 @@ class DisplayView: NSView { } // Show difference images in independant mode to simulate - if preferences.newViewingMode == Preferences.NewViewingMode.independant.rawValue { + if preferences.newViewingMode == Preferences.NewViewingMode.independent.rawValue { if idx < 2 { idx += 1 } else { diff --git a/Resources/Info.plist b/Resources/Info.plist index 3c5dbb35..66eef456 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.98beta1 + 1.4.99beta2 CFBundleSignature ???? CFBundleVersion - 1.4.98beta1 + 1.4.99beta2 LSApplicationCategoryType LSMinimumSystemVersion diff --git a/Resources/PreferencesWindow.xib b/Resources/PreferencesWindow.xib index 72c339ff..19cbfe11 100644 --- a/Resources/PreferencesWindow.xib +++ b/Resources/PreferencesWindow.xib @@ -45,6 +45,7 @@ + @@ -69,6 +70,7 @@ + @@ -79,6 +81,7 @@ + @@ -112,6 +115,7 @@ + @@ -633,6 +637,84 @@ is disabled + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1777,7 +1859,7 @@ Shift, but macOS 10.12.4 or above and a compatible Mac are required) - + @@ -1786,7 +1868,7 @@ Shift, but macOS 10.12.4 or above and a compatible Mac are required) - + @@ -1799,7 +1881,7 @@ Shift, but macOS 10.12.4 or above and a compatible Mac are required) - + @@ -1808,7 +1890,7 @@ Shift, but macOS 10.12.4 or above and a compatible Mac are required) - + @@ -1898,7 +1980,7 @@ Shift, but macOS 10.12.4 or above and a compatible Mac are required) + @@ -2580,6 +2673,59 @@ Unless you want to manually manage your updates, we highly recommand you leave t + + + + + + + + + + + + + + + + + + + + You are currently downloading videos, if you quit the configuration panel those downloads will be cancelled. Are you sure you want to quit ? + + + + + + + + + +