20 Commits

39 changed files with 339 additions and 233 deletions

View File

@@ -33,8 +33,8 @@ env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2024.07.12 # vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "1de2026f28ead93ff1773e6e680387643e914ea1" VCPKG_COMMIT_ID: "1de2026f28ead93ff1773e6e680387643e914ea1"
VERSION: "1.3.1" VERSION: "1.3.2"
NDK_VERSION: "r27" NDK_VERSION: "r27b"
#signing keys env variable checks #signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}" MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"

View File

@@ -18,7 +18,7 @@ env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2024.06.15 # vcpkg version: 2024.06.15
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625" VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
VERSION: "1.3.1" VERSION: "1.3.2"
NDK_VERSION: "r26d" NDK_VERSION: "r26d"
#signing keys env variable checks #signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"

4
Cargo.lock generated
View File

@@ -5480,7 +5480,7 @@ dependencies = [
[[package]] [[package]]
name = "rustdesk" name = "rustdesk"
version = "1.3.1" version = "1.3.2"
dependencies = [ dependencies = [
"android-wakelock", "android-wakelock",
"android_logger", "android_logger",
@@ -5580,7 +5580,7 @@ dependencies = [
[[package]] [[package]]
name = "rustdesk-portable-packer" name = "rustdesk-portable-packer"
version = "1.3.1" version = "1.3.2"
dependencies = [ dependencies = [
"brotli", "brotli",
"dirs 5.0.1", "dirs 5.0.1",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "rustdesk" name = "rustdesk"
version = "1.3.1" version = "1.3.2"
authors = ["rustdesk <info@rustdesk.com>"] authors = ["rustdesk <info@rustdesk.com>"]
edition = "2021" edition = "2021"
build= "build.rs" build= "build.rs"

View File

@@ -18,7 +18,7 @@ AppDir:
id: rustdesk id: rustdesk
name: rustdesk name: rustdesk
icon: rustdesk icon: rustdesk
version: 1.3.1 version: 1.3.2
exec: usr/lib/rustdesk/rustdesk exec: usr/lib/rustdesk/rustdesk
exec_args: $@ exec_args: $@
apt: apt:

View File

@@ -18,7 +18,7 @@ AppDir:
id: rustdesk id: rustdesk
name: rustdesk name: rustdesk
icon: rustdesk icon: rustdesk
version: 1.3.1 version: 1.3.2
exec: usr/lib/rustdesk/rustdesk exec: usr/lib/rustdesk/rustdesk
exec_args: $@ exec_args: $@
apt: apt:

View File

@@ -8,7 +8,7 @@
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br> [<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
</p> </p>
Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) 与我们交流: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
@@ -32,7 +32,9 @@ RustDesk 期待各位的贡献. 如何参与开发? 详情请看 [CONTRIBUTING-Z
## 依赖 ## 依赖
桌面版本界面使用[sciter](https://sciter.com/), 请自行下载 桌面版本使用 Flutter 或 Sciter已弃用作为 GUI本教程仅适用于 Sciter因为它更简单且更易于上手。查看我们的[CI](https://github.com/rustdesk/rustdesk/blob/master/.github/workflows/flutter-build.yml)以构建 Flutter 版本
请自行下载Sciter动态库。
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) | [Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) | [Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
@@ -207,12 +209,13 @@ target/release/rustdesk
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: 视频编解码, 配置, tcp/udp 封装, protobuf, 文件传输相关文件系统操作函数, 以及一些其他实用函数 - **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: 视频编解码, 配置, tcp/udp 封装, protobuf, 文件传输相关文件系统操作函数, 以及一些其他实用函数
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: 屏幕截取 - **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: 屏幕截取
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: 平台相关的鼠标键盘输入 - **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: 平台相关的鼠标键盘输入
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI - **[libs/clipboard](https://github.com/rustdesk/rustdesk/tree/master/libs/clipboard)**: Windows、Linux、macOS 的文件复制和粘贴实现
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: 过时的 Sciter UI已弃用
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: 被控端服务音频、剪切板、输入、视频服务、网络连接的实现 - **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: 被控端服务音频、剪切板、输入、视频服务、网络连接的实现
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: 控制端 - **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: 控制端
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: 与[rustdesk-server](https://github.com/rustdesk/rustdesk-server)保持UDP通讯, 等待远程连接(通过打洞直连或者中继) - **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: 与[rustdesk-server](https://github.com/rustdesk/rustdesk-server)保持UDP通讯, 等待远程连接(通过打洞直连或者中继)
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: 平台服务相关代码 - **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: 平台服务相关代码
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: 移动版本的Flutter代码 - **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: 适用于桌面和移动设备的 Flutter 代码
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Flutter Web版本中的Javascript代码 - **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Flutter Web版本中的Javascript代码
## 截图 ## 截图

View File

@@ -17,7 +17,7 @@
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://github.com/linux-pam/linux-pam/releases/download/v1.3.1/Linux-PAM-1.3.1.tar.xz", "url": "https://github.com/linux-pam/linux-pam/releases/download/v1.3.2/Linux-PAM-1.3.2.tar.xz",
"sha256": "eff47a4ecd833fbf18de9686632a70ee8d0794b79aecb217ebd0ce11db4cd0db" "sha256": "eff47a4ecd833fbf18de9686632a70ee8d0794b79aecb217ebd0ce11db4cd0db"
} }
] ]

View File

@@ -302,6 +302,7 @@ prebuild)
sed \ sed \
-i \ -i \
-e 's/extended_text: .*/extended_text: 11.1.0/' \
-e 's/uni_links_desktop/#uni_links_desktop/g' \ -e 's/uni_links_desktop/#uni_links_desktop/g' \
flutter/pubspec.yaml flutter/pubspec.yaml

View File

@@ -241,14 +241,15 @@ class _AddressBookState extends State<AddressBook> {
bind.setLocalFlutterOption(k: kOptionCurrentAbName, v: value); bind.setLocalFlutterOption(k: kOptionCurrentAbName, v: value);
} }
}, },
customButton: Obx(()=>Container( customButton: Obx(() => Container(
height: stateGlobal.isPortrait.isFalse ? 48 : 40, height: stateGlobal.isPortrait.isFalse ? 48 : 40,
child: Row(children: [ child: Row(children: [
Expanded( Expanded(
child: buildItem(gFFI.abModel.currentName.value, button: true)), child:
Icon(Icons.arrow_drop_down), buildItem(gFFI.abModel.currentName.value, button: true)),
]), Icon(Icons.arrow_drop_down),
)), ]),
)),
underline: Container( underline: Container(
height: 0.7, height: 0.7,
color: Theme.of(context).dividerColor.withOpacity(0.1), color: Theme.of(context).dividerColor.withOpacity(0.1),
@@ -358,7 +359,6 @@ class _AddressBookState extends State<AddressBook> {
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: AddressBookPeersView( child: AddressBookPeersView(
menuPadding: widget.menuPadding, menuPadding: widget.menuPadding,
getInitPeers: () => gFFI.abModel.currentAbPeers,
)), )),
); );
} }
@@ -509,19 +509,19 @@ class _AddressBookState extends State<AddressBook> {
row({required Widget lable, required Widget input}) { row({required Widget lable, required Widget input}) {
makeChild(bool isPortrait) => Row( makeChild(bool isPortrait) => Row(
children: [ children: [
!isPortrait !isPortrait
? ConstrainedBox( ? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: lable.marginOnly(right: 10)) child: lable.marginOnly(right: 10))
: SizedBox.shrink(), : SizedBox.shrink(),
Expanded( Expanded(
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 200), constraints: const BoxConstraints(minWidth: 200),
child: input), child: input),
), ),
], ],
).marginOnly(bottom: !isPortrait ? 8 : 0); ).marginOnly(bottom: !isPortrait ? 8 : 0);
return Obx(() => makeChild(stateGlobal.isPortrait.isTrue)); return Obx(() => makeChild(stateGlobal.isPortrait.isTrue));
} }
@@ -546,23 +546,28 @@ class _AddressBookState extends State<AddressBook> {
], ],
), ),
input: Obx(() => TextField( input: Obx(() => TextField(
controller: idController, controller: idController,
inputFormatters: [IDTextInputFormatter()], inputFormatters: [IDTextInputFormatter()],
decoration: InputDecoration( decoration: InputDecoration(
labelText: stateGlobal.isPortrait.isFalse ? null : translate('ID'), labelText: stateGlobal.isPortrait.isFalse
errorText: errorMsg, ? null
errorMaxLines: 5), : translate('ID'),
))), errorText: errorMsg,
errorMaxLines: 5),
))),
row( row(
lable: Text( lable: Text(
translate('Alias'), translate('Alias'),
style: style, style: style,
), ),
input: Obx(() => TextField( input: Obx(() => TextField(
controller: aliasController, controller: aliasController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: stateGlobal.isPortrait.isFalse ? null : translate('Alias'), labelText: stateGlobal.isPortrait.isFalse
),)), ? null
: translate('Alias'),
),
)),
), ),
if (isCurrentAbShared) if (isCurrentAbShared)
row( row(
@@ -570,25 +575,29 @@ class _AddressBookState extends State<AddressBook> {
translate('Password'), translate('Password'),
style: style, style: style,
), ),
input: Obx(() => TextField( input: Obx(
controller: passwordController, () => TextField(
obscureText: !passwordVisible, controller: passwordController,
decoration: InputDecoration( obscureText: !passwordVisible,
labelText: stateGlobal.isPortrait.isFalse ? null : translate('Password'), decoration: InputDecoration(
suffixIcon: IconButton( labelText: stateGlobal.isPortrait.isFalse
icon: Icon( ? null
passwordVisible : translate('Password'),
? Icons.visibility suffixIcon: IconButton(
: Icons.visibility_off, icon: Icon(
color: MyTheme.lightTheme.primaryColor), passwordVisible
onPressed: () { ? Icons.visibility
setState(() { : Icons.visibility_off,
passwordVisible = !passwordVisible; color: MyTheme.lightTheme.primaryColor),
}); onPressed: () {
}, setState(() {
passwordVisible = !passwordVisible;
});
},
),
), ),
), ),
),)), )),
if (gFFI.abModel.currentAbTags.isNotEmpty) if (gFFI.abModel.currentAbTags.isNotEmpty)
Align( Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,

View File

@@ -83,8 +83,8 @@ class _MyGroupState extends State<MyGroup> {
child: Align( child: Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: MyGroupPeerView( child: MyGroupPeerView(
menuPadding: widget.menuPadding, menuPadding: widget.menuPadding,
getInitPeers: () => gFFI.groupModel.peers)), )),
) )
], ],
); );
@@ -115,8 +115,8 @@ class _MyGroupState extends State<MyGroup> {
child: Align( child: Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: MyGroupPeerView( child: MyGroupPeerView(
menuPadding: widget.menuPadding, menuPadding: widget.menuPadding,
getInitPeers: () => gFFI.groupModel.peers)), )),
) )
], ],
); );

View File

@@ -595,8 +595,7 @@ class QualityMonitor extends StatelessWidget {
"${qualityMonitorModel.data.targetBitrate ?? '-'}kb"), "${qualityMonitorModel.data.targetBitrate ?? '-'}kb"),
_row( _row(
"Codec", qualityMonitorModel.data.codecFormat ?? '-'), "Codec", qualityMonitorModel.data.codecFormat ?? '-'),
if (!isWeb) _row("Chroma", qualityMonitorModel.data.chroma ?? '-'),
_row("Chroma", qualityMonitorModel.data.chroma ?? '-'),
], ],
), ),
) )

View File

@@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart'; import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart';
import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -42,6 +43,14 @@ class LoadEvent {
static const String group = 'load_group_peers'; static const String group = 'load_group_peers';
} }
class PeersModelName {
static const String recent = 'recent peer';
static const String favorite = 'fav peer';
static const String lan = 'discovered peer';
static const String addressBook = 'address book peer';
static const String group = 'group peer';
}
/// for peer search text, global obs value /// for peer search text, global obs value
final peerSearchText = "".obs; final peerSearchText = "".obs;
@@ -128,8 +137,9 @@ class _PeersViewState extends State<_PeersView>
// //
// Although `onWindowRestore()` is called after `onWindowBlur()` in my test, // Although `onWindowRestore()` is called after `onWindowBlur()` in my test,
// we need the following comparison to ensure that `_isActive` is true in the end. // we need the following comparison to ensure that `_isActive` is true in the end.
if (isWindows && DateTime.now().difference(_lastWindowRestoreTime) < if (isWindows &&
const Duration(milliseconds: 300)) { DateTime.now().difference(_lastWindowRestoreTime) <
const Duration(milliseconds: 300)) {
return; return;
} }
_queryCount = _maxQueryCount; _queryCount = _maxQueryCount;
@@ -170,8 +180,8 @@ class _PeersViewState extends State<_PeersView>
// We should avoid too many rebuilds. MacOS(m1, 14.6.1) on Flutter 3.19.6. // We should avoid too many rebuilds. MacOS(m1, 14.6.1) on Flutter 3.19.6.
// Continious rebuilds of `ChangeNotifierProvider` will cause memory leak. // Continious rebuilds of `ChangeNotifierProvider` will cause memory leak.
// Simple demo can reproduce this issue. // Simple demo can reproduce this issue.
return ChangeNotifierProvider<Peers>( return ChangeNotifierProvider<Peers>.value(
create: (context) => widget.peers, value: widget.peers,
child: Consumer<Peers>(builder: (context, peers, child) { child: Consumer<Peers>(builder: (context, peers, child) {
if (peers.peers.isEmpty) { if (peers.peers.isEmpty) {
gFFI.peerTabModel.setCurrentTabCachedPeers([]); gFFI.peerTabModel.setCurrentTabCachedPeers([]);
@@ -403,28 +413,39 @@ class _PeersViewState extends State<_PeersView>
} }
abstract class BasePeersView extends StatelessWidget { abstract class BasePeersView extends StatelessWidget {
final String name; final PeerTabIndex peerTabIndex;
final String loadEvent;
final PeerFilter? peerFilter; final PeerFilter? peerFilter;
final PeerCardBuilder peerCardBuilder; final PeerCardBuilder peerCardBuilder;
final GetInitPeers? getInitPeers;
const BasePeersView({ const BasePeersView({
Key? key, Key? key,
required this.name, required this.peerTabIndex,
required this.loadEvent,
this.peerFilter, this.peerFilter,
required this.peerCardBuilder, required this.peerCardBuilder,
required this.getInitPeers,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Peers peers;
switch (peerTabIndex) {
case PeerTabIndex.recent:
peers = gFFI.recentPeersModel;
break;
case PeerTabIndex.fav:
peers = gFFI.favoritePeersModel;
break;
case PeerTabIndex.lan:
peers = gFFI.lanPeersModel;
break;
case PeerTabIndex.ab:
peers = gFFI.abModel.peersModel;
break;
case PeerTabIndex.group:
peers = gFFI.groupModel.peersModel;
break;
}
return _PeersView( return _PeersView(
peers: peers: peers, peerFilter: peerFilter, peerCardBuilder: peerCardBuilder);
Peers(name: name, loadEvent: loadEvent, getInitPeers: getInitPeers),
peerFilter: peerFilter,
peerCardBuilder: peerCardBuilder);
} }
} }
@@ -433,13 +454,11 @@ class RecentPeersView extends BasePeersView {
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController}) {Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
: super( : super(
key: key, key: key,
name: 'recent peer', peerTabIndex: PeerTabIndex.recent,
loadEvent: LoadEvent.recent,
peerCardBuilder: (Peer peer) => RecentPeerCard( peerCardBuilder: (Peer peer) => RecentPeerCard(
peer: peer, peer: peer,
menuPadding: menuPadding, menuPadding: menuPadding,
), ),
getInitPeers: null,
); );
@override @override
@@ -455,13 +474,11 @@ class FavoritePeersView extends BasePeersView {
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController}) {Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
: super( : super(
key: key, key: key,
name: 'favorite peer', peerTabIndex: PeerTabIndex.fav,
loadEvent: LoadEvent.favorite,
peerCardBuilder: (Peer peer) => FavoritePeerCard( peerCardBuilder: (Peer peer) => FavoritePeerCard(
peer: peer, peer: peer,
menuPadding: menuPadding, menuPadding: menuPadding,
), ),
getInitPeers: null,
); );
@override @override
@@ -477,13 +494,11 @@ class DiscoveredPeersView extends BasePeersView {
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController}) {Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
: super( : super(
key: key, key: key,
name: 'discovered peer', peerTabIndex: PeerTabIndex.lan,
loadEvent: LoadEvent.lan,
peerCardBuilder: (Peer peer) => DiscoveredPeerCard( peerCardBuilder: (Peer peer) => DiscoveredPeerCard(
peer: peer, peer: peer,
menuPadding: menuPadding, menuPadding: menuPadding,
), ),
getInitPeers: null,
); );
@override @override
@@ -496,21 +511,16 @@ class DiscoveredPeersView extends BasePeersView {
class AddressBookPeersView extends BasePeersView { class AddressBookPeersView extends BasePeersView {
AddressBookPeersView( AddressBookPeersView(
{Key? key, {Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
EdgeInsets? menuPadding,
ScrollController? scrollController,
required GetInitPeers getInitPeers})
: super( : super(
key: key, key: key,
name: 'address book peer', peerTabIndex: PeerTabIndex.ab,
loadEvent: LoadEvent.addressBook,
peerFilter: (Peer peer) => peerFilter: (Peer peer) =>
_hitTag(gFFI.abModel.selectedTags, peer.tags), _hitTag(gFFI.abModel.selectedTags, peer.tags),
peerCardBuilder: (Peer peer) => AddressBookPeerCard( peerCardBuilder: (Peer peer) => AddressBookPeerCard(
peer: peer, peer: peer,
menuPadding: menuPadding, menuPadding: menuPadding,
), ),
getInitPeers: getInitPeers,
); );
static bool _hitTag(List<dynamic> selectedTags, List<dynamic> idents) { static bool _hitTag(List<dynamic> selectedTags, List<dynamic> idents) {
@@ -537,20 +547,15 @@ class AddressBookPeersView extends BasePeersView {
class MyGroupPeerView extends BasePeersView { class MyGroupPeerView extends BasePeersView {
MyGroupPeerView( MyGroupPeerView(
{Key? key, {Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
EdgeInsets? menuPadding,
ScrollController? scrollController,
required GetInitPeers getInitPeers})
: super( : super(
key: key, key: key,
name: 'group peer', peerTabIndex: PeerTabIndex.group,
loadEvent: LoadEvent.group,
peerFilter: filter, peerFilter: filter,
peerCardBuilder: (Peer peer) => MyGroupPeerCard( peerCardBuilder: (Peer peer) => MyGroupPeerCard(
peer: peer, peer: peer,
menuPadding: menuPadding, menuPadding: menuPadding,
), ),
getInitPeers: getInitPeers,
); );
static bool filter(Peer peer) { static bool filter(Peer peer) {

View File

@@ -570,3 +570,5 @@ enum WindowsTarget {
extension WindowsTargetExt on int { extension WindowsTargetExt on int {
WindowsTarget get windowsVersion => getWindowsTarget(this); WindowsTarget get windowsVersion => getWindowsTarget(this);
} }
const kCheckSoftwareUpdateFinish = 'check_software_update_finish';

View File

@@ -664,9 +664,17 @@ class _DesktopHomePageState extends State<DesktopHomePage>
void initState() { void initState() {
super.initState(); super.initState();
if (!bind.isCustomClient()) { if (!bind.isCustomClient()) {
platformFFI.registerEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
(Map<String, dynamic> evt) async {
if (evt['url'] is String) {
setState(() {
updateUrl = evt['url'];
});
}
});
Timer(const Duration(seconds: 1), () async { Timer(const Duration(seconds: 1), () async {
updateUrl = await bind.mainGetSoftwareUpdateUrl(); bind.mainGetSoftwareUpdateUrl();
if (updateUrl.isNotEmpty) setState(() {});
}); });
} }
_updateTimer = periodic_immediate(const Duration(seconds: 1), () async { _updateTimer = periodic_immediate(const Duration(seconds: 1), () async {
@@ -824,6 +832,10 @@ class _DesktopHomePageState extends State<DesktopHomePage>
_uniLinksSubscription?.cancel(); _uniLinksSubscription?.cancel();
Get.delete<RxBool>(tag: 'stop-service'); Get.delete<RxBool>(tag: 'stop-service');
_updateTimer?.cancel(); _updateTimer?.cancel();
if (!bind.isCustomClient()) {
platformFFI.unregisterEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish);
}
super.dispose(); super.dispose();
} }

View File

@@ -70,9 +70,17 @@ class _ConnectionPageState extends State<ConnectionPage> {
} }
if (isAndroid) { if (isAndroid) {
if (!bind.isCustomClient()) { if (!bind.isCustomClient()) {
platformFFI.registerEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
(Map<String, dynamic> evt) async {
if (evt['url'] is String) {
setState(() {
_updateUrl = evt['url'];
});
}
});
Timer(const Duration(seconds: 1), () async { Timer(const Duration(seconds: 1), () async {
_updateUrl = await bind.mainGetSoftwareUpdateUrl(); bind.mainGetSoftwareUpdateUrl();
if (_updateUrl.isNotEmpty) setState(() {});
}); });
} }
} }
@@ -353,6 +361,10 @@ class _ConnectionPageState extends State<ConnectionPage> {
if (Get.isRegistered<IDTextEditingController>()) { if (Get.isRegistered<IDTextEditingController>()) {
Get.delete<IDTextEditingController>(); Get.delete<IDTextEditingController>();
} }
if (!bind.isCustomClient()) {
platformFFI.unregisterEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish);
}
super.dispose(); super.dispose();
} }
} }

View File

@@ -19,95 +19,48 @@ class ScanPage extends StatefulWidget {
class _ScanPageState extends State<ScanPage> { class _ScanPageState extends State<ScanPage> {
QRViewController? controller; QRViewController? controller;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
StreamSubscription? scanSubscription;
// In order to get hot reload to work we need to pause the camera if the platform
// is android, or resume the camera if the platform is iOS.
@override @override
void reassemble() { void reassemble() {
super.reassemble(); super.reassemble();
if (isAndroid) { if (isAndroid && controller != null) {
controller!.pauseCamera(); controller!.pauseCamera();
} else if (controller != null) {
controller!.resumeCamera();
} }
controller!.resumeCamera();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Scan QR'), title: const Text('Scan QR'),
actions: [ actions: [
IconButton( _buildImagePickerButton(),
color: Colors.white, _buildFlashToggleButton(),
icon: Icon(Icons.image_search), _buildCameraSwitchButton(),
iconSize: 32.0, ],
onPressed: () async { ),
final ImagePicker picker = ImagePicker(); body: _buildQrView(context),
final XFile? file = );
await picker.pickImage(source: ImageSource.gallery);
if (file != null) {
var image = img.decodeNamedImage(
file.path, File(file.path).readAsBytesSync())!;
LuminanceSource source = RGBLuminanceSource(
image.width,
image.height,
image
.getBytes(order: img.ChannelOrder.abgr)
.buffer
.asInt32List());
var bitmap = BinaryBitmap(HybridBinarizer(source));
var reader = QRCodeReader();
try {
var result = reader.decode(bitmap);
if (result.text.startsWith(bind.mainUriPrefixSync())) {
handleUriLink(uriString: result.text);
} else {
showServerSettingFromQr(result.text);
}
} catch (e) {
showToast('No QR code found');
}
}
}),
IconButton(
color: Colors.yellow,
icon: Icon(Icons.flash_on),
iconSize: 32.0,
onPressed: () async {
await controller?.toggleFlash();
}),
IconButton(
color: Colors.white,
icon: Icon(Icons.switch_camera),
iconSize: 32.0,
onPressed: () async {
await controller?.flipCamera();
},
),
],
),
body: _buildQrView(context));
} }
Widget _buildQrView(BuildContext context) { Widget _buildQrView(BuildContext context) {
// For this example we check how width or tall the device is and change the scanArea and overlay accordingly. var scanArea = MediaQuery.of(context).size.width < 400 ||
var scanArea = (MediaQuery.of(context).size.width < 400 || MediaQuery.of(context).size.height < 400
MediaQuery.of(context).size.height < 400)
? 150.0 ? 150.0
: 300.0; : 300.0;
// To ensure the Scanner view is properly sizes after rotation
// we need to listen for Flutter SizeChanged notification and update controller
return QRView( return QRView(
key: qrKey, key: qrKey,
onQRViewCreated: _onQRViewCreated, onQRViewCreated: _onQRViewCreated,
overlay: QrScannerOverlayShape( overlay: QrScannerOverlayShape(
borderColor: Colors.red, borderColor: Colors.red,
borderRadius: 10, borderRadius: 10,
borderLength: 30, borderLength: 30,
borderWidth: 10, borderWidth: 10,
cutOutSize: scanArea), cutOutSize: scanArea,
),
onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p), onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
); );
} }
@@ -116,7 +69,7 @@ class _ScanPageState extends State<ScanPage> {
setState(() { setState(() {
this.controller = controller; this.controller = controller;
}); });
controller.scannedDataStream.listen((scanData) { scanSubscription = controller.scannedDataStream.listen((scanData) {
if (scanData.code != null) { if (scanData.code != null) {
showServerSettingFromQr(scanData.code!); showServerSettingFromQr(scanData.code!);
} }
@@ -129,8 +82,66 @@ class _ScanPageState extends State<ScanPage> {
} }
} }
Future<void> _pickImage() async {
final ImagePicker picker = ImagePicker();
final XFile? file = await picker.pickImage(source: ImageSource.gallery);
if (file != null) {
try {
var image = img.decodeImage(await File(file.path).readAsBytes())!;
LuminanceSource source = RGBLuminanceSource(
image.width,
image.height,
image.getBytes(order: img.ChannelOrder.abgr).buffer.asInt32List(),
);
var bitmap = BinaryBitmap(HybridBinarizer(source));
var reader = QRCodeReader();
var result = reader.decode(bitmap);
if (result.text.startsWith(bind.mainUriPrefixSync())) {
handleUriLink(uriString: result.text);
} else {
showServerSettingFromQr(result.text);
}
} catch (e) {
showToast('No QR code found');
}
}
}
Widget _buildImagePickerButton() {
return IconButton(
color: Colors.white,
icon: Icon(Icons.image_search),
iconSize: 32.0,
onPressed: _pickImage,
);
}
Widget _buildFlashToggleButton() {
return IconButton(
color: Colors.yellow,
icon: Icon(Icons.flash_on),
iconSize: 32.0,
onPressed: () async {
await controller?.toggleFlash();
},
);
}
Widget _buildCameraSwitchButton() {
return IconButton(
color: Colors.white,
icon: Icon(Icons.switch_camera),
iconSize: 32.0,
onPressed: () async {
await controller?.flipCamera();
},
);
}
@override @override
void dispose() { void dispose() {
scanSubscription?.cancel();
controller?.dispose(); controller?.dispose();
super.dispose(); super.dispose();
} }

View File

@@ -66,10 +66,16 @@ class AbModel {
var listInitialized = false; var listInitialized = false;
var _maxPeerOneAb = 0; var _maxPeerOneAb = 0;
late final Peers peersModel;
WeakReference<FFI> parent; WeakReference<FFI> parent;
AbModel(this.parent) { AbModel(this.parent) {
addressbooks.clear(); addressbooks.clear();
peersModel = Peers(
name: PeersModelName.addressBook,
getInitPeers: () => currentAbPeers,
loadEvent: LoadEvent.addressBook);
if (desktopType == DesktopType.main) { if (desktopType == DesktopType.main) {
Timer.periodic(Duration(milliseconds: 500), (timer) async { Timer.periodic(Duration(milliseconds: 500), (timer) async {
if (_timerCounter++ % 6 == 0) { if (_timerCounter++ % 6 == 0) {

View File

@@ -23,7 +23,14 @@ class GroupModel {
bool get emtpy => users.isEmpty && peers.isEmpty; bool get emtpy => users.isEmpty && peers.isEmpty;
GroupModel(this.parent); late final Peers peersModel;
GroupModel(this.parent) {
peersModel = Peers(
name: PeersModelName.group,
getInitPeers: () => peers,
loadEvent: LoadEvent.group);
}
Future<void> pull({force = true, quiet = false}) async { Future<void> pull({force = true, quiet = false}) async {
if (bind.isDisableGroupPanel()) return; if (bind.isDisableGroupPanel()) return;

View File

@@ -469,8 +469,12 @@ class InputModel {
KeyEventResult handleRawKeyEvent(RawKeyEvent e) { KeyEventResult handleRawKeyEvent(RawKeyEvent e) {
if (isViewOnly) return KeyEventResult.handled; if (isViewOnly) return KeyEventResult.handled;
if ((isDesktop || isWebDesktop) && !isInputSourceFlutter) { if (!isInputSourceFlutter) {
return KeyEventResult.handled; if (isDesktop) {
return KeyEventResult.handled;
} else if (isWeb) {
return KeyEventResult.ignored;
}
} }
final key = e.logicalKey; final key = e.logicalKey;
@@ -519,8 +523,12 @@ class InputModel {
KeyEventResult handleKeyEvent(KeyEvent e) { KeyEventResult handleKeyEvent(KeyEvent e) {
if (isViewOnly) return KeyEventResult.handled; if (isViewOnly) return KeyEventResult.handled;
if ((isDesktop || isWebDesktop) && !isInputSourceFlutter) { if (!isInputSourceFlutter) {
return KeyEventResult.handled; if (isDesktop) {
return KeyEventResult.handled;
} else if (isWeb) {
return KeyEventResult.ignored;
}
} }
if (isWindows || isLinux) { if (isWindows || isLinux) {
// Ignore meta keys. Because flutter window will loose focus if meta key is pressed. // Ignore meta keys. Because flutter window will loose focus if meta key is pressed.

View File

@@ -7,12 +7,14 @@ import 'dart:ui' as ui;
import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hbb/common/widgets/peers_view.dart';
import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/ab_model.dart'; import 'package:flutter_hbb/models/ab_model.dart';
import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_hbb/models/chat_model.dart';
import 'package:flutter_hbb/models/cm_file_model.dart'; import 'package:flutter_hbb/models/cm_file_model.dart';
import 'package:flutter_hbb/models/file_model.dart'; import 'package:flutter_hbb/models/file_model.dart';
import 'package:flutter_hbb/models/group_model.dart'; import 'package:flutter_hbb/models/group_model.dart';
import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart'; import 'package:flutter_hbb/models/peer_tab_model.dart';
import 'package:flutter_hbb/models/server_model.dart'; import 'package:flutter_hbb/models/server_model.dart';
import 'package:flutter_hbb/models/user_model.dart'; import 'package:flutter_hbb/models/user_model.dart';
@@ -839,7 +841,7 @@ class FfiModel with ChangeNotifier {
for (final mode in [kKeyMapMode, kKeyLegacyMode]) { for (final mode in [kKeyMapMode, kKeyLegacyMode]) {
if (bind.sessionIsKeyboardModeSupported( if (bind.sessionIsKeyboardModeSupported(
sessionId: sessionId, mode: mode)) { sessionId: sessionId, mode: mode)) {
bind.sessionSetKeyboardMode(sessionId: sessionId, value: mode); await bind.sessionSetKeyboardMode(sessionId: sessionId, value: mode);
break; break;
} }
} }
@@ -2397,6 +2399,9 @@ class FFI {
late final ElevationModel elevationModel; // session late final ElevationModel elevationModel; // session
late final CmFileModel cmFileModel; // cm late final CmFileModel cmFileModel; // cm
late final TextureModel textureModel; //session late final TextureModel textureModel; //session
late final Peers recentPeersModel; // global
late final Peers favoritePeersModel; // global
late final Peers lanPeersModel; // global
FFI(SessionID? sId) { FFI(SessionID? sId) {
sessionId = sId ?? (isDesktop ? Uuid().v4obj() : _constSessionId); sessionId = sId ?? (isDesktop ? Uuid().v4obj() : _constSessionId);
@@ -2417,6 +2422,16 @@ class FFI {
elevationModel = ElevationModel(WeakReference(this)); elevationModel = ElevationModel(WeakReference(this));
cmFileModel = CmFileModel(WeakReference(this)); cmFileModel = CmFileModel(WeakReference(this));
textureModel = TextureModel(WeakReference(this)); textureModel = TextureModel(WeakReference(this));
recentPeersModel = Peers(
name: PeersModelName.recent,
loadEvent: LoadEvent.recent,
getInitPeers: null);
favoritePeersModel = Peers(
name: PeersModelName.favorite,
loadEvent: LoadEvent.favorite,
getInitPeers: null);
lanPeersModel = Peers(
name: PeersModelName.lan, loadEvent: LoadEvent.lan, getInitPeers: null);
} }
/// Mobile reuse FFI /// Mobile reuse FFI

View File

@@ -352,7 +352,11 @@ class RustdeskImpl {
bool sessionIsKeyboardModeSupported( bool sessionIsKeyboardModeSupported(
{required UuidValue sessionId, required String mode, dynamic hint}) { {required UuidValue sessionId, required String mode, dynamic hint}) {
return [kKeyLegacyMode, kKeyMapMode].contains(mode); if (mainGetInputSource(hint: hint) == 'Input source 1') {
return [kKeyMapMode, kKeyTranslateMode].contains(mode);
} else {
return [kKeyLegacyMode, kKeyMapMode].contains(mode);
}
} }
bool sessionIsMultiUiSession({required UuidValue sessionId, dynamic hint}) { bool sessionIsMultiUiSession({required UuidValue sessionId, dynamic hint}) {
@@ -429,7 +433,7 @@ class RustdeskImpl {
void sessionEnterOrLeave( void sessionEnterOrLeave(
{required UuidValue sessionId, required bool enter, dynamic hint}) { {required UuidValue sessionId, required bool enter, dynamic hint}) {
throw UnimplementedError(); js.context.callMethod('setByName', ['enter_or_leave', enter]);
} }
Future<void> sessionInputKey( Future<void> sessionInputKey(
@@ -472,7 +476,7 @@ class RustdeskImpl {
required String name, required String name,
required String value, required String value,
dynamic hint}) { dynamic hint}) {
return Future(() => js.context.callMethod('SetByName', [ return Future(() => js.context.callMethod('setByName', [
'option:session', 'option:session',
jsonEncode({'name': name, 'value': value}) jsonEncode({'name': name, 'value': value})
])); ]));
@@ -604,7 +608,7 @@ class RustdeskImpl {
Future<void> sessionElevateDirect( Future<void> sessionElevateDirect(
{required UuidValue sessionId, dynamic hint}) { {required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError(); return Future(() => js.context.callMethod('setByName', ['elevate_direct']));
} }
Future<void> sessionElevateWithLogon( Future<void> sessionElevateWithLogon(
@@ -614,7 +618,7 @@ class RustdeskImpl {
dynamic hint}) { dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [ return Future(() => js.context.callMethod('setByName', [
'elevate_with_logon', 'elevate_with_logon',
jsonEncode({username, password}) jsonEncode({'username': username, 'password': password})
])); ]));
} }
@@ -846,16 +850,21 @@ class RustdeskImpl {
} }
String mainGetInputSource({dynamic hint}) { String mainGetInputSource({dynamic hint}) {
// // rdev grab mode final inputSource =
// const CONFIG_INPUT_SOURCE_1 = "Input source 1"; js.context.callMethod('getByName', ['option:local', 'input-source']);
// // js grab mode
// export const CONFIG_INPUT_SOURCE_1 = "Input source 1";
// // flutter grab mode // // flutter grab mode
// const CONFIG_INPUT_SOURCE_2 = "Input source 2"; // export const CONFIG_INPUT_SOURCE_2 = "Input source 2";
return 'Input source 2'; return inputSource != '' ? inputSource : 'Input source 1';
} }
Future<void> mainSetInputSource( Future<void> mainSetInputSource(
{required UuidValue sessionId, required String value, dynamic hint}) { {required UuidValue sessionId, required String value, dynamic hint}) {
return Future.value(); return Future(() => js.context.callMethod('setByName', [
'option:local',
jsonEncode({'name': 'input-source', 'value': value})
]));
} }
Future<String> mainGetMyId({dynamic hint}) { Future<String> mainGetMyId({dynamic hint}) {
@@ -1054,7 +1063,7 @@ class RustdeskImpl {
() => js.context.callMethod('getByName', ['option', 'last_remote_id'])); () => js.context.callMethod('getByName', ['option', 'last_remote_id']));
} }
Future<String> mainGetSoftwareUpdateUrl({dynamic hint}) { Future<void> mainGetSoftwareUpdateUrl({dynamic hint}) {
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -1610,6 +1619,7 @@ class RustdeskImpl {
String mainSupportedInputSource({dynamic hint}) { String mainSupportedInputSource({dynamic hint}) {
return jsonEncode([ return jsonEncode([
['Input source 1', 'input_source_1_tip'],
['Input source 2', 'input_source_2_tip'] ['Input source 2', 'input_source_2_tip']
]); ]);
} }

View File

@@ -1,5 +1,7 @@
import 'dart:js' as js; import 'dart:js' as js;
import 'dart:html' as html; import 'dart:html' as html;
// cycle imports, maybe we can improve this
import 'package:flutter_hbb/consts.dart';
final isAndroid_ = false; final isAndroid_ = false;
final isIOS_ = false; final isIOS_ = false;
@@ -13,8 +15,7 @@ final isDesktop_ = false;
String get screenInfo_ => js.context.callMethod('getByName', ['screen_info']); String get screenInfo_ => js.context.callMethod('getByName', ['screen_info']);
final _userAgent = html.window.navigator.userAgent.toLowerCase(); final _localOs = js.context.callMethod('getByName', ['local_os', '']);
final isWebOnWindows_ = _localOs == kPeerPlatformWindows;
final isWebOnWindows_ = _userAgent.contains('win'); final isWebOnLinux_ = _localOs == kPeerPlatformLinux;
final isWebOnLinux_ = _userAgent.contains('linux'); final isWebOnMacOS_ = _localOs == kPeerPlatformMacOS;
final isWebOnMacOS_ = _userAgent.contains('mac');

View File

@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 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 # 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.1+47 version: 1.3.2+51
environment: environment:
sdk: '^3.1.0' sdk: '^3.1.0'

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "rustdesk-portable-packer" name = "rustdesk-portable-packer"
version = "1.3.1" version = "1.3.2"
edition = "2021" edition = "2021"
description = "RustDesk Remote Desktop" description = "RustDesk Remote Desktop"

View File

@@ -1,5 +1,5 @@
pkgname=rustdesk pkgname=rustdesk
pkgver=1.3.1 pkgver=1.3.2
pkgrel=0 pkgrel=0
epoch= epoch=
pkgdesc="" pkgdesc=""

View File

@@ -1,5 +1,5 @@
Name: rustdesk Name: rustdesk
Version: 1.3.1 Version: 1.3.2
Release: 0 Release: 0
Summary: RPM package Summary: RPM package
License: GPL-3.0 License: GPL-3.0

View File

@@ -1,5 +1,5 @@
Name: rustdesk Name: rustdesk
Version: 1.3.1 Version: 1.3.2
Release: 0 Release: 0
Summary: RPM package Summary: RPM package
License: GPL-3.0 License: GPL-3.0

View File

@@ -1,5 +1,5 @@
Name: rustdesk Name: rustdesk
Version: 1.3.1 Version: 1.3.2
Release: 0 Release: 0
Summary: RPM package Summary: RPM package
License: GPL-3.0 License: GPL-3.0

View File

@@ -828,6 +828,15 @@ async fn check_software_update_() -> hbb_common::ResultType<()> {
let response_url = latest_release_response.url().to_string(); let response_url = latest_release_response.url().to_string();
if get_version_number(&latest_release_version) > get_version_number(crate::VERSION) { if get_version_number(&latest_release_version) > get_version_number(crate::VERSION) {
#[cfg(feature = "flutter")]
{
let mut m = HashMap::new();
m.insert("name", "check_software_update_finish");
m.insert("url", &response_url);
if let Ok(data) = serde_json::to_string(&m) {
let _ = crate::flutter::push_global_event(crate::flutter::APP_TYPE_MAIN, data);
}
}
*SOFTWARE_UPDATE_URL.lock().unwrap() = response_url; *SOFTWARE_UPDATE_URL.lock().unwrap() = response_url;
} }
Ok(()) Ok(())

View File

@@ -61,7 +61,6 @@ fn initialize(app_dir: &str, custom_client_config: &str) {
scrap::mediacodec::check_mediacodec(); scrap::mediacodec::check_mediacodec();
crate::common::test_rendezvous_server(); crate::common::test_rendezvous_server();
crate::common::test_nat_type(); crate::common::test_nat_type();
crate::common::check_software_update();
} }
#[cfg(target_os = "ios")] #[cfg(target_os = "ios")]
{ {
@@ -1376,11 +1375,10 @@ pub fn main_get_last_remote_id() -> String {
LocalConfig::get_remote_id() LocalConfig::get_remote_id()
} }
pub fn main_get_software_update_url() -> String { pub fn main_get_software_update_url() {
if get_local_option("enable-check-update".to_string()) != "N" { if get_local_option("enable-check-update".to_string()) != "N" {
crate::common::check_software_update(); crate::common::check_software_update();
} }
crate::common::SOFTWARE_UPDATE_URL.lock().unwrap().clone()
} }
pub fn main_get_home_dir() -> String { pub fn main_get_home_dir() -> String {

View File

@@ -644,7 +644,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Parent directory", "父目录"), ("Parent directory", "父目录"),
("Resume", "继续"), ("Resume", "继续"),
("Invalid file name", "无效文件名"), ("Invalid file name", "无效文件名"),
("one-way-file-transfer-tip", "被控端启用了单文件传输"), ("one-way-file-transfer-tip", "被控端启用了单文件传输"),
("Authentication Required", "需要身份验证"), ("Authentication Required", "需要身份验证"),
("Authenticate", "认证"), ("Authenticate", "认证"),
].iter().cloned().collect(); ].iter().cloned().collect();

View File

@@ -644,8 +644,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Parent directory", "Directorio superior"), ("Parent directory", "Directorio superior"),
("Resume", "Continuar"), ("Resume", "Continuar"),
("Invalid file name", "Nombre de archivo no válido"), ("Invalid file name", "Nombre de archivo no válido"),
("one-way-file-transfer-tip", ""), ("one-way-file-transfer-tip", "La transferencia en un sentido está habilitada en el lado controlado."),
("Authentication Required", ""), ("Authentication Required", "Se requiere autenticación"),
("Authenticate", ""), ("Authenticate", "Autenticar"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@@ -369,7 +369,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop session recording", "Ferma registrazione sessione"), ("Stop session recording", "Ferma registrazione sessione"),
("Enable recording session", "Abilita registrazione sessione"), ("Enable recording session", "Abilita registrazione sessione"),
("Enable LAN discovery", "Abilita rilevamento LAN"), ("Enable LAN discovery", "Abilita rilevamento LAN"),
("Deny LAN discovery", "Nega rilevamento LAN"), ("Deny LAN discovery", "Disabilita rilevamento LAN"),
("Write a message", "Scrivi un messaggio"), ("Write a message", "Scrivi un messaggio"),
("Prompt", "Richiedi"), ("Prompt", "Richiedi"),
("Please wait for confirmation of UAC...", "Attendi la conferma dell'UAC..."), ("Please wait for confirmation of UAC...", "Attendi la conferma dell'UAC..."),
@@ -532,7 +532,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("HSV Color", "Colore HSV"), ("HSV Color", "Colore HSV"),
("Installation Successful!", "Installazione completata"), ("Installation Successful!", "Installazione completata"),
("Installation failed!", "Installazione fallita"), ("Installation failed!", "Installazione fallita"),
("Reverse mouse wheel", "Rotella mouse inversa"), ("Reverse mouse wheel", "Funzione rotellina mouse inversa"),
("{} sessions", "{} sessioni"), ("{} sessions", "{} sessioni"),
("scam_title", "Potresti essere stato TRUFFATO!"), ("scam_title", "Potresti essere stato TRUFFATO!"),
("scam_text1", "Se sei al telefono con qualcuno che NON conosci NON DI TUA FIDUCIA che ti ha chiesto di usare RustDesk e di avviare il servizio, non procedere e riattacca subito."), ("scam_text1", "Se sei al telefono con qualcuno che NON conosci NON DI TUA FIDUCIA che ti ha chiesto di usare RustDesk e di avviare il servizio, non procedere e riattacca subito."),
@@ -632,7 +632,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("About RustDesk", "Info su RustDesk"), ("About RustDesk", "Info su RustDesk"),
("Send clipboard keystrokes", "Invia sequenze tasti appunti"), ("Send clipboard keystrokes", "Invia sequenze tasti appunti"),
("network_error_tip", "Controlla la connessione di rete, quindi seleziona 'Riprova'."), ("network_error_tip", "Controlla la connessione di rete, quindi seleziona 'Riprova'."),
("Unlock with PIN", "Sblocca con PIN"), ("Unlock with PIN", "Abilita sblocco con PIN"),
("Requires at least {} characters", "Richiede almeno {} caratteri"), ("Requires at least {} characters", "Richiede almeno {} caratteri"),
("Wrong PIN", "PIN errato"), ("Wrong PIN", "PIN errato"),
("Set PIN", "Imposta PIN"), ("Set PIN", "Imposta PIN"),
@@ -644,7 +644,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Parent directory", "Cartella principale"), ("Parent directory", "Cartella principale"),
("Resume", "Riprendi"), ("Resume", "Riprendi"),
("Invalid file name", "Nome file non valido"), ("Invalid file name", "Nome file non valido"),
("one-way-file-transfer-tip", "Il trasferimento file unidirezionale è abilitato sul lato controllato."), ("one-way-file-transfer-tip", "Sul lato controllato è abilitato il trasferimento file unidirezionale."),
("Authentication Required", "Richiesta autenticazione"), ("Authentication Required", "Richiesta autenticazione"),
("Authenticate", "Autentica"), ("Authenticate", "Autentica"),
].iter().cloned().collect(); ].iter().cloned().collect();

View File

@@ -644,8 +644,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Parent directory", "Vecākdirektorijs"), ("Parent directory", "Vecākdirektorijs"),
("Resume", "Atsākt"), ("Resume", "Atsākt"),
("Invalid file name", "Nederīgs faila nosaukums"), ("Invalid file name", "Nederīgs faila nosaukums"),
("one-way-file-transfer-tip", ""), ("one-way-file-transfer-tip", "Kontrolējamajā pusē ir iespējota vienvirziena failu pārsūtīšana."),
("Authentication Required", ""), ("Authentication Required", "Nepieciešama autentifikācija"),
("Authenticate", ""), ("Authenticate", "Autentificēt"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@@ -644,8 +644,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Parent directory", "Hoofdmap"), ("Parent directory", "Hoofdmap"),
("Resume", "Hervatten"), ("Resume", "Hervatten"),
("Invalid file name", "Ongeldige bestandsnaam"), ("Invalid file name", "Ongeldige bestandsnaam"),
("one-way-file-transfer-tip", ""), ("one-way-file-transfer-tip", "Eenzijdige bestandsoverdracht is ingeschakeld aan de gecontroleerde kant."),
("Authentication Required", ""), ("Authentication Required", "Verificatie vereist"),
("Authenticate", ""), ("Authenticate", "Verificatie"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@@ -645,7 +645,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resume", "Продолжить"), ("Resume", "Продолжить"),
("Invalid file name", "Неправильное имя файла"), ("Invalid file name", "Неправильное имя файла"),
("one-way-file-transfer-tip", "На управляемой стороне включена односторонняя передача файлов."), ("one-way-file-transfer-tip", "На управляемой стороне включена односторонняя передача файлов."),
("Authentication Required", ""), ("Authentication Required", "Требуется аутентификация"),
("Authenticate", ""), ("Authenticate", "Аутентификация"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@@ -644,8 +644,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Parent directory", "父目錄"), ("Parent directory", "父目錄"),
("Resume", "繼續"), ("Resume", "繼續"),
("Invalid file name", "無效文件名"), ("Invalid file name", "無效文件名"),
("one-way-file-transfer-tip", ""), ("one-way-file-transfer-tip", "被控端啟用了單向文件傳輸"),
("Authentication Required", ""), ("Authentication Required", "需要身分驗證"),
("Authenticate", ""), ("Authenticate", "認證"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@@ -12,8 +12,6 @@ fn main() {
} }
common::test_rendezvous_server(); common::test_rendezvous_server();
common::test_nat_type(); common::test_nat_type();
#[cfg(target_os = "android")]
crate::common::check_software_update();
common::global_clean(); common::global_clean();
} }