Add dual-screen presenter mode (slide on beamer, notes on laptop)
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>
2026-06-06 21:25:34 +02:00
|
|
|
import 'dart:convert';
|
2026-06-02 23:28:39 +02:00
|
|
|
import 'dart:io';
|
2026-06-06 20:41:24 +02:00
|
|
|
import 'package:flutter/foundation.dart';
|
2026-06-02 23:28:39 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
|
import 'package:window_manager/window_manager.dart';
|
|
|
|
|
import 'app.dart';
|
Add dual-screen presenter mode (slide on beamer, notes on laptop)
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>
2026-06-06 21:25:34 +02:00
|
|
|
import 'widgets/presentation/audience_window.dart';
|
2026-06-02 23:28:39 +02:00
|
|
|
|
Add dual-screen presenter mode (slide on beamer, notes on laptop)
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>
2026-06-06 21:25:34 +02:00
|
|
|
void main(List<String> args) async {
|
2026-06-02 23:28:39 +02:00
|
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
|
|
Add dual-screen presenter mode (slide on beamer, notes on laptop)
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>
2026-06-06 21:25:34 +02:00
|
|
|
// Secondary windows (e.g. the audience/beamer slide window) are launched by
|
|
|
|
|
// desktop_multi_window with these entrypoint arguments. They run a minimal app
|
|
|
|
|
// and must not touch the main window's window_manager setup.
|
|
|
|
|
if (args.isNotEmpty && args.first == 'multi_window') {
|
|
|
|
|
final raw = args.length >= 3 ? args[2] : '';
|
|
|
|
|
final parsed = raw.isEmpty ? const {} : jsonDecode(raw);
|
|
|
|
|
final map = Map<String, dynamic>.from(parsed as Map);
|
|
|
|
|
runApp(AudienceWindowApp(args: map));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 20:41:24 +02:00
|
|
|
if (!kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux)) {
|
2026-06-02 23:28:39 +02:00
|
|
|
await windowManager.ensureInitialized();
|
|
|
|
|
const options = WindowOptions(
|
|
|
|
|
minimumSize: Size(1000, 650),
|
|
|
|
|
title: 'OciDeck',
|
|
|
|
|
);
|
|
|
|
|
windowManager.waitUntilReadyToShow(options, () async {
|
|
|
|
|
await windowManager.show();
|
|
|
|
|
await windowManager.focus();
|
|
|
|
|
await windowManager.setPreventClose(true);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
runApp(const ProviderScope(child: OciDeckApp()));
|
|
|
|
|
}
|