From 30b641d52179eeacb1a03484ac1d18a99d93979c Mon Sep 17 00:00:00 2001 From: "tattn (Tatsuya Tanaka)" Date: Sun, 22 Oct 2023 00:47:53 +0900 Subject: [PATCH] Open source --- .../App/VCamUIPreview/VCamUIPreviewApp.swift | 12 +- .../VCamCamera/CoreMediaSinkStream.swift | 2 +- .../VCamCamera/VirtualCameraManager.swift | 19 +- .../Sources/VCamData/UserDefaultsKey.swift | 1 - .../en.lproj/Localizable.strings | 1 - .../ja.lproj/Localizable.strings | 1 - app/xcode/Sources/VCamUI/AppMenu.swift | 228 ++++++++++++++++++ .../Settings/VCamSettingGeneralView.swift | 2 +- .../Settings/VCamSettingTrackingView.swift | 2 - app/xcode/Sources/VCamUI/UnityCallback.swift | 24 +- .../Sources/VCamUI/VCamDisplayView.swift | 4 +- .../VCamUI/VCamMainObjectListView.swift | 2 +- app/xcode/Sources/VCamUI/VCamSystem.swift | 32 ++- app/xcode/Sources/VCamUI/WindowManager.swift | 21 +- 14 files changed, 306 insertions(+), 45 deletions(-) create mode 100644 app/xcode/Sources/VCamUI/AppMenu.swift diff --git a/app/xcode/App/VCamUIPreview/VCamUIPreviewApp.swift b/app/xcode/App/VCamUIPreview/VCamUIPreviewApp.swift index 96eebb9..3334a99 100644 --- a/app/xcode/App/VCamUIPreview/VCamUIPreviewApp.swift +++ b/app/xcode/App/VCamUIPreview/VCamUIPreviewApp.swift @@ -34,20 +34,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate { @MainActor private func configureApp() async { VCamUIPreviewStub.stub() + VCamSystem.shared.configure() // TODO: Refactor - - await Migration.migrate() - WindowManager.shared.setUpView() - - Camera.configure() - AudioDevice.configure() try? SceneManager.shared.loadCurrentScene() - Tracking.shared.configure() - WindowManager.shared.system.isUniVCamSystemEnabled = true - - VirtualCameraManager.shared.startCameraExtension() + VCamSystem.shared.isUniVCamSystemEnabled = true } } diff --git a/app/xcode/Sources/VCamCamera/CoreMediaSinkStream.swift b/app/xcode/Sources/VCamCamera/CoreMediaSinkStream.swift index 0678fde..e375288 100644 --- a/app/xcode/Sources/VCamCamera/CoreMediaSinkStream.swift +++ b/app/xcode/Sources/VCamCamera/CoreMediaSinkStream.swift @@ -61,7 +61,7 @@ public final class CoreMediaSinkStream: NSObject { size: .init(width: 1280, height: 720) ).ciImage! - VirtualCameraManager.shared.sendImageToVirtualCamera(with: debugImage, useHMirror: false) + VirtualCameraManager.shared.sendImageToVirtualCamera(with: debugImage) } #endif diff --git a/app/xcode/Sources/VCamCamera/VirtualCameraManager.swift b/app/xcode/Sources/VCamCamera/VirtualCameraManager.swift index 9cf1b6d..82c6d18 100644 --- a/app/xcode/Sources/VCamCamera/VirtualCameraManager.swift +++ b/app/xcode/Sources/VCamCamera/VirtualCameraManager.swift @@ -8,19 +8,28 @@ import Foundation import CoreImage import VCamDefaults +import Combine public final class VirtualCameraManager { public static let shared = VirtualCameraManager() public let sinkStream = CoreMediaSinkStream() + private var useHMirror = false + private var cancellables: Set = [] - public func sendImageToVirtualCamera(with image: CIImage, useHMirror: Bool) { + private init() { + UserDefaults.standard.publisher(for: \.vc_use_hmirror, options: [.initial, .new]) + .sink { [unowned self] in useHMirror = $0 } + .store(in: &cancellables) + } + + public func sendImageToVirtualCamera(with image: CIImage) { guard sinkStream.isStarting else { return } - let result = processImage(image, useHMirror: useHMirror) + let result = processImage(image) sinkStream.render(result) } - private func processImage(_ image: CIImage, useHMirror: Bool) -> CIImage { + private func processImage(_ image: CIImage) -> CIImage { var processedImage = image if useHMirror { @@ -44,3 +53,7 @@ public final class VirtualCameraManager { sinkStream.start() } } + +private extension UserDefaults { + @objc dynamic var vc_use_hmirror: Bool { value(for: .useHMirror) } +} diff --git a/app/xcode/Sources/VCamData/UserDefaultsKey.swift b/app/xcode/Sources/VCamData/UserDefaultsKey.swift index a4adf2d..9f7f92f 100644 --- a/app/xcode/Sources/VCamData/UserDefaultsKey.swift +++ b/app/xcode/Sources/VCamData/UserDefaultsKey.swift @@ -27,7 +27,6 @@ public extension UserDefaults.Key { static var trackingMethodFace: Key { .init("vc_tracking_method_face", default: .default) } static var trackingMethodHand: Key { .init("vc_tracking_method_hand", default: .default) } static var trackingMethodFinger: Key { .init("vc_tracking_method_finger", default: .default) } - static var moveXIntensity: Key { .init("vc_move_x_intensity", default: 1) } static var eyeTrackingOffsetY: Key { .init("vc_eye_tracking_offset_y", default: -0.2) } static var eyeTrackingIntensityX: Key { .init("vc_eye_tracking_intensity_x", default: 6) } static var eyeTrackingIntensityY: Key { .init("vc_eye_tracking_intensity_y", default: 3) } diff --git a/app/xcode/Sources/VCamLocalization/VCamResources/en.lproj/Localizable.strings b/app/xcode/Sources/VCamLocalization/VCamResources/en.lproj/Localizable.strings index 44579a2..7a97487 100644 --- a/app/xcode/Sources/VCamLocalization/VCamResources/en.lproj/Localizable.strings +++ b/app/xcode/Sources/VCamLocalization/VCamResources/en.lproj/Localizable.strings @@ -161,7 +161,6 @@ "eyesVerticalSensitivity" = "Vertical sensitivity"; "easeOfOpeningFingers" = "Finger opening sensitivity"; "easeOfCloseFingers" = "Finger closing sensitivity"; -"easeOfBodyMovement" = "Horizontal sensitivity of body"; "shoulderRotationWeight" = "Weight of shoulder rotation"; "swivelOffset" = "Elbow offset"; "faceEyeMouth" = "Face / Eye / Mouth"; diff --git a/app/xcode/Sources/VCamLocalization/VCamResources/ja.lproj/Localizable.strings b/app/xcode/Sources/VCamLocalization/VCamResources/ja.lproj/Localizable.strings index d397e73..b94906d 100644 --- a/app/xcode/Sources/VCamLocalization/VCamResources/ja.lproj/Localizable.strings +++ b/app/xcode/Sources/VCamLocalization/VCamResources/ja.lproj/Localizable.strings @@ -161,7 +161,6 @@ "eyesVerticalSensitivity" = "目の上下の動きの感度"; "easeOfOpeningFingers" = "指の開きやすさ"; "easeOfCloseFingers" = "指の閉じやすさ"; -"easeOfBodyMovement" = "体の左右の動きの大きさ"; "shoulderRotationWeight" = "肩の回転の大きさ"; "swivelOffset" = "肘の調整"; "faceEyeMouth" = "顔・目・口"; diff --git a/app/xcode/Sources/VCamUI/AppMenu.swift b/app/xcode/Sources/VCamUI/AppMenu.swift new file mode 100644 index 0000000..81ab603 --- /dev/null +++ b/app/xcode/Sources/VCamUI/AppMenu.swift @@ -0,0 +1,228 @@ +// +// AppMenu.swift +// +// +// Created by Tatsuya Tanaka on 2022/02/07. +// + +import Foundation +import AppKit +import VCamUIFoundation +import VCamBridge +import VCamData +import VCamCamera +import VCamTracking +import VCamLogger +import SystemExtensions + +public final class AppMenu: NSObject { + public static let shared = AppMenu() + + public let menu: NSMenu + + private override init() { + let mainMenu: NSMenu + if VCamSystem.shared.windowManager.isUnity { + menu = NSApp.mainMenu! + mainMenu = menu.items[0].submenu! + } else { + menu = NSMenu() + mainMenu = Self.makeSubMenu(menu: menu, title: "VCam", items: []) + NSApp.mainMenu = menu + } + + super.init() + + setupMainMenu(subMenu: mainMenu) + setupFileMenu(subMenu: menu) + setupEditMenu(subMenu: menu) + setupObjectMenu(subMenu: menu) + setupAvatarMenu(subMenu: menu) + setupWindowMenu(subMenu: menu) + setupHelpMenu(subMenu: menu) + } + + public func configure() {} + + private func makeMenuItem(title: String, action: Selector, keyEquivalent: String = "") -> NSMenuItem { + let item = NSMenuItem(title: title, action: action, keyEquivalent: keyEquivalent) + item.target = self + return item + } + + @discardableResult + public static func makeSubMenu(menu: NSMenu, title: String, items: [NSMenuItem]) -> NSMenu { + let rootItem = NSMenuItem() + menu.addItem(rootItem) + let subMenu = NSMenu(title: title) + rootItem.submenu = subMenu + subMenu.items = items + return subMenu + } + + private func setupEditMenu(subMenu: NSMenu) { + Self.makeSubMenu(menu: subMenu, title: L10n.edit.text, items: [ + NSMenuItem(title: L10n.cut.text, action: #selector(NSText.copy(_:)), keyEquivalent: "x"), + NSMenuItem(title: L10n.copy.text, action: #selector(NSText.copy(_:)), keyEquivalent: "c"), + NSMenuItem(title: L10n.paste.text, action: #selector(NSText.paste(_:)), keyEquivalent: "v"), + NSMenuItem(title: L10n.selectAll.text, action: #selector(NSText.selectAll(_:)), keyEquivalent: "a"), + ]) + } +} + +// MARK: - Main +private extension AppMenu { + private func setupMainMenu(subMenu: NSMenu) { + let quitItem = makeMenuItem(title: L10n.quitVCam.text, action: #selector(quit), keyEquivalent: "q") + subMenu.items.insert(quitItem, at: 0) + subMenu.items.insert(.separator(), at: 0) + let updateItem = makeMenuItem(title: L10n.checkForUpdates.text, action: #selector(checkUpdates)) + subMenu.items.insert(updateItem, at: 0) + subMenu.items.insert(.separator(), at: 0) + let preferenceItem = makeMenuItem(title: L10n.settings.text, action: #selector(openPreferences), keyEquivalent: ",") + subMenu.items.insert(preferenceItem, at: 0) + let aboutItem = makeMenuItem(title: L10n.aboutApp.text, action: #selector(about)) + subMenu.items.insert(aboutItem, at: 0) + } + + @objc private func quit() { + Logger.log("") + UniBridge.shared.quitApp() + } + + @objc private func about() { + MacWindowManager.shared.openCredits() + } + + @objc private func openPreferences() { + MacWindowManager.shared.open(VCamSettingView()) + } + + @objc private func checkUpdates() { + Task { + await AppUpdater.vcam.presentUpdateAlert() + } + } +} + +// MARK: - File +private extension AppMenu { + private func setupFileMenu(subMenu: NSMenu) { + Self.makeSubMenu(menu: subMenu, title: L10n.file.text, items: [ + makeMenuItem(title: L10n.loadVRMFile.text, action: #selector(loadVRM)), + makeMenuItem(title: L10n.loadOnVRoidHub.text, action: #selector(openVRoidHub)), + ]) + } + + @objc private func loadVRM() { + Logger.log(event: .loadVRMFile) + Logger.log("") + guard let url = FileUtility.openFile(type: .vrm) else { return } + UniBridge.shared.loadVRM(url.path) + } + + @objc private func openVRoidHub() { + Logger.log(event: .openVRoidHub) + Logger.log("") + UniBridge.shared.openVRoidHub() + } +} + +// MARK: - Object +private extension AppMenu { + private func setupObjectMenu(subMenu: NSMenu) { + Self.makeSubMenu(menu: subMenu, title: L10n.object.text, items: [ + makeMenuItem(title: L10n.resetAvatarPosition.text, action: #selector(resetAvatarPosition)), + .separator(), + makeMenuItem(title: L10n.addImage.text, action: #selector(addImage)), + makeMenuItem(title: L10n.addScreenCapture.text, action: #selector(addScreenCapture)), + makeMenuItem(title: L10n.addVideoCapture.text, action: #selector(addVideoCapture)), + makeMenuItem(title: L10n.addWeb.text, action: #selector(addWeb)), + .separator(), + makeMenuItem(title: L10n.addWind.text, action: #selector(addWind)), + ]) + } + + @objc private func resetAvatarPosition() { + UniBridge.shared.resetCamera() + } + + @objc private func addImage() { + guard let url = FileUtility.openFile(type: .image) else { return } + SceneObjectManager.shared.addImage(url: url) + } + + @objc private func addScreenCapture() { + showScreenRecorderPreferenceView { recorder in + guard let config = recorder.captureConfig, let screenId = config.id else { return } + let id = RenderTextureManager.shared.add(recorder) + SceneObjectManager.shared.add(.init(id: id, type: .screen(.init(id: screenId, captureType: config.captureType.type, textureSize: recorder.size, crop: recorder.cropRect, filter: nil)), isHidden: false, isLocked: false)) + } + } + + @objc private func addVideoCapture() { + CaptureDeviceRenderer.selectDevice { drawer in + let id = RenderTextureManager.shared.add(drawer) + SceneObjectManager.shared.add(.init(id: id, type: .videoCapture(.init(id: drawer.id, textureSize: drawer.size, crop: drawer.cropRect, filter: nil)), isHidden: false, isLocked: false)) + } + } + + @objc private func addWeb() { + WebRenderer.showPreferencesForAdding() + } + + @objc private func addWind() { + SceneObjectManager.shared.add(.init(type: .wind(), isHidden: false, isLocked: false)) + } +} + +// MARK: - Avatar +private extension AppMenu { + private func setupAvatarMenu(subMenu: NSMenu) { + Self.makeSubMenu(menu: subMenu, title: L10n.avatar.text, items: [ + makeMenuItem(title: L10n.editAvatar.text, action: #selector(editAvatar)), + .separator(), + makeMenuItem(title: L10n.calibrate.text, action: #selector(resetCalibration)), + ]) + } + + @objc private func editAvatar() { + Logger.log("") + UniBridge.shared.editAvatar() + } + + @objc private func resetCalibration() { + Tracking.shared.resetCalibration() + } +} + +// MARK: - Window +private extension AppMenu { + private func setupWindowMenu(subMenu: NSMenu) { + let alwaysOnTop = makeMenuItem(title: L10n.alwaysOnTop.text, action: #selector(toggleAlwaysOnTop)) + alwaysOnTop.state = UserDefaults.standard.value(for: .alwaysOnTopEnabled) ? .on : .off + Self.makeSubMenu(menu: subMenu, title: L10n.window.text, items: [ + alwaysOnTop, + ]) + } + + @objc private func toggleAlwaysOnTop(_ sender: NSMenuItem) { + let enabled = !UserDefaults.standard.value(for: .alwaysOnTopEnabled) + VCamSystem.shared.windowManager.setAlwaysOnTopEnabled(enabled) + sender.state = enabled ? .on : .off + } +} + +// MARK: - Help +private extension AppMenu { + private func setupHelpMenu(subMenu: NSMenu) { + Self.makeSubMenu(menu: subMenu, title: L10n.help.text, items: [ + makeMenuItem(title: L10n.anyProblem.text, action: #selector(help)), + ]) + } + + @objc private func help() { + let url = URL(string: "https://tattn.gitbook.io/vcam/sono-others/sono/faq")! + NSWorkspace.shared.open(url) + } +} diff --git a/app/xcode/Sources/VCamUI/Settings/VCamSettingGeneralView.swift b/app/xcode/Sources/VCamUI/Settings/VCamSettingGeneralView.swift index f754300..0ca22bb 100644 --- a/app/xcode/Sources/VCamUI/Settings/VCamSettingGeneralView.swift +++ b/app/xcode/Sources/VCamUI/Settings/VCamSettingGeneralView.swift @@ -43,7 +43,7 @@ public struct VCamSettingGeneralView: View { .frame(maxWidth: .infinity, alignment: .leading) } .onChange(of: useAddToMacOSMenuBar) { newValue in - WindowManager.shared.isMacOSMenubarVisible = newValue + VCamSystem.shared.windowManager.isMacOSMenubarVisible = newValue } GroupBox { diff --git a/app/xcode/Sources/VCamUI/Settings/VCamSettingTrackingView.swift b/app/xcode/Sources/VCamUI/Settings/VCamSettingTrackingView.swift index 1f0e3ad..73d9d0b 100644 --- a/app/xcode/Sources/VCamUI/Settings/VCamSettingTrackingView.swift +++ b/app/xcode/Sources/VCamUI/Settings/VCamSettingTrackingView.swift @@ -16,7 +16,6 @@ public struct VCamSettingTrackingView: View { public init() {} @AppStorage(key: .cameraFps) private var cameraFps - @AppStorage(key: .moveXIntensity) private var moveXIntensity @AppStorage(key: .eyeTrackingIntensityX) private var eyeTrackingIntensityX @AppStorage(key: .eyeTrackingIntensityY) private var eyeTrackingIntensityY @AppStorage(key: .useVowelEstimation) private var useVowelEstimation @@ -59,7 +58,6 @@ public struct VCamSettingTrackingView: View { GroupBox { Form { - ValueEditField(L10n.easeOfBodyMovement.key, value: $moveXIntensity.map(), type: .slider(0.1...3)) ValueEditField(L10n.shoulderRotationWeight.key, value: $shoulderRotationWeight, type: .slider(0.0...1)) ValueEditField(L10n.swivelOffset.key, value: $swivelOffset, type: .slider(0.0...30)) } diff --git a/app/xcode/Sources/VCamUI/UnityCallback.swift b/app/xcode/Sources/VCamUI/UnityCallback.swift index e5e1a69..a58d395 100644 --- a/app/xcode/Sources/VCamUI/UnityCallback.swift +++ b/app/xcode/Sources/VCamUI/UnityCallback.swift @@ -15,15 +15,15 @@ import VCamLogger @_cdecl("uniOnVCamSystemStart") public func uniOnVCamSystemStart() { Logger.log("uniOnVCamSystemStart") - WindowManager.shared.system.isUniVCamSystemEnabled = true - WindowManager.shared.system.startSystem() + VCamSystem.shared.isUniVCamSystemEnabled = true + VCamSystem.shared.startSystem() UniReload.Reloader.shared.objectWillChange.send() } @_cdecl("uniOnVCamSystemDestroy") public func uniOnVCamSystemDestroy() { Logger.log("uniOnVCamSystemDestroy") - WindowManager.shared.system.isUniVCamSystemEnabled = false + VCamSystem.shared.isUniVCamSystemEnabled = false UniReload.Reloader.shared.objectWillChange.send() } @@ -43,22 +43,32 @@ public func uniUseAutoConvertVRM1() -> Bool { @_cdecl("uniDisposeWindow") public func uniDisposeWindow() { Logger.log("") - WindowManager.shared.dispose() + VCamSystem.shared.windowManager.dispose() } @_cdecl("uniHideWindow") public func uniHideWindow() { Logger.log("") - WindowManager.shared.hide() + VCamSystem.shared.windowManager.hide() } @_cdecl("uniReloadUI") public func uniReloadUI() { - guard WindowManager.shared.isConfigured, - WindowManager.shared.system.isUniVCamSystemEnabled else { return } + guard VCamSystem.shared.windowManager.isConfigured, + VCamSystem.shared.isUniVCamSystemEnabled else { return } UniReload.Reloader.shared.objectWillChange.send() } +@_cdecl("uniUpdateRenderFrame") +public func uniUpdateRenderFrame() { + guard VCamSystem.shared.windowManager.isConfigured else { return } + VirtualCameraManager.shared.sendImageToVirtualCamera( + with: MainTexture.shared.texture + ) + + VideoRecorder.shared.renderFrame(MainTexture.shared.texture) +} + @_cdecl("uniRegisterString") public func uniRegisterString(_ get: @escaping @convention(c) (Int32) -> UnsafePointer, _ set: @escaping @convention(c) (Int32, UnsafePointer) -> Void) { UniBridge.shared.stringMapper.getValue = { String(cString: get($0.rawValue)) } diff --git a/app/xcode/Sources/VCamUI/VCamDisplayView.swift b/app/xcode/Sources/VCamUI/VCamDisplayView.swift index aebf43b..09b6169 100644 --- a/app/xcode/Sources/VCamUI/VCamDisplayView.swift +++ b/app/xcode/Sources/VCamUI/VCamDisplayView.swift @@ -20,7 +20,7 @@ public struct VCamDisplayView: View { @UniAction(.addDisplayParameter) private var addDisplayParameter @UniAction(.deleteDisplayParameter) private var deleteDisplayParameter - @ObservedObject private var windowManager = WindowManager.shared + @ObservedObject private var windowManager = VCamSystem.shared.windowManager public var body: some View { VStack { @@ -71,7 +71,7 @@ private struct VCamDisplayParameterView: View { @ExternalStateBinding(.lensFlare) private var lensFlare @ExternalStateBinding(.vignetteColor) private var vignetteColor: Color - @ObservedObject private var windowManager = WindowManager.shared + @ObservedObject private var windowManager = VCamSystem.shared.windowManager var body: some View { let minHeight = windowManager.size.height * 0.4 diff --git a/app/xcode/Sources/VCamUI/VCamMainObjectListView.swift b/app/xcode/Sources/VCamUI/VCamMainObjectListView.swift index f6a9870..bccbc31 100644 --- a/app/xcode/Sources/VCamUI/VCamMainObjectListView.swift +++ b/app/xcode/Sources/VCamUI/VCamMainObjectListView.swift @@ -66,7 +66,7 @@ public struct VCamMainObjectListView: View { } private struct VCamMainObjectListAddButton: View { - @ObservedObject private var pasteboard = WindowManager.shared.system.pasteboardObserver + @ObservedObject private var pasteboard = VCamSystem.shared.pasteboardObserver var body: some View { let objectManager = SceneObjectManager.shared diff --git a/app/xcode/Sources/VCamUI/VCamSystem.swift b/app/xcode/Sources/VCamUI/VCamSystem.swift index cf01e75..09d8f80 100644 --- a/app/xcode/Sources/VCamUI/VCamSystem.swift +++ b/app/xcode/Sources/VCamUI/VCamSystem.swift @@ -10,14 +10,20 @@ import VCamAppExtension import VCamBridge import VCamTracking import VCamLogger +import VCamCamera +import VCamEntity +import VCamWorkaround public final class VCamSystem { + public static let shared = VCamSystem() + + public let windowManager = WindowManager() public let pasteboardObserver = PasteboardObserver() public private(set) var isStarted = false public var isUniVCamSystemEnabled = false - init() { + private init() { ExtensionNotificationCenter.default.setObserver(for: .startCameraExtensionStream) { [weak self] in self?.startSystem() } @@ -25,8 +31,28 @@ public final class VCamSystem { ExtensionNotificationCenter.default.setObserver(for: .stopAllCameraExtensionStreams) { [weak self] in self?.stopSystem() } + + Workaround.fixColorPickerOpacity_macOS14() + windowManager.setUpWindow() + + Task { @MainActor in + if !windowManager.isUnity { + await AppUpdater.vcam.presentUpdateAlertIfAvailable() + } + + await Migration.migrate() + windowManager.setUpView() + AppMenu.shared.configure() + + Camera.configure() + AudioDevice.configure() + + VirtualCameraManager.shared.startCameraExtension() + } } + public func configure() {} + public func startSystem() { Logger.log("\(isStarted)") guard !isStarted else { return } @@ -39,8 +65,8 @@ public final class VCamSystem { } public func stopSystem() { - Logger.log("\(isStarted), \(WindowManager.shared.isWindowClosed)") - guard isStarted, WindowManager.shared.isWindowClosed else { return } + Logger.log("\(isStarted), \(windowManager.isWindowClosed)") + guard isStarted, windowManager.isWindowClosed else { return } isStarted = false Tracking.shared.stop() AvatarAudioManager.shared.stop(usage: .all) diff --git a/app/xcode/Sources/VCamUI/WindowManager.swift b/app/xcode/Sources/VCamUI/WindowManager.swift index 57dfc12..a718f24 100644 --- a/app/xcode/Sources/VCamUI/WindowManager.swift +++ b/app/xcode/Sources/VCamUI/WindowManager.swift @@ -11,11 +11,8 @@ import VCamCamera import VCamBridge public final class WindowManager: ObservableObject { - public static let shared = WindowManager() - @Published public private(set) var size = NSSize(width: 1280, height: 720) - public let system = VCamSystem() public let isUnity = Bundle.main.bundlePath.hasSuffix("Unity.app") public private(set) var isConfigured = false @@ -39,7 +36,7 @@ public final class WindowManager: ObservableObject { NotificationCenter.default.addObserver(forName: NSApplication.didBecomeActiveNotification, object: nil, queue: .main) { _ in // Display the window when launching the app while it's stored in the menu bar. - WindowManager.shared.unhide() + VCamSystem.shared.windowManager.unhide() } } @@ -78,12 +75,12 @@ public final class WindowManager: ObservableObject { isConfigured = true } - guard !WindowManager.shared.isConfigured, containerView.subviews.isEmpty, let window = NSApp.vcamWindow, let unityView = window.contentView else { + guard !isConfigured, containerView.subviews.isEmpty, let window = NSApp.vcamWindow, let unityView = window.contentView else { return } containerView.addFilledView(RootView(unityView: { - if WindowManager.shared.isUnity { + if isUnity { return NSView() } else { NSLayoutConstraint.deactivate(unityView.constraints) @@ -94,11 +91,11 @@ public final class WindowManager: ObservableObject { }())) window.contentView = containerView - if WindowManager.shared.isUnity { + if isUnity { window.setContentSize(containerView.fittingSize) } else { - WindowManager.shared.setupMenuBar() - WindowManager.shared.setAlwaysOnTopEnabled(UserDefaults.standard.value(for: .alwaysOnTopEnabled)) + setupMenuBar() + setAlwaysOnTopEnabled(UserDefaults.standard.value(for: .alwaysOnTopEnabled)) } } @@ -135,7 +132,7 @@ public final class WindowManager: ObservableObject { NSApp.vcamWindow?.setIsVisible(false) NSApp.setActivationPolicy(.accessory) if VirtualCameraManager.shared.sinkStream.streamingCount() == 0 { - system.stopSystem() + VCamSystem.shared.stopSystem() } } @@ -145,12 +142,12 @@ public final class WindowManager: ObservableObject { NSApp.setActivationPolicy(.regular) NSApp.vcamWindow?.setIsVisible(true) NSApp.activate(ignoringOtherApps: true) - system.startSystem() + VCamSystem.shared.startSystem() } public func dispose() { Logger.log("") - system.dispose() + VCamSystem.shared.dispose() isConfigured = false if isUnity {