From 3e702c834a90164ce85f6e2e82f90af74ffd8c7a Mon Sep 17 00:00:00 2001 From: csf Date: Mon, 15 Aug 2022 16:51:33 +0800 Subject: [PATCH 1/3] fix showLoading dark theme & add doubleTap to connect --- flutter/lib/common.dart | 1 - flutter/lib/desktop/widgets/peercard_widget.dart | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index dd48cefea..fb36d3aae 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -200,7 +200,6 @@ class OverlayDialogManager { VoidCallback? onCancel}) { show((setState, close) => CustomAlertDialog( content: Container( - color: MyTheme.white, constraints: BoxConstraints(maxWidth: 240), child: Column( mainAxisSize: MainAxisSize.min, diff --git a/flutter/lib/desktop/widgets/peercard_widget.dart b/flutter/lib/desktop/widgets/peercard_widget.dart index 85e6e20e6..5a5780431 100644 --- a/flutter/lib/desktop/widgets/peercard_widget.dart +++ b/flutter/lib/desktop/widgets/peercard_widget.dart @@ -53,7 +53,9 @@ class _PeerCardState extends State<_PeerCard> border: Border.all(color: Colors.transparent, width: 1.0), borderRadius: BorderRadius.circular(20)); }, - child: _buildPeerTile(context, peer, deco), + child: GestureDetector( + onDoubleTap: () => _connect(peer.id), + child: _buildPeerTile(context, peer, deco)), )); } From f99ab7d0a73db34379f3707e13d664c3d164ad04 Mon Sep 17 00:00:00 2001 From: csf Date: Mon, 15 Aug 2022 19:31:58 +0800 Subject: [PATCH 2/3] fix dialog res bug ; add desktop restart remote device --- flutter/lib/common.dart | 5 ++-- flutter/lib/desktop/pages/remote_page.dart | 13 ++++++++-- flutter/lib/mobile/pages/remote_page.dart | 23 ------------------ flutter/lib/mobile/widgets/dialog.dart | 23 ++++++++++++++++++ flutter/lib/models/model.dart | 1 - src/flutter.rs | 28 +++++++++------------- src/flutter_ffi.rs | 5 ++-- 7 files changed, 51 insertions(+), 47 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index fb36d3aae..d115156de 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -92,7 +92,7 @@ typedef DialogBuilder = CustomAlertDialog Function( class Dialog { OverlayEntry? entry; - Completer completer = Completer(); + Completer completer = Completer(); Dialog(); @@ -101,9 +101,10 @@ class Dialog { if (!completer.isCompleted) { completer.complete(res); } - entry?.remove(); } catch (e) { debugPrint("Dialog complete catch error: $e"); + } finally { + entry?.remove(); } } } diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 02060dee5..6be097854 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -589,11 +589,10 @@ class _RemotePageState extends State more.add(PopupMenuItem( child: Row( children: ([ - Container(width: 100.0, child: Text(translate('OS Password'))), + Text(translate('OS Password')), TextButton( style: flatButtonStyle, onPressed: () { - Navigator.pop(context); showSetOSPassword(widget.id, false, _ffi.dialogManager); }, child: Icon(Icons.edit, color: MyTheme.accent), @@ -625,6 +624,13 @@ class _RemotePageState extends State value: 'block-input')); } } + if (gFFI.ffiModel.permissions["restart"] != false && + (pi.platform == "Linux" || + pi.platform == "Windows" || + pi.platform == "Mac OS")) { + more.add(PopupMenuItem( + child: Text(translate('Restart Remote Device')), value: 'restart')); + } () async { var value = await showMenu( context: context, @@ -652,6 +658,7 @@ class _RemotePageState extends State }(); } else if (value == 'enter_os_password') { // FIXME: + // TODO icon diff // null means no session of id // empty string means no password var password = await bind.getSessionOption(id: id, arg: "os-password"); @@ -662,6 +669,8 @@ class _RemotePageState extends State } } else if (value == 'reset_canvas') { _ffi.cursorModel.reset(); + } else if (value == 'restart') { + showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager); } }(); } diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index d64c83707..c7d4202f2 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -670,7 +670,6 @@ class _RemotePageState extends State { TextButton( style: flatButtonStyle, onPressed: () { - Navigator.pop(context); showSetOSPassword(id, false, gFFI.dialogManager); }, child: Icon(Icons.edit, color: MyTheme.accent), @@ -1110,28 +1109,6 @@ void showOptions(String id, OverlayDialogManager dialogManager) async { }, clickMaskDismiss: true, backDismiss: true); } -void showRestartRemoteDevice( - PeerInfo pi, String id, OverlayDialogManager dialogManager) async { - final res = - await dialogManager.show((setState, close) => CustomAlertDialog( - title: Row(children: [ - Icon(Icons.warning_amber_sharp, - color: Colors.redAccent, size: 28), - SizedBox(width: 10), - Text(translate("Restart Remote Device")), - ]), - content: Text( - "${translate('Are you sure you want to restart')} \n${pi.username}@${pi.hostname}($id) ?"), - actions: [ - TextButton( - onPressed: () => close(), child: Text(translate("Cancel"))), - ElevatedButton( - onPressed: () => close(true), child: Text(translate("OK"))), - ], - )); - if (res == true) bind.sessionRestartRemoteDevice(id: id); -} - void showSetOSPassword( String id, bool login, OverlayDialogManager dialogManager) async { final controller = TextEditingController(); diff --git a/flutter/lib/mobile/widgets/dialog.dart b/flutter/lib/mobile/widgets/dialog.dart index 098f8d912..e0f98443b 100644 --- a/flutter/lib/mobile/widgets/dialog.dart +++ b/flutter/lib/mobile/widgets/dialog.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import '../../common.dart'; +import '../../models/model.dart'; import '../../models/platform_model.dart'; void clientClose(OverlayDialogManager dialogManager) { @@ -16,6 +17,28 @@ void showError() { showToast(translate("Error")); } +void showRestartRemoteDevice( + PeerInfo pi, String id, OverlayDialogManager dialogManager) async { + final res = + await dialogManager.show((setState, close) => CustomAlertDialog( + title: Row(children: [ + Icon(Icons.warning_amber_sharp, + color: Colors.redAccent, size: 28), + SizedBox(width: 10), + Text(translate("Restart Remote Device")), + ]), + content: Text( + "${translate('Are you sure you want to restart')} \n${pi.username}@${pi.hostname}($id) ?"), + actions: [ + TextButton( + onPressed: () => close(), child: Text(translate("Cancel"))), + ElevatedButton( + onPressed: () => close(true), child: Text(translate("OK"))), + ], + )); + if (res == true) bind.sessionRestartRemoteDevice(id: id); +} + void setPermanentPasswordDialog(OverlayDialogManager dialogManager) async { final pw = await bind.mainGetPermanentPassword(); final p0 = TextEditingController(text: pw); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index c4b10b377..18fe6a7f9 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1050,7 +1050,6 @@ class FFI { await for (final message in stream) { if (message is Event) { try { - debugPrint("event:${message.field0}"); Map event = json.decode(message.field0); cb(event); } catch (e) { diff --git a/src/flutter.rs b/src/flutter.rs index bb8881c58..418abc8af 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -31,9 +31,10 @@ use hbb_common::{ Stream, }; -use crate::common::{ - self, check_clipboard, make_fd_to_json, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL, -}; +use crate::common::{self, make_fd_to_json, CLIPBOARD_INTERVAL}; + +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use crate::common::{check_clipboard, update_clipboard, ClipboardContext}; use crate::{client::*, flutter_ffi::EventToUI, make_fd_flutter}; @@ -127,26 +128,18 @@ impl Session { } lc.set_option(name, value); } - // TODO - // input_os_password - // restart_remote_device /// Input the OS password. pub fn input_os_password(&self, pass: String, activate: bool) { input_os_password(pass, activate, self.clone()); } - // impl Interface - /// Send message to the remote session. - /// - /// # Arguments - /// - /// * `data` - The data to send. See [`Data`] for more details. - // fn send(data: Data) { - // if let Some(session) = SESSION.read().unwrap().as_ref() { - // session.send(data); - // } - // } + pub fn restart_remote_device(&self) { + let mut lc = self.lc.write().unwrap(); + lc.restarting_remote_device = true; + let msg = lc.restart_remote_device(); + self.send_msg(msg); + } /// Toggle an option. pub fn toggle_option(&self, name: &str) { @@ -670,6 +663,7 @@ impl Connection { lc: Arc>, ) -> Option> { let (tx, rx) = std::sync::mpsc::channel(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] match ClipboardContext::new() { Ok(mut ctx) => { let old_clipboard: Arc> = Default::default(); diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 4d062ab11..686111715 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -696,8 +696,9 @@ pub fn session_send_mouse(id: String, msg: String) { } pub fn session_restart_remote_device(id: String) { - // TODO - // Session::restart_remote_device(); + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.restart_remote_device(); + } } pub fn main_set_home_dir(home: String) { From 710ffcd0c7310ac54b88ea0238a46da398d08c47 Mon Sep 17 00:00:00 2001 From: csf Date: Mon, 15 Aug 2022 20:26:20 +0800 Subject: [PATCH 3/3] update quality monitor & remove remote_page.dart desktop unused code --- flutter/lib/common.dart | 5 +- flutter/lib/desktop/pages/remote_page.dart | 323 +++++---------------- flutter/lib/mobile/pages/remote_page.dart | 1 + src/flutter.rs | 1 + 4 files changed, 71 insertions(+), 259 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index d115156de..93c151bee 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -480,7 +480,8 @@ RadioListTile getRadio( } CheckboxListTile getToggle( - String id, void Function(void Function()) setState, option, name) { + String id, void Function(void Function()) setState, option, name, + {FFI? ffi}) { final opt = bind.getSessionToggleOptionSync(id: id, arg: option); return CheckboxListTile( value: opt, @@ -489,7 +490,7 @@ CheckboxListTile getToggle( bind.sessionToggleOption(id: id, value: option); }); if (option == "show-quality-monitor") { - gFFI.qualityMonitorModel.checkShowQualityMonitor(id); + (ffi ?? gFFI).qualityMonitorModel.checkShowQualityMonitor(id); } }, dense: true, diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 6be097854..e64d7a59a 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -5,7 +5,6 @@ import 'dart:ui' as ui; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_hbb/mobile/widgets/gesture_help.dart'; import 'package:flutter_hbb/models/chat_model.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; @@ -34,20 +33,13 @@ class RemotePage extends StatefulWidget { class _RemotePageState extends State with AutomaticKeepAliveClientMixin { - Timer? _interval; Timer? _timer; bool _showBar = !isWebDesktop; - double _bottom = 0; String _value = ''; - double _scale = 1; - double _mouseScrollIntegral = 0; // mouse scroll speed controller var _cursorOverImage = false.obs; - var _more = true; - var _fn = false; final FocusNode _mobileFocusNode = FocusNode(); final FocusNode _physicalFocusNode = FocusNode(); - var _showEdit = false; // use soft keyboard var _isPhysicalMouse = false; late FFI _ffi; @@ -63,8 +55,6 @@ class _RemotePageState extends State SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); _ffi.dialogManager .showLoading(translate('Connecting...'), onCancel: backToHomePage); - _interval = - Timer.periodic(Duration(milliseconds: 30), (timer) => interval()); }); if (!Platform.isLinux) { Wakelock.enable(); @@ -72,6 +62,7 @@ class _RemotePageState extends State _physicalFocusNode.requestFocus(); _ffi.ffiModel.updateEventListener(widget.id); _ffi.listenToMouse(true); + _ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); // WindowManager.instance.addListener(this); } @@ -80,11 +71,9 @@ class _RemotePageState extends State print("REMOTE PAGE dispose ${widget.id}"); hideMobileActionsOverlay(); _ffi.listenToMouse(false); - _ffi.invokeMethod("enable_soft_keyboard", true); _mobileFocusNode.dispose(); _physicalFocusNode.dispose(); _ffi.close(); - _interval?.cancel(); _timer?.cancel(); _ffi.dialogManager.dismissAll(); SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, @@ -101,31 +90,6 @@ class _RemotePageState extends State _ffi.resetModifiers(); } - bool isKeyboardShown() { - return _bottom >= 100; - } - - // crash on web before widget initiated. - void intervalUnsafe() { - var v = MediaQuery.of(context).viewInsets.bottom; - if (v != _bottom) { - resetTool(); - setState(() { - _bottom = v; - if (v < 100) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, - overlays: []); - } - }); - } - } - - void interval() { - try { - intervalUnsafe(); - } catch (e) {} - } - // handle mobile virtual keyboard void handleInput(String newValue) { var oldValue = _value; @@ -185,7 +149,6 @@ class _RemotePageState extends State content == '【】')) { // can not only input content[0], because when input ], [ are also auo insert, which cause ] never be input bind.sessionInputString(id: widget.id, value: content); - openKeyboard(); return; } bind.sessionInputString(id: widget.id, value: content); @@ -204,25 +167,6 @@ class _RemotePageState extends State _ffi.inputKey(char); } - void openKeyboard() { - _ffi.invokeMethod("enable_soft_keyboard", true); - // destroy first, so that our _value trick can work - _value = initText; - setState(() => _showEdit = false); - _timer?.cancel(); - _timer = Timer(Duration(milliseconds: 30), () { - // show now, and sleep a while to requestFocus to - // make sure edit ready, so that keyboard wont show/hide/show/hide happen - setState(() => _showEdit = true); - _timer?.cancel(); - _timer = Timer(Duration(milliseconds: 30), () { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, - overlays: SystemUiOverlay.values); - _mobileFocusNode.requestFocus(); - }); - }); - } - void sendRawKey(RawKeyEvent e, {bool? down, bool? press}) { // for maximum compatibility final label = _logicalKeyMap[e.logicalKey.keyId] ?? @@ -233,28 +177,18 @@ class _RemotePageState extends State Widget buildBody(FfiModel ffiModel) { final hasDisplays = ffiModel.pi.displays.length > 0; - final hideKeyboard = isKeyboardShown() && _showEdit; - final showActionButton = !_showBar || hideKeyboard; final keyboard = ffiModel.permissions['keyboard'] != false; return Scaffold( // resizeToAvoidBottomInset: true, - floatingActionButton: !showActionButton + floatingActionButton: _showBar ? null : FloatingActionButton( - mini: !hideKeyboard, - child: - Icon(hideKeyboard ? Icons.expand_more : Icons.expand_less), + mini: true, + child: Icon(Icons.expand_less), backgroundColor: MyTheme.accent, onPressed: () { setState(() { - if (hideKeyboard) { - _showEdit = false; - _ffi.invokeMethod("enable_soft_keyboard", false); - _mobileFocusNode.unfocus(); - _physicalFocusNode.requestFocus(); - } else { - _showBar = !_showBar; - } + _showBar = !_showBar; }); }), bottomNavigationBar: @@ -322,8 +256,7 @@ class _RemotePageState extends State sendRawKey(e, down: true); } } - // [!_showEdit] workaround for soft-keyboard's control_key like Backspace / Enter - if (!_showEdit && e is RawKeyUpEvent) { + if (e is RawKeyUpEvent) { if (key == LogicalKeyboardKey.altLeft || key == LogicalKeyboardKey.altRight) { _ffi.alt = false; @@ -369,8 +302,8 @@ class _RemotePageState extends State color: Colors.white, icon: Icon(Icons.tv), onPressed: () { - setState(() => _showEdit = false); - showOptions(widget.id, _ffi.dialogManager); + _ffi.dialogManager.dismissAll(); + showOptions(widget.id); }, ) ] + @@ -390,19 +323,7 @@ class _RemotePageState extends State }, ) ] - : [ - IconButton( - color: Colors.white, - icon: Icon(Icons.keyboard), - onPressed: openKeyboard), - IconButton( - color: Colors.white, - icon: Icon(_ffi.ffiModel.touchMode - ? Icons.touch_app - : Icons.mouse), - onPressed: changeTouchMode, - ), - ]) + + : []) + (isWeb ? [] : [ @@ -421,7 +342,6 @@ class _RemotePageState extends State color: Colors.white, icon: Icon(Icons.more_vert), onPressed: () { - setState(() => _showEdit = false); showActions(widget.id, ffiModel); }, ), @@ -548,7 +468,7 @@ class _RemotePageState extends State id: widget.id, )); } - paints.add(getHelpTools()); + paints.add(QualityMonitor(_ffi.qualityMonitorModel)); return Stack( children: paints, ); @@ -675,165 +595,6 @@ class _RemotePageState extends State }(); } - void changeTouchMode() { - setState(() => _showEdit = false); - showModalBottomSheet( - backgroundColor: MyTheme.grayBg, - isScrollControlled: true, - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(5))), - builder: (context) => DraggableScrollableSheet( - expand: false, - builder: (context, scrollController) { - return SingleChildScrollView( - padding: EdgeInsets.symmetric(vertical: 10), - child: GestureHelp( - touchMode: _ffi.ffiModel.touchMode, - onTouchModeChange: (t) { - _ffi.ffiModel.toggleTouchMode(); - final v = _ffi.ffiModel.touchMode ? 'Y' : ''; - bind.sessionPeerOption( - id: widget.id, name: "touch-mode", value: v); - })); - })); - } - - Widget getHelpTools() { - final keyboard = isKeyboardShown(); - if (!keyboard) { - return SizedBox(); - } - var wrap = (String text, void Function() onPressed, - [bool? active, IconData? icon]) { - return TextButton( - style: TextButton.styleFrom( - minimumSize: Size(0, 0), - padding: EdgeInsets.symmetric(vertical: 10, horizontal: 9.75), - //adds padding inside the button - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - //limits the touch area to the button area - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5.0), - ), - backgroundColor: active == true ? MyTheme.accent80 : null, - ), - child: icon != null - ? Icon(icon, size: 17, color: Colors.white) - : Text(translate(text), - style: TextStyle(color: Colors.white, fontSize: 11)), - onPressed: onPressed); - }; - final pi = _ffi.ffiModel.pi; - final isMac = pi.platform == "Mac OS"; - final modifiers = [ - wrap('Ctrl ', () { - setState(() => _ffi.ctrl = !_ffi.ctrl); - }, _ffi.ctrl), - wrap(' Alt ', () { - setState(() => _ffi.alt = !_ffi.alt); - }, _ffi.alt), - wrap('Shift', () { - setState(() => _ffi.shift = !_ffi.shift); - }, _ffi.shift), - wrap(isMac ? ' Cmd ' : ' Win ', () { - setState(() => _ffi.command = !_ffi.command); - }, _ffi.command), - ]; - final keys = [ - wrap( - ' Fn ', - () => setState( - () { - _fn = !_fn; - if (_fn) { - _more = false; - } - }, - ), - _fn), - wrap( - ' ... ', - () => setState( - () { - _more = !_more; - if (_more) { - _fn = false; - } - }, - ), - _more), - ]; - final fn = [ - SizedBox(width: 9999), - ]; - for (var i = 1; i <= 12; ++i) { - final name = 'F' + i.toString(); - fn.add(wrap(name, () { - _ffi.inputKey('VK_' + name); - })); - } - final more = [ - SizedBox(width: 9999), - wrap('Esc', () { - _ffi.inputKey('VK_ESCAPE'); - }), - wrap('Tab', () { - _ffi.inputKey('VK_TAB'); - }), - wrap('Home', () { - _ffi.inputKey('VK_HOME'); - }), - wrap('End', () { - _ffi.inputKey('VK_END'); - }), - wrap('Del', () { - _ffi.inputKey('VK_DELETE'); - }), - wrap('PgUp', () { - _ffi.inputKey('VK_PRIOR'); - }), - wrap('PgDn', () { - _ffi.inputKey('VK_NEXT'); - }), - SizedBox(width: 9999), - wrap('', () { - _ffi.inputKey('VK_LEFT'); - }, false, Icons.keyboard_arrow_left), - wrap('', () { - _ffi.inputKey('VK_UP'); - }, false, Icons.keyboard_arrow_up), - wrap('', () { - _ffi.inputKey('VK_DOWN'); - }, false, Icons.keyboard_arrow_down), - wrap('', () { - _ffi.inputKey('VK_RIGHT'); - }, false, Icons.keyboard_arrow_right), - wrap(isMac ? 'Cmd+C' : 'Ctrl+C', () { - sendPrompt(widget.id, isMac, 'VK_C'); - }), - wrap(isMac ? 'Cmd+V' : 'Ctrl+V', () { - sendPrompt(widget.id, isMac, 'VK_V'); - }), - wrap(isMac ? 'Cmd+S' : 'Ctrl+S', () { - sendPrompt(widget.id, isMac, 'VK_S'); - }), - ]; - final space = MediaQuery.of(context).size.width > 320 ? 4.0 : 2.0; - return Container( - color: Color(0xAA000000), - padding: EdgeInsets.only( - top: keyboard ? 24 : 4, left: 0, right: 0, bottom: 8), - child: Wrap( - spacing: space, - runSpacing: space, - children: [SizedBox(width: 9999)] + - (keyboard - ? modifiers + keys + (_fn ? fn : []) + (_more ? more : []) - : modifiers), - )); - } - @override void onWindowEvent(String eventName) { print("window event: $eventName"); @@ -1001,7 +762,52 @@ class ImagePainter extends CustomPainter { } } -void showOptions(String id, OverlayDialogManager dialogManager) async { +class QualityMonitor extends StatelessWidget { + final QualityMonitorModel qualityMonitorModel; + QualityMonitor(this.qualityMonitorModel); + + @override + Widget build(BuildContext context) => ChangeNotifierProvider.value( + value: qualityMonitorModel, + child: Consumer( + builder: (context, qualityMonitorModel, child) => Positioned( + top: 10, + right: 10, + child: qualityMonitorModel.show + ? Container( + padding: EdgeInsets.all(8), + color: MyTheme.canvasColor.withAlpha(120), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Speed: ${qualityMonitorModel.data.speed ?? ''}", + style: TextStyle(color: MyTheme.grayBg), + ), + Text( + "FPS: ${qualityMonitorModel.data.fps ?? ''}", + style: TextStyle(color: MyTheme.grayBg), + ), + Text( + "Delay: ${qualityMonitorModel.data.delay ?? ''} ms", + style: TextStyle(color: MyTheme.grayBg), + ), + Text( + "Target Bitrate: ${qualityMonitorModel.data.targetBitrate ?? ''}kb", + style: TextStyle(color: MyTheme.grayBg), + ), + Text( + "Codec: ${qualityMonitorModel.data.codecFormat ?? ''}", + style: TextStyle(color: MyTheme.grayBg), + ), + ], + ), + ) + : SizedBox.shrink()))); +} + +void showOptions(String id) async { + final _ffi = ffi(id); String quality = await bind.getSessionImageQuality(id: id) ?? 'balanced'; if (quality == '') quality = 'balanced'; String viewStyle = @@ -1009,8 +815,8 @@ void showOptions(String id, OverlayDialogManager dialogManager) async { String scrollStyle = await bind.getSessionOption(id: id, arg: 'scroll-style') ?? ''; var displays = []; - final pi = ffi(id).ffiModel.pi; - final image = ffi(id).ffiModel.getConnectionImage(); + final pi = _ffi.ffiModel.pi; + final image = _ffi.ffiModel.getConnectionImage(); if (image != null) displays.add(Padding(padding: const EdgeInsets.only(top: 8), child: image)); if (pi.displays.length > 1) { @@ -1021,7 +827,7 @@ void showOptions(String id, OverlayDialogManager dialogManager) async { onTap: () { if (i == cur) return; bind.sessionSwitchDisplay(id: id, value: i); - dialogManager.dismissAll(); + _ffi.dialogManager.dismissAll(); }, child: Ink( width: 40, @@ -1044,9 +850,9 @@ void showOptions(String id, OverlayDialogManager dialogManager) async { if (displays.isNotEmpty) { displays.add(Divider(color: MyTheme.border)); } - final perms = ffi(id).ffiModel.permissions; + final perms = _ffi.ffiModel.permissions; - dialogManager.show((setState, close) { + _ffi.dialogManager.show((setState, close) { final more = []; if (perms['audio'] != false) { more.add(getToggle(id, setState, 'disable-audio', 'Mute')); @@ -1077,7 +883,7 @@ void showOptions(String id, OverlayDialogManager dialogManager) async { setState(() { viewStyle = value; bind.sessionPeerOption(id: id, name: "view-style", value: value); - ffi(id).canvasModel.updateViewStyle(); + _ffi.canvasModel.updateViewStyle(); }); }; var setScrollStyle = (String? value) { @@ -1085,7 +891,7 @@ void showOptions(String id, OverlayDialogManager dialogManager) async { setState(() { scrollStyle = value; bind.sessionPeerOption(id: id, name: "scroll-style", value: value); - ffi(id).canvasModel.updateScrollStyle(); + _ffi.canvasModel.updateScrollStyle(); }); }; return CustomAlertDialog( @@ -1108,6 +914,9 @@ void showOptions(String id, OverlayDialogManager dialogManager) async { Divider(color: MyTheme.border), getToggle( id, setState, 'show-remote-cursor', 'Show remote cursor'), + getToggle(id, setState, 'show-quality-monitor', + 'Show quality monitor', + ffi: _ffi), ] + more), actions: [], diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index c7d4202f2..6a5be8b8d 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -59,6 +59,7 @@ class _RemotePageState extends State { _physicalFocusNode.requestFocus(); gFFI.ffiModel.updateEventListener(widget.id); gFFI.listenToMouse(true); + gFFI.qualityMonitorModel.checkShowQualityMonitor(widget.id); } @override diff --git a/src/flutter.rs b/src/flutter.rs index 418abc8af..b5553e475 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -850,6 +850,7 @@ impl Connection { }; if let Ok(true) = self.video_handler.handle_frame(vf) { let stream = self.session.events2ui.read().unwrap(); + self.frame_count.fetch_add(1, Ordering::Relaxed); stream.add(EventToUI::Rgba(ZeroCopyBuffer( self.video_handler.rgb.clone(), )));