When a second display is connected (macOS), presenting now opens a borderless audience window on the beamer showing the slide, while the main window shows the presenter view (current/next slide, speaker notes, clock, controls) on the laptop. The two windows stay in sync over method channels: navigation, blank screen, audio-complete and beamer clicks are forwarded between them, and media plays only on the beamer to avoid double audio. Falls back to the existing single-window presenter when there is one display or the second window can't be created. - Vendors a fork of desktop_multi_window in third_party/ that re-adds the native macOS window geometry/fullscreen calls (coverScreen, setFrame, close) the published 0.3.0 dropped; wired via a path dependency. - Registers the app's plugins for sub-windows in MainFlutterWindow so video/image rendering works on the beamer. - Routes the multi_window dart entrypoint to a minimal AudienceWindowApp. Compiles (flutter analyze + macOS debug build) and all tests pass; runtime two-screen behaviour still needs verification on real hardware. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
1.6 KiB
Swift
51 lines
1.6 KiB
Swift
import Foundation
|
|
import Cocoa
|
|
|
|
|
|
struct WindowConfiguration: Codable {
|
|
|
|
let arguments: String
|
|
let hiddenAtLaunch: Bool
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case arguments
|
|
case hiddenAtLaunch
|
|
}
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
arguments = try container.decodeIfPresent(String.self, forKey: .arguments) ?? ""
|
|
hiddenAtLaunch = try container.decodeIfPresent(Bool.self, forKey: .hiddenAtLaunch) ?? false
|
|
}
|
|
|
|
init(arguments: String, hiddenAtLaunch: Bool) {
|
|
self.arguments = arguments
|
|
self.hiddenAtLaunch = hiddenAtLaunch
|
|
}
|
|
|
|
static let defaultConfiguration = WindowConfiguration(
|
|
arguments: "",
|
|
hiddenAtLaunch: false
|
|
)
|
|
|
|
static func fromJson(_ json: [String: Any?]) -> WindowConfiguration {
|
|
guard let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []) else {
|
|
debugPrint("invalid json object: \(json)")
|
|
return defaultConfiguration
|
|
}
|
|
|
|
do {
|
|
let decoder = JSONDecoder()
|
|
return try decoder.decode(WindowConfiguration.self, from: jsonData)
|
|
} catch {
|
|
debugPrint("Failed to parse window configuration: \(error)")
|
|
return defaultConfiguration
|
|
}
|
|
}
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(arguments, forKey: .arguments)
|
|
try container.encode(hiddenAtLaunch, forKey: .hiddenAtLaunch)
|
|
}
|
|
}
|