From 9b694cbac07a703b5da93483df98cf01bbb894f2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 7 Sep 2022 07:06:05 -0700 Subject: [PATCH 1/2] flutter_desktop: cursor cache - linux Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 15 ++-- flutter/lib/models/model.dart | 84 ++++++++++++++++++---- flutter/pubspec.yaml | 2 +- 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 86372d868..cd23328a4 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -568,13 +568,16 @@ class ImagePaint extends StatelessWidget { cursor: (cursorOverImage.isTrue && keyboardEnabled.isTrue) ? (remoteCursorMoved.isTrue ? SystemMouseCursors.none - : (cursor.pngData != null + : (cursor.cacheLinux != null ? FlutterCustomMemoryImageCursor( - pixbuf: cursor.pngData!, - hotx: cursor.hotx, - hoty: cursor.hoty, - imageWidth: (cursor.image!.width * s).toInt(), - imageHeight: (cursor.image!.height * s).toInt(), + pixbuf: cursor.cacheLinux!.data, + key: cursor.cacheLinux!.key, + hotx: cursor.cacheLinux!.hotx, + hoty: cursor.cacheLinux!.hoty, + imageWidth: + (cursor.cacheLinux!.width * s).toInt(), + imageHeight: + (cursor.cacheLinux!.height * s).toInt(), ) : MouseCursor.defer)) : MouseCursor.defer, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index c02cd1ee1..7cf54492a 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -17,6 +17,7 @@ import 'package:flutter_hbb/models/server_model.dart'; import 'package:flutter_hbb/models/user_model.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tuple/tuple.dart'; +import 'package:flutter_custom_cursor/flutter_custom_cursor.dart'; import '../common.dart'; import '../common/shared_state.dart'; @@ -351,7 +352,7 @@ class ImageModel with ChangeNotifier { // my throw exception, because the listener maybe already dispose update(image, tabBarHeight); } catch (e) { - print('update image: $e'); + debugPrint('update image: $e'); } }); } @@ -594,11 +595,36 @@ class CanvasModel with ChangeNotifier { } } +// data for cursor +class CursorData { + final String peerId; + final int id; + final Uint8List? data; + final double hotx; + final double hoty; + final int width; + final int height; + late String key; + + CursorData({ + required this.peerId, + required this.id, + required this.data, + required this.hotx, + required this.hoty, + required this.width, + required this.height, + }) { + key = + '${peerId}_${id}_${(hotx * 10e6).round().toInt()}_${(hoty * 10e6).round().toInt()}_${width}_$height'; + } +} + class CursorModel with ChangeNotifier { ui.Image? _image; final _images = >{}; - Uint8List? _pngData; - final _pngs = {}; + CursorData? _cacheLinux; + final _cacheMapLinux = {}; double _x = -10000; double _y = -10000; double _hotx = 0; @@ -609,7 +635,7 @@ class CursorModel with ChangeNotifier { WeakReference parent; ui.Image? get image => _image; - Uint8List? get pngData => _pngData; + CursorData? get cacheLinux => _cacheLinux; double get x => _x - _displayOriginX; @@ -623,6 +649,9 @@ class CursorModel with ChangeNotifier { CursorModel(this.parent); + List get cachedKeysLinux => + _cacheMapLinux.values.map((v) => v.key).toList(); + // remote physical display coordinate Rect getVisibleRect() { final size = MediaQueryData.fromWindow(ui.window).size; @@ -763,13 +792,7 @@ class CursorModel with ChangeNotifier { if (parent.target?.id != pid) return; _image = image; _images[id] = Tuple3(image, _hotx, _hoty); - final data = await image.toByteData(format: ImageByteFormat.png); - if (data != null) { - _pngData = data.buffer.asUint8List(); - } else { - _pngData = null; - } - _pngs[id] = _pngData; + _updateCacheLinux(image, id, width, height); try { // my throw exception, because the listener maybe already dispose notifyListeners(); @@ -780,8 +803,28 @@ class CursorModel with ChangeNotifier { }); } + void _updateCacheLinux(ui.Image image, int id, int w, int h) async { + final data = await image.toByteData(format: ImageByteFormat.png); + late Uint8List? dataLinux; + if (data != null) { + dataLinux = data.buffer.asUint8List(); + } else { + dataLinux = null; + } + _cacheLinux = CursorData( + peerId: this.id, + data: dataLinux, + id: id, + hotx: _hotx, + hoty: _hoty, + width: w, + height: h, + ); + _cacheMapLinux[id] = _cacheLinux!; + } + void updateCursorId(Map evt) { - _pngData = _pngs[int.parse(evt['id'])]; + _cacheLinux = _cacheMapLinux[int.parse(evt['id'])]; final tmp = _images[int.parse(evt['id'])]; if (tmp != null) { _image = tmp.item1; @@ -828,6 +871,17 @@ class CursorModel with ChangeNotifier { _x = -10000; _image = null; _images.clear(); + + _clearCacheLinux(); + _cacheLinux = null; + _cacheMapLinux.clear(); + } + + void _clearCacheLinux() { + final cachedKeys = [...cachedKeysLinux]; + for (var key in cachedKeys) { + customCursorController.freeCache(key); + } } } @@ -871,7 +925,9 @@ class QualityMonitorModel with ChangeNotifier { _data.codecFormat = evt['codec_format']; } notifyListeners(); - } catch (e) {} + } catch (e) { + // + } } } @@ -1230,7 +1286,7 @@ class FFI { Future> getHttpHeaders() async { return { 'Authorization': - 'Bearer ' + await bind.mainGetLocalOption(key: 'access_token') + 'Bearer ${await bind.mainGetLocalOption(key: 'access_token')}' }; } } diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 601f1b94a..5556b0dd5 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -71,7 +71,7 @@ dependencies: flutter_custom_cursor: git: url: https://github.com/Kingtous/rustdesk_flutter_custom_cursor - ref: 7fe78c139c711bafbae52d924e9caf18bd193e28 + ref: 9021e21de36c84edf01d5034f38eda580463163b get: ^4.6.5 visibility_detector: ^0.3.3 contextmenu: ^3.0.0 From 41a5d53de6184c682efde6c0a13ea80c73127816 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 7 Sep 2022 18:39:45 -0700 Subject: [PATCH 2/2] flutter_desktop: refactor GetX in popup menu Signed-off-by: fufesou --- flutter/lib/desktop/widgets/popup_menu.dart | 138 +++++++++--------- .../lib/desktop/widgets/remote_menubar.dart | 2 +- 2 files changed, 66 insertions(+), 74 deletions(-) diff --git a/flutter/lib/desktop/widgets/popup_menu.dart b/flutter/lib/desktop/widgets/popup_menu.dart index 0d469043f..02376ff71 100644 --- a/flutter/lib/desktop/widgets/popup_menu.dart +++ b/flutter/lib/desktop/widgets/popup_menu.dart @@ -8,7 +8,7 @@ import './material_mod_popup_menu.dart' as mod_menu; // https://stackoverflow.com/questions/68318314/flutter-popup-menu-inside-popup-menu class PopupMenuChildrenItem extends mod_menu.PopupMenuEntry { - const PopupMenuChildrenItem({ + PopupMenuChildrenItem({ key, this.height = kMinInteractiveDimension, this.padding, @@ -43,6 +43,16 @@ class PopupMenuChildrenItem extends mod_menu.PopupMenuEntry { class MyPopupMenuItemState> extends State { + RxBool enabled = true.obs; + + @override + void initState() { + super.initState(); + if (widget.enabled != null) { + enabled.value = widget.enabled!.value; + } + } + @protected void handleTap(T value) { widget.onTap?.call(); @@ -56,27 +66,25 @@ class MyPopupMenuItemState> TextStyle style = widget.textStyle ?? popupMenuTheme.textStyle ?? theme.textTheme.subtitle1!; - return Obx(() { - return mod_menu.PopupMenuButton( - enabled: widget.enabled != null ? widget.enabled!.value : true, - position: widget.position, - offset: widget.offset, - onSelected: handleTap, - itemBuilder: widget.itemBuilder, - padding: EdgeInsets.zero, - child: AnimatedDefaultTextStyle( - style: style, - duration: kThemeChangeDuration, - child: Container( - alignment: AlignmentDirectional.centerStart, - constraints: BoxConstraints(minHeight: widget.height), - padding: - widget.padding ?? const EdgeInsets.symmetric(horizontal: 16), - child: widget.child, + return Obx(() => mod_menu.PopupMenuButton( + enabled: enabled.value, + position: widget.position, + offset: widget.offset, + onSelected: handleTap, + itemBuilder: widget.itemBuilder, + padding: EdgeInsets.zero, + child: AnimatedDefaultTextStyle( + style: style, + duration: kThemeChangeDuration, + child: Container( + alignment: AlignmentDirectional.centerStart, + constraints: BoxConstraints(minHeight: widget.height), + padding: + widget.padding ?? const EdgeInsets.symmetric(horizontal: 16), + child: widget.child, + ), ), - ), - ); - }); + )); } } @@ -342,7 +350,7 @@ typedef SwitchSetter = Future Function(bool); abstract class MenuEntrySwitchBase extends MenuEntryBase { final String text; - final Rx? textStyle; + Rx? textStyle; MenuEntrySwitchBase({ required this.text, @@ -357,6 +365,11 @@ abstract class MenuEntrySwitchBase extends MenuEntryBase { @override List> build( BuildContext context, MenuConfig conf) { + textStyle ??= const TextStyle( + color: Colors.black, + fontSize: MenuConfig.fontSize, + fontWeight: FontWeight.normal) + .obs; return [ mod_menu.PopupMenuItem( padding: EdgeInsets.zero, @@ -366,23 +379,10 @@ abstract class MenuEntrySwitchBase extends MenuEntryBase { alignment: AlignmentDirectional.centerStart, height: conf.height, child: Row(children: [ - () { - if (textStyle != null) { - final style = textStyle!; - return Obx(() => Text( - text, - style: style.value, - )); - } else { - return Text( + Obx(() => Text( text, - style: const TextStyle( - color: Colors.black, - fontSize: MenuConfig.fontSize, - fontWeight: FontWeight.normal), - ); - } - }(), + style: textStyle!.value, + )), Expanded( child: Align( alignment: Alignment.centerRight, @@ -485,6 +485,7 @@ class MenuEntrySubMenu extends MenuEntryBase { @override List> build( BuildContext context, MenuConfig conf) { + super.enabled ??= true.obs; return [ PopupMenuChildrenItem( enabled: super.enabled, @@ -500,9 +501,7 @@ class MenuEntrySubMenu extends MenuEntryBase { Obx(() => Text( text, style: TextStyle( - color: (super.enabled != null ? super.enabled!.value : true) - ? Colors.black - : Colors.grey, + color: super.enabled!.value ? Colors.black : Colors.grey, fontSize: MenuConfig.fontSize, fontWeight: FontWeight.normal), )), @@ -511,9 +510,7 @@ class MenuEntrySubMenu extends MenuEntryBase { alignment: Alignment.centerRight, child: Obx(() => Icon( Icons.keyboard_arrow_right, - color: (super.enabled != null ? super.enabled!.value : true) - ? conf.commonColor - : Colors.grey, + color: super.enabled!.value ? conf.commonColor : Colors.grey, )), )) ]), @@ -537,35 +534,31 @@ class MenuEntryButton extends MenuEntryBase { ); Widget _buildChild(BuildContext context, MenuConfig conf) { - return Obx(() { - bool enabled = true; - if (super.enabled != null) { - enabled = super.enabled!.value; - } - const enabledStyle = TextStyle( - color: Colors.black, - fontSize: MenuConfig.fontSize, - fontWeight: FontWeight.normal); - const disabledStyle = TextStyle( - color: Colors.grey, - fontSize: MenuConfig.fontSize, - fontWeight: FontWeight.normal); - return TextButton( - onPressed: enabled - ? () { - if (super.dismissOnClicked && Navigator.canPop(context)) { - Navigator.pop(context); + const enabledStyle = TextStyle( + color: Colors.black, + fontSize: MenuConfig.fontSize, + fontWeight: FontWeight.normal); + const disabledStyle = TextStyle( + color: Colors.grey, + fontSize: MenuConfig.fontSize, + fontWeight: FontWeight.normal); + super.enabled ??= true.obs; + return Obx(() => TextButton( + onPressed: super.enabled!.value + ? () { + if (super.dismissOnClicked && Navigator.canPop(context)) { + Navigator.pop(context); + } + proc(); } - proc(); - } - : null, - child: Container( - alignment: AlignmentDirectional.centerStart, - constraints: BoxConstraints(minHeight: conf.height), - child: childBuilder(enabled ? enabledStyle : disabledStyle), - ), - ); - }); + : null, + child: Container( + alignment: AlignmentDirectional.centerStart, + constraints: BoxConstraints(minHeight: conf.height), + child: childBuilder( + super.enabled!.value ? enabledStyle : disabledStyle), + ), + )); } @override @@ -573,7 +566,6 @@ class MenuEntryButton extends MenuEntryBase { BuildContext context, MenuConfig conf) { return [ mod_menu.PopupMenuItem( - enabled: super.enabled != null ? super.enabled!.value : true, padding: EdgeInsets.zero, height: conf.height, child: _buildChild(context, conf), diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index 2dcc81d4d..28085246f 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -611,7 +611,7 @@ class _RemoteMenubarState extends State { MenuEntryRadioOption(text: translate('Map mode'), value: 'map'), ], curOptionGetter: () async { - return await bind.sessionGetKeyboardName(id: widget.id) ?? 'legacy'; + return await bind.sessionGetKeyboardName(id: widget.id); }, optionSetter: (String oldValue, String newValue) async { await bind.sessionSetKeyboardMode(