- opt: win7 frameless - opt: disable cursor output & macos free cache - opt: main window, set location before show/hide
		
			
				
	
	
		
			234 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			234 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'dart:convert';
 | 
						|
 | 
						|
import 'package:desktop_multi_window/desktop_multi_window.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:flutter/services.dart';
 | 
						|
import 'package:flutter_hbb/common.dart';
 | 
						|
 | 
						|
/// must keep the order
 | 
						|
enum WindowType { Main, RemoteDesktop, FileTransfer, PortForward, Unknown }
 | 
						|
 | 
						|
extension Index on int {
 | 
						|
  WindowType get windowType {
 | 
						|
    switch (this) {
 | 
						|
      case 0:
 | 
						|
        return WindowType.Main;
 | 
						|
      case 1:
 | 
						|
        return WindowType.RemoteDesktop;
 | 
						|
      case 2:
 | 
						|
        return WindowType.FileTransfer;
 | 
						|
      case 3:
 | 
						|
        return WindowType.PortForward;
 | 
						|
      default:
 | 
						|
        return WindowType.Unknown;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// Window Manager
 | 
						|
/// mainly use it in `Main Window`
 | 
						|
/// use it in sub window is not recommended
 | 
						|
class RustDeskMultiWindowManager {
 | 
						|
  RustDeskMultiWindowManager._();
 | 
						|
 | 
						|
  static final instance = RustDeskMultiWindowManager._();
 | 
						|
 | 
						|
  final List<int> _activeWindows = List.empty(growable: true);
 | 
						|
  final List<VoidCallback> _windowActiveCallbacks = List.empty(growable: true);
 | 
						|
  int? _remoteDesktopWindowId;
 | 
						|
  int? _fileTransferWindowId;
 | 
						|
  int? _portForwardWindowId;
 | 
						|
 | 
						|
  Future<dynamic> newRemoteDesktop(String remoteId) async {
 | 
						|
    final msg =
 | 
						|
        jsonEncode({"type": WindowType.RemoteDesktop.index, "id": remoteId});
 | 
						|
 | 
						|
    try {
 | 
						|
      final ids = await DesktopMultiWindow.getAllSubWindowIds();
 | 
						|
      if (!ids.contains(_remoteDesktopWindowId)) {
 | 
						|
        _remoteDesktopWindowId = null;
 | 
						|
      }
 | 
						|
    } on Error {
 | 
						|
      _remoteDesktopWindowId = null;
 | 
						|
    }
 | 
						|
    if (_remoteDesktopWindowId == null) {
 | 
						|
      final remoteDesktopController =
 | 
						|
          await DesktopMultiWindow.createWindow(msg);
 | 
						|
      remoteDesktopController
 | 
						|
        ..setFrame(const Offset(0, 0) & const Size(1280, 720))
 | 
						|
        ..center()
 | 
						|
        ..setTitle("rustdesk - remote desktop")
 | 
						|
        ..show();
 | 
						|
      registerActiveWindow(remoteDesktopController.windowId);
 | 
						|
      _remoteDesktopWindowId = remoteDesktopController.windowId;
 | 
						|
    } else {
 | 
						|
      return call(WindowType.RemoteDesktop, "new_remote_desktop", msg);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Future<dynamic> newFileTransfer(String remoteId) async {
 | 
						|
    final msg =
 | 
						|
        jsonEncode({"type": WindowType.FileTransfer.index, "id": remoteId});
 | 
						|
 | 
						|
    try {
 | 
						|
      final ids = await DesktopMultiWindow.getAllSubWindowIds();
 | 
						|
      if (!ids.contains(_fileTransferWindowId)) {
 | 
						|
        _fileTransferWindowId = null;
 | 
						|
      }
 | 
						|
    } on Error {
 | 
						|
      _fileTransferWindowId = null;
 | 
						|
    }
 | 
						|
    if (_fileTransferWindowId == null) {
 | 
						|
      final fileTransferController = await DesktopMultiWindow.createWindow(msg);
 | 
						|
      fileTransferController
 | 
						|
        ..setFrame(const Offset(0, 0) & const Size(1280, 720))
 | 
						|
        ..center()
 | 
						|
        ..setTitle("rustdesk - file transfer")
 | 
						|
        ..show();
 | 
						|
      registerActiveWindow(fileTransferController.windowId);
 | 
						|
      _fileTransferWindowId = fileTransferController.windowId;
 | 
						|
    } else {
 | 
						|
      return call(WindowType.FileTransfer, "new_file_transfer", msg);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Future<dynamic> newPortForward(String remoteId, bool isRDP) async {
 | 
						|
    final msg = jsonEncode(
 | 
						|
        {"type": WindowType.PortForward.index, "id": remoteId, "isRDP": isRDP});
 | 
						|
 | 
						|
    try {
 | 
						|
      final ids = await DesktopMultiWindow.getAllSubWindowIds();
 | 
						|
      if (!ids.contains(_portForwardWindowId)) {
 | 
						|
        _portForwardWindowId = null;
 | 
						|
      }
 | 
						|
    } on Error {
 | 
						|
      _portForwardWindowId = null;
 | 
						|
    }
 | 
						|
    if (_portForwardWindowId == null) {
 | 
						|
      final portForwardController = await DesktopMultiWindow.createWindow(msg);
 | 
						|
      portForwardController
 | 
						|
        ..setFrame(const Offset(0, 0) & const Size(1280, 720))
 | 
						|
        ..center()
 | 
						|
        ..setTitle("rustdesk - port forward")
 | 
						|
        ..show();
 | 
						|
      registerActiveWindow(portForwardController.windowId);
 | 
						|
      _portForwardWindowId = portForwardController.windowId;
 | 
						|
    } else {
 | 
						|
      return call(WindowType.PortForward, "new_port_forward", msg);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Future<dynamic> call(WindowType type, String methodName, dynamic args) async {
 | 
						|
    int? windowId = findWindowByType(type);
 | 
						|
    if (windowId == null) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    return await DesktopMultiWindow.invokeMethod(windowId, methodName, args);
 | 
						|
  }
 | 
						|
 | 
						|
  int? findWindowByType(WindowType type) {
 | 
						|
    switch (type) {
 | 
						|
      case WindowType.Main:
 | 
						|
        return 0;
 | 
						|
      case WindowType.RemoteDesktop:
 | 
						|
        return _remoteDesktopWindowId;
 | 
						|
      case WindowType.FileTransfer:
 | 
						|
        return _fileTransferWindowId;
 | 
						|
      case WindowType.PortForward:
 | 
						|
        return _portForwardWindowId;
 | 
						|
      case WindowType.Unknown:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  void setMethodHandler(
 | 
						|
      Future<dynamic> Function(MethodCall call, int fromWindowId)? handler) {
 | 
						|
    DesktopMultiWindow.setMethodHandler(handler);
 | 
						|
  }
 | 
						|
 | 
						|
  Future<void> closeAllSubWindows() async {
 | 
						|
    await Future.wait(WindowType.values.map((e) => closeWindows(e)));
 | 
						|
  }
 | 
						|
 | 
						|
  Future<void> closeWindows(WindowType type) async {
 | 
						|
    if (type == WindowType.Main) {
 | 
						|
      // skip main window, use window manager instead
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    int? wId = findWindowByType(type);
 | 
						|
    if (wId != null) {
 | 
						|
      debugPrint("closing multi window: ${type.toString()}");
 | 
						|
      await saveWindowPosition(type, windowId: wId);
 | 
						|
      try {
 | 
						|
        final ids = await DesktopMultiWindow.getAllSubWindowIds();
 | 
						|
        if (!ids.contains(wId)) {
 | 
						|
          // no such window already
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        await WindowController.fromWindowId(wId).setPreventClose(false);
 | 
						|
        await WindowController.fromWindowId(wId).close();
 | 
						|
      } on Error {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Future<List<int>> getAllSubWindowIds() async {
 | 
						|
    try {
 | 
						|
      final windows = await DesktopMultiWindow.getAllSubWindowIds();
 | 
						|
      return windows;
 | 
						|
    } catch (err) {
 | 
						|
      if (err is AssertionError) {
 | 
						|
        return [];
 | 
						|
      } else {
 | 
						|
        rethrow;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  List<int> getActiveWindows() {
 | 
						|
    return _activeWindows;
 | 
						|
  }
 | 
						|
 | 
						|
  void _notifyActiveWindow() {
 | 
						|
    for (final callback in _windowActiveCallbacks) {
 | 
						|
      callback.call();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void registerActiveWindow(int windowId) {
 | 
						|
    if (_activeWindows.contains(windowId)) {
 | 
						|
      // ignore
 | 
						|
    } else {
 | 
						|
      _activeWindows.add(windowId);
 | 
						|
    }
 | 
						|
    _notifyActiveWindow();
 | 
						|
  }
 | 
						|
 | 
						|
  /// Remove active window which has [`windowId`]
 | 
						|
  /// 
 | 
						|
  /// [Avaliability]
 | 
						|
  /// This function should only be called from main window.
 | 
						|
  /// For other windows, please post a unregister(hide) event to main window handler:
 | 
						|
  /// `rustDeskWinManager.call(WindowType.Main, kWindowEventHide, {"id": windowId!});`
 | 
						|
  void unregisterActiveWindow(int windowId) {
 | 
						|
    if (!_activeWindows.contains(windowId)) {
 | 
						|
      // ignore
 | 
						|
    } else {
 | 
						|
      _activeWindows.remove(windowId);
 | 
						|
    }
 | 
						|
    _notifyActiveWindow();
 | 
						|
  }
 | 
						|
 | 
						|
  void registerActiveWindowListener(VoidCallback callback) {
 | 
						|
    _windowActiveCallbacks.add(callback);
 | 
						|
  }
 | 
						|
 | 
						|
  void unregisterActiveWindowListener(VoidCallback callback) {
 | 
						|
    _windowActiveCallbacks.remove(callback);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
final rustDeskWinManager = RustDeskMultiWindowManager.instance;
 |