From 8a70932cd663971bb1ec32696f5a27025a3e7563 Mon Sep 17 00:00:00 2001 From: Yevhen Popok Date: Tue, 26 Nov 2024 03:51:27 +0200 Subject: [PATCH 01/28] Update Ukrainian UI translation (#10056) --- src/lang/uk.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lang/uk.rs b/src/lang/uk.rs index 3ef8f4c6fdde..ad83430505e6 100644 --- a/src/lang/uk.rs +++ b/src/lang/uk.rs @@ -147,7 +147,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("OS Password", "Пароль ОС"), ("install_tip", "Через UAC, в деяких випадках RustDesk може працювати некоректно на віддаленому вузлі. Щоб уникнути UAC, натисніть кнопку нижче для встановлення RustDesk в системі"), ("Click to upgrade", "Натисніть, щоб перевірити наявність оновлень"), - ("Click to download", "Натисніть, щоб завантажити"), + ("Click to download", "Натисніть, щоб отримати"), ("Click to update", "Натисніть, щоб оновити"), ("Configure", "Налаштувати"), ("config_acc", "Для віддаленого керування вашою стільницею, вам необхідно надати RustDesk дозволи \"Спеціальні можливості\""), @@ -246,7 +246,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Paste", "Вставити"), ("Paste here?", "Вставити сюди?"), ("Are you sure to close the connection?", "Ви впевнені, що хочете завершити підключення?"), - ("Download new version", "Завантажте нову версію"), + ("Download new version", "Отримайте нову версію"), ("Touch mode", "Сенсорний режим"), ("Mouse mode", "Режим миші"), ("One-Finger Tap", "Дотик одним пальцем"), @@ -307,7 +307,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ignore Battery Optimizations", "Ігнорувати оптимізації батареї"), ("android_open_battery_optimizations_tip", "Перейдіть на наступну сторінку налаштувань"), ("Start on boot", "Автозапуск"), - ("Start the screen sharing service on boot, requires special permissions", "Запустити службу службу спільного доступу до екрана під час завантаження, потребує спеціальних дозволів"), + ("Start the screen sharing service on boot, requires special permissions", "Запускати службу спільного доступу до екрана під час завантаження, потребує спеціальних дозволів"), ("Connection not allowed", "Підключення не дозволено"), ("Legacy mode", "Застарілий режим"), ("Map mode", "Режим карти"), @@ -648,11 +648,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("one-way-file-transfer-tip", "На стороні, що керується, увімкнено односторонню передачу файлів."), ("Authentication Required", "Потрібна автентифікація"), ("Authenticate", "Автентифікувати"), - ("web_id_input_tip", "Ви можете ввести ID з того самого серверу, прямий IP-доступ у веб-клієнті не підтримується.\nЯкщо ви хочете отримати доступ до пристрою на іншому сервері, будь ласка, додайте адресу сервера (@<адреса_сервера>?key=<значення_ключа>), наприклад,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЯкщо ви хочете отримати доступ до пристрою на публічному сервері, будь ласка, введіть \"@public\", для публічного сервера ключ не потрібен."), - ("Download", ""), - ("Upload folder", ""), - ("Upload files", ""), - ("Clipboard is synchronized", ""), - ("Update client clipboard", ""), + ("web_id_input_tip", "Ви можете ввести ID на тому самому серверу, прямий IP-доступ у веб-клієнті не підтримується.\nЯкщо ви хочете отримати доступ до пристрою на іншому сервері, будь ласка, додайте адресу сервера (@<адреса_сервера>?key=<значення_ключа>). Наприклад,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЯкщо ви хочете отримати доступ до пристрою на публічному сервері, будь ласка, введіть \"@public\". Для публічного сервера ключ не потрібен."), + ("Download", "Отримати"), + ("Upload folder", "Надіслати теку"), + ("Upload files", "Надіслати файли"), + ("Clipboard is synchronized", "Буфер обміну синхронізовано"), + ("Update client clipboard", "Оновити буфер обміну клієнта"), ].iter().cloned().collect(); } From 458a88fb894c3d32a371fa9a129c2288969a476f Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:24:51 +0800 Subject: [PATCH 02/28] fix: mobile autocomplete options (#10060) Signed-off-by: fufesou --- flutter/lib/mobile/pages/connection_page.dart | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index 89b71c177c91..de68aa510b67 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -48,6 +48,9 @@ class _ConnectionPageState extends State { bool isPeersLoaded = false; StreamSubscription? _uniLinksSubscription; + // https://github.com/flutter/flutter/issues/157244 + Iterable _autocompleteOpts = []; + _ConnectionPageState() { if (!isWeb) _uniLinksSubscription = listenUniLinks(); _idController.addListener(() { @@ -166,7 +169,7 @@ class _ConnectionPageState extends State { child: Autocomplete( optionsBuilder: (TextEditingValue textEditingValue) { if (textEditingValue.text == '') { - return const Iterable.empty(); + _autocompleteOpts = const Iterable.empty(); } else if (peers.isEmpty && !isPeersLoaded) { Peer emptyPeer = Peer( id: '', @@ -182,7 +185,7 @@ class _ConnectionPageState extends State { rdpUsername: '', loginName: '', ); - return [emptyPeer]; + _autocompleteOpts = [emptyPeer]; } else { String textWithoutSpaces = textEditingValue.text.replaceAll(" ", ""); @@ -194,7 +197,7 @@ class _ConnectionPageState extends State { } String textToFind = textEditingValue.text.toLowerCase(); - return peers + _autocompleteOpts = peers .where((peer) => peer.id.toLowerCase().contains(textToFind) || peer.username @@ -206,6 +209,7 @@ class _ConnectionPageState extends State { peer.alias.toLowerCase().contains(textToFind)) .toList(); } + return _autocompleteOpts; }, fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, @@ -274,6 +278,7 @@ class _ConnectionPageState extends State { optionsViewBuilder: (BuildContext context, AutocompleteOnSelected onSelected, Iterable options) { + options = _autocompleteOpts; double maxHeight = options.length * 50; if (options.length == 1) { maxHeight = 52; From 84dab0e96f3a30ad0d17f062db35b157389823de Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:33:54 +0800 Subject: [PATCH 03/28] Fix/android keyboard map mode workaround (#10064) * fix: Android, keyboard, map mode, workaround The `KeyEvent.physicalKey.usbHidUsage` are wrong if using Microsoft SwiftKey keyboard. `window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)` is a workaround for this issue. Signed-off-by: fufesou * fix: clear callback on first image Signed-off-by: fufesou * Android disable soft keyboard in remote page if not editing. Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/mobile/pages/remote_page.dart | 27 ++++++++++++++++++++--- flutter/lib/models/model.dart | 1 + 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 1dee69b94ee7..4457cbe26a26 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -26,6 +26,19 @@ import '../widgets/dialog.dart'; final initText = '1' * 1024; +// Workaround for Android (default input method, Microsoft SwiftKey keyboard) when using physical keyboard. +// When connecting a physical keyboard, `KeyEvent.physicalKey.usbHidUsage` are wrong is using Microsoft SwiftKey keyboard. +// https://github.com/flutter/flutter/issues/159384 +// https://github.com/flutter/flutter/issues/159383 +void _disableAndroidSoftKeyboard({bool? isKeyboardVisible}) { + if (isAndroid) { + if (isKeyboardVisible != true) { + // `enable_soft_keyboard` will be set to `true` when clicking the keyboard icon, in `openKeyboard()`. + gFFI.invokeMethod("enable_soft_keyboard", false); + } + } +} + class RemotePage extends StatefulWidget { RemotePage({Key? key, required this.id, this.password, this.isSharedPassword}) : super(key: key); @@ -99,6 +112,8 @@ class _RemotePageState extends State with WidgetsBindingObserver { if (gFFI.recordingModel.start) { showToast(translate('Automatically record outgoing sessions')); } + _disableAndroidSoftKeyboard( + isKeyboardVisible: keyboardVisibilityController.isVisible); }); WidgetsBinding.instance.addObserver(this); } @@ -1244,7 +1259,9 @@ void showOptions( toggles + [privacyModeWidget]), ); - }, clickMaskDismiss: true, backDismiss: true); + }, clickMaskDismiss: true, backDismiss: true).then((value) { + _disableAndroidSoftKeyboard(); + }); } TTextMenu? getVirtualDisplayMenu(FFI ffi, String id) { @@ -1263,7 +1280,9 @@ TTextMenu? getVirtualDisplayMenu(FFI ffi, String id) { children: children, ), ); - }, clickMaskDismiss: true, backDismiss: true); + }, clickMaskDismiss: true, backDismiss: true).then((value) { + _disableAndroidSoftKeyboard(); + }); }, ); } @@ -1305,7 +1324,9 @@ TTextMenu? getResolutionMenu(FFI ffi, String id) { children: children, ), ); - }, clickMaskDismiss: true, backDismiss: true); + }, clickMaskDismiss: true, backDismiss: true).then((value) { + _disableAndroidSoftKeyboard(); + }); }, ); } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index d029aa3951ff..2c04eae6c87f 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -2859,6 +2859,7 @@ class FFI { canvasModel.scale, ffiModel.pi.currentDisplay); } + imageModel.callbacksOnFirstImage.clear(); await imageModel.update(null); cursorModel.clear(); ffiModel.clear(); From b99c540210a98df39b96ec65f2ec40fd703c7506 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 26 Nov 2024 20:35:17 +0800 Subject: [PATCH 04/28] add "Untagged" to filter addressbook peers without tags (#10063) Signed-off-by: 21pages --- flutter/lib/common/widgets/address_book.dart | 45 +++++++++++++------- flutter/lib/common/widgets/peers_view.dart | 12 +++++- flutter/lib/models/ab_model.dart | 3 ++ src/lang/ar.rs | 1 + src/lang/be.rs | 1 + src/lang/bg.rs | 1 + src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/el.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/et.rs | 1 + src/lang/eu.rs | 1 + src/lang/fa.rs | 1 + src/lang/fr.rs | 1 + src/lang/he.rs | 1 + src/lang/hr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 1 + src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/lt.rs | 1 + src/lang/lv.rs | 1 + src/lang/nb.rs | 1 + src/lang/nl.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ro.rs | 1 + src/lang/ru.rs | 1 + src/lang/sk.rs | 1 + src/lang/sl.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/template.rs | 1 + src/lang/th.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/uk.rs | 1 + src/lang/vn.rs | 1 + 46 files changed, 85 insertions(+), 18 deletions(-) diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index 78bd20ef0336..ae07c1498cf1 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:bot_toast/bot_toast.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dynamic_layouts/dynamic_layouts.dart'; import 'package:flutter/material.dart'; @@ -316,13 +317,14 @@ class _AddressBookState extends State { Widget _buildTags() { return Obx(() { - final List tags; + List tags; if (gFFI.abModel.sortTags.value) { tags = gFFI.abModel.currentAbTags.toList(); tags.sort(); } else { - tags = gFFI.abModel.currentAbTags; + tags = gFFI.abModel.currentAbTags.toList(); } + tags = [kUntagged, ...tags].toList(); final editPermission = gFFI.abModel.current.canWrite(); tagBuilder(String e) { return AddressBookTag( @@ -669,6 +671,14 @@ class _AddressBookState extends State { } else { final tags = field.trim().split(RegExp(r"[\s,;\n]+")); field = tags.join(','); + for (var t in [kUntagged, translate(kUntagged)]) { + if (tags.contains(t)) { + BotToast.showText( + contentColor: Colors.red, text: 'Tag name cannot be "$t"'); + isInProgress = false; + return; + } + } gFFI.abModel.addTags(tags); // final currentPeers } @@ -741,12 +751,14 @@ class AddressBookTag extends StatelessWidget { } const double radius = 8; + final isUnTagged = name == kUntagged; + final showAction = showActionMenu && !isUnTagged; return GestureDetector( onTap: onTap, - onTapDown: showActionMenu ? setPosition : null, - onSecondaryTapDown: showActionMenu ? setPosition : null, - onSecondaryTap: showActionMenu ? () => _showMenu(context, pos) : null, - onLongPress: showActionMenu ? () => _showMenu(context, pos) : null, + onTapDown: showAction ? setPosition : null, + onSecondaryTapDown: showAction ? setPosition : null, + onSecondaryTap: showAction ? () => _showMenu(context, pos) : null, + onLongPress: showAction ? () => _showMenu(context, pos) : null, child: Obx(() => Container( decoration: BoxDecoration( color: tags.contains(name) @@ -758,17 +770,18 @@ class AddressBookTag extends StatelessWidget { child: IntrinsicWidth( child: Row( children: [ - Container( - width: radius, - height: radius, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: tags.contains(name) - ? Colors.white - : gFFI.abModel.getCurrentAbTagColor(name)), - ).marginOnly(right: radius / 2), + if (!isUnTagged) + Container( + width: radius, + height: radius, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: tags.contains(name) + ? Colors.white + : gFFI.abModel.getCurrentAbTagColor(name)), + ).marginOnly(right: radius / 2), Expanded( - child: Text(name, + child: Text(isUnTagged ? translate(name) : name, style: TextStyle( overflow: TextOverflow.ellipsis, color: tags.contains(name) ? Colors.white : null)), diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index 7f16850219f4..e14e198bd105 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart'; +import 'package:flutter_hbb/models/ab_model.dart'; import 'package:flutter_hbb/models/peer_tab_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:get/get.dart'; @@ -532,15 +533,22 @@ class AddressBookPeersView extends BasePeersView { if (selectedTags.isEmpty) { return true; } + // The result of a no-tag union with normal tags, still allows normal tags to perform union or intersection operations. + final selectedNormalTags = + selectedTags.where((tag) => tag != kUntagged).toList(); + if (selectedTags.contains(kUntagged)) { + if (idents.isEmpty) return true; + if (selectedNormalTags.isEmpty) return false; + } if (gFFI.abModel.filterByIntersection.value) { - for (final tag in selectedTags) { + for (final tag in selectedNormalTags) { if (!idents.contains(tag)) { return false; } } return true; } else { - for (final tag in selectedTags) { + for (final tag in selectedNormalTags) { if (idents.contains(tag)) { return true; } diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index 0da84e0f26c8..613ec1ed350d 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -33,6 +33,8 @@ bool filterAbTagByIntersection() { const _personalAddressBookName = "My address book"; const _legacyAddressBookName = "Legacy address book"; +const kUntagged = "Untagged"; + enum ForcePullAb { listAndCurrent, current, @@ -424,6 +426,7 @@ class AbModel { // #region tags Future addTags(List tagList) async { + tagList.removeWhere((e) => e == kUntagged); final ret = await current.addTags(tagList, {}); await pullNonLegacyAfterChange(); _saveCache(); diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 039ad4b114ec..1b20ebc4a99a 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index 26281c26b4aa..7c081983782a 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 46126056c9be..3c1d202eeb41 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index d680b66a5e87..120200b3520c 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index d15c1b6ba0e1..901b4cdb90ff 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "上传文件"), ("Clipboard is synchronized", "剪贴板已同步"), ("Update client clipboard", "更新客户端的粘贴板"), + ("Untagged", "无标签"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index c4ff80c7e3ff..25046cbcb40e 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 905f31739480..fa3e6f100697 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index ba91471c92d4..482b45bfc79b 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "Dateien hochladen"), ("Clipboard is synchronized", "Zwischenablage ist synchronisiert"), ("Update client clipboard", "Client-Zwischenablage aktualisieren"), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 73a306c7c9aa..57984b7288f0 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 83747f03cd48..5fe2c8d3a65b 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index ee0ffe569942..4b84b1c0f46a 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "Subir archivos"), ("Clipboard is synchronized", "Portapapeles sincronizado"), ("Update client clipboard", "Actualizar portapapeles del cliente"), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index 9f67c12262d7..931a3da2d3e5 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eu.rs b/src/lang/eu.rs index 93f5a60b4ef6..e191a74f09c9 100644 --- a/src/lang/eu.rs +++ b/src/lang/eu.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 33c6b7427c1a..051859f60eab 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 8c8332ad1edc..9366c6a19309 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index 400b5156b40e..39cd98fc4c10 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index d6389480a04d..d3163ae03c7e 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index fc58fe5a6949..c348db1d1d60 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "Fájlok feltöltése"), ("Clipboard is synchronized", "A vágólap szinkronizálva van"), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index b488f5740d9e..d52c11a384e0 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index b5a191f093ef..5bec55f4f62e 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "File upload"), ("Clipboard is synchronized", "Gli appunti sono sincronizzati"), ("Update client clipboard", "Aggiorna appunti client"), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 1d0f3b7ea157..14c06e0d59bc 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index f266f2536895..527813d09eba 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "파일 업로드"), ("Clipboard is synchronized", "클립보드가 동기화됨"), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 46733ce713e5..f9ee96ca2518 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 723b46a30a81..31522364c1b5 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 0439a45e65f5..166830c16b4b 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "Augšupielādēt failus"), ("Clipboard is synchronized", "Starpliktuve ir sinhronizēta"), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index c9f3ce243921..00ee513b14c0 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 78c6f77b753a..f1c7ba97f9b1 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "Bestanden uploaden"), ("Clipboard is synchronized", "Klembord is gesynchroniseerd"), ("Update client clipboard", "Klembord van client bijwerken"), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index b6cc5318ab86..872fd5bf713b 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "Wyślij pliki"), ("Clipboard is synchronized", "Schowek jest zsynchronizowany"), ("Update client clipboard", "Uaktualnij schowek klienta"), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 3fe7951870a4..ad3a5f83171a 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index f382b7aba22d..466952c023e0 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 7aaef0e01c4f..830a26a4929a 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index bcc5ed996740..582fdf6dcc6a 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "Загрузить файлы"), ("Clipboard is synchronized", "Буфер обмена синхронизирован"), ("Update client clipboard", "Обновить буфер обмена клиента"), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 50ba1aeb080a..d6bd0f7111f4 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 4e52bbe40fcb..3fbf30ba3011 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index abab6acd8936..b68e6a1e03bb 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 96bf3e1e06da..9def539e59a9 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 69806fa7f6f9..af00b788ed73 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 1b0cf69e4f25..ce3e99abdee0 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index a657201e993b..09fde8671f81 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 1b7b783d30f7..b7f9750f1e8b 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index fb9259ef9b67..e99ee8d36461 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "上傳檔案"), ("Clipboard is synchronized", "剪貼簿已同步"), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/uk.rs b/src/lang/uk.rs index ad83430505e6..6b5ec4381887 100644 --- a/src/lang/uk.rs +++ b/src/lang/uk.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "Надіслати файли"), ("Clipboard is synchronized", "Буфер обміну синхронізовано"), ("Update client clipboard", "Оновити буфер обміну клієнта"), + ("Untagged", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 0d4751cd4f91..1970e17ca9c9 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -654,5 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", ""), ("Clipboard is synchronized", ""), ("Update client clipboard", ""), + ("Untagged", ""), ].iter().cloned().collect(); } From 3c7f6d3127e7c35c0b949d5299e271bc27b7d708 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Wed, 27 Nov 2024 00:37:49 +0100 Subject: [PATCH 05/28] Italian language update (#10067) --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 5bec55f4f62e..838df4d99726 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -654,6 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "File upload"), ("Clipboard is synchronized", "Gli appunti sono sincronizzati"), ("Update client clipboard", "Aggiorna appunti client"), - ("Untagged", ""), + ("Untagged", "Senza tag"), ].iter().cloned().collect(); } From 734fb8d6f7a226786efb533e30e3ee75b4246fc3 Mon Sep 17 00:00:00 2001 From: BoyChai <1972567225@qq.com> Date: Wed, 27 Nov 2024 10:47:09 +0800 Subject: [PATCH 06/28] Update README-ZH.md (#10069) Modify Alibaba Cloud apt source --- docs/README-ZH.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/README-ZH.md b/docs/README-ZH.md index 5a5f56b204e0..4920ade6d960 100644 --- a/docs/README-ZH.md +++ b/docs/README-ZH.md @@ -135,8 +135,8 @@ docker build -t "rustdesk-builder" . # 构建容器 ``` 在Dockerfile的RUN apt update之前插入两行: - RUN sed -i "s/deb.debian.org/mirrors.163.com/g" /etc/apt/sources.list - RUN sed -i "s/security.debian.org/mirrors.163.com/g" /etc/apt/sources.list + RUN sed -i "s|deb.debian.org|mirrors.aliyun.com|g" /etc/apt/sources.list && \ + sed -i "s|security.debian.org|mirrors.aliyun.com|g" /etc/apt/sources.list ``` 2. 修改容器系统中的 cargo 源,在`RUN ./rustup.sh -y`后插入下面代码: From 2cf43042e659352e5c3b4e9375d5cf39e080ed45 Mon Sep 17 00:00:00 2001 From: Leo Mozoloa Date: Wed, 27 Nov 2024 16:00:41 +0100 Subject: [PATCH 07/28] Update fr.rs (#10075) --- src/lang/fr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 9366c6a19309..774e03865c2f 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -145,7 +145,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Failed to make direct connection to remote desktop", "Impossible d'établir une connexion directe"), ("Set Password", "Définir le mot de passe"), ("OS Password", "Mot de passe du système d'exploitation"), - ("install_tip", "Vous utilisez une version non installée. En raison des restrictions UAC, en tant que terminal contrôlé, dans certains cas, il ne sera pas en mesure de contrôler la souris et le clavier ou d'enregistrer l'écran. Veuillez cliquer sur le bouton ci-dessous pour installer RustDesk au système pour éviter la question ci-dessus."), + ("install_tip", "RustDesk n'est pas installé, ce qui peut limiter son utilisation à cause de l'UAC. Cliquez ci-dessous pour l'installer."), ("Click to upgrade", "Cliquer pour mettre à niveau"), ("Click to download", "Cliquer pour télécharger"), ("Click to update", "Cliquer pour mettre à jour"), From 4f86169f7f2f2e643ef230e17818e58c2ce35633 Mon Sep 17 00:00:00 2001 From: XLion Date: Thu, 28 Nov 2024 00:10:47 +0800 Subject: [PATCH 08/28] Update tw.rs (#10076) --- src/lang/tw.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/tw.rs b/src/lang/tw.rs index e99ee8d36461..8a42a331182f 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -653,7 +653,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload folder", "上傳資料夾"), ("Upload files", "上傳檔案"), ("Clipboard is synchronized", "剪貼簿已同步"), - ("Update client clipboard", ""), - ("Untagged", ""), + ("Update client clipboard", "更新客戶端的剪貼簿"), + ("Untagged", "無標籤"), ].iter().cloned().collect(); } From afc8bb71dc1c738d92c4fa6cfef9e999a916cdf5 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Fri, 29 Nov 2024 00:56:38 +0800 Subject: [PATCH 09/28] feat: mobile, key help tool, more keys (#10068) * feat: mobile, key help tool, vk_enter Signed-off-by: fufesou * Mobile, add more function keys Signed-off-by: fufesou * Mobile, more virtual function keys Signed-off-by: fufesou * uinput, menu maps key_compose Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/mobile/pages/remote_page.dart | 24 +++++++++++++++++++++++ libs/enigo/src/linux/nix_impl.rs | 2 +- src/client.rs | 1 + src/server/uinput.rs | 4 ++-- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 4457cbe26a26..fa7c35bb64e2 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -872,6 +872,8 @@ class _KeyHelpToolsState extends State { final pi = gFFI.ffiModel.pi; final isMac = pi.platform == kPeerPlatformMacOS; + final isWin = pi.platform == kPeerPlatformWindows; + final isLinux = pi.platform == kPeerPlatformLinux; final modifiers = [ wrap('Ctrl ', () { setState(() => inputModel.ctrl = !inputModel.ctrl); @@ -952,6 +954,28 @@ class _KeyHelpToolsState extends State { wrap('PgDn', () { inputModel.inputKey('VK_NEXT'); }), + // to-do: support PrtScr on Mac + if (isWin || isLinux) + wrap('PrtScr', () { + inputModel.inputKey('VK_SNAPSHOT'); + }), + if (isWin || isLinux) + wrap('ScrollLock', () { + inputModel.inputKey('VK_SCROLL'); + }), + if (isWin || isLinux) + wrap('Pause', () { + inputModel.inputKey('VK_PAUSE'); + }), + if (isWin || isLinux) + // Maybe it's better to call it "Menu" + // https://en.wikipedia.org/wiki/Menu_key + wrap('Menu', () { + inputModel.inputKey('Apps'); + }), + wrap('Enter', () { + inputModel.inputKey('VK_ENTER'); + }), SizedBox(width: 9999), wrap('', () { inputModel.inputKey('VK_LEFT'); diff --git a/libs/enigo/src/linux/nix_impl.rs b/libs/enigo/src/linux/nix_impl.rs index c082236e3f5d..902d77948e28 100644 --- a/libs/enigo/src/linux/nix_impl.rs +++ b/libs/enigo/src/linux/nix_impl.rs @@ -345,7 +345,7 @@ fn convert_to_tfc_key(key: Key) -> Option { Key::Numpad9 => TFC_Key::N9, Key::Decimal => TFC_Key::NumpadDecimal, Key::Clear => TFC_Key::NumpadClear, - Key::Pause => TFC_Key::PlayPause, + Key::Pause => TFC_Key::Pause, Key::Print => TFC_Key::Print, Key::Snapshot => TFC_Key::PrintScreen, Key::Insert => TFC_Key::Insert, diff --git a/src/client.rs b/src/client.rs index 97fa3a0424a2..474c7fdfc0b8 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3293,6 +3293,7 @@ lazy_static::lazy_static! { ("VK_PRINT", Key::ControlKey(ControlKey::Print)), ("VK_EXECUTE", Key::ControlKey(ControlKey::Execute)), ("VK_SNAPSHOT", Key::ControlKey(ControlKey::Snapshot)), + ("VK_SCROLL", Key::ControlKey(ControlKey::Scroll)), ("VK_INSERT", Key::ControlKey(ControlKey::Insert)), ("VK_DELETE", Key::ControlKey(ControlKey::Delete)), ("VK_HELP", Key::ControlKey(ControlKey::Help)), diff --git a/src/server/uinput.rs b/src/server/uinput.rs index 60c647862ad7..894ce82f90d7 100644 --- a/src/server/uinput.rs +++ b/src/server/uinput.rs @@ -239,7 +239,7 @@ pub mod service { (enigo::Key::Select, evdev::Key::KEY_SELECT), (enigo::Key::Print, evdev::Key::KEY_PRINT), // (enigo::Key::Execute, evdev::Key::KEY_EXECUTE), - // (enigo::Key::Snapshot, evdev::Key::KEY_SNAPSHOT), + (enigo::Key::Snapshot, evdev::Key::KEY_SYSRQ), (enigo::Key::Insert, evdev::Key::KEY_INSERT), (enigo::Key::Help, evdev::Key::KEY_HELP), (enigo::Key::Sleep, evdev::Key::KEY_SLEEP), @@ -247,7 +247,7 @@ pub mod service { (enigo::Key::Scroll, evdev::Key::KEY_SCROLLLOCK), (enigo::Key::NumLock, evdev::Key::KEY_NUMLOCK), (enigo::Key::RWin, evdev::Key::KEY_RIGHTMETA), - (enigo::Key::Apps, evdev::Key::KEY_CONTEXT_MENU), + (enigo::Key::Apps, evdev::Key::KEY_COMPOSE), // it's a little strange that the key is mapped to KEY_COMPOSE, not KEY_MENU (enigo::Key::Multiply, evdev::Key::KEY_KPASTERISK), (enigo::Key::Add, evdev::Key::KEY_KPPLUS), (enigo::Key::Subtract, evdev::Key::KEY_KPMINUS), From b91b49229aa827d0d2b425d1ff1ad7ba9a0040f4 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 29 Nov 2024 19:10:57 +0800 Subject: [PATCH 10/28] enable our engine to fix dart supporting win7, https://github.com/rustdesk/rustdesk/issues/10085#issuecomment-2506485955 --- .github/workflows/flutter-build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index aefd06e7d536..555bcb96b0cc 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -107,7 +107,6 @@ jobs: # https://github.com/flutter/flutter/issues/155685 - name: Replace engine with rustdesk custom flutter engine - if: false run: | flutter doctor -v flutter precache --windows From b32ff87c6e059fcb377d1e09de48827a1f746df3 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Fri, 29 Nov 2024 22:39:07 +0800 Subject: [PATCH 11/28] fix: android, pan, canvas, remove `toInt()` (#10103) Signed-off-by: fufesou --- flutter/lib/models/input_model.dart | 40 +++++++++++++---------------- flutter/lib/models/model.dart | 10 ++++---- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 0692ba2df4d4..a30bb79fdbd3 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -1080,7 +1080,7 @@ class InputModel { onExit: true, ); - static int tryGetNearestRange(int v, int min, int max, int n) { + static double tryGetNearestRange(double v, double min, double max, double n) { if (v < min && v >= min - n) { v = min; } @@ -1138,8 +1138,8 @@ class InputModel { return; } evtValue = { - 'x': pos.x, - 'y': pos.y, + 'x': pos.x.toInt(), + 'y': pos.y.toInt(), }; } @@ -1221,8 +1221,8 @@ class InputModel { evt['x'] = '0'; evt['y'] = '0'; } else { - evt['x'] = '${pos.x}'; - evt['y'] = '${pos.y}'; + evt['x'] = '${pos.x.toInt()}'; + evt['y'] = '${pos.y.toInt()}'; } Map mapButtons = { @@ -1362,31 +1362,27 @@ class InputModel { y = pos.dy; } - var evtX = 0; - var evtY = 0; - try { - evtX = x.round(); - evtY = y.round(); - } catch (e) { - debugPrintStack(label: 'canvas.scale value ${canvas.scale}, $e'); - return null; - } - return InputModel.getPointInRemoteRect( - true, peerPlatform, kind, evtType, evtX, evtY, rect, + true, peerPlatform, kind, evtType, x, y, rect, buttons: buttons); } - static Point? getPointInRemoteRect(bool isLocalDesktop, String? peerPlatform, - String kind, String evtType, int evtX, int evtY, Rect rect, + static Point? getPointInRemoteRect( + bool isLocalDesktop, + String? peerPlatform, + String kind, + String evtType, + double evtX, + double evtY, + Rect rect, {int buttons = kPrimaryMouseButton}) { - int minX = rect.left.toInt(); + double minX = rect.left; // https://github.com/rustdesk/rustdesk/issues/6678 // For Windows, [0,maxX], [0,maxY] should be set to enable window snapping. - int maxX = (rect.left + rect.width).toInt() - + double maxX = (rect.left + rect.width) - (peerPlatform == kPeerPlatformWindows ? 0 : 1); - int minY = rect.top.toInt(); - int maxY = (rect.top + rect.height).toInt() - + double minY = rect.top; + double maxY = (rect.top + rect.height) - (peerPlatform == kPeerPlatformWindows ? 0 : 1); evtX = InputModel.tryGetNearestRange(evtX, minX, maxX, 5); evtY = InputModel.tryGetNearestRange(evtY, minY, maxY, 5); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 2c04eae6c87f..fdcd28f8d04c 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -2184,7 +2184,7 @@ class CursorModel with ChangeNotifier { if (dx == 0 && dy == 0) return; - Point? newPos; + Point? newPos; final rect = parent.target?.ffiModel.rect; if (rect == null) { // unreachable @@ -2195,8 +2195,8 @@ class CursorModel with ChangeNotifier { parent.target?.ffiModel.pi.platform, kPointerEventKindMouse, kMouseEventTypeDefault, - (_x + dx).toInt(), - (_y + dy).toInt(), + _x + dx, + _y + dy, rect, buttons: kPrimaryButton); if (newPos == null) { @@ -2204,8 +2204,8 @@ class CursorModel with ChangeNotifier { } dx = newPos.x - _x; dy = newPos.y - _y; - _x = newPos.x.toDouble(); - _y = newPos.y.toDouble(); + _x = newPos.x; + _y = newPos.y; if (tryMoveCanvasX && dx != 0) { parent.target?.canvasModel.panX(-dx * scale); } From d3f0c80e94657285da23b0bd42b85b16c20a8c40 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 30 Nov 2024 09:24:05 +0800 Subject: [PATCH 12/28] "Untagged" tag uses the theme accent color (#10111) Signed-off-by: 21pages --- flutter/lib/models/ab_model.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index 613ec1ed350d..3aa722a5abf1 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -648,6 +648,9 @@ class AbModel { } Color getCurrentAbTagColor(String tag) { + if (tag == kUntagged) { + return MyTheme.accent; + } int? colorValue = current.tagColors[tag]; if (colorValue != null) { return Color(colorValue); From e0ed6ee986e6b68cacbfdaefd5bf306e935e73f6 Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Sat, 30 Nov 2024 03:24:45 +0200 Subject: [PATCH 13/28] Fix F-Droid build and bump Android NDK to r27c (#10105) * Fix fdroid build * Refactor recent @fufesou edits to reflect the fact that .gclient file is needed only on x86 to build jit-release version of flutter-engine and `flutter-sdk` directory is not affected by flutter version checkouts * Install cargo-ndk and flutter-rust-codegen with `--locked` argument to avoid bumping `cargo-platform` to require newer Rust toolchain Signed-off-by: Vasyl Gello * Bump Android NDK to r27c Signed-off-by: Vasyl Gello --------- Signed-off-by: Vasyl Gello --- .github/workflows/flutter-build.yml | 2 +- flutter/build_fdroid.sh | 141 +++++++++++++++++++--------- 2 files changed, 97 insertions(+), 46 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 555bcb96b0cc..fd39302163f3 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -34,7 +34,7 @@ env: # vcpkg version: 2024.07.12 VCPKG_COMMIT_ID: "1de2026f28ead93ff1773e6e680387643e914ea1" VERSION: "1.3.3" - NDK_VERSION: "r27b" + NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}" diff --git a/flutter/build_fdroid.sh b/flutter/build_fdroid.sh index e0a4eb5991b9..1821c529afb5 100755 --- a/flutter/build_fdroid.sh +++ b/flutter/build_fdroid.sh @@ -1,7 +1,5 @@ #!/bin/bash -set -x - # # Script to build F-Droid release of RustDesk # @@ -23,6 +21,43 @@ set -x # + build: perform actual build of APK file # +# Start of functions + +# Install Flutter of version `VERSION` from Github repository +# into directory `FLUTTER_DIR` and apply patches if needed + +prepare_flutter() { + VERSION="${1}" + FLUTTER_DIR="${2}" + + if [ ! -f "${FLUTTER_DIR}/bin/flutter" ]; then + git clone https://github.com/flutter/flutter "${FLUTTER_DIR}" + fi + + pushd "${FLUTTER_DIR}" + + git restore . + git checkout "${VERSION}" + + # Patch flutter + + if dpkg --compare-versions "${VERSION}" ge "3.24.4"; then + git apply "${ROOTDIR}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff" + fi + + flutter config --no-analytics + + popd # ${FLUTTER_DIR} +} + +# Start of script + +set -x + +# Note current working directory as root dir for patches + +ROOTDIR="${PWD}" + # Parse command-line arguments VERNAME="${1}" @@ -82,21 +117,6 @@ export PATH="${PATH}:${HOME}/flutter/bin:${HOME}/depot_tools" export VCPKG_ROOT="${HOME}/vcpkg" -prepare_Flutter() { - version="${1}" - - pushd "${HOME}" - if [ ! -f "${HOME}/flutter/bin/flutter" ]; then - git clone https://github.com/flutter/flutter - fi - pushd flutter - git restore . - git checkout "${version}" - flutter config --no-analytics - popd # flutter - popd # ${HOME} -} - # Now act depending on build step # NOTE: F-Droid maintainers require explicit declaration of dependencies @@ -116,15 +136,20 @@ prebuild) .env.CARGO_NDK_VERSION \ .github/workflows/flutter-build.yml)" + # Flutter used to compile main Rustdesk library + FLUTTER_VERSION="$(yq -r \ .env.ANDROID_FLUTTER_VERSION \ .github/workflows/flutter-build.yml)" + if [ -z "${FLUTTER_VERSION}" ]; then FLUTTER_VERSION="$(yq -r \ .env.FLUTTER_VERSION \ .github/workflows/flutter-build.yml)" fi + # Flutter used to compile Flutter<->Rust bridge files + FLUTTER_BRIDGE_VERSION="$(yq -r \ .env.FLUTTER_VERSION \ .github/workflows/bridge.yml)" @@ -207,14 +232,16 @@ prebuild) cargo install \ cargo-ndk \ - --version "${CARGO_NDK_VERSION}" + --version "${CARGO_NDK_VERSION}" \ + --locked # Install rust bridge generator cargo install cargo-expand cargo install flutter_rust_bridge_codegen \ --version "${FLUTTER_RUST_BRIDGE_VERSION}" \ - --features "uuid" + --features "uuid" \ + --locked # Populate native vcpkg dependencies @@ -277,46 +304,66 @@ prebuild) git apply res/fdroid/patches/*.patch - # Backup .gclient file, for later restore + # If Flutter version used to generate bridge files differs from Flutter + # version used to compile Rustdesk library, generate bridge using the + # `FLUTTER_BRIDGE_VERSION` an restore the pubspec later - cp flutter-sdk/.gclient flutter-sdk/.gclient.bak + if [ "${FLUTTER_VERSION}" != "${FLUTTER_BRIDGE_VERSION}" ]; then + # Install Flutter bridge version - # For FLUTTER_BRIDGE_VERSION - sed \ - -i \ - -e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' \ - flutter/pubspec.yaml + prepare_flutter "${FLUTTER_BRIDGE_VERSION}" "${HOME}/flutter" - # Install Flutter bridge version - prepare_Flutter "${FLUTTER_BRIDGE_VERSION}" - cp flutter-sdk/.gclient.bak flutter-sdk/.gclient - sed -i "s/FLUTTER_VERSION_PLACEHOLDER/${FLUTTER_BRIDGE_VERSION}/" flutter-sdk/.gclient + # Save changes - # Download Flutter dependencies - pushd flutter - flutter clean && flutter packages pub get - popd # flutter + git add . + + # Edit pubspec to make flutter bridge version work + + sed \ + -i \ + -e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' \ + flutter/pubspec.yaml - # Generate FFI bindings - flutter_rust_bridge_codegen \ - --rust-input ./src/flutter_ffi.rs \ - --dart-output ./flutter/lib/generated_bridge.dart + # Download Flutter dependencies - git restore flutter/pubspec.* + pushd flutter - # Install Flutter - prepare_Flutter "${FLUTTER_VERSION}" - cp flutter-sdk/.gclient.bak flutter-sdk/.gclient - sed -i "s/FLUTTER_VERSION_PLACEHOLDER/${FLUTTER_VERSION}/" flutter-sdk/.gclient + flutter clean + flutter packages pub get + + popd # flutter + + # Generate FFI bindings + + flutter_rust_bridge_codegen \ + --rust-input ./src/flutter_ffi.rs \ + --dart-output ./flutter/lib/generated_bridge.dart + + # Add bridge files to save-list + + git add -f ./flutter/lib/generated_bridge.* ./src/bridge_generated.* + + # Restore everything + + git checkout '*' + git clean -dffx + git reset + fi + + # Install Flutter version for RustDesk library build + + prepare_flutter "${FLUTTER_VERSION}" "${HOME}/flutter" # gms is not in thoes files now, but we still keep the following line for future reference(maybe). + sed \ -i \ -e '/gms/d' \ flutter/android/build.gradle \ flutter/android/app/build.gradle - # `firebase_analytics`` is not in thoes files now, but we still keep the following lines. + # `firebase_analytics` is not in these files now, but we still keep the following lines. + sed \ -i \ -e '/firebase_analytics/d' \ @@ -343,9 +390,12 @@ build) # '.github/workflows/flutter-build.yml' # + # Flutter used to compile main Rustdesk library + FLUTTER_VERSION="$(yq -r \ .env.ANDROID_FLUTTER_VERSION \ .github/workflows/flutter-build.yml)" + if [ -z "${FLUTTER_VERSION}" ]; then FLUTTER_VERSION="$(yq -r \ .env.FLUTTER_VERSION \ @@ -381,7 +431,8 @@ build) pushd flutter - flutter clean && flutter packages pub get + flutter clean + flutter packages pub get popd # flutter From d60b5a6ca097916758d8dfdd2e89b2ec3b581dbf Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 30 Nov 2024 11:44:51 +0800 Subject: [PATCH 14/28] videotoolbox/mediacodec support changing bitrate dynamically (#10117) Signed-off-by: 21pages --- .github/workflows/bridge.yml | 2 +- .github/workflows/flutter-build.yml | 2 +- .github/workflows/playground.yml | 6 +- libs/scrap/src/common/hwcodec.rs | 8 +- .../0004-videotoolbox-changing-bitrate.patch | 84 ++++++ .../0005-mediacodec-changing-bitrate.patch | 259 ++++++++++++++++++ res/vcpkg/ffmpeg/portfile.cmake | 2 + 7 files changed, 352 insertions(+), 11 deletions(-) create mode 100644 res/vcpkg/ffmpeg/patch/0004-videotoolbox-changing-bitrate.patch create mode 100644 res/vcpkg/ffmpeg/patch/0005-mediacodec-changing-bitrate.patch diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml index 1c0fec8d21f9..9d56fbe6f2b5 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/bridge.yml @@ -73,7 +73,7 @@ jobs: - name: Install flutter rust bridge deps shell: bash run: | - cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" + cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" --locked pushd flutter && sed -i -e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' pubspec.yaml && flutter pub get && popd - name: Run flutter rust bridge diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index fd39302163f3..b295e70f6f87 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1032,7 +1032,7 @@ jobs: ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} run: | rustup target add ${{ matrix.job.target }} - cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }} + cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }} --locked case ${{ matrix.job.target }} in aarch64-linux-android) ./flutter/ndk_arm64.sh diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 843e07835cdb..9d4d42cb3eda 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -149,7 +149,7 @@ jobs: shell: bash run: | sed -i '' 's/3.1.0/2.17.0/g' flutter/pubspec.yaml; - cargo install flutter_rust_bridge_codegen --version ${{ matrix.job.bridge }} --features "uuid" + cargo install flutter_rust_bridge_codegen --version ${{ matrix.job.bridge }} --features "uuid" --locked # below works for mac to make buildable on 3.13.9 # pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; pushd flutter && flutter pub get && popd @@ -302,7 +302,7 @@ jobs: - name: Install flutter rust bridge deps run: | git config --global core.longpaths true - cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" + cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" --locked sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml pushd flutter/lib; find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g'; popd; pushd flutter ; flutter pub get ; popd @@ -347,7 +347,7 @@ jobs: ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} run: | rustup target add ${{ matrix.job.target }} - cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }} + cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }} --locked case ${{ matrix.job.target }} in aarch64-linux-android) ./flutter/ndk_arm64.sh diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index d929024d84eb..3d56472eedc4 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -193,15 +193,11 @@ impl EncoderApi for HwRamEncoder { } fn support_abr(&self) -> bool { - ["qsv", "vaapi", "mediacodec", "videotoolbox"] - .iter() - .all(|&x| !self.config.name.contains(x)) + ["qsv", "vaapi"].iter().all(|&x| !self.config.name.contains(x)) } fn support_changing_quality(&self) -> bool { - ["vaapi", "mediacodec", "videotoolbox"] - .iter() - .all(|&x| !self.config.name.contains(x)) + ["vaapi"].iter().all(|&x| !self.config.name.contains(x)) } fn latency_free(&self) -> bool { diff --git a/res/vcpkg/ffmpeg/patch/0004-videotoolbox-changing-bitrate.patch b/res/vcpkg/ffmpeg/patch/0004-videotoolbox-changing-bitrate.patch new file mode 100644 index 000000000000..a0b337c5bae5 --- /dev/null +++ b/res/vcpkg/ffmpeg/patch/0004-videotoolbox-changing-bitrate.patch @@ -0,0 +1,84 @@ +From 7f12898fe8fd12c1042c98b34825ab2eda89e54d Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Sun, 24 Nov 2024 12:58:39 +0800 +Subject: [PATCH 1/2] videotoolbox changing bitrate + +Signed-off-by: 21pages +--- + libavcodec/videotoolboxenc.c | 39 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/libavcodec/videotoolboxenc.c b/libavcodec/videotoolboxenc.c +index 5ea9afee22..89c927cdcc 100644 +--- a/libavcodec/videotoolboxenc.c ++++ b/libavcodec/videotoolboxenc.c +@@ -278,6 +278,8 @@ typedef struct VTEncContext { + int max_slice_bytes; + int power_efficient; + int max_ref_frames; ++ ++ int last_bit_rate; + } VTEncContext; + + static int vt_dump_encoder(AVCodecContext *avctx) +@@ -1174,6 +1176,7 @@ static int vtenc_create_encoder(AVCodecContext *avctx, + int64_t one_second_value = 0; + void *nums[2]; + ++ vtctx->last_bit_rate = bit_rate; + int status = VTCompressionSessionCreate(kCFAllocatorDefault, + avctx->width, + avctx->height, +@@ -2618,6 +2621,41 @@ static int vtenc_send_frame(AVCodecContext *avctx, + return 0; + } + ++static void update_config(AVCodecContext *avctx) ++{ ++ VTEncContext *vtctx = avctx->priv_data; ++ ++ if (avctx->codec_id != AV_CODEC_ID_PRORES) { ++ if (avctx->bit_rate != vtctx->last_bit_rate) { ++ av_log(avctx, AV_LOG_INFO, "Setting bit rate to %d\n", avctx->bit_rate); ++ vtctx->last_bit_rate = avctx->bit_rate; ++ SInt32 bit_rate = avctx->bit_rate; ++ CFNumberRef bit_rate_num = CFNumberCreate(kCFAllocatorDefault, ++ kCFNumberSInt32Type, ++ &bit_rate); ++ if (!bit_rate_num) return; ++ ++ if (vtctx->constant_bit_rate) { ++ int status = VTSessionSetProperty(vtctx->session, ++ compat_keys.kVTCompressionPropertyKey_ConstantBitRate, ++ bit_rate_num); ++ if (status == kVTPropertyNotSupportedErr) { ++ av_log(avctx, AV_LOG_ERROR, "Error: -constant_bit_rate true is not supported by the encoder.\n"); ++ } ++ } else { ++ int status = VTSessionSetProperty(vtctx->session, ++ kVTCompressionPropertyKey_AverageBitRate, ++ bit_rate_num); ++ if (!status) { ++ av_log(avctx, AV_LOG_ERROR, "Error: cannot set average bit rate: %d\n", status); ++ } ++ } ++ ++ CFRelease(bit_rate_num); ++ } ++ } ++} ++ + static av_cold int vtenc_frame( + AVCodecContext *avctx, + AVPacket *pkt, +@@ -2630,6 +2668,7 @@ static av_cold int vtenc_frame( + CMSampleBufferRef buf = NULL; + ExtraSEI *sei = NULL; + ++ update_config(avctx); + if (frame) { + status = vtenc_send_frame(avctx, vtctx, frame); + +-- +2.43.0.windows.1 + diff --git a/res/vcpkg/ffmpeg/patch/0005-mediacodec-changing-bitrate.patch b/res/vcpkg/ffmpeg/patch/0005-mediacodec-changing-bitrate.patch new file mode 100644 index 000000000000..90c3613a43b0 --- /dev/null +++ b/res/vcpkg/ffmpeg/patch/0005-mediacodec-changing-bitrate.patch @@ -0,0 +1,259 @@ +From fb5cc7909a9b288f6bd13c75992b66ed257ab019 Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Sun, 24 Nov 2024 14:17:39 +0800 +Subject: [PATCH 2/2] mediacodec changing bitrate + +Signed-off-by: 21pages +--- + libavcodec/mediacodec_wrapper.c | 96 +++++++++++++++++++++++++++++++++ + libavcodec/mediacodec_wrapper.h | 7 +++ + libavcodec/mediacodecenc.c | 18 +++++++ + 3 files changed, 121 insertions(+) + +diff --git a/libavcodec/mediacodec_wrapper.c b/libavcodec/mediacodec_wrapper.c +index 306359071e..44fdd71869 100644 +--- a/libavcodec/mediacodec_wrapper.c ++++ b/libavcodec/mediacodec_wrapper.c +@@ -35,6 +35,8 @@ + #include "ffjni.h" + #include "mediacodec_wrapper.h" + ++#define PARAMETER_KEY_VIDEO_BITRATE "video-bitrate" ++ + struct JNIAMediaCodecListFields { + + jclass mediacodec_list_class; +@@ -195,6 +197,8 @@ struct JNIAMediaCodecFields { + jmethodID set_input_surface_id; + jmethodID signal_end_of_input_stream_id; + ++ jmethodID set_parameters_id; ++ + jclass mediainfo_class; + + jmethodID init_id; +@@ -248,6 +252,8 @@ static const struct FFJniField jni_amediacodec_mapping[] = { + { "android/media/MediaCodec", "setInputSurface", "(Landroid/view/Surface;)V", FF_JNI_METHOD, OFFSET(set_input_surface_id), 0 }, + { "android/media/MediaCodec", "signalEndOfInputStream", "()V", FF_JNI_METHOD, OFFSET(signal_end_of_input_stream_id), 0 }, + ++ { "android/media/MediaCodec", "setParameters", "(Landroid/os/Bundle;)V", FF_JNI_METHOD, OFFSET(set_parameters_id), 0 }, ++ + { "android/media/MediaCodec$BufferInfo", NULL, NULL, FF_JNI_CLASS, OFFSET(mediainfo_class), 1 }, + + { "android/media/MediaCodec.BufferInfo", "", "()V", FF_JNI_METHOD, OFFSET(init_id), 1 }, +@@ -292,6 +298,24 @@ typedef struct FFAMediaCodecJni { + + static const FFAMediaCodec media_codec_jni; + ++struct JNIABundleFields ++{ ++ jclass bundle_class; ++ jmethodID init_id; ++ jmethodID put_int_id; ++}; ++ ++#define OFFSET(x) offsetof(struct JNIABundleFields, x) ++static const struct FFJniField jni_abundle_mapping[] = { ++ { "android/os/Bundle", NULL, NULL, FF_JNI_CLASS, OFFSET(bundle_class), 1 }, ++ ++ { "android/os/Bundle", "", "()V", FF_JNI_METHOD, OFFSET(init_id), 1 }, ++ { "android/os/Bundle", "putInt", "(Ljava/lang/String;I)V", FF_JNI_METHOD, OFFSET(put_int_id), 1 }, ++ ++ { NULL } ++}; ++#undef OFFSET ++ + #define JNI_GET_ENV_OR_RETURN(env, log_ctx, ret) do { \ + (env) = ff_jni_get_env(log_ctx); \ + if (!(env)) { \ +@@ -1761,6 +1785,64 @@ static int mediacodec_jni_signalEndOfInputStream(FFAMediaCodec *ctx) + return 0; + } + ++static int mediacodec_jni_setParameter(FFAMediaCodec *ctx, const char* name, int value) ++{ ++ JNIEnv *env = NULL; ++ struct JNIABundleFields jfields = { 0 }; ++ jobject object = NULL; ++ jstring key = NULL; ++ FFAMediaCodecJni *codec = (FFAMediaCodecJni *)ctx; ++ void *log_ctx = codec; ++ int ret = -1; ++ ++ JNI_GET_ENV_OR_RETURN(env, codec, AVERROR_EXTERNAL); ++ ++ if (ff_jni_init_jfields(env, &jfields, jni_abundle_mapping, 0, log_ctx) < 0) { ++ av_log(log_ctx, AV_LOG_ERROR, "Failed to init jfields\n"); ++ goto fail; ++ } ++ ++ object = (*env)->NewObject(env, jfields.bundle_class, jfields.init_id); ++ if (!object) { ++ av_log(log_ctx, AV_LOG_ERROR, "Failed to create bundle object\n"); ++ goto fail; ++ } ++ ++ key = ff_jni_utf_chars_to_jstring(env, name, log_ctx); ++ if (!key) { ++ av_log(log_ctx, AV_LOG_ERROR, "Failed to convert key to jstring\n"); ++ goto fail; ++ } ++ ++ (*env)->CallVoidMethod(env, object, jfields.put_int_id, key, value); ++ if (ff_jni_exception_check(env, 1, log_ctx) < 0) { ++ goto fail; ++ } ++ ++ (*env)->CallVoidMethod(env, codec->object, codec->jfields.set_parameters_id, object); ++ if (ff_jni_exception_check(env, 1, log_ctx) < 0) { ++ goto fail; ++ } ++ ++ ret = 0; ++ ++fail: ++ if (key) { ++ (*env)->DeleteLocalRef(env, key); ++ } ++ if (object) { ++ (*env)->DeleteLocalRef(env, object); ++ } ++ ff_jni_reset_jfields(env, &jfields, jni_abundle_mapping, 0, log_ctx); ++ ++ return ret; ++} ++ ++static int mediacodec_jni_setDynamicBitrate(FFAMediaCodec *ctx, int bitrate) ++{ ++ return mediacodec_jni_setParameter(ctx, PARAMETER_KEY_VIDEO_BITRATE, bitrate); ++} ++ + static const FFAMediaFormat media_format_jni = { + .class = &amediaformat_class, + +@@ -1820,6 +1902,8 @@ static const FFAMediaCodec media_codec_jni = { + .getConfigureFlagEncode = mediacodec_jni_getConfigureFlagEncode, + .cleanOutputBuffers = mediacodec_jni_cleanOutputBuffers, + .signalEndOfInputStream = mediacodec_jni_signalEndOfInputStream, ++ ++ .setDynamicBitrate = mediacodec_jni_setDynamicBitrate, + }; + + typedef struct FFAMediaFormatNdk { +@@ -1893,6 +1977,8 @@ typedef struct FFAMediaCodecNdk { + // Available since API level 26. + media_status_t (*setInputSurface)(AMediaCodec*, ANativeWindow *); + media_status_t (*signalEndOfInputStream)(AMediaCodec *); ++ ++ media_status_t (*setParameters)(AMediaCodec *, const AMediaFormat *format); + } FFAMediaCodecNdk; + + static const FFAMediaFormat media_format_ndk; +@@ -2154,6 +2240,8 @@ static inline FFAMediaCodec *ndk_codec_create(int method, const char *arg) { + GET_SYMBOL(setInputSurface, 0) + GET_SYMBOL(signalEndOfInputStream, 0) + ++ GET_SYMBOL(setParameters, 0) ++ + #undef GET_SYMBOL + + switch (method) { +@@ -2428,6 +2516,12 @@ static int mediacodec_ndk_signalEndOfInputStream(FFAMediaCodec *ctx) + return 0; + } + ++static int mediacodec_ndk_setDynamicBitrate(FFAMediaCodec *ctx, int bitrate) ++{ ++ av_log(ctx, AV_LOG_ERROR, "ndk setDynamicBitrate unavailable\n"); ++ return -1; ++} ++ + static const FFAMediaFormat media_format_ndk = { + .class = &amediaformat_ndk_class, + +@@ -2489,6 +2583,8 @@ static const FFAMediaCodec media_codec_ndk = { + .getConfigureFlagEncode = mediacodec_ndk_getConfigureFlagEncode, + .cleanOutputBuffers = mediacodec_ndk_cleanOutputBuffers, + .signalEndOfInputStream = mediacodec_ndk_signalEndOfInputStream, ++ ++ .setDynamicBitrate = mediacodec_ndk_setDynamicBitrate, + }; + + FFAMediaFormat *ff_AMediaFormat_new(int ndk) +diff --git a/libavcodec/mediacodec_wrapper.h b/libavcodec/mediacodec_wrapper.h +index 11a4260497..86c64556ad 100644 +--- a/libavcodec/mediacodec_wrapper.h ++++ b/libavcodec/mediacodec_wrapper.h +@@ -219,6 +219,8 @@ struct FFAMediaCodec { + + // For encoder with FFANativeWindow as input. + int (*signalEndOfInputStream)(FFAMediaCodec *); ++ ++ int (*setDynamicBitrate)(FFAMediaCodec *codec, int bitrate); + }; + + static inline char *ff_AMediaCodec_getName(FFAMediaCodec *codec) +@@ -343,6 +345,11 @@ static inline int ff_AMediaCodec_signalEndOfInputStream(FFAMediaCodec *codec) + return codec->signalEndOfInputStream(codec); + } + ++static inline int ff_AMediaCodec_setDynamicBitrate(FFAMediaCodec *codec, int bitrate) ++{ ++ return codec->setDynamicBitrate(codec, bitrate); ++} ++ + int ff_Build_SDK_INT(AVCodecContext *avctx); + + enum FFAMediaFormatColorRange { +diff --git a/libavcodec/mediacodecenc.c b/libavcodec/mediacodecenc.c +index d3bf27cb7f..621529d686 100644 +--- a/libavcodec/mediacodecenc.c ++++ b/libavcodec/mediacodecenc.c +@@ -73,6 +73,8 @@ typedef struct MediaCodecEncContext { + int bitrate_mode; + int level; + int pts_as_dts; ++ ++ int last_bit_rate; + } MediaCodecEncContext; + + enum { +@@ -155,6 +157,8 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) + int ret; + int gop; + ++ s->last_bit_rate = avctx->bit_rate; ++ + if (s->use_ndk_codec < 0) + s->use_ndk_codec = !av_jni_get_java_vm(avctx); + +@@ -515,12 +519,26 @@ static int mediacodec_send(AVCodecContext *avctx, + return 0; + } + ++static void update_config(AVCodecContext *avctx) ++{ ++ MediaCodecEncContext *s = avctx->priv_data; ++ if (avctx->bit_rate != s->last_bit_rate) { ++ s->last_bit_rate = avctx->bit_rate; ++ if (0 != ff_AMediaCodec_setDynamicBitrate(s->codec, avctx->bit_rate)) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to set bitrate to %d\n", avctx->bit_rate); ++ } else { ++ av_log(avctx, AV_LOG_INFO, "Set bitrate to %d\n", avctx->bit_rate); ++ } ++ } ++} ++ + static int mediacodec_encode(AVCodecContext *avctx, AVPacket *pkt) + { + MediaCodecEncContext *s = avctx->priv_data; + int ret; + int got_packet = 0; + ++ update_config(avctx); + // Return on three case: + // 1. Serious error + // 2. Got a packet success +-- +2.43.0.windows.1 + diff --git a/res/vcpkg/ffmpeg/portfile.cmake b/res/vcpkg/ffmpeg/portfile.cmake index 3d4c10906dfa..d56475c059f8 100644 --- a/res/vcpkg/ffmpeg/portfile.cmake +++ b/res/vcpkg/ffmpeg/portfile.cmake @@ -13,6 +13,8 @@ vcpkg_from_github( patch/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch patch/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch patch/0003-amf-colorspace.patch + patch/0004-videotoolbox-changing-bitrate.patch + patch/0005-mediacodec-changing-bitrate.patch ) if(SOURCE_PATH MATCHES " ") From 9d9b67aca58e35e95ff08fb0686b3494a3cedfc7 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 30 Nov 2024 12:19:42 +0800 Subject: [PATCH 15/28] update flutter texture rgba renderer plugin, remove switch rgba (#10070) Signed-off-by: 21pages --- flutter/lib/models/model.dart | 4 +++- flutter/pubspec.lock | 9 +++++---- flutter/pubspec.yaml | 5 ++++- src/client.rs | 8 +++++++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index fdcd28f8d04c..e5cde939fb76 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1268,7 +1268,9 @@ class ImageModel with ChangeNotifier { rgba, rect?.width.toInt() ?? 0, rect?.height.toInt() ?? 0, - isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888, + isWeb | isWindows | isLinux + ? ui.PixelFormat.rgba8888 + : ui.PixelFormat.bgra8888, ); if (parent.target?.id != pid) return; await update(image); diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 7c60e037a57d..31f04ce4076d 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -1277,10 +1277,11 @@ packages: texture_rgba_renderer: dependency: "direct main" description: - name: texture_rgba_renderer - sha256: cb048abdd800468ca40749ca10d1db9d1e6a055d1cde6234c05191293f0c7d61 - url: "https://pub.dev" - source: hosted + path: "." + ref: "42797e0f03141dc2b585f76c64a13974508058b4" + resolved-ref: "42797e0f03141dc2b585f76c64a13974508058b4" + url: "https://github.com/rustdesk-org/flutter_texture_rgba_renderer" + source: git version: "0.0.16" timing: dependency: transitive diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index afe09a0dc72e..bec58a4d6ced 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -91,7 +91,10 @@ dependencies: password_strength: ^0.2.0 flutter_launcher_icons: ^0.13.1 flutter_keyboard_visibility: ^5.4.0 - texture_rgba_renderer: ^0.0.16 + texture_rgba_renderer: + git: + url: https://github.com/rustdesk-org/flutter_texture_rgba_renderer + ref: 42797e0f03141dc2b585f76c64a13974508058b4 percent_indicator: ^4.2.2 dropdown_button2: ^2.0.0 flutter_gpu_texture_renderer: diff --git a/src/client.rs b/src/client.rs index 474c7fdfc0b8..4ff2c6b522b6 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1188,9 +1188,15 @@ impl VideoHandler { pub fn new(format: CodecFormat, _display: usize) -> Self { let luid = Self::get_adapter_luid(); log::info!("new video handler for display #{_display}, format: {format:?}, luid: {luid:?}"); + let rgba_format = + if cfg!(feature = "flutter") && (cfg!(windows) || cfg!(target_os = "linux")) { + ImageFormat::ABGR + } else { + ImageFormat::ARGB + }; VideoHandler { decoder: Decoder::new(format, luid), - rgb: ImageRgb::new(ImageFormat::ARGB, crate::get_dst_align_rgba()), + rgb: ImageRgb::new(rgba_format, crate::get_dst_align_rgba()), texture: Default::default(), recorder: Default::default(), record: false, From 743b0ce8ce8537ee54e283af6753b1c0dcbcc344 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 30 Nov 2024 13:29:03 +0800 Subject: [PATCH 16/28] fix mediacodec patch (#10119) ensure set_parameters_id is not null Signed-off-by: 21pages --- .../0005-mediacodec-changing-bitrate.patch | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/res/vcpkg/ffmpeg/patch/0005-mediacodec-changing-bitrate.patch b/res/vcpkg/ffmpeg/patch/0005-mediacodec-changing-bitrate.patch index 90c3613a43b0..1f70a5659308 100644 --- a/res/vcpkg/ffmpeg/patch/0005-mediacodec-changing-bitrate.patch +++ b/res/vcpkg/ffmpeg/patch/0005-mediacodec-changing-bitrate.patch @@ -1,17 +1,17 @@ -From fb5cc7909a9b288f6bd13c75992b66ed257ab019 Mon Sep 17 00:00:00 2001 +From 51ac90d8084f7b153eac5133765fa9d0365aa239 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 24 Nov 2024 14:17:39 +0800 -Subject: [PATCH 2/2] mediacodec changing bitrate +Subject: [PATCH 1/4] mediacodec changing bitrate Signed-off-by: 21pages --- - libavcodec/mediacodec_wrapper.c | 96 +++++++++++++++++++++++++++++++++ - libavcodec/mediacodec_wrapper.h | 7 +++ - libavcodec/mediacodecenc.c | 18 +++++++ - 3 files changed, 121 insertions(+) + libavcodec/mediacodec_wrapper.c | 101 ++++++++++++++++++++++++++++++++ + libavcodec/mediacodec_wrapper.h | 7 +++ + libavcodec/mediacodecenc.c | 18 ++++++ + 3 files changed, 126 insertions(+) diff --git a/libavcodec/mediacodec_wrapper.c b/libavcodec/mediacodec_wrapper.c -index 306359071e..44fdd71869 100644 +index 306359071e..1ab4e673f6 100644 --- a/libavcodec/mediacodec_wrapper.c +++ b/libavcodec/mediacodec_wrapper.c @@ -35,6 +35,8 @@ @@ -66,7 +66,7 @@ index 306359071e..44fdd71869 100644 #define JNI_GET_ENV_OR_RETURN(env, log_ctx, ret) do { \ (env) = ff_jni_get_env(log_ctx); \ if (!(env)) { \ -@@ -1761,6 +1785,64 @@ static int mediacodec_jni_signalEndOfInputStream(FFAMediaCodec *ctx) +@@ -1761,6 +1785,69 @@ static int mediacodec_jni_signalEndOfInputStream(FFAMediaCodec *ctx) return 0; } @@ -104,6 +104,11 @@ index 306359071e..44fdd71869 100644 + goto fail; + } + ++ if (!codec->jfields.set_parameters_id) { ++ av_log(log_ctx, AV_LOG_ERROR, "System doesn't support setParameters\n"); ++ goto fail; ++ } ++ + (*env)->CallVoidMethod(env, codec->object, codec->jfields.set_parameters_id, object); + if (ff_jni_exception_check(env, 1, log_ctx) < 0) { + goto fail; @@ -131,7 +136,7 @@ index 306359071e..44fdd71869 100644 static const FFAMediaFormat media_format_jni = { .class = &amediaformat_class, -@@ -1820,6 +1902,8 @@ static const FFAMediaCodec media_codec_jni = { +@@ -1820,6 +1907,8 @@ static const FFAMediaCodec media_codec_jni = { .getConfigureFlagEncode = mediacodec_jni_getConfigureFlagEncode, .cleanOutputBuffers = mediacodec_jni_cleanOutputBuffers, .signalEndOfInputStream = mediacodec_jni_signalEndOfInputStream, @@ -140,7 +145,7 @@ index 306359071e..44fdd71869 100644 }; typedef struct FFAMediaFormatNdk { -@@ -1893,6 +1977,8 @@ typedef struct FFAMediaCodecNdk { +@@ -1893,6 +1982,8 @@ typedef struct FFAMediaCodecNdk { // Available since API level 26. media_status_t (*setInputSurface)(AMediaCodec*, ANativeWindow *); media_status_t (*signalEndOfInputStream)(AMediaCodec *); @@ -149,7 +154,7 @@ index 306359071e..44fdd71869 100644 } FFAMediaCodecNdk; static const FFAMediaFormat media_format_ndk; -@@ -2154,6 +2240,8 @@ static inline FFAMediaCodec *ndk_codec_create(int method, const char *arg) { +@@ -2154,6 +2245,8 @@ static inline FFAMediaCodec *ndk_codec_create(int method, const char *arg) { GET_SYMBOL(setInputSurface, 0) GET_SYMBOL(signalEndOfInputStream, 0) @@ -158,7 +163,7 @@ index 306359071e..44fdd71869 100644 #undef GET_SYMBOL switch (method) { -@@ -2428,6 +2516,12 @@ static int mediacodec_ndk_signalEndOfInputStream(FFAMediaCodec *ctx) +@@ -2428,6 +2521,12 @@ static int mediacodec_ndk_signalEndOfInputStream(FFAMediaCodec *ctx) return 0; } @@ -171,7 +176,7 @@ index 306359071e..44fdd71869 100644 static const FFAMediaFormat media_format_ndk = { .class = &amediaformat_ndk_class, -@@ -2489,6 +2583,8 @@ static const FFAMediaCodec media_codec_ndk = { +@@ -2489,6 +2588,8 @@ static const FFAMediaCodec media_codec_ndk = { .getConfigureFlagEncode = mediacodec_ndk_getConfigureFlagEncode, .cleanOutputBuffers = mediacodec_ndk_cleanOutputBuffers, .signalEndOfInputStream = mediacodec_ndk_signalEndOfInputStream, From 082a66b28237035ab30f21081daa03e8acda1ad9 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sat, 30 Nov 2024 15:01:44 +0800 Subject: [PATCH 17/28] refact: remove flutter_improved_scrolling (#10120) Signed-off-by: fufesou --- flutter/lib/common.dart | 24 -- flutter/lib/common/widgets/peers_view.dart | 44 +-- flutter/lib/consts.dart | 4 - .../lib/desktop/pages/desktop_home_page.dart | 71 ++-- .../desktop/pages/desktop_setting_page.dart | 324 ++++++++---------- flutter/lib/desktop/pages/remote_page.dart | 51 +-- .../lib/desktop/widgets/scroll_wrapper.dart | 27 -- flutter/pubspec.lock | 9 - flutter/pubspec.yaml | 7 - 9 files changed, 213 insertions(+), 348 deletions(-) delete mode 100644 flutter/lib/desktop/widgets/scroll_wrapper.dart diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index fb389b45e646..4e97449124f7 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2730,30 +2730,6 @@ Future osxRequestAudio() async { return await kMacOSPermChannel.invokeMethod("requestRecordAudio"); } -class DraggableNeverScrollableScrollPhysics extends ScrollPhysics { - /// Creates scroll physics that does not let the user scroll. - const DraggableNeverScrollableScrollPhysics({super.parent}); - - @override - DraggableNeverScrollableScrollPhysics applyTo(ScrollPhysics? ancestor) { - return DraggableNeverScrollableScrollPhysics(parent: buildParent(ancestor)); - } - - @override - bool shouldAcceptUserOffset(ScrollMetrics position) { - // TODO: find a better solution to check if the offset change is caused by the scrollbar. - // Workaround: when dragging with the scrollbar, it always triggers an [IdleScrollActivity]. - if (position is ScrollPositionWithSingleContext) { - // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member - return position.activity is IdleScrollActivity; - } - return false; - } - - @override - bool get allowImplicitScrolling => false; -} - Widget futureBuilder( {required Future? future, required Widget Function(dynamic data) hasData}) { return FutureBuilder( diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index e14e198bd105..3e34f882d1de 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -5,7 +5,6 @@ import 'package:dynamic_layouts/dynamic_layouts.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/consts.dart'; -import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart'; import 'package:flutter_hbb/models/ab_model.dart'; import 'package:flutter_hbb/models/peer_tab_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; @@ -271,33 +270,24 @@ class _PeersViewState extends State<_PeersView> }, ) : peerCardUiType.value == PeerUiType.list - ? DesktopScrollWrapper( - scrollController: _scrollController, - child: ListView.builder( - controller: _scrollController, - physics: DraggableNeverScrollableScrollPhysics(), - itemCount: peers.length, - itemBuilder: (BuildContext context, int index) { - return buildOnePeer(peers[index], false) - .marginOnly( - right: space, - top: index == 0 ? 0 : space / 2, - bottom: space / 2); - }), + ? ListView.builder( + controller: _scrollController, + itemCount: peers.length, + itemBuilder: (BuildContext context, int index) { + return buildOnePeer(peers[index], false).marginOnly( + right: space, + top: index == 0 ? 0 : space / 2, + bottom: space / 2); + }, ) - : DesktopScrollWrapper( - scrollController: _scrollController, - child: DynamicGridView.builder( - controller: _scrollController, - physics: DraggableNeverScrollableScrollPhysics(), - gridDelegate: SliverGridDelegateWithWrapping( - mainAxisSpacing: space / 2, - crossAxisSpacing: space), - itemCount: peers.length, - itemBuilder: (BuildContext context, int index) { - return buildOnePeer(peers[index], false); - }), - )); + : DynamicGridView.builder( + gridDelegate: SliverGridDelegateWithWrapping( + mainAxisSpacing: space / 2, + crossAxisSpacing: space), + itemCount: peers.length, + itemBuilder: (BuildContext context, int index) { + return buildOnePeer(peers[index], false); + })); if (updateEvent == UpdateEvent.load) { _curPeers.clear(); diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index c313958bddfc..f6f9c4d34f9a 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -244,10 +244,6 @@ const double kDesktopIconButtonSplashRadius = 20; /// [kMinCursorSize] indicates min cursor (w, h) const int kMinCursorSize = 12; -/// [kDefaultScrollAmountMultiplier] indicates how many rows can be scrolled after a minimum scroll action of mouse -const kDefaultScrollAmountMultiplier = 5.0; -const kDefaultScrollDuration = Duration(milliseconds: 50); -const kDefaultMouseWheelThrottleDuration = Duration(milliseconds: 50); const kFullScreenEdgeSize = 0.0; const kMaximizeEdgeSize = 0.0; // Do not use kWindowResizeEdgeSize directly. Use `windowResizeEdgeSize` in `common.dart` instead. diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 9728d6b478e2..04a186b84c63 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -12,7 +12,6 @@ import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/desktop/pages/connection_page.dart'; import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart'; import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart'; -import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/server_model.dart'; import 'package:flutter_hbb/plugin/ui_manager.dart'; @@ -125,47 +124,43 @@ class _DesktopHomePageState extends State child: Container( width: isIncomingOnly ? 280.0 : 200.0, color: Theme.of(context).colorScheme.background, - child: DesktopScrollWrapper( - scrollController: _leftPaneScrollController, - child: Stack( - children: [ - SingleChildScrollView( - controller: _leftPaneScrollController, - physics: DraggableNeverScrollableScrollPhysics(), - child: Column( - key: _childKey, - children: children, - ), + child: Stack( + children: [ + SingleChildScrollView( + controller: _leftPaneScrollController, + child: Column( + key: _childKey, + children: children, ), - if (isOutgoingOnly) - Positioned( - bottom: 6, - left: 12, - child: Align( - alignment: Alignment.centerLeft, - child: InkWell( - child: Obx( - () => Icon( - Icons.settings, - color: _editHover.value - ? textColor - : Colors.grey.withOpacity(0.5), - size: 22, - ), + ), + if (isOutgoingOnly) + Positioned( + bottom: 6, + left: 12, + child: Align( + alignment: Alignment.centerLeft, + child: InkWell( + child: Obx( + () => Icon( + Icons.settings, + color: _editHover.value + ? textColor + : Colors.grey.withOpacity(0.5), + size: 22, ), - onTap: () => { - if (DesktopSettingPage.tabKeys.isNotEmpty) - { - DesktopSettingPage.switch2page( - DesktopSettingPage.tabKeys[0]) - } - }, - onHover: (value) => _editHover.value = value, ), + onTap: () => { + if (DesktopSettingPage.tabKeys.isNotEmpty) + { + DesktopSettingPage.switch2page( + DesktopSettingPage.tabKeys[0]) + } + }, + onHover: (value) => _editHover.value = value, ), - ) - ], - ), + ), + ) + ], ), ), ); diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 69100470f0ea..0c7586e7036c 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -19,7 +19,6 @@ import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart'; -import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart'; import '../../common/widgets/dialog.dart'; import '../../common/widgets/login.dart'; @@ -226,13 +225,11 @@ class _DesktopSettingPageState extends State Expanded( child: Container( color: Theme.of(context).scaffoldBackgroundColor, - child: DesktopScrollWrapper( - scrollController: controller, - child: PageView( - controller: controller, - physics: NeverScrollableScrollPhysics(), - children: _children(), - )), + child: PageView( + controller: controller, + physics: NeverScrollableScrollPhysics(), + children: _children(), + ), ), ) ], @@ -281,13 +278,10 @@ class _DesktopSettingPageState extends State Widget _listView({required List<_TabInfo> tabs}) { final scrollController = ScrollController(); - return DesktopScrollWrapper( - scrollController: scrollController, - child: ListView( - physics: DraggableNeverScrollableScrollPhysics(), - controller: scrollController, - children: tabs.map((tab) => _listItem(tab: tab)).toList(), - )); + return ListView( + controller: scrollController, + children: tabs.map((tab) => _listItem(tab: tab)).toList(), + ); } Widget _listItem({required _TabInfo tab}) { @@ -349,22 +343,19 @@ class _GeneralState extends State<_General> { @override Widget build(BuildContext context) { final scrollController = ScrollController(); - return DesktopScrollWrapper( - scrollController: scrollController, - child: ListView( - physics: DraggableNeverScrollableScrollPhysics(), - controller: scrollController, - children: [ - if (!isWeb) service(), - theme(), - _Card(title: 'Language', children: [language()]), - if (!isWeb) hwcodec(), - if (!isWeb) audio(context), - if (!isWeb) record(context), - if (!isWeb) WaylandCard(), - other() - ], - ).marginOnly(bottom: _kListViewBottomMargin)); + return ListView( + controller: scrollController, + children: [ + if (!isWeb) service(), + theme(), + _Card(title: 'Language', children: [language()]), + if (!isWeb) hwcodec(), + if (!isWeb) audio(context), + if (!isWeb) record(context), + if (!isWeb) WaylandCard(), + other() + ], + ).marginOnly(bottom: _kListViewBottomMargin); } Widget theme() { @@ -705,29 +696,26 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); - return DesktopScrollWrapper( - scrollController: scrollController, - child: SingleChildScrollView( - physics: DraggableNeverScrollableScrollPhysics(), - controller: scrollController, - child: Column( - children: [ - _lock(locked, 'Unlock Security Settings', () { - locked = false; - setState(() => {}); - }), - AbsorbPointer( - absorbing: locked, - child: Column(children: [ - permissions(context), - password(context), - _Card(title: '2FA', children: [tfa()]), - _Card(title: 'ID', children: [changeId()]), - more(context), - ]), - ), - ], - )).marginOnly(bottom: _kListViewBottomMargin)); + return SingleChildScrollView( + controller: scrollController, + child: Column( + children: [ + _lock(locked, 'Unlock Security Settings', () { + locked = false; + setState(() => {}); + }), + AbsorbPointer( + absorbing: locked, + child: Column(children: [ + permissions(context), + password(context), + _Card(title: '2FA', children: [tfa()]), + _Card(title: 'ID', children: [changeId()]), + more(context), + ]), + ), + ], + )).marginOnly(bottom: _kListViewBottomMargin); } Widget tfa() { @@ -1384,28 +1372,23 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin { // TODO: support web proxy final hideProxy = isWeb || bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y'; - return DesktopScrollWrapper( - scrollController: scrollController, - child: ListView( - controller: scrollController, - physics: DraggableNeverScrollableScrollPhysics(), - children: [ - _lock(locked, 'Unlock Network Settings', () { - locked = false; - setState(() => {}); - }), - AbsorbPointer( - absorbing: locked, - child: Column(children: [ - if (!hideServer) server(enabled), - if (!hideProxy) - _Card(title: 'Proxy', children: [ - _Button('Socks5/Http(s) Proxy', changeSocks5Proxy, - enabled: enabled), - ]), - ]), - ), - ]).marginOnly(bottom: _kListViewBottomMargin)); + return ListView(controller: scrollController, children: [ + _lock(locked, 'Unlock Network Settings', () { + locked = false; + setState(() => {}); + }), + AbsorbPointer( + absorbing: locked, + child: Column(children: [ + if (!hideServer) server(enabled), + if (!hideProxy) + _Card(title: 'Proxy', children: [ + _Button('Socks5/Http(s) Proxy', changeSocks5Proxy, + enabled: enabled), + ]), + ]), + ), + ]).marginOnly(bottom: _kListViewBottomMargin); } server(bool enabled) { @@ -1494,19 +1477,14 @@ class _DisplayState extends State<_Display> { @override Widget build(BuildContext context) { final scrollController = ScrollController(); - return DesktopScrollWrapper( - scrollController: scrollController, - child: ListView( - controller: scrollController, - physics: DraggableNeverScrollableScrollPhysics(), - children: [ - viewStyle(context), - scrollStyle(context), - imageQuality(context), - codec(context), - if (!isWeb) privacyModeImpl(context), - other(context), - ]).marginOnly(bottom: _kListViewBottomMargin)); + return ListView(controller: scrollController, children: [ + viewStyle(context), + scrollStyle(context), + imageQuality(context), + codec(context), + if (!isWeb) privacyModeImpl(context), + other(context), + ]).marginOnly(bottom: _kListViewBottomMargin); } Widget viewStyle(BuildContext context) { @@ -1729,15 +1707,12 @@ class _AccountState extends State<_Account> { @override Widget build(BuildContext context) { final scrollController = ScrollController(); - return DesktopScrollWrapper( - scrollController: scrollController, - child: ListView( - physics: DraggableNeverScrollableScrollPhysics(), - controller: scrollController, - children: [ - _Card(title: 'Account', children: [accountAction(), useInfo()]), - ], - ).marginOnly(bottom: _kListViewBottomMargin)); + return ListView( + controller: scrollController, + children: [ + _Card(title: 'Account', children: [accountAction(), useInfo()]), + ], + ).marginOnly(bottom: _kListViewBottomMargin); } Widget accountAction() { @@ -1834,18 +1809,14 @@ class _PluginState extends State<_Plugin> { Widget build(BuildContext context) { bind.pluginListReload(); final scrollController = ScrollController(); - return DesktopScrollWrapper( - scrollController: scrollController, - child: ChangeNotifierProvider.value( - value: pluginManager, - child: Consumer(builder: (context, model, child) { - return ListView( - physics: DraggableNeverScrollableScrollPhysics(), - controller: scrollController, - children: model.plugins.map((entry) => pluginCard(entry)).toList(), - ).marginOnly(bottom: _kListViewBottomMargin); - }), - ), + return ChangeNotifierProvider.value( + value: pluginManager, + child: Consumer(builder: (context, model, child) { + return ListView( + controller: scrollController, + children: model.plugins.map((entry) => pluginCard(entry)).toList(), + ).marginOnly(bottom: _kListViewBottomMargin); + }), ); } @@ -1897,75 +1868,72 @@ class _AboutState extends State<_About> { final fingerprint = data['fingerprint'].toString(); const linkStyle = TextStyle(decoration: TextDecoration.underline); final scrollController = ScrollController(); - return DesktopScrollWrapper( - scrollController: scrollController, - child: SingleChildScrollView( - controller: scrollController, - physics: DraggableNeverScrollableScrollPhysics(), - child: _Card(title: translate('About RustDesk'), children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 8.0, - ), - SelectionArea( - child: Text('${translate('Version')}: $version') - .marginSymmetric(vertical: 4.0)), - SelectionArea( - child: Text('${translate('Build Date')}: $buildDate') - .marginSymmetric(vertical: 4.0)), - if (!isWeb) - SelectionArea( - child: Text('${translate('Fingerprint')}: $fingerprint') - .marginSymmetric(vertical: 4.0)), - InkWell( - onTap: () { - launchUrlString('https://rustdesk.com/privacy.html'); - }, - child: Text( - translate('Privacy Statement'), - style: linkStyle, - ).marginSymmetric(vertical: 4.0)), - InkWell( - onTap: () { - launchUrlString('https://rustdesk.com'); - }, - child: Text( - translate('Website'), - style: linkStyle, - ).marginSymmetric(vertical: 4.0)), - Container( - decoration: const BoxDecoration(color: Color(0xFF2c8cff)), - padding: - const EdgeInsets.symmetric(vertical: 24, horizontal: 8), - child: SelectionArea( - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Copyright © ${DateTime.now().toString().substring(0, 4)} Purslane Ltd.\n$license', - style: const TextStyle(color: Colors.white), - ), - Text( - translate('Slogan_tip'), - style: TextStyle( - fontWeight: FontWeight.w800, - color: Colors.white), - ) - ], + return SingleChildScrollView( + controller: scrollController, + child: _Card(title: translate('About RustDesk'), children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 8.0, + ), + SelectionArea( + child: Text('${translate('Version')}: $version') + .marginSymmetric(vertical: 4.0)), + SelectionArea( + child: Text('${translate('Build Date')}: $buildDate') + .marginSymmetric(vertical: 4.0)), + if (!isWeb) + SelectionArea( + child: Text('${translate('Fingerprint')}: $fingerprint') + .marginSymmetric(vertical: 4.0)), + InkWell( + onTap: () { + launchUrlString('https://rustdesk.com/privacy.html'); + }, + child: Text( + translate('Privacy Statement'), + style: linkStyle, + ).marginSymmetric(vertical: 4.0)), + InkWell( + onTap: () { + launchUrlString('https://rustdesk.com'); + }, + child: Text( + translate('Website'), + style: linkStyle, + ).marginSymmetric(vertical: 4.0)), + Container( + decoration: const BoxDecoration(color: Color(0xFF2c8cff)), + padding: + const EdgeInsets.symmetric(vertical: 24, horizontal: 8), + child: SelectionArea( + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Copyright © ${DateTime.now().toString().substring(0, 4)} Purslane Ltd.\n$license', + style: const TextStyle(color: Colors.white), ), - ), - ], - )), - ).marginSymmetric(vertical: 4.0) - ], - ).marginOnly(left: _kContentHMargin) - ]), - )); + Text( + translate('Slogan_tip'), + style: TextStyle( + fontWeight: FontWeight.w800, + color: Colors.white), + ) + ], + ), + ), + ], + )), + ).marginSymmetric(vertical: 4.0) + ], + ).marginOnly(left: _kContentHMargin) + ]), + ); }); } } diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index cca2074a242c..912b06b0282b 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -6,7 +6,6 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; -import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart'; import 'package:flutter_hbb/models/state_model.dart'; import '../../consts.dart'; @@ -742,12 +741,6 @@ class _ImagePaintState extends State { ScrollController horizontal, ScrollController vertical, ) { - final scrollConfig = CustomMouseWheelScrollConfig( - scrollDuration: kDefaultScrollDuration, - scrollCurve: Curves.linearToEaseOut, - mouseWheelTurnsThrottleTimeMs: - kDefaultMouseWheelThrottleDuration.inMilliseconds, - scrollAmountMultiplier: kDefaultScrollAmountMultiplier); var widget = child; if (layoutSize.width < size.width) { widget = ScrollConfiguration( @@ -793,36 +786,26 @@ class _ImagePaintState extends State { ); } if (layoutSize.width < size.width) { - widget = ImprovedScrolling( - scrollController: horizontal, - enableCustomMouseWheelScrolling: cursorOverImage.isFalse, - customMouseWheelScrollConfig: scrollConfig, - child: RawScrollbar( - thickness: kScrollbarThickness, - thumbColor: Colors.grey, - controller: horizontal, - thumbVisibility: false, - trackVisibility: false, - notificationPredicate: layoutSize.height < size.height - ? (notification) => notification.depth == 1 - : defaultScrollNotificationPredicate, - child: widget, - ), + widget = RawScrollbar( + thickness: kScrollbarThickness, + thumbColor: Colors.grey, + controller: horizontal, + thumbVisibility: false, + trackVisibility: false, + notificationPredicate: layoutSize.height < size.height + ? (notification) => notification.depth == 1 + : defaultScrollNotificationPredicate, + child: widget, ); } if (layoutSize.height < size.height) { - widget = ImprovedScrolling( - scrollController: vertical, - enableCustomMouseWheelScrolling: cursorOverImage.isFalse, - customMouseWheelScrollConfig: scrollConfig, - child: RawScrollbar( - thickness: kScrollbarThickness, - thumbColor: Colors.grey, - controller: vertical, - thumbVisibility: false, - trackVisibility: false, - child: widget, - ), + widget = RawScrollbar( + thickness: kScrollbarThickness, + thumbColor: Colors.grey, + controller: vertical, + thumbVisibility: false, + trackVisibility: false, + child: widget, ); } diff --git a/flutter/lib/desktop/widgets/scroll_wrapper.dart b/flutter/lib/desktop/widgets/scroll_wrapper.dart deleted file mode 100644 index c5bc3394b439..000000000000 --- a/flutter/lib/desktop/widgets/scroll_wrapper.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_hbb/consts.dart'; -import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart'; - -class DesktopScrollWrapper extends StatelessWidget { - final ScrollController scrollController; - final Widget child; - const DesktopScrollWrapper( - {Key? key, required this.scrollController, required this.child}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return ImprovedScrolling( - scrollController: scrollController, - enableCustomMouseWheelScrolling: true, - // enableKeyboardScrolling: true, // strange behavior on mac - customMouseWheelScrollConfig: CustomMouseWheelScrollConfig( - scrollDuration: kDefaultScrollDuration, - scrollCurve: Curves.linearToEaseOut, - mouseWheelTurnsThrottleTimeMs: - kDefaultMouseWheelThrottleDuration.inMilliseconds, - scrollAmountMultiplier: kDefaultScrollAmountMultiplier), - child: child, - ); - } -} diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 31f04ce4076d..98f3eef96353 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -530,15 +530,6 @@ packages: url: "https://github.com/rustdesk-org/flutter_gpu_texture_renderer" source: git version: "0.0.1" - flutter_improved_scrolling: - dependency: "direct main" - description: - path: "." - ref: HEAD - resolved-ref: "62f09545149f320616467c306c8c5f71714a18e6" - url: "https://github.com/rustdesk-org/flutter_improved_scrolling" - source: git - version: "0.0.3" flutter_keyboard_visibility: dependency: "direct main" description: diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index bec58a4d6ced..3a4f46b2a36f 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -71,13 +71,6 @@ dependencies: debounce_throttle: ^2.0.0 file_picker: ^5.1.0 flutter_svg: ^2.0.5 - flutter_improved_scrolling: - # currently, we use flutter 3.10.0+. - # - # for flutter 3.0.5, please use official version(just comment code below). - # if build rustdesk by flutter >=3.3, please use our custom pub below (uncomment code below). - git: - url: https://github.com/rustdesk-org/flutter_improved_scrolling uni_links: git: url: https://github.com/rustdesk-org/uni_links From f8c2713c5bc945f0758026c33558515d5e7a4719 Mon Sep 17 00:00:00 2001 From: Kleofass <4000163+Kleofass@users.noreply.github.com> Date: Sat, 30 Nov 2024 18:27:39 +0200 Subject: [PATCH 18/28] Update lv.rs (#10124) --- src/lang/lv.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 166830c16b4b..31a8d89c02c8 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -653,7 +653,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload folder", "Augšupielādēt mapi"), ("Upload files", "Augšupielādēt failus"), ("Clipboard is synchronized", "Starpliktuve ir sinhronizēta"), - ("Update client clipboard", ""), - ("Untagged", ""), + ("Update client clipboard", "Atjaunināt klienta starpliktuvi"), + ("Untagged", "Neatzīmēts"), ].iter().cloned().collect(); } From 8d4c86fe7ff96e38c526c910e8381d697ab66446 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:19:21 +0800 Subject: [PATCH 19/28] fix: workaround, linux window, transparent rounded corner (#10128) * fix: linux window, rounded corner Signed-off-by: fufesou * Update my_application.cc --------- Signed-off-by: fufesou Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com> --- flutter/linux/my_application.cc | 45 +++++++++++++++++++++++++++++++-- flutter/pubspec.lock | 2 +- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/flutter/linux/my_application.cc b/flutter/linux/my_application.cc index 56b85ccae6dd..f4247bd94613 100644 --- a/flutter/linux/my_application.cc +++ b/flutter/linux/my_application.cc @@ -16,6 +16,8 @@ G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) extern bool gIsConnectionManager; +GtkWidget *find_gl_area(GtkWidget *widget); + // Implements GApplication::activate. static void my_application_activate(GApplication* application) { MyApplication* self = MY_APPLICATION(application); @@ -39,9 +41,10 @@ static void my_application_activate(GApplication* application) { // If running on Wayland assume the header bar will work (may need changing // if future cases occur). gboolean use_header_bar = TRUE; + GdkScreen* screen = NULL; #ifdef GDK_WINDOWING_X11 - GdkScreen* screen = gtk_window_get_screen(window); - if (GDK_IS_X11_SCREEN(screen)) { + screen = gtk_window_get_screen(window); + if (screen != NULL && GDK_IS_X11_SCREEN(screen)) { const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); if (g_strcmp0(wm_name, "GNOME Shell") != 0) { use_header_bar = FALSE; @@ -76,6 +79,22 @@ static void my_application_activate(GApplication* application) { gtk_widget_show(GTK_WIDGET(view)); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + // https://github.com/flutter/flutter/issues/152154 + // Remove this workaround when flutter version is updated. + GtkWidget *gl_area = find_gl_area(GTK_WIDGET(view)); + if (gl_area != NULL) { + gtk_gl_area_set_has_alpha(GTK_GL_AREA(gl_area), TRUE); + } + + if (screen != NULL) { + GdkVisual *visual = NULL; + gtk_widget_set_app_paintable(GTK_WIDGET(window), TRUE); + visual = gdk_screen_get_rgba_visual(screen); + if (visual != NULL && gdk_screen_is_composited(screen)) { + gtk_widget_set_visual(GTK_WIDGET(window), visual); + } + } + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); gtk_widget_grab_focus(GTK_WIDGET(view)); @@ -121,3 +140,25 @@ MyApplication* my_application_new() { "flags", G_APPLICATION_NON_UNIQUE, nullptr)); } + +GtkWidget *find_gl_area(GtkWidget *widget) +{ + if (GTK_IS_GL_AREA(widget)) { + return widget; + } + + if (GTK_IS_CONTAINER(widget)) { + GList *children = gtk_container_get_children(GTK_CONTAINER(widget)); + for (GList *iter = children; iter != NULL; iter = g_list_next(iter)) { + GtkWidget *child = GTK_WIDGET(iter->data); + GtkWidget *gl_area = find_gl_area(child); + if (gl_area != NULL) { + g_list_free(children); + return gl_area; + } + } + g_list_free(children); + } + + return NULL; +} diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 98f3eef96353..58df59a5521f 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -335,7 +335,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "519350f1f40746798299e94786197d058353bac9" + resolved-ref: "4f562ab49d289cfa36bfda7cff12746ec0200033" url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window" source: git version: "0.1.0" From f330953f4f59cbfb3bca08b3466a7188152bdb85 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 1 Dec 2024 18:49:24 +0800 Subject: [PATCH 20/28] bump to 1.3.4 --- .github/workflows/flutter-build.yml | 4 ++-- .github/workflows/playground.yml | 2 +- Cargo.lock | 4 ++-- Cargo.toml | 2 +- appimage/AppImageBuilder-aarch64.yml | 2 +- appimage/AppImageBuilder-x86_64.yml | 2 +- flutter/pubspec.yaml | 2 +- libs/portable/Cargo.toml | 2 +- res/PKGBUILD | 2 +- res/rpm-flutter-suse.spec | 2 +- res/rpm-flutter.spec | 2 +- res/rpm.spec | 2 +- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index b295e70f6f87..5ac769ea65df 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -33,7 +33,7 @@ env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" # vcpkg version: 2024.07.12 VCPKG_COMMIT_ID: "1de2026f28ead93ff1773e6e680387643e914ea1" - VERSION: "1.3.3" + VERSION: "1.3.4" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" @@ -714,7 +714,7 @@ jobs: shell: bash run: | cd "$(dirname "$(which flutter)")" - # https://github.com/flutter/flutter/issues/133533 + # https://github.com/flutter/flutter/issues/1.3.43 sed -i -e 's/_setFramesEnabledState(false);/\/\/_setFramesEnabledState(false);/g' ../packages/flutter/lib/src/scheduler/binding.dart grep -n '_setFramesEnabledState(false);' ../packages/flutter/lib/src/scheduler/binding.dart diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 9d4d42cb3eda..282f678e8304 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -18,7 +18,7 @@ env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" # vcpkg version: 2024.06.15 VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625" - VERSION: "1.3.3" + VERSION: "1.3.4" NDK_VERSION: "r26d" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" diff --git a/Cargo.lock b/Cargo.lock index 6e2e56b3bcb5..62f612397b98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5494,7 +5494,7 @@ dependencies = [ [[package]] name = "rustdesk" -version = "1.3.3" +version = "1.3.4" dependencies = [ "android-wakelock", "android_logger", @@ -5594,7 +5594,7 @@ dependencies = [ [[package]] name = "rustdesk-portable-packer" -version = "1.3.3" +version = "1.3.4" dependencies = [ "brotli", "dirs 5.0.1", diff --git a/Cargo.toml b/Cargo.toml index dbf819bf8389..3cc05b780551 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustdesk" -version = "1.3.3" +version = "1.3.4" authors = ["rustdesk "] edition = "2021" build= "build.rs" diff --git a/appimage/AppImageBuilder-aarch64.yml b/appimage/AppImageBuilder-aarch64.yml index a6bd632dc0cb..65a73ee52b68 100644 --- a/appimage/AppImageBuilder-aarch64.yml +++ b/appimage/AppImageBuilder-aarch64.yml @@ -18,7 +18,7 @@ AppDir: id: rustdesk name: rustdesk icon: rustdesk - version: 1.3.3 + version: 1.3.4 exec: usr/lib/rustdesk/rustdesk exec_args: $@ apt: diff --git a/appimage/AppImageBuilder-x86_64.yml b/appimage/AppImageBuilder-x86_64.yml index 9ea820fec475..430400721a8b 100644 --- a/appimage/AppImageBuilder-x86_64.yml +++ b/appimage/AppImageBuilder-x86_64.yml @@ -18,7 +18,7 @@ AppDir: id: rustdesk name: rustdesk icon: rustdesk - version: 1.3.3 + version: 1.3.4 exec: usr/lib/rustdesk/rustdesk exec_args: $@ apt: diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 3a4f46b2a36f..88e9dda1b874 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers -version: 1.3.3+52 +version: 1.3.4+53 environment: sdk: '^3.1.0' diff --git a/libs/portable/Cargo.toml b/libs/portable/Cargo.toml index 3bf827865dd6..3cada5a19022 100644 --- a/libs/portable/Cargo.toml +++ b/libs/portable/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustdesk-portable-packer" -version = "1.3.3" +version = "1.3.4" edition = "2021" description = "RustDesk Remote Desktop" diff --git a/res/PKGBUILD b/res/PKGBUILD index 3da7c98da0ab..d4bef334710c 100644 --- a/res/PKGBUILD +++ b/res/PKGBUILD @@ -1,5 +1,5 @@ pkgname=rustdesk -pkgver=1.3.3 +pkgver=1.3.4 pkgrel=0 epoch= pkgdesc="" diff --git a/res/rpm-flutter-suse.spec b/res/rpm-flutter-suse.spec index 06653a8ce7f0..a18bb2462bfc 100644 --- a/res/rpm-flutter-suse.spec +++ b/res/rpm-flutter-suse.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.3.3 +Version: 1.3.4 Release: 0 Summary: RPM package License: GPL-3.0 diff --git a/res/rpm-flutter.spec b/res/rpm-flutter.spec index 74115f8877c1..aba6aa21e967 100644 --- a/res/rpm-flutter.spec +++ b/res/rpm-flutter.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.3.3 +Version: 1.3.4 Release: 0 Summary: RPM package License: GPL-3.0 diff --git a/res/rpm.spec b/res/rpm.spec index 3dfc496c2ec8..5f36a8b56005 100644 --- a/res/rpm.spec +++ b/res/rpm.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.3.3 +Version: 1.3.4 Release: 0 Summary: RPM package License: GPL-3.0 From 5a2a94d2cc7a2a4894b20af9e741b9c0db974453 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Mon, 2 Dec 2024 00:09:03 +0800 Subject: [PATCH 21/28] fix: macos, input (#10133) 1. Workaround sticky `Fn` for more keys. 2. Workaround stikey `Help`. Signed-off-by: fufesou --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 62f612397b98..7c5843189547 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5219,7 +5219,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/rustdesk-org/rdev#961d25cc00c6b3ef80f444e6a7bed9872e2c35ea" +source = "git+https://github.com/rustdesk-org/rdev#01ac3ec8009f04f7615842b9152338844b806184" dependencies = [ "cocoa 0.24.1", "core-foundation 0.9.4", From dc58c85e30e8df6aec6aa80a16e1a2647e29aee5 Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 2 Dec 2024 12:35:44 +0800 Subject: [PATCH 22/28] try fix mac textedit of server config (#10135) Signed-off-by: 21pages --- .../desktop/pages/desktop_setting_page.dart | 69 ++++++++++++------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 0c7586e7036c..577b1e8c5a68 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1362,11 +1362,30 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin { bool get wantKeepAlive => true; bool locked = !isWeb && bind.mainIsInstalled(); + final scrollController = ScrollController(); + late final TextEditingController idController; + late final TextEditingController relayController; + late final TextEditingController apiController; + late final TextEditingController keyController; + + @override + void initState() { + super.initState(); + Map oldOptions = jsonDecode(bind.mainGetOptionsSync()); + old(String key) { + return (oldOptions[key] ?? '').trim(); + } + + idController = TextEditingController(text: old('custom-rendezvous-server')); + relayController = TextEditingController(text: old('relay-server')); + apiController = TextEditingController(text: old('api-server')); + keyController = TextEditingController(text: old('key')); + } + @override Widget build(BuildContext context) { super.build(context); bool enabled = !locked; - final scrollController = ScrollController(); final hideServer = bind.mainGetBuildinOption(key: kOptionHideServerSetting) == 'Y'; // TODO: support web proxy @@ -1395,19 +1414,10 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin { // Simple temp wrapper for PR check tmpWrapper() { // Setting page is not modal, oldOptions should only be used when getting options, never when setting. - Map oldOptions = jsonDecode(bind.mainGetOptionsSync()); - old(String key) { - return (oldOptions[key] ?? '').trim(); - } RxString idErrMsg = ''.obs; RxString relayErrMsg = ''.obs; RxString apiErrMsg = ''.obs; - var idController = - TextEditingController(text: old('custom-rendezvous-server')); - var relayController = TextEditingController(text: old('relay-server')); - var apiController = TextEditingController(text: old('api-server')); - var keyController = TextEditingController(text: old('key')); final controllers = [ idController, relayController, @@ -2251,26 +2261,39 @@ _LabeledTextField( String errorText, bool enabled, bool secure) { - return Row( + return Table( + columnWidths: const { + 0: FixedColumnWidth(150), + 1: FlexColumnWidth(), + }, + defaultVerticalAlignment: TableCellVerticalAlignment.middle, children: [ - ConstrainedBox( - constraints: const BoxConstraints(minWidth: 140), - child: Text( - '${translate(label)}:', - textAlign: TextAlign.right, - style: TextStyle( - fontSize: 16, color: disabledTextColor(context, enabled)), - ).marginOnly(right: 10)), - Expanded( - child: TextField( + TableRow( + children: [ + Padding( + padding: const EdgeInsets.only(right: 10), + child: Text( + '${translate(label)}:', + textAlign: TextAlign.right, + style: TextStyle( + fontSize: 16, + color: disabledTextColor(context, enabled), + ), + ), + ), + TextField( controller: controller, enabled: enabled, obscureText: secure, + autocorrect: false, decoration: InputDecoration( - errorText: errorText.isNotEmpty ? errorText : null), + errorText: errorText.isNotEmpty ? errorText : null, + ), style: TextStyle( color: disabledTextColor(context, enabled), - )), + ), + ), + ], ), ], ).marginOnly(bottom: 8); From 3251045e22c705f2e20f48449b9378a5d99480ce Mon Sep 17 00:00:00 2001 From: jkh0kr Date: Mon, 2 Dec 2024 16:47:23 +0900 Subject: [PATCH 23/28] Update ko.rs (#10138) Update ko.rs --- src/lang/ko.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 527813d09eba..93c174894119 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -653,7 +653,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload folder", "폴더 업로드"), ("Upload files", "파일 업로드"), ("Clipboard is synchronized", "클립보드가 동기화됨"), - ("Update client clipboard", ""), - ("Untagged", ""), + ("Update client clipboard", "클라이언트 클립보드 업데이트"), + ("Untagged", "태그 없음"), ].iter().cloned().collect(); } From dea99ffb3ab34fee562090d214af419b557debdf Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 2 Dec 2024 16:11:12 +0800 Subject: [PATCH 24/28] fix rustdesk exit crash --- flutter/pubspec.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 58df59a5521f..ec72da0a4140 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -335,7 +335,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "4f562ab49d289cfa36bfda7cff12746ec0200033" + resolved-ref: "925df98a740b168ae3dad42592db4af66401053c" url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window" source: git version: "0.1.0" From 773b9d6645fad2159471881a9ed865e229bbb00b Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 2 Dec 2024 17:10:34 +0800 Subject: [PATCH 25/28] win7 uses soft rendering by default (#10139) win7 vm got black screen on remote window with texture rendering Signed-off-by: 21pages --- src/platform/windows.cc | 5 +++++ src/platform/windows.rs | 6 ++++++ src/ui_interface.rs | 39 +++++++++++++++++++++++++++------------ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/platform/windows.cc b/src/platform/windows.cc index 9ee3c1f5c9a6..04095a2d63f0 100644 --- a/src/platform/windows.cc +++ b/src/platform/windows.cc @@ -222,6 +222,11 @@ extern "C" return IsWindowsServer(); } + bool is_windows_10_or_greater() + { + return IsWindows10OrGreater(); + } + HANDLE LaunchProcessWin(LPCWSTR cmd, DWORD dwSessionId, BOOL as_user, DWORD *pDwTokenPid) { HANDLE hProcess = NULL; diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 4495af538a7b..c0839dc55acd 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -479,6 +479,7 @@ extern "C" { fn selectInputDesktop() -> BOOL; fn inputDesktopSelected() -> BOOL; fn is_windows_server() -> BOOL; + fn is_windows_10_or_greater() -> BOOL; fn handleMask( out: *mut u8, mask: *const u8, @@ -1559,6 +1560,11 @@ pub fn is_win_server() -> bool { unsafe { is_windows_server() > 0 } } +#[inline] +pub fn is_win_10_or_greater() -> bool { + unsafe { is_windows_10_or_greater() > 0 } +} + pub fn bootstrap() { if let Ok(lic) = get_license_from_exe_name() { *config::EXE_RENDEZVOUS_SERVER.write().unwrap() = lic.host.clone(); diff --git a/src/ui_interface.rs b/src/ui_interface.rs index caff46b84f33..5d7f9ee039cd 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -173,21 +173,36 @@ pub fn get_option>(key: T) -> String { } #[inline] -#[cfg(target_os = "macos")] pub fn use_texture_render() -> bool { - cfg!(feature = "flutter") && LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) == "Y" -} + #[cfg(target_os = "android")] + return false; + #[cfg(target_os = "ios")] + return false; -#[inline] -#[cfg(any(target_os = "windows", target_os = "linux"))] -pub fn use_texture_render() -> bool { - cfg!(feature = "flutter") && LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) != "N" -} + #[cfg(target_os = "macos")] + return cfg!(feature = "flutter") + && LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) == "Y"; -#[inline] -#[cfg(any(target_os = "android", target_os = "ios"))] -pub fn use_texture_render() -> bool { - false + #[cfg(target_os = "linux")] + return cfg!(feature = "flutter") + && LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) != "N"; + + #[cfg(target_os = "windows")] + { + if !cfg!(feature = "flutter") { + return false; + } + // https://learn.microsoft.com/en-us/windows/win32/sysinfo/targeting-your-application-at-windows-8-1 + #[cfg(debug_assertions)] + let default_texture = true; + #[cfg(not(debug_assertions))] + let default_texture = crate::platform::is_win_10_or_greater(); + if default_texture { + LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) != "N" + } else { + return LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) == "Y"; + } + } } #[inline] From f38d89aaeefd008689bc98b11c2559576636c669 Mon Sep 17 00:00:00 2001 From: solokot Date: Mon, 2 Dec 2024 13:30:24 +0300 Subject: [PATCH 26/28] Update ru.rs (#10143) --- src/lang/ru.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 582fdf6dcc6a..5979961ddf91 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -654,6 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload files", "Загрузить файлы"), ("Clipboard is synchronized", "Буфер обмена синхронизирован"), ("Update client clipboard", "Обновить буфер обмена клиента"), - ("Untagged", ""), + ("Untagged", "Без метки"), ].iter().cloned().collect(); } From b8d36b6558548602961d4a7ea35373e543608be5 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 2 Dec 2024 22:29:20 +0800 Subject: [PATCH 27/28] revert multi-window plugin --- flutter/pubspec.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index ec72da0a4140..58df59a5521f 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -335,7 +335,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "925df98a740b168ae3dad42592db4af66401053c" + resolved-ref: "4f562ab49d289cfa36bfda7cff12746ec0200033" url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window" source: git version: "0.1.0" From bd0a33e46796a9f24bba6fabb2534490fe1757a0 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Tue, 3 Dec 2024 01:02:41 +0800 Subject: [PATCH 28/28] fix: linux, window, workaround, mint, mate (#10146) * refact: linux, window, workaround, mint, mate Signed-off-by: fufesou * refact: case insensitive Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/common.dart | 30 ++++++++ flutter/lib/main.dart | 11 ++- flutter/lib/web/bridge.dart | 4 ++ flutter/linux/my_application.cc | 98 +++++++++++++++++++++++---- libs/hbb_common/src/platform/linux.rs | 15 +++- src/flutter_ffi.rs | 20 ++++++ 6 files changed, 161 insertions(+), 17 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 4e97449124f7..13ee4dd84a43 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; +import 'dart:io'; import 'package:back_button_interceptor/back_button_interceptor.dart'; import 'package:desktop_multi_window/desktop_multi_window.dart'; @@ -3459,6 +3460,35 @@ Widget buildPresetPasswordWarning() { ); } +bool get isLinuxMateDesktop => + isLinux && + (Platform.environment['XDG_CURRENT_DESKTOP']?.toLowerCase() == 'mate' || + Platform.environment['XDG_SESSION_DESKTOP']?.toLowerCase() == 'mate' || + Platform.environment['DESKTOP_SESSION']?.toLowerCase() == 'mate'); + +Map? _linuxOsDistro; + +String getLinuxOsDistroId() { + if (_linuxOsDistro == null) { + String osInfo = bind.getOsDistroInfo(); + if (osInfo.isEmpty) { + _linuxOsDistro = {}; + } else { + try { + _linuxOsDistro = jsonDecode(osInfo); + } catch (e) { + debugPrint('Failed to parse os info: $e'); + // Don't call `bind.getOsDistroInfo()` again if failed to parse osInfo. + _linuxOsDistro = {}; + } + } + } + return (_linuxOsDistro?['id'] ?? '') as String; +} + +bool get isLinuxMint => + getLinuxOsDistroId().toLowerCase().contains('linuxmint'); + // https://github.com/leanflutter/window_manager/blob/87dd7a50b4cb47a375b9fc697f05e56eea0a2ab3/lib/src/widgets/virtual_window_frame.dart#L44 Widget buildVirtualWindowFrame(BuildContext context, Widget child) { boxShadow() => isMainDesktopWindow diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 00afbb001e76..3176bfb86eba 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -483,7 +483,16 @@ class _AppState extends State with WidgetsBindingObserver { child = keyListenerBuilder(context, child); } if (isLinux) { - child = buildVirtualWindowFrame(context, child); + // `(!(isLinuxMateDesktop || isLinuxMint))` is not used here for clarity. + // `isLinuxMint` will call ffi function. + if (!isLinuxMateDesktop) { + if (!isLinuxMint) { + debugPrint( + 'Linux distro is not linuxmint, and desktop is not mate, ' + 'so we build virtual window frame.'); + child = buildVirtualWindowFrame(context, child); + } + } } return child; }, diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index 20891281455d..ffbf6638253d 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -1828,5 +1828,9 @@ class RustdeskImpl { throw UnimplementedError("sessionGetConnToken"); } + String getOsDistroInfo({dynamic hint}) { + return ''; + } + void dispose() {} } diff --git a/flutter/linux/my_application.cc b/flutter/linux/my_application.cc index f4247bd94613..9fa947002d3e 100644 --- a/flutter/linux/my_application.cc +++ b/flutter/linux/my_application.cc @@ -17,6 +17,7 @@ G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) extern bool gIsConnectionManager; GtkWidget *find_gl_area(GtkWidget *widget); +void try_set_transparent(GtkWindow* window, GdkScreen* screen, FlView* view); // Implements GApplication::activate. static void my_application_activate(GApplication* application) { @@ -79,21 +80,7 @@ static void my_application_activate(GApplication* application) { gtk_widget_show(GTK_WIDGET(view)); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - // https://github.com/flutter/flutter/issues/152154 - // Remove this workaround when flutter version is updated. - GtkWidget *gl_area = find_gl_area(GTK_WIDGET(view)); - if (gl_area != NULL) { - gtk_gl_area_set_has_alpha(GTK_GL_AREA(gl_area), TRUE); - } - - if (screen != NULL) { - GdkVisual *visual = NULL; - gtk_widget_set_app_paintable(GTK_WIDGET(window), TRUE); - visual = gdk_screen_get_rgba_visual(screen); - if (visual != NULL && gdk_screen_is_composited(screen)) { - gtk_widget_set_visual(GTK_WIDGET(window), visual); - } - } + try_set_transparent(window, screen, view); fl_register_plugins(FL_PLUGIN_REGISTRY(view)); @@ -162,3 +149,84 @@ GtkWidget *find_gl_area(GtkWidget *widget) return NULL; } + +bool is_linux_mint() +{ + bool is_mint = false; + char line[256]; + FILE *fp = fopen("/etc/os-release", "r"); + if (fp == NULL) { + return false; + } + while (fgets(line, sizeof(line), fp)) { + if (strstr(line, "ID=linuxmint") != NULL) { + is_mint = true; + break; + } + } + fclose(fp); + + return is_mint; +} + +bool is_desktop_mate() +{ + const char* desktop = NULL; + desktop = getenv("XDG_CURRENT_DESKTOP"); + printf("Linux desktop, XDG_CURRENT_DESKTOP: %s\n", desktop == NULL ? "" : desktop); + if (desktop == NULL) { + desktop = getenv("XDG_SESSION_DESKTOP"); + printf("Linux desktop, XDG_SESSION_DESKTOP: %s\n", desktop == NULL ? "" : desktop); + } + if (desktop == NULL) { + desktop = getenv("DESKTOP_SESSION"); + printf("Linux desktop, DESKTOP_SESSION: %s\n", desktop == NULL ? "" : desktop); + } + if (desktop != NULL && strcasecmp(desktop, "mate") == 0) { + return true; + } + return false; +} + +bool skip_setting_transparent() +{ + if (is_desktop_mate()) { + printf("Linux desktop, MATE\n"); + return true; + } + + if (is_linux_mint()) { + printf("Linux desktop, Linux Mint\n"); + return true; + } + + return false; +} + +// https://github.com/flutter/flutter/issues/152154 +// Remove this workaround when flutter version is updated. +void try_set_transparent(GtkWindow* window, GdkScreen* screen, FlView* view) +{ + GtkWidget *gl_area = NULL; + + if (skip_setting_transparent()) { + printf("Skip setting transparent\n"); + return; + } + + printf("Try setting transparent\n"); + + gl_area = find_gl_area(GTK_WIDGET(view)); + if (gl_area != NULL) { + gtk_gl_area_set_has_alpha(GTK_GL_AREA(gl_area), TRUE); + } + + if (screen != NULL) { + GdkVisual *visual = NULL; + gtk_widget_set_app_paintable(GTK_WIDGET(window), TRUE); + visual = gdk_screen_get_rgba_visual(screen); + if (visual != NULL && gdk_screen_is_composited(screen)) { + gtk_widget_set_visual(GTK_WIDGET(window), visual); + } + } +} diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 60c8714d8212..31481ca78f23 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -13,22 +13,35 @@ pub const XDG_CURRENT_DESKTOP: &str = "XDG_CURRENT_DESKTOP"; pub struct Distro { pub name: String, + pub id: String, pub version_id: String, } impl Distro { fn new() -> Self { + // to-do: + // 1. Remove `run_cmds`, read file once + // 2. Add more distro infos let name = run_cmds("awk -F'=' '/^NAME=/ {print $2}' /etc/os-release") .unwrap_or_default() .trim() .trim_matches('"') .to_string(); + let id = run_cmds("awk -F'=' '/^ID=/ {print $2}' /etc/os-release") + .unwrap_or_default() + .trim() + .trim_matches('"') + .to_string(); let version_id = run_cmds("awk -F'=' '/^VERSION_ID=/ {print $2}' /etc/os-release") .unwrap_or_default() .trim() .trim_matches('"') .to_string(); - Self { name, version_id } + Self { + name, + id, + version_id, + } } } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 4c875be49b72..4630ac3337d7 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -19,6 +19,7 @@ use hbb_common::allow_err; use hbb_common::{ config::{self, LocalConfig, PeerConfig, PeerInfoSerde}, fs, lazy_static, log, + message_proto::Hash, rendezvous_proto::ConnType, ResultType, }; @@ -2341,6 +2342,25 @@ pub fn main_audio_support_loopback() -> SyncReturn { SyncReturn(is_surpport) } +pub fn get_os_distro_info() -> SyncReturn { + #[cfg(target_os = "linux")] + { + let distro = &hbb_common::platform::linux::DISTRO; + SyncReturn( + serde_json::to_string(&HashMap::from([ + ("name", distro.name.clone()), + ("id", distro.id.clone()), + ("version_id", distro.version_id.clone()), + ])) + .unwrap_or_default(), + ) + } + #[cfg(not(target_os = "linux"))] + { + SyncReturn("".to_owned()) + } +} + #[cfg(target_os = "android")] pub mod server_side { use hbb_common::{config, log};