Skip to content

Presentation Screen in Secondary Display #388

@joabeliot

Description

@joabeliot

I am creating a desktop app that does presentations. The logic I am trying to implement is I want a child window that takes up the entire space of the secondary monitor exactly like PowerPoint does, this window renders a widget where I'll make change which has to be reflected in the Second Display.

I got to the point where I was able to create a second display with desktop_multi_window but I couldn't set it to take up all the screen. I don't want the title bar, close min action buttons, no taskbar, typical presentation screen

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:ui' as ui;

import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flock_desktop/utils/widgets.dart';
import 'package:flock_desktop/widgets/AudioList.dart';
import 'package:flock_desktop/widgets/menu.dart';
import 'package:flock_desktop/widgets/player.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:screen_retriever/screen_retriever.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:window_manager/window_manager.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';

late double minWidth;

class DisplayMonitor { // monitoring the changes in display connection and triggering New display and Display removed triggers
  List<String> previousDisplayIds = [];
  Window? secondaryWindow; // Store the secondary window reference

  // Call this method to start monitoring display changes
  void startMonitoring() {
    Timer.periodic(Duration(seconds: 2), (timer) async {
      await detectDisplays();
    });
  }

  // Detects displays and checks for new ones
  Future<void> detectDisplays() async {
    try {
      // Retrieve all displays
      final displays = await screenRetriever.getAllDisplays();
      List<String> currentDisplayIds = [];

      // Iterate through each display and print its dimensions
      for (var display in displays) {
        final id = display.id;
        currentDisplayIds.add(id);
      }

      final newDisplays = currentDisplayIds
          .where((id) => !previousDisplayIds.contains(id))
          .toList();
      final removedDisplays = previousDisplayIds
          .where((id) => !currentDisplayIds.contains(id))
          .toList();

      // Handle new displays
      if (newDisplays.isNotEmpty) {
        for (var display in displays) {
          if (newDisplays.contains(display.id)) {
            final id = display.id;
            final displayName = display.name;
            final width = display.size.width;
            final height = display.size.height;
            final positionX = display.visiblePosition?.dx.toInt();
            final positionY = display.visiblePosition?.dy.toInt();

            Display primaryDisplay = await screenRetriever.getPrimaryDisplay();

            if (primaryDisplay.id != display.id) {
              print(
                  'New Display ID: $id | Name: $displayName | Dimensions: ${width}x${height} | Position: $positionX, $positionY');
              if (isSecondMonitor(display)) {
                await createSecondaryWindow(display);
              }
            }
          }
        }
      }

      // Handle removed displays (screens that have been disconnected)
      if (removedDisplays.isNotEmpty) {
        for (var removedDisplayId in removedDisplays) {
          print('Display removed: $removedDisplayId');
          // Trigger the screen removed event (you can use a callback or custom event here)
          onScreenRemoved(removedDisplayId);
        }
      }

      previousDisplayIds = currentDisplayIds;
    } catch (e) {
      print('Failed to retrieve displays: $e');
    }
  }

  // Callback or event handler for screen removal
  void onScreenRemoved(String removedDisplayId) {
    // Handle the event for the removed display here
    print('Handling removal for screen: $removedDisplayId');
    // For example, you can destroy the secondary window associated with this display
    if (secondaryWindow != null) {
      // secondaryWindow?.close(); // Close the secondary window if one exists
      secondaryWindow = null;   // Reset the reference
    }
    // Additional logic, like freeing resources or updating UI, can go here
  }

  bool isSecondMonitor(Display display) {
    return display.visiblePosition?.dx != 0;
  }

  Future<void> createSecondaryWindow(Display display) async {
    final width = display.size.width;
    final height = display.size.height;
    final positionX = display.visiblePosition?.dx.toDouble();
    final positionY = display.visiblePosition?.dy.toDouble();
    print("$width, $height, $positionX, $positionY");

    final primaryDisplay = await screenRetriever.getPrimaryDisplay();

    if (primaryDisplay.id != display.id) { 
       DesktopMultiWindow.createWindow(jsonEncode({'args1': 'Sub window'}))
        .then((value) {
      value
        ..setFrame(Offset(positionX!, positionY!) &
            ui.Size(width,
                height)) // Set the window size to secondary screen's size
        // ..setSkipTaskbar(true)  // Hide the window from the taskbar
        // ..setResizable(false) // Make the window non-resizable
        // ..setMinimizable(false) // Hide minimize button
        // ..setMaximizable(false) // Hide maximize button
        // ..setClosable(false) // Hide close button
        ..setTitle("") // Remove the title
        // ..setFullScreen(true) // Enable fullscreen mode
        ..show();
    });
     }
  }
}

void main(
    List<String> args
    ) async {
  WidgetsFlutterBinding.ensureInitialized();

  await windowManager.ensureInitialized();
  Display primaryDisplay = await screenRetriever.getPrimaryDisplay();
  double displayWidth = primaryDisplay.size.width;
  minWidth = displayWidth / 2;

  final displayMonitor = DisplayMonitor();
  displayMonitor.startMonitoring();

  if (Platform.isWindows) {
    WindowManager.instance.setMinimumSize(ui.Size((minWidth + 50), 640));
  }
  runApp(MyApp(
      args
      ));
}

class MyApp extends StatelessWidget {
  // final double minWidth;
  final List<String> args;
  MyApp(
      this.args,
      {
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return args.firstOrNull == "multi_window"
        ? SecondaryWindow()
        : const PrimaryWindow();
    // return const PrimaryWindow();
  }
}

And when the second display is removed the second window should be closed... can you help me out this...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions