From 8a9323a9157b60bd0609ce9f6d8080bbab761bcc Mon Sep 17 00:00:00 2001 From: Caten Date: Wed, 10 Sep 2025 17:32:08 +0800 Subject: [PATCH] Add AVNC screen scale feature --- l10n.yaml | 1 - lib/l10n/intl_en.arb | 11 +++--- lib/l10n/intl_zh.arb | 11 +++--- lib/l10n/intl_zh_Hant.arb | 11 +++--- lib/main.dart | 71 +++++++++++++++++++++++++++++---------- lib/workflow.dart | 6 +++- 6 files changed, 80 insertions(+), 31 deletions(-) diff --git a/l10n.yaml b/l10n.yaml index 8619e43..12168c4 100644 --- a/l10n.yaml +++ b/l10n.yaml @@ -1,3 +1,2 @@ -synthetic-package: false arb-dir: lib/l10n template-arb-file: intl_en.arb \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 8bb268e..329300a 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -33,7 +33,7 @@ "fixGetifaddrsPermission": "Fix getifaddrs permission on Android 13", "fakeUOSSystem": "Pretend System as UOS", "displaySettings": "Display Settings", - "avncAdvantages": "AVNC provides better control experience than noVNC:\nTouchpad controls, two-finger tap for keyboard, auto clipboard, picture-in-picture mode, etc. This is an experimental feature.", + "avncAdvantages": "AVNC provides better control experience than noVNC:\nTouchpad controls, two-finger tap for keyboard, auto clipboard, picture-in-picture mode, etc.", "avncSettings": "AVNC Settings", "aboutAVNC": "About AVNC", "avncResolution": "AVNC Startup Resolution", @@ -44,11 +44,11 @@ "save": "Save", "applyOnNextLaunch": "Apply on next launch", "useAVNCByDefault": "Use AVNC by default", - "termuxX11Advantages": "Termux:X11 provides faster performance than VNC with better compatibility in some cases.\nSupports DRI3 (enable in Graphics Acceleration) for significant performance boost.\nNow supports features like bidirectional clipboard.\nExperimental feature! If black screen occurs, try fully closing and restarting the app.", + "termuxX11Advantages": "Termux:X11 may provide faster speeds than VNC in certain scenarios.\n\nNote that Termux:X11 operates slightly differently from AVNC:\n- A two-finger tap acts as a right mouse click\n- Pressing the back button reveals the additional keyboard\n\nSupports DRI3 (requires enabling in graphics acceleration settings), which can significantly improve performance.\nIf you encounter a black screen, try completely closing and restarting the application.", "termuxX11Preferences": "Termux:X11 Preferences", "useTermuxX11ByDefault": "Use Termux:X11 by default", "disableVNC": "Disable VNC. Requires restart", - "hidpiAdvantages": "HiDPI support provides sharper display for high-resolution screens!\n\nNote:\nDisplay may appear too large - set appropriate resolution.\nSome apps may have display issues or slower rendering.", + "hidpiAdvantages": "One-click to enable HiDPI mode for clearer display... at the cost of reduced speed.", "hidpiEnvVar": "HiDPI Environment Variables", "hidpiSupport": "HiDPI Support", "fileAccess": "File Access", @@ -107,5 +107,8 @@ "solutionUrl": "Usage Guide", "discussionUrl": "Discussion", "firstLoadInstructions": "The first load may take about 5 to 10 minutes...\n\nNormally, the software will automatically redirect to the graphical interface after loading.\n\nIn the graphical interface:\n- Tap for left-click\n- Long press for right-click\n- Two-finger tap to open the keyboard\n- Two-finger swipe for mouse wheel\n\nPlease do not exit the software during installation.\n\nWhile waiting, you can click the button below to request permissions.\n\nMany folders in Tiny Computer (e.g., Downloads, Documents, Pictures) are bound to the corresponding device folders. Without these permissions, access to these folders will be denied.\n\nIf you don't need to access these folders, you can skip granting file permissions (but this may cause Firefox to fail when downloading files due to denied access to the Downloads folder).", - "updateRequest": "Please try to use the latest version. Visit the project address to check for the latest version." + "updateRequest": "Please try to use the latest version. Visit the project address to check for the latest version.", + "avncScreenResize": "Adaptive Screen Size", + "avncResizeFactor": "Screen Scaling Ratio", + "avncResizeFactorValue": "Current scaling is" } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 0cff29b..9420a90 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -33,7 +33,7 @@ "fixGetifaddrsPermission": "修复安卓13设备getifaddrs无权限", "fakeUOSSystem": "伪装系统为UOS", "displaySettings": "显示设置", - "avncAdvantages": "AVNC可以带来相比noVNC更好的操控体验;\n如触摸板触控,双指单击弹出键盘,自动剪切板,画中画模式等等。这是一个实验性功能。", + "avncAdvantages": "AVNC可以带来相比noVNC更好的操控体验;\n如触摸板触控,双指单击弹出键盘,自动剪切板,画中画模式等等。", "avncSettings": "AVNC设置", "aboutAVNC": "关于AVNC", "avncResolution": "AVNC启动时分辨率设置", @@ -44,11 +44,11 @@ "applyOnNextLaunch": "下次启动时生效", "save": "保存", "useAVNCByDefault": "默认使用AVNC", - "termuxX11Advantages": "Termux:X11可以带来比VNC更快的速度,某些情况下兼容性也会更好。\n支持使用DRI3(需在图形加速中开启),可以带来相当大的性能提升。\n随着版本的迭代,Termux:X11如今也支持了双向剪切板等功能。\n这是一个实验性功能!如果黑屏,请尝试彻底关闭本应用再重新启动。", + "termuxX11Advantages": "Termux:X11某些情况下可以带来比VNC更快的速度。\n\n注意Termux:X11的操作与AVNC略有不同。\n- 双指点击为鼠标右键\n- 返回弹出附加键盘\n\n支持使用DRI3(需在图形加速中开启),可以带来相当大的性能提升。\n如果黑屏,请尝试彻底关闭本应用再重新启动。", "termuxX11Preferences": "Termux:X11偏好设置", "useTermuxX11ByDefault": "默认使用Termux:X11", "disableVNC": "不使用VNC。重启生效", - "hidpiAdvantages": "高分辨率支持可以为拥有高分辨率屏幕的设备带来更高清的体验!\n\n注意:\n选项开启后显示会变得很大,请设置一个合适的分辨率。\n\n一些软件可能会存在显示问题,或者显示速度变慢。", + "hidpiAdvantages": "一键开启高清模式,显示更清晰的同时...速度会变慢。", "hidpiEnvVar": "HiDPI环境变量", "hidpiSupport": "高分辨率支持", "fileAccess": "文件访问", @@ -107,5 +107,8 @@ "solutionUrl": "典型场景使用指南", "discussionUrl": "论坛与讨论", "firstLoadInstructions": "第一次加载大概需要5到10分钟...\n\n正常情况下,加载完成后软件会自动跳转到图形界面。\n\n在图形界面时:\n- 点击为鼠标左键\n- 长按为鼠标右键\n- 双指点击可弹出键盘\n- 双指划动为鼠标滚轮\n\n请不要在安装时退出软件。\n\n在等待时,可以点击下面的按钮申请一下权限。\n\n小小电脑的许多文件夹,比如下载、文档、图片等等都和设备的对应文件夹绑定,如果不授予这些权限会导致这些文件夹无权访问。\n\n但如果你不需要访问这些文件夹,也可以不授予文件权限(可能导致火狐浏览器下载文件失败,因为无权访问下载文件夹)。", - "updateRequest": "请尽量使用最新版本。前往项目地址可查看最新版本。" + "updateRequest": "请尽量使用最新版本。前往项目地址可查看最新版本。", + "avncScreenResize": "自适应屏幕尺寸", + "avncResizeFactor": "屏幕缩放比", + "avncResizeFactorValue": "当前缩放为" } \ No newline at end of file diff --git a/lib/l10n/intl_zh_Hant.arb b/lib/l10n/intl_zh_Hant.arb index d075c24..62448c0 100644 --- a/lib/l10n/intl_zh_Hant.arb +++ b/lib/l10n/intl_zh_Hant.arb @@ -33,7 +33,7 @@ "fixGetifaddrsPermission": "修復 Android 13 裝置 getifaddrs 無權限", "fakeUOSSystem": "偽裝系統為 UOS", "displaySettings": "顯示設定", - "avncAdvantages": "AVNC 可帶來比 noVNC 更好的操作體驗;如觸控板觸控、雙指單擊喚出鍵盤、自動剪貼簿、畫中畫模式等。這是一個實驗性功能。", + "avncAdvantages": "AVNC 可帶來比 noVNC 更好的操作體驗;如觸控板觸控、雙指單擊喚出鍵盤、自動剪貼簿、畫中畫模式等。", "avncSettings": "AVNC 設定", "aboutAVNC": "關於 AVNC", "avncResolution": "AVNC 啟動時解析度設定", @@ -44,11 +44,11 @@ "applyOnNextLaunch": "下次啟動時生效", "save": "儲存", "useAVNCByDefault": "預設使用 AVNC", - "termuxX11Advantages": "Termux:X11 可帶來比 VNC 更快的速度,有時兼容性也更好。\n支援使用 DRI3(需在圖形加速中開啟),可大幅提升效能。", + "termuxX11Advantages": "Termux:X11 在某些情況下可能比 VNC 提供更快的速度。\n\n請注意 Termux:X11 的操作與 AVNC 略有不同:\n- 雙指點擊相當於滑鼠右鍵\n- 返回鍵可彈出附加鍵盤\n\n支援 DRI3(需在圖形加速設定中開啟),可帶來顯著的性能提升。\n若出現黑屏,請嘗試完全關閉本應用後重新啟動。", "termuxX11Preferences": "Termux:X11 偏好設定", "useTermuxX11ByDefault": "預設使用 Termux:X11", "disableVNC": "停用 VNC。重啟生效", - "hidpiAdvantages": "高解析度支援可讓有高解析度螢幕的裝置獲得更清晰的體驗!\n\n注意:\n啟用後顯示會變很大,請設定合適的解析度。", + "hidpiAdvantages": "一鍵開啟高清模式,顯示更清晰的同時...速度會變慢。", "hidpiEnvVar": "HiDPI 環境變數", "hidpiSupport": "高解析度支援", "fileAccess": "檔案存取", @@ -107,5 +107,8 @@ "solutionUrl": "操作指南", "discussionUrl": "討論", "firstLoadInstructions": "第一次載入大概需要5到10分鐘...\n\n正常情況下,載入完成後軟體會自動跳轉到圖形介面。\n\n在圖形介面時:\n- 點擊為滑鼠左鍵\n- 長按為滑鼠右鍵\n- 雙指點擊可彈出鍵盤\n- 雙指滑動為滑鼠滾輪\n\n請不要在安裝時退出軟體。\n\n在等待時,可以點擊下面的按鈕申請權限。\n\n小小電腦的許多資料夾,比如下載、文件、圖片等等都和裝置的這些資料夾綁定,如果不授予這些權限會導致這些資料夾無權存取。\n\n但如果你不需要存取這些資料夾,也可以不授予檔案權限(可能導致火狐瀏覽器下載檔案失敗,因為無權存取下載資料夾)。", - "updateRequest": "請盡量使用最新版本。前往專案網址查看最新版本。" + "updateRequest": "請盡量使用最新版本。前往專案網址查看最新版本。", + "avncScreenResize": "自適應螢幕尺寸", + "avncResizeFactor": "螢幕縮放比", + "avncResizeFactorValue": "目前縮放為" } diff --git a/lib/main.dart b/lib/main.dart index a9f93c5..c4bd5d3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -172,6 +172,8 @@ class _SettingPageState extends State { final List _expandState = [false, false, false, false, false, false]; + double _avncScaleFactor = Util.getGlobal("avncScaleFactor") as double; + @override Widget build(BuildContext context) { return ExpansionPanelList( @@ -337,6 +339,25 @@ class _SettingPageState extends State { headerBuilder: ((context, isExpanded) { return ListTile(title: Text(AppLocalizations.of(context)!.displaySettings)); }), body: Padding(padding: const EdgeInsets.all(12), child: Column(children: [ + const SizedBox.square(dimension: 16), + Text(AppLocalizations.of(context)!.hidpiAdvantages), + const SizedBox.square(dimension: 16), + TextFormField(maxLines: null, initialValue: Util.getGlobal("defaultHidpiOpt") as String, decoration: InputDecoration(border: OutlineInputBorder(), labelText: AppLocalizations.of(context)!.hidpiEnvVar), + onChanged: (value) async { + await G.prefs.setString("defaultHidpiOpt", value); + }, + ), + const SizedBox.square(dimension: 8), + SwitchListTile(title: Text(AppLocalizations.of(context)!.hidpiSupport), subtitle: Text(AppLocalizations.of(context)!.applyOnNextLaunch), value: Util.getGlobal("isHidpiEnabled") as bool, onChanged:(value) { + G.prefs.setBool("isHidpiEnabled", value); + // 开启高分辨率后把缩放比调为原来的两倍 +log4(2) = 0.5 + _avncScaleFactor += value ? 0.5 : -0.5; + _avncScaleFactor = _avncScaleFactor.clamp(-1, 1); + G.prefs.setDouble("avncScaleFactor", _avncScaleFactor); + setState(() {}); + },), + const SizedBox.square(dimension: 16), + const Divider(height: 2, indent: 8, endIndent: 8), const SizedBox.square(dimension: 16), Text(AppLocalizations.of(context)!.avncAdvantages), const SizedBox.square(dimension: 16), @@ -347,7 +368,7 @@ class _SettingPageState extends State { OutlinedButton(style: D.commandButtonStyle, child: Text(AppLocalizations.of(context)!.aboutAVNC), onPressed: () async { await AvncFlutter.launchAboutPage(); }), - OutlinedButton(style: D.commandButtonStyle, child: Text(AppLocalizations.of(context)!.avncResolution), onPressed: () async { + OutlinedButton(style: D.commandButtonStyle, onPressed: Util.getGlobal("avncResizeDesktop") as bool ? null : () async { final s = WidgetsBinding.instance.platformDispatcher.views.first.physicalSize; final w0 = max(s.width, s.height); final h0 = min(s.width, s.height); @@ -388,13 +409,44 @@ sed -i -E "s@^(VNC_RESOLUTION)=.*@\\1=${w}x${h}@" \$(command -v startvnc)"""); }, child: Text(AppLocalizations.of(context)!.save)), ]); }); - }), + }, child: Text(AppLocalizations.of(context)!.avncResolution)), ]), const SizedBox.square(dimension: 8), SwitchListTile(title: Text(AppLocalizations.of(context)!.useAVNCByDefault), subtitle: Text(AppLocalizations.of(context)!.applyOnNextLaunch), value: Util.getGlobal("useAvnc") as bool, onChanged:(value) { G.prefs.setBool("useAvnc", value); setState(() {}); },), + const SizedBox.square(dimension: 8), + SwitchListTile(title: Text(AppLocalizations.of(context)!.avncScreenResize), value: Util.getGlobal("avncResizeDesktop") as bool, onChanged:(value) { + G.prefs.setBool("avncResizeDesktop", value); + setState(() {}); + },), + const SizedBox.square(dimension: 8), + ListTile( + title: Text(AppLocalizations.of(context)!.avncResizeFactor), + onTap: () {}, + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 8), + Text('${AppLocalizations.of(context)!.avncResizeFactorValue} ${pow(4, _avncScaleFactor).toStringAsFixed(2)}x'), + SizedBox(height: 12), + Slider( + value: _avncScaleFactor, + min: -1, + max: 1, + divisions: 96, + onChangeEnd: (double value) { + G.prefs.setDouble("avncScaleFactor", value); + }, + onChanged: Util.getGlobal("avncResizeDesktop") as bool ? (double value) { + _avncScaleFactor = value; + setState(() {}); + } : null, + ), + ], + ), + ), const SizedBox.square(dimension: 16), const Divider(height: 2, indent: 8, endIndent: 8), const SizedBox.square(dimension: 16), @@ -414,21 +466,6 @@ sed -i -E "s@^(VNC_RESOLUTION)=.*@\\1=${w}x${h}@" \$(command -v startvnc)"""); setState(() {}); },), const SizedBox.square(dimension: 16), - const Divider(height: 2, indent: 8, endIndent: 8), - const SizedBox.square(dimension: 16), - Text(AppLocalizations.of(context)!.hidpiAdvantages), - const SizedBox.square(dimension: 16), - TextFormField(maxLines: null, initialValue: Util.getGlobal("defaultHidpiOpt") as String, decoration: InputDecoration(border: OutlineInputBorder(), labelText: AppLocalizations.of(context)!.hidpiEnvVar), - onChanged: (value) async { - await G.prefs.setString("defaultHidpiOpt", value); - }, - ), - const SizedBox.square(dimension: 8), - SwitchListTile(title: Text(AppLocalizations.of(context)!.hidpiSupport), subtitle: Text(AppLocalizations.of(context)!.applyOnNextLaunch), value: Util.getGlobal("isHidpiEnabled") as bool, onChanged:(value) { - G.prefs.setBool("isHidpiEnabled", value); - setState(() {}); - },), - const SizedBox.square(dimension: 16), ],))), ExpansionPanel( isExpanded: _expandState[3], diff --git a/lib/workflow.dart b/lib/workflow.dart index 1e5cad3..5182d5f 100644 --- a/lib/workflow.dart +++ b/lib/workflow.dart @@ -92,6 +92,8 @@ class Util { //bool isHidpiEnabled = false 是否开启高分辨率 //bool isJpEnabled = false 是否切换系统到日语 //bool useAvnc = false 是否默认使用AVNC + //bool avncResizeDesktop = true 是否默认AVNC按当前屏幕大小调整分辨率 + //double avncScaleFactor = -0.5 AVNC:在当前屏幕大小的基础上调整缩放的比例。范围-1~1,对应比例4^-1~4^1 //String defaultHidpiOpt 默认HiDPI环境变量 //? int bootstrapVersion: 启动包版本 //String[] containersInfo: 所有容器信息(json) @@ -120,6 +122,8 @@ class Util { case "isHidpiEnabled" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false); case "isJpEnabled" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false); case "useAvnc" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(true); + case "avncResizeDesktop" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(true); + case "avncScaleFactor" : return b ? G.prefs.getDouble(key)!.clamp(-1.0, 1.0) : (value){G.prefs.setDouble(key, value); return value;}(-0.5); case "useX11" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false); case "defaultFFmpegCommand" : return b ? G.prefs.getString(key)! : (value){G.prefs.setString(key, value); return value;}("-hide_banner -an -max_delay 1000000 -r 30 -f android_camera -camera_index 0 -i 0:0 -vf scale=iw/2:-1 -rtsp_transport udp -f rtsp rtsp://127.0.0.1:8554/stream"); case "defaultVirglCommand" : return b ? G.prefs.getString(key)! : (value){G.prefs.setString(key, value); return value;}("--use-egl-surfaceless --use-gles --socket-path=\$CONTAINER_DIR/tmp/.virgl_test"); @@ -768,7 +772,7 @@ clear"""); } static Future launchAvnc() async { - await AvncFlutter.launchUsingUri(Util.getCurrentProp("vncUri") as String); + await AvncFlutter.launchUsingUri(Util.getCurrentProp("vncUri") as String, resizeRemoteDesktop: Util.getGlobal("avncResizeDesktop") as bool, resizeRemoteDesktopScaleFactor: pow(4, Util.getGlobal("avncScaleFactor") as double).toDouble()); } static Future launchXServer() async {