]> git.r.bdr.sh - rbdr/captura/commitdiff
Add multimonitor support
authorRuben Beltran del Rio <redacted>
Tue, 19 Sep 2023 20:58:44 +0000 (22:58 +0200)
committerRuben Beltran del Rio <redacted>
Tue, 19 Sep 2023 20:58:44 +0000 (22:58 +0200)
Captura.xcodeproj/project.pbxproj
Captura/Core Extensions/CVImageBuffer+cgImage.swift
Captura/Core Extensions/NSScreen+screenWithMouse.swift [new file with mode: 0644]
Captura/Data/CapturaFile.swift
Captura/Presentation/Windows/RecordingWindow.swift

index 4a11599ce3aba0ad8917f691472a32327f07edd4..31e5fb642469c5e1d21bfc0f4c9ed089ccff1e7c 100644 (file)
@@ -31,6 +31,7 @@
                B55DDFCC2A6F0253001A5E76 /* Notification+AppEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55DDFCB2A6F0253001A5E76 /* Notification+AppEvents.swift */; };
                B55DDFCE2A6F069D001A5E76 /* RecordingWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55DDFCD2A6F069D001A5E76 /* RecordingWindow.swift */; };
                B56C70CD2A6EFDF4009B97EB /* CaptureState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56C70CC2A6EFDF4009B97EB /* CaptureState.swift */; };
+               B5E7B75F2AB5D84700D5F03B /* NSScreen+screenWithMouse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E7B75E2AB5D84700D5F03B /* NSScreen+screenWithMouse.swift */; };
                B5F915522A6EF80D007ECE8E /* CapturaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F915512A6EF80D007ECE8E /* CapturaApp.swift */; };
                B5F915542A6EF80D007ECE8E /* PreferencesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F915532A6EF80D007ECE8E /* PreferencesScreen.swift */; };
                B5F915562A6EF80E007ECE8E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B5F915552A6EF80E007ECE8E /* Assets.xcassets */; };
@@ -85,6 +86,7 @@
                B55DDFCB2A6F0253001A5E76 /* Notification+AppEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+AppEvents.swift"; sourceTree = "<group>"; };
                B55DDFCD2A6F069D001A5E76 /* RecordingWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingWindow.swift; sourceTree = "<group>"; };
                B56C70CC2A6EFDF4009B97EB /* CaptureState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaptureState.swift; sourceTree = "<group>"; };
+               B5E7B75E2AB5D84700D5F03B /* NSScreen+screenWithMouse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSScreen+screenWithMouse.swift"; sourceTree = "<group>"; };
                B5F9154E2A6EF80D007ECE8E /* Captura.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Captura.app; sourceTree = BUILT_PRODUCTS_DIR; };
                B5F915512A6EF80D007ECE8E /* CapturaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapturaApp.swift; sourceTree = "<group>"; };
                B5F915532A6EF80D007ECE8E /* PreferencesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesScreen.swift; sourceTree = "<group>"; };
                                B55DDFCB2A6F0253001A5E76 /* Notification+AppEvents.swift */,
                                B5278B272A739871009F6462 /* CGImage+resize.swift */,
                                B5278B302A73AEAE009F6462 /* CVImageBuffer+cgImage.swift */,
+                               B5E7B75E2AB5D84700D5F03B /* NSScreen+screenWithMouse.swift */,
                        );
                        path = "Core Extensions";
                        sourceTree = "<group>";
                                B5278B452A77D924009F6462 /* CaptureSessionConfiguration.swift in Sources */,
                                B5278B2C2A739B3A009F6462 /* CapturaFile.swift in Sources */,
                                B55403E72A79A08C004BCBAB /* CapturaShortcutsProvider.swift in Sources */,
+                               B5E7B75F2AB5D84700D5F03B /* NSScreen+screenWithMouse.swift in Sources */,
                                B5278B1F2A71BD9B009F6462 /* OutputSettings.swift in Sources */,
                                B5278B2A2A73992D009F6462 /* GifRenderer.swift in Sources */,
                                B5278B212A71BFC3009F6462 /* OutputFormatSetting.swift in Sources */,
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
                                INFOPLIST_FILE = Captura/Info.plist;
+                               INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
                                INFOPLIST_KEY_LSUIElement = YES;
                                INFOPLIST_KEY_NSHumanReadableCopyright = "";
                                LD_RUNPATH_SEARCH_PATHS = (
                                        "@executable_path/../Frameworks",
                                );
                                MACOSX_DEPLOYMENT_TARGET = 13.0;
-                               MARKETING_VERSION = 1.0;
+                               MARKETING_VERSION = 1.0.0;
                                PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.Captura;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SWIFT_EMIT_LOC_STRINGS = YES;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
                                INFOPLIST_FILE = Captura/Info.plist;
+                               INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
                                INFOPLIST_KEY_LSUIElement = YES;
                                INFOPLIST_KEY_NSHumanReadableCopyright = "";
                                LD_RUNPATH_SEARCH_PATHS = (
                                        "@executable_path/../Frameworks",
                                );
                                MACOSX_DEPLOYMENT_TARGET = 13.0;
-                               MARKETING_VERSION = 1.0;
+                               MARKETING_VERSION = 1.0.0;
                                PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.Captura;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SWIFT_EMIT_LOC_STRINGS = YES;
index c30cb0c9433f4858e78d919fed16f8d0ed3d74d1..f8958f22eb5a89ab956abba7c784faf9560ec92f 100644 (file)
@@ -3,9 +3,10 @@ import ReplayKit
 
 extension CVImageBuffer {
   
+  static let sharedContext = CIContext()
+  
   var cgImage: CGImage? {
     let ciImage = CIImage(cvImageBuffer: self)
-    let context = CIContext()
-    return context.createCGImage(ciImage, from: CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(self), height: CVPixelBufferGetHeight(self)))
+    return CVImageBuffer.sharedContext.createCGImage(ciImage, from: CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(self), height: CVPixelBufferGetHeight(self)))
   }
 }
diff --git a/Captura/Core Extensions/NSScreen+screenWithMouse.swift b/Captura/Core Extensions/NSScreen+screenWithMouse.swift
new file mode 100644 (file)
index 0000000..8c7f566
--- /dev/null
@@ -0,0 +1,13 @@
+import Cocoa
+
+extension NSScreen {
+  static var screenWithMouse: NSScreen? {
+    let mouseLocation = NSEvent.mouseLocation
+    for screen in NSScreen.screens {
+      if NSMouseInRect(mouseLocation, screen.frame, false) {
+        return screen
+      }
+    }
+    return NSScreen.main
+  }
+}
index da1f2fb3fced4baa86a65eea36b8bc5951c1e527..4af71dcebcaf8f531fcb020d8ee7175b384f1c48 100644 (file)
@@ -27,5 +27,8 @@ struct CapturaFile {
     
     self.name = "Captura \(dateString)"
     self.baseDirectory = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask).first!
+    try? FileManager.default.createDirectory(at: self.baseDirectory.appendingPathComponent(appDirectory),
+                                            withIntermediateDirectories: true,
+                                            attributes: nil)
   }
 }
index f5941dc82bdbde22970439157d5088a2572eef23..370a47af63ed69d4b3e6f5c7457277f835a81d76 100644 (file)
@@ -13,11 +13,7 @@ class RecordingWindow: NSWindow {
   
   init(_ configuration: CaptureSessionConfiguration, _ button: NSRect?) {
     
-    let screens = NSScreen.screens
-    var boundingBox = NSZeroRect
-    for screen in screens {
-        boundingBox = NSUnionRect(boundingBox, screen.frame)
-    }
+    let boundingBox = NSScreen.screenWithMouse?.frame ?? NSZeroRect
     
     super.init(
       contentRect: boundingBox,
@@ -25,6 +21,7 @@ class RecordingWindow: NSWindow {
       backing: .buffered,
       defer: false)
 
+
     self.isReleasedWhenClosed = false
     self.collectionBehavior = [.canJoinAllSpaces]
     self.isMovableByWindowBackground = false
@@ -37,10 +34,16 @@ class RecordingWindow: NSWindow {
     recordingView.frame = boundingBox
     recordingView.button = button
     self.contentView = recordingView
-    self.backgroundColor = NSColor(white: 1.0, alpha: 0.001)
+    //self.backgroundColor = NSColor(white: 1.0, alpha: 0.001)
+    self.backgroundColor = NSColor(red: 1.0, green: 0.0, blue: 1.0, alpha: 0.5)
     self.level = .screenSaver
     self.isOpaque = false
     self.hasShadow = false
+    
+    print("AAAAH INIT CHANGE")
+    print("AAAAH FRAME X: \(recordingView.frame.minX) \(recordingView.frame.maxX) // Y: \(recordingView.frame.minY) \(recordingView.frame.maxY)")
+    print("AAAAH BOUNDS X: \(recordingView.bounds.minX) \(recordingView.bounds.maxX) // Y: \(recordingView.bounds.minY) \(recordingView.bounds.maxY)")
+    print("AAAAH WIN F X: \(self.frame.minX) \(self.frame.maxX) // Y: \(self.frame.minY) \(self.frame.maxY)")
   }
   
   // MARK: - Window Behavior Overrides
@@ -167,6 +170,12 @@ class RecordingContentView: NSView {
     self.addTrackingArea(trackingArea)
   }
   
+  override func mouseExited(with event: NSEvent) {
+    if state == .idle && box == nil {
+      self.moveWindow()
+    }
+  }
+  
   override func mouseMoved(with event: NSEvent) {
     
     self.mouseLocation = self.convert(event.locationInWindow, from: nil)
@@ -305,6 +314,17 @@ class RecordingContentView: NSView {
     
     let dashLength: CGFloat = 5.0
     let lineWidth = 0.5
+    
+    if let button {
+      let buttonPath = NSBezierPath()
+      buttonPath.move(to: NSPoint(x: button.minX, y: button.minY))
+      buttonPath.line(to: NSPoint(x: button.maxX, y: button.minY))
+      buttonPath.line(to: NSPoint(x: button.maxX, y: button.maxY))
+      buttonPath.line(to: NSPoint(x: button.minX, y: button.maxY))
+      buttonPath.line(to: NSPoint(x: button.minX, y: button.minY))
+      NSColor(red: 1, green: 0, blue: 1, alpha: 1).setFill()
+      buttonPath.fill()
+    }
 
     if state == .idle && box == nil {
       let blackLine = NSBezierPath()
@@ -424,4 +444,31 @@ class RecordingContentView: NSView {
 
     text.draw(in: textRect, withAttributes: textAttributes)
   }
+  
+  private func moveWindow() {
+    print("AAAAH BEFORE WE CHANGE")
+    print("AAAAH FRAME X: \(self.frame.minX) \(self.frame.maxX) // Y: \(self.frame.minY) \(self.frame.maxY)")
+    print("AAAAH BOUNDS X: \(self.bounds.minX) \(self.bounds.maxX) // Y: \(self.bounds.minY) \(self.bounds.maxY)")
+    print("AAAAH WIN F X: \(self.window?.frame.minX) \(self.window?.frame.maxX) // Y: \(self.window?.frame.minY) \(self.window?.frame.maxY)")
+    let screen = NSScreen.screenWithMouse
+    if let currentScreen = self.window?.screen {
+      if currentScreen != screen {
+        let frame = screen?.frame ?? NSZeroRect
+        self.frame = frame
+        self.bounds = frame
+        self.updateTrackingAreas()
+        
+        if let window = self.window {
+          self.bounds = frame
+          window.setFrame(frame, display: true, animate: false)
+          window.makeKeyAndOrderFront(nil)
+          window.orderFrontRegardless()
+          print("AAAAH AFTER CHANGE")
+          print("AAAAH FRAME X: \(self.frame.minX) \(self.frame.maxX) // Y: \(self.frame.minY) \(self.frame.maxY)")
+          print("AAAAH BOUNDS X: \(self.bounds.minX) \(self.bounds.maxX) // Y: \(self.bounds.minY) \(self.bounds.maxY)")
+          print("AAAAH WIN F X: \(self.window?.frame.minX) \(self.window?.frame.maxX) // Y: \(self.window?.frame.minY) \(self.window?.frame.maxY)")
+        }
+      }
+    }
+  }
 }