Compare commits
20 Commits
4
.github/workflows/flutter-build.yml
vendored
4
.github/workflows/flutter-build.yml
vendored
@@ -33,8 +33,8 @@ env:
|
||||
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||
# vcpkg version: 2024.07.12
|
||||
VCPKG_COMMIT_ID: "1de2026f28ead93ff1773e6e680387643e914ea1"
|
||||
VERSION: "1.3.1"
|
||||
NDK_VERSION: "r27"
|
||||
VERSION: "1.3.2"
|
||||
NDK_VERSION: "r27b"
|
||||
#signing keys env variable checks
|
||||
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
|
||||
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
|
||||
|
||||
2
.github/workflows/playground.yml
vendored
2
.github/workflows/playground.yml
vendored
@@ -18,7 +18,7 @@ env:
|
||||
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||
# vcpkg version: 2024.06.15
|
||||
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
|
||||
VERSION: "1.3.1"
|
||||
VERSION: "1.3.2"
|
||||
NDK_VERSION: "r26d"
|
||||
#signing keys env variable checks
|
||||
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
|
||||
|
||||
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -5480,7 +5480,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustdesk"
|
||||
version = "1.3.1"
|
||||
version = "1.3.2"
|
||||
dependencies = [
|
||||
"android-wakelock",
|
||||
"android_logger",
|
||||
@@ -5580,7 +5580,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustdesk-portable-packer"
|
||||
version = "1.3.1"
|
||||
version = "1.3.2"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"dirs 5.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustdesk"
|
||||
version = "1.3.1"
|
||||
version = "1.3.2"
|
||||
authors = ["rustdesk <info@rustdesk.com>"]
|
||||
edition = "2021"
|
||||
build= "build.rs"
|
||||
|
||||
@@ -18,7 +18,7 @@ AppDir:
|
||||
id: rustdesk
|
||||
name: rustdesk
|
||||
icon: rustdesk
|
||||
version: 1.3.1
|
||||
version: 1.3.2
|
||||
exec: usr/lib/rustdesk/rustdesk
|
||||
exec_args: $@
|
||||
apt:
|
||||
|
||||
@@ -18,7 +18,7 @@ AppDir:
|
||||
id: rustdesk
|
||||
name: rustdesk
|
||||
icon: rustdesk
|
||||
version: 1.3.1
|
||||
version: 1.3.2
|
||||
exec: usr/lib/rustdesk/rustdesk
|
||||
exec_args: $@
|
||||
apt:
|
||||
|
||||
@@ -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>
|
||||
</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)
|
||||
|
||||
[](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) |
|
||||
[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/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: 屏幕截取
|
||||
- **[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/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/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代码
|
||||
|
||||
## 截图
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -302,6 +302,7 @@ prebuild)
|
||||
|
||||
sed \
|
||||
-i \
|
||||
-e 's/extended_text: .*/extended_text: 11.1.0/' \
|
||||
-e 's/uni_links_desktop/#uni_links_desktop/g' \
|
||||
flutter/pubspec.yaml
|
||||
|
||||
|
||||
@@ -241,14 +241,15 @@ class _AddressBookState extends State<AddressBook> {
|
||||
bind.setLocalFlutterOption(k: kOptionCurrentAbName, v: value);
|
||||
}
|
||||
},
|
||||
customButton: Obx(()=>Container(
|
||||
height: stateGlobal.isPortrait.isFalse ? 48 : 40,
|
||||
child: Row(children: [
|
||||
Expanded(
|
||||
child: buildItem(gFFI.abModel.currentName.value, button: true)),
|
||||
Icon(Icons.arrow_drop_down),
|
||||
]),
|
||||
)),
|
||||
customButton: Obx(() => Container(
|
||||
height: stateGlobal.isPortrait.isFalse ? 48 : 40,
|
||||
child: Row(children: [
|
||||
Expanded(
|
||||
child:
|
||||
buildItem(gFFI.abModel.currentName.value, button: true)),
|
||||
Icon(Icons.arrow_drop_down),
|
||||
]),
|
||||
)),
|
||||
underline: Container(
|
||||
height: 0.7,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
@@ -358,7 +359,6 @@ class _AddressBookState extends State<AddressBook> {
|
||||
alignment: Alignment.topLeft,
|
||||
child: AddressBookPeersView(
|
||||
menuPadding: widget.menuPadding,
|
||||
getInitPeers: () => gFFI.abModel.currentAbPeers,
|
||||
)),
|
||||
);
|
||||
}
|
||||
@@ -509,19 +509,19 @@ class _AddressBookState extends State<AddressBook> {
|
||||
|
||||
row({required Widget lable, required Widget input}) {
|
||||
makeChild(bool isPortrait) => Row(
|
||||
children: [
|
||||
!isPortrait
|
||||
? ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 100),
|
||||
child: lable.marginOnly(right: 10))
|
||||
: SizedBox.shrink(),
|
||||
Expanded(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 200),
|
||||
child: input),
|
||||
),
|
||||
],
|
||||
).marginOnly(bottom: !isPortrait ? 8 : 0);
|
||||
children: [
|
||||
!isPortrait
|
||||
? ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 100),
|
||||
child: lable.marginOnly(right: 10))
|
||||
: SizedBox.shrink(),
|
||||
Expanded(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 200),
|
||||
child: input),
|
||||
),
|
||||
],
|
||||
).marginOnly(bottom: !isPortrait ? 8 : 0);
|
||||
return Obx(() => makeChild(stateGlobal.isPortrait.isTrue));
|
||||
}
|
||||
|
||||
@@ -546,23 +546,28 @@ class _AddressBookState extends State<AddressBook> {
|
||||
],
|
||||
),
|
||||
input: Obx(() => TextField(
|
||||
controller: idController,
|
||||
inputFormatters: [IDTextInputFormatter()],
|
||||
decoration: InputDecoration(
|
||||
labelText: stateGlobal.isPortrait.isFalse ? null : translate('ID'),
|
||||
errorText: errorMsg,
|
||||
errorMaxLines: 5),
|
||||
))),
|
||||
controller: idController,
|
||||
inputFormatters: [IDTextInputFormatter()],
|
||||
decoration: InputDecoration(
|
||||
labelText: stateGlobal.isPortrait.isFalse
|
||||
? null
|
||||
: translate('ID'),
|
||||
errorText: errorMsg,
|
||||
errorMaxLines: 5),
|
||||
))),
|
||||
row(
|
||||
lable: Text(
|
||||
translate('Alias'),
|
||||
style: style,
|
||||
),
|
||||
input: Obx(() => TextField(
|
||||
controller: aliasController,
|
||||
decoration: InputDecoration(
|
||||
labelText: stateGlobal.isPortrait.isFalse ? null : translate('Alias'),
|
||||
),)),
|
||||
controller: aliasController,
|
||||
decoration: InputDecoration(
|
||||
labelText: stateGlobal.isPortrait.isFalse
|
||||
? null
|
||||
: translate('Alias'),
|
||||
),
|
||||
)),
|
||||
),
|
||||
if (isCurrentAbShared)
|
||||
row(
|
||||
@@ -570,25 +575,29 @@ class _AddressBookState extends State<AddressBook> {
|
||||
translate('Password'),
|
||||
style: style,
|
||||
),
|
||||
input: Obx(() => TextField(
|
||||
controller: passwordController,
|
||||
obscureText: !passwordVisible,
|
||||
decoration: InputDecoration(
|
||||
labelText: stateGlobal.isPortrait.isFalse ? null : translate('Password'),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
passwordVisible
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
color: MyTheme.lightTheme.primaryColor),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
passwordVisible = !passwordVisible;
|
||||
});
|
||||
},
|
||||
input: Obx(
|
||||
() => TextField(
|
||||
controller: passwordController,
|
||||
obscureText: !passwordVisible,
|
||||
decoration: InputDecoration(
|
||||
labelText: stateGlobal.isPortrait.isFalse
|
||||
? null
|
||||
: translate('Password'),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
passwordVisible
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
color: MyTheme.lightTheme.primaryColor),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
passwordVisible = !passwordVisible;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),)),
|
||||
)),
|
||||
if (gFFI.abModel.currentAbTags.isNotEmpty)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
|
||||
@@ -83,8 +83,8 @@ class _MyGroupState extends State<MyGroup> {
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: MyGroupPeerView(
|
||||
menuPadding: widget.menuPadding,
|
||||
getInitPeers: () => gFFI.groupModel.peers)),
|
||||
menuPadding: widget.menuPadding,
|
||||
)),
|
||||
)
|
||||
],
|
||||
);
|
||||
@@ -115,8 +115,8 @@ class _MyGroupState extends State<MyGroup> {
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: MyGroupPeerView(
|
||||
menuPadding: widget.menuPadding,
|
||||
getInitPeers: () => gFFI.groupModel.peers)),
|
||||
menuPadding: widget.menuPadding,
|
||||
)),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
@@ -595,8 +595,7 @@ class QualityMonitor extends StatelessWidget {
|
||||
"${qualityMonitorModel.data.targetBitrate ?? '-'}kb"),
|
||||
_row(
|
||||
"Codec", qualityMonitorModel.data.codecFormat ?? '-'),
|
||||
if (!isWeb)
|
||||
_row("Chroma", qualityMonitorModel.data.chroma ?? '-'),
|
||||
_row("Chroma", qualityMonitorModel.data.chroma ?? '-'),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
@@ -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/peer_tab_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@@ -42,6 +43,14 @@ class LoadEvent {
|
||||
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
|
||||
final peerSearchText = "".obs;
|
||||
|
||||
@@ -128,8 +137,9 @@ class _PeersViewState extends State<_PeersView>
|
||||
//
|
||||
// Although `onWindowRestore()` is called after `onWindowBlur()` in my test,
|
||||
// we need the following comparison to ensure that `_isActive` is true in the end.
|
||||
if (isWindows && DateTime.now().difference(_lastWindowRestoreTime) <
|
||||
const Duration(milliseconds: 300)) {
|
||||
if (isWindows &&
|
||||
DateTime.now().difference(_lastWindowRestoreTime) <
|
||||
const Duration(milliseconds: 300)) {
|
||||
return;
|
||||
}
|
||||
_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.
|
||||
// Continious rebuilds of `ChangeNotifierProvider` will cause memory leak.
|
||||
// Simple demo can reproduce this issue.
|
||||
return ChangeNotifierProvider<Peers>(
|
||||
create: (context) => widget.peers,
|
||||
return ChangeNotifierProvider<Peers>.value(
|
||||
value: widget.peers,
|
||||
child: Consumer<Peers>(builder: (context, peers, child) {
|
||||
if (peers.peers.isEmpty) {
|
||||
gFFI.peerTabModel.setCurrentTabCachedPeers([]);
|
||||
@@ -403,28 +413,39 @@ class _PeersViewState extends State<_PeersView>
|
||||
}
|
||||
|
||||
abstract class BasePeersView extends StatelessWidget {
|
||||
final String name;
|
||||
final String loadEvent;
|
||||
final PeerTabIndex peerTabIndex;
|
||||
final PeerFilter? peerFilter;
|
||||
final PeerCardBuilder peerCardBuilder;
|
||||
final GetInitPeers? getInitPeers;
|
||||
|
||||
const BasePeersView({
|
||||
Key? key,
|
||||
required this.name,
|
||||
required this.loadEvent,
|
||||
required this.peerTabIndex,
|
||||
this.peerFilter,
|
||||
required this.peerCardBuilder,
|
||||
required this.getInitPeers,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
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(
|
||||
peers:
|
||||
Peers(name: name, loadEvent: loadEvent, getInitPeers: getInitPeers),
|
||||
peerFilter: peerFilter,
|
||||
peerCardBuilder: peerCardBuilder);
|
||||
peers: peers, peerFilter: peerFilter, peerCardBuilder: peerCardBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,13 +454,11 @@ class RecentPeersView extends BasePeersView {
|
||||
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
|
||||
: super(
|
||||
key: key,
|
||||
name: 'recent peer',
|
||||
loadEvent: LoadEvent.recent,
|
||||
peerTabIndex: PeerTabIndex.recent,
|
||||
peerCardBuilder: (Peer peer) => RecentPeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
),
|
||||
getInitPeers: null,
|
||||
);
|
||||
|
||||
@override
|
||||
@@ -455,13 +474,11 @@ class FavoritePeersView extends BasePeersView {
|
||||
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
|
||||
: super(
|
||||
key: key,
|
||||
name: 'favorite peer',
|
||||
loadEvent: LoadEvent.favorite,
|
||||
peerTabIndex: PeerTabIndex.fav,
|
||||
peerCardBuilder: (Peer peer) => FavoritePeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
),
|
||||
getInitPeers: null,
|
||||
);
|
||||
|
||||
@override
|
||||
@@ -477,13 +494,11 @@ class DiscoveredPeersView extends BasePeersView {
|
||||
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
|
||||
: super(
|
||||
key: key,
|
||||
name: 'discovered peer',
|
||||
loadEvent: LoadEvent.lan,
|
||||
peerTabIndex: PeerTabIndex.lan,
|
||||
peerCardBuilder: (Peer peer) => DiscoveredPeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
),
|
||||
getInitPeers: null,
|
||||
);
|
||||
|
||||
@override
|
||||
@@ -496,21 +511,16 @@ class DiscoveredPeersView extends BasePeersView {
|
||||
|
||||
class AddressBookPeersView extends BasePeersView {
|
||||
AddressBookPeersView(
|
||||
{Key? key,
|
||||
EdgeInsets? menuPadding,
|
||||
ScrollController? scrollController,
|
||||
required GetInitPeers getInitPeers})
|
||||
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
|
||||
: super(
|
||||
key: key,
|
||||
name: 'address book peer',
|
||||
loadEvent: LoadEvent.addressBook,
|
||||
peerTabIndex: PeerTabIndex.ab,
|
||||
peerFilter: (Peer peer) =>
|
||||
_hitTag(gFFI.abModel.selectedTags, peer.tags),
|
||||
peerCardBuilder: (Peer peer) => AddressBookPeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
),
|
||||
getInitPeers: getInitPeers,
|
||||
);
|
||||
|
||||
static bool _hitTag(List<dynamic> selectedTags, List<dynamic> idents) {
|
||||
@@ -537,20 +547,15 @@ class AddressBookPeersView extends BasePeersView {
|
||||
|
||||
class MyGroupPeerView extends BasePeersView {
|
||||
MyGroupPeerView(
|
||||
{Key? key,
|
||||
EdgeInsets? menuPadding,
|
||||
ScrollController? scrollController,
|
||||
required GetInitPeers getInitPeers})
|
||||
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
|
||||
: super(
|
||||
key: key,
|
||||
name: 'group peer',
|
||||
loadEvent: LoadEvent.group,
|
||||
peerTabIndex: PeerTabIndex.group,
|
||||
peerFilter: filter,
|
||||
peerCardBuilder: (Peer peer) => MyGroupPeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
),
|
||||
getInitPeers: getInitPeers,
|
||||
);
|
||||
|
||||
static bool filter(Peer peer) {
|
||||
|
||||
@@ -570,3 +570,5 @@ enum WindowsTarget {
|
||||
extension WindowsTargetExt on int {
|
||||
WindowsTarget get windowsVersion => getWindowsTarget(this);
|
||||
}
|
||||
|
||||
const kCheckSoftwareUpdateFinish = 'check_software_update_finish';
|
||||
@@ -664,9 +664,17 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
void initState() {
|
||||
super.initState();
|
||||
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 {
|
||||
updateUrl = await bind.mainGetSoftwareUpdateUrl();
|
||||
if (updateUrl.isNotEmpty) setState(() {});
|
||||
bind.mainGetSoftwareUpdateUrl();
|
||||
});
|
||||
}
|
||||
_updateTimer = periodic_immediate(const Duration(seconds: 1), () async {
|
||||
@@ -824,6 +832,10 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
_uniLinksSubscription?.cancel();
|
||||
Get.delete<RxBool>(tag: 'stop-service');
|
||||
_updateTimer?.cancel();
|
||||
if (!bind.isCustomClient()) {
|
||||
platformFFI.unregisterEventHandler(
|
||||
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -70,9 +70,17 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
||||
}
|
||||
if (isAndroid) {
|
||||
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 {
|
||||
_updateUrl = await bind.mainGetSoftwareUpdateUrl();
|
||||
if (_updateUrl.isNotEmpty) setState(() {});
|
||||
bind.mainGetSoftwareUpdateUrl();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -353,6 +361,10 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
||||
if (Get.isRegistered<IDTextEditingController>()) {
|
||||
Get.delete<IDTextEditingController>();
|
||||
}
|
||||
if (!bind.isCustomClient()) {
|
||||
platformFFI.unregisterEventHandler(
|
||||
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,95 +19,48 @@ class ScanPage extends StatefulWidget {
|
||||
class _ScanPageState extends State<ScanPage> {
|
||||
QRViewController? controller;
|
||||
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
|
||||
void reassemble() {
|
||||
super.reassemble();
|
||||
if (isAndroid) {
|
||||
if (isAndroid && controller != null) {
|
||||
controller!.pauseCamera();
|
||||
} else if (controller != null) {
|
||||
controller!.resumeCamera();
|
||||
}
|
||||
controller!.resumeCamera();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Scan QR'),
|
||||
actions: [
|
||||
IconButton(
|
||||
color: Colors.white,
|
||||
icon: Icon(Icons.image_search),
|
||||
iconSize: 32.0,
|
||||
onPressed: () async {
|
||||
final ImagePicker picker = ImagePicker();
|
||||
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));
|
||||
appBar: AppBar(
|
||||
title: const Text('Scan QR'),
|
||||
actions: [
|
||||
_buildImagePickerButton(),
|
||||
_buildFlashToggleButton(),
|
||||
_buildCameraSwitchButton(),
|
||||
],
|
||||
),
|
||||
body: _buildQrView(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 ||
|
||||
MediaQuery.of(context).size.height < 400)
|
||||
var scanArea = MediaQuery.of(context).size.width < 400 ||
|
||||
MediaQuery.of(context).size.height < 400
|
||||
? 150.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(
|
||||
key: qrKey,
|
||||
onQRViewCreated: _onQRViewCreated,
|
||||
overlay: QrScannerOverlayShape(
|
||||
borderColor: Colors.red,
|
||||
borderRadius: 10,
|
||||
borderLength: 30,
|
||||
borderWidth: 10,
|
||||
cutOutSize: scanArea),
|
||||
borderColor: Colors.red,
|
||||
borderRadius: 10,
|
||||
borderLength: 30,
|
||||
borderWidth: 10,
|
||||
cutOutSize: scanArea,
|
||||
),
|
||||
onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
|
||||
);
|
||||
}
|
||||
@@ -116,7 +69,7 @@ class _ScanPageState extends State<ScanPage> {
|
||||
setState(() {
|
||||
this.controller = controller;
|
||||
});
|
||||
controller.scannedDataStream.listen((scanData) {
|
||||
scanSubscription = controller.scannedDataStream.listen((scanData) {
|
||||
if (scanData.code != null) {
|
||||
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
|
||||
void dispose() {
|
||||
scanSubscription?.cancel();
|
||||
controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -66,10 +66,16 @@ class AbModel {
|
||||
var listInitialized = false;
|
||||
var _maxPeerOneAb = 0;
|
||||
|
||||
late final Peers peersModel;
|
||||
|
||||
WeakReference<FFI> parent;
|
||||
|
||||
AbModel(this.parent) {
|
||||
addressbooks.clear();
|
||||
peersModel = Peers(
|
||||
name: PeersModelName.addressBook,
|
||||
getInitPeers: () => currentAbPeers,
|
||||
loadEvent: LoadEvent.addressBook);
|
||||
if (desktopType == DesktopType.main) {
|
||||
Timer.periodic(Duration(milliseconds: 500), (timer) async {
|
||||
if (_timerCounter++ % 6 == 0) {
|
||||
|
||||
@@ -23,7 +23,14 @@ class GroupModel {
|
||||
|
||||
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 {
|
||||
if (bind.isDisableGroupPanel()) return;
|
||||
|
||||
@@ -469,8 +469,12 @@ class InputModel {
|
||||
|
||||
KeyEventResult handleRawKeyEvent(RawKeyEvent e) {
|
||||
if (isViewOnly) return KeyEventResult.handled;
|
||||
if ((isDesktop || isWebDesktop) && !isInputSourceFlutter) {
|
||||
return KeyEventResult.handled;
|
||||
if (!isInputSourceFlutter) {
|
||||
if (isDesktop) {
|
||||
return KeyEventResult.handled;
|
||||
} else if (isWeb) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
}
|
||||
|
||||
final key = e.logicalKey;
|
||||
@@ -519,8 +523,12 @@ class InputModel {
|
||||
|
||||
KeyEventResult handleKeyEvent(KeyEvent e) {
|
||||
if (isViewOnly) return KeyEventResult.handled;
|
||||
if ((isDesktop || isWebDesktop) && !isInputSourceFlutter) {
|
||||
return KeyEventResult.handled;
|
||||
if (!isInputSourceFlutter) {
|
||||
if (isDesktop) {
|
||||
return KeyEventResult.handled;
|
||||
} else if (isWeb) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
}
|
||||
if (isWindows || isLinux) {
|
||||
// Ignore meta keys. Because flutter window will loose focus if meta key is pressed.
|
||||
|
||||
@@ -7,12 +7,14 @@ import 'dart:ui' as ui;
|
||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||
import 'package:flutter/material.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/models/ab_model.dart';
|
||||
import 'package:flutter_hbb/models/chat_model.dart';
|
||||
import 'package:flutter_hbb/models/cm_file_model.dart';
|
||||
import 'package:flutter_hbb/models/file_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/server_model.dart';
|
||||
import 'package:flutter_hbb/models/user_model.dart';
|
||||
@@ -839,7 +841,7 @@ class FfiModel with ChangeNotifier {
|
||||
for (final mode in [kKeyMapMode, kKeyLegacyMode]) {
|
||||
if (bind.sessionIsKeyboardModeSupported(
|
||||
sessionId: sessionId, mode: mode)) {
|
||||
bind.sessionSetKeyboardMode(sessionId: sessionId, value: mode);
|
||||
await bind.sessionSetKeyboardMode(sessionId: sessionId, value: mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2397,6 +2399,9 @@ class FFI {
|
||||
late final ElevationModel elevationModel; // session
|
||||
late final CmFileModel cmFileModel; // cm
|
||||
late final TextureModel textureModel; //session
|
||||
late final Peers recentPeersModel; // global
|
||||
late final Peers favoritePeersModel; // global
|
||||
late final Peers lanPeersModel; // global
|
||||
|
||||
FFI(SessionID? sId) {
|
||||
sessionId = sId ?? (isDesktop ? Uuid().v4obj() : _constSessionId);
|
||||
@@ -2417,6 +2422,16 @@ class FFI {
|
||||
elevationModel = ElevationModel(WeakReference(this));
|
||||
cmFileModel = CmFileModel(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
|
||||
|
||||
@@ -352,7 +352,11 @@ class RustdeskImpl {
|
||||
|
||||
bool sessionIsKeyboardModeSupported(
|
||||
{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}) {
|
||||
@@ -429,7 +433,7 @@ class RustdeskImpl {
|
||||
|
||||
void sessionEnterOrLeave(
|
||||
{required UuidValue sessionId, required bool enter, dynamic hint}) {
|
||||
throw UnimplementedError();
|
||||
js.context.callMethod('setByName', ['enter_or_leave', enter]);
|
||||
}
|
||||
|
||||
Future<void> sessionInputKey(
|
||||
@@ -472,7 +476,7 @@ class RustdeskImpl {
|
||||
required String name,
|
||||
required String value,
|
||||
dynamic hint}) {
|
||||
return Future(() => js.context.callMethod('SetByName', [
|
||||
return Future(() => js.context.callMethod('setByName', [
|
||||
'option:session',
|
||||
jsonEncode({'name': name, 'value': value})
|
||||
]));
|
||||
@@ -604,7 +608,7 @@ class RustdeskImpl {
|
||||
|
||||
Future<void> sessionElevateDirect(
|
||||
{required UuidValue sessionId, dynamic hint}) {
|
||||
throw UnimplementedError();
|
||||
return Future(() => js.context.callMethod('setByName', ['elevate_direct']));
|
||||
}
|
||||
|
||||
Future<void> sessionElevateWithLogon(
|
||||
@@ -614,7 +618,7 @@ class RustdeskImpl {
|
||||
dynamic hint}) {
|
||||
return Future(() => js.context.callMethod('setByName', [
|
||||
'elevate_with_logon',
|
||||
jsonEncode({username, password})
|
||||
jsonEncode({'username': username, 'password': password})
|
||||
]));
|
||||
}
|
||||
|
||||
@@ -846,16 +850,21 @@ class RustdeskImpl {
|
||||
}
|
||||
|
||||
String mainGetInputSource({dynamic hint}) {
|
||||
// // rdev grab mode
|
||||
// const CONFIG_INPUT_SOURCE_1 = "Input source 1";
|
||||
final inputSource =
|
||||
js.context.callMethod('getByName', ['option:local', 'input-source']);
|
||||
// // js grab mode
|
||||
// export const CONFIG_INPUT_SOURCE_1 = "Input source 1";
|
||||
// // flutter grab mode
|
||||
// const CONFIG_INPUT_SOURCE_2 = "Input source 2";
|
||||
return 'Input source 2';
|
||||
// export const CONFIG_INPUT_SOURCE_2 = "Input source 2";
|
||||
return inputSource != '' ? inputSource : 'Input source 1';
|
||||
}
|
||||
|
||||
Future<void> mainSetInputSource(
|
||||
{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}) {
|
||||
@@ -1054,7 +1063,7 @@ class RustdeskImpl {
|
||||
() => js.context.callMethod('getByName', ['option', 'last_remote_id']));
|
||||
}
|
||||
|
||||
Future<String> mainGetSoftwareUpdateUrl({dynamic hint}) {
|
||||
Future<void> mainGetSoftwareUpdateUrl({dynamic hint}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@@ -1610,6 +1619,7 @@ class RustdeskImpl {
|
||||
|
||||
String mainSupportedInputSource({dynamic hint}) {
|
||||
return jsonEncode([
|
||||
['Input source 1', 'input_source_1_tip'],
|
||||
['Input source 2', 'input_source_2_tip']
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'dart:js' as js;
|
||||
import 'dart:html' as html;
|
||||
// cycle imports, maybe we can improve this
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
|
||||
final isAndroid_ = false;
|
||||
final isIOS_ = false;
|
||||
@@ -13,8 +15,7 @@ final isDesktop_ = false;
|
||||
|
||||
String get screenInfo_ => js.context.callMethod('getByName', ['screen_info']);
|
||||
|
||||
final _userAgent = html.window.navigator.userAgent.toLowerCase();
|
||||
|
||||
final isWebOnWindows_ = _userAgent.contains('win');
|
||||
final isWebOnLinux_ = _userAgent.contains('linux');
|
||||
final isWebOnMacOS_ = _userAgent.contains('mac');
|
||||
final _localOs = js.context.callMethod('getByName', ['local_os', '']);
|
||||
final isWebOnWindows_ = _localOs == kPeerPlatformWindows;
|
||||
final isWebOnLinux_ = _localOs == kPeerPlatformLinux;
|
||||
final isWebOnMacOS_ = _localOs == kPeerPlatformMacOS;
|
||||
|
||||
@@ -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.1+47
|
||||
version: 1.3.2+51
|
||||
|
||||
environment:
|
||||
sdk: '^3.1.0'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustdesk-portable-packer"
|
||||
version = "1.3.1"
|
||||
version = "1.3.2"
|
||||
edition = "2021"
|
||||
description = "RustDesk Remote Desktop"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pkgname=rustdesk
|
||||
pkgver=1.3.1
|
||||
pkgver=1.3.2
|
||||
pkgrel=0
|
||||
epoch=
|
||||
pkgdesc=""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: rustdesk
|
||||
Version: 1.3.1
|
||||
Version: 1.3.2
|
||||
Release: 0
|
||||
Summary: RPM package
|
||||
License: GPL-3.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: rustdesk
|
||||
Version: 1.3.1
|
||||
Version: 1.3.2
|
||||
Release: 0
|
||||
Summary: RPM package
|
||||
License: GPL-3.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: rustdesk
|
||||
Version: 1.3.1
|
||||
Version: 1.3.2
|
||||
Release: 0
|
||||
Summary: RPM package
|
||||
License: GPL-3.0
|
||||
|
||||
@@ -828,7 +828,16 @@ async fn check_software_update_() -> hbb_common::ResultType<()> {
|
||||
let response_url = latest_release_response.url().to_string();
|
||||
|
||||
if get_version_number(&latest_release_version) > get_version_number(crate::VERSION) {
|
||||
*SOFTWARE_UPDATE_URL.lock().unwrap() = response_url;
|
||||
#[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;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -61,7 +61,6 @@ fn initialize(app_dir: &str, custom_client_config: &str) {
|
||||
scrap::mediacodec::check_mediacodec();
|
||||
crate::common::test_rendezvous_server();
|
||||
crate::common::test_nat_type();
|
||||
crate::common::check_software_update();
|
||||
}
|
||||
#[cfg(target_os = "ios")]
|
||||
{
|
||||
@@ -1376,11 +1375,10 @@ pub fn main_get_last_remote_id() -> String {
|
||||
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" {
|
||||
crate::common::check_software_update();
|
||||
}
|
||||
crate::common::SOFTWARE_UPDATE_URL.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn main_get_home_dir() -> String {
|
||||
|
||||
@@ -644,7 +644,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Parent directory", "父目录"),
|
||||
("Resume", "继续"),
|
||||
("Invalid file name", "无效文件名"),
|
||||
("one-way-file-transfer-tip", "被控端启用了单项文件传输"),
|
||||
("one-way-file-transfer-tip", "被控端启用了单向文件传输"),
|
||||
("Authentication Required", "需要身份验证"),
|
||||
("Authenticate", "认证"),
|
||||
].iter().cloned().collect();
|
||||
|
||||
@@ -644,8 +644,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Parent directory", "Directorio superior"),
|
||||
("Resume", "Continuar"),
|
||||
("Invalid file name", "Nombre de archivo no válido"),
|
||||
("one-way-file-transfer-tip", ""),
|
||||
("Authentication Required", ""),
|
||||
("Authenticate", ""),
|
||||
("one-way-file-transfer-tip", "La transferencia en un sentido está habilitada en el lado controlado."),
|
||||
("Authentication Required", "Se requiere autenticación"),
|
||||
("Authenticate", "Autenticar"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Stop session recording", "Ferma registrazione sessione"),
|
||||
("Enable recording session", "Abilita registrazione sessione"),
|
||||
("Enable LAN discovery", "Abilita rilevamento LAN"),
|
||||
("Deny LAN discovery", "Nega rilevamento LAN"),
|
||||
("Deny LAN discovery", "Disabilita rilevamento LAN"),
|
||||
("Write a message", "Scrivi un messaggio"),
|
||||
("Prompt", "Richiedi"),
|
||||
("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"),
|
||||
("Installation Successful!", "Installazione completata"),
|
||||
("Installation failed!", "Installazione fallita"),
|
||||
("Reverse mouse wheel", "Rotella mouse inversa"),
|
||||
("Reverse mouse wheel", "Funzione rotellina mouse inversa"),
|
||||
("{} sessions", "{} sessioni"),
|
||||
("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."),
|
||||
@@ -632,7 +632,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("About RustDesk", "Info su RustDesk"),
|
||||
("Send clipboard keystrokes", "Invia sequenze tasti appunti"),
|
||||
("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"),
|
||||
("Wrong PIN", "PIN errato"),
|
||||
("Set PIN", "Imposta PIN"),
|
||||
@@ -644,7 +644,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Parent directory", "Cartella principale"),
|
||||
("Resume", "Riprendi"),
|
||||
("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"),
|
||||
("Authenticate", "Autentica"),
|
||||
].iter().cloned().collect();
|
||||
|
||||
@@ -644,8 +644,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Parent directory", "Vecākdirektorijs"),
|
||||
("Resume", "Atsākt"),
|
||||
("Invalid file name", "Nederīgs faila nosaukums"),
|
||||
("one-way-file-transfer-tip", ""),
|
||||
("Authentication Required", ""),
|
||||
("Authenticate", ""),
|
||||
("one-way-file-transfer-tip", "Kontrolējamajā pusē ir iespējota vienvirziena failu pārsūtīšana."),
|
||||
("Authentication Required", "Nepieciešama autentifikācija"),
|
||||
("Authenticate", "Autentificēt"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -644,8 +644,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Parent directory", "Hoofdmap"),
|
||||
("Resume", "Hervatten"),
|
||||
("Invalid file name", "Ongeldige bestandsnaam"),
|
||||
("one-way-file-transfer-tip", ""),
|
||||
("Authentication Required", ""),
|
||||
("Authenticate", ""),
|
||||
("one-way-file-transfer-tip", "Eenzijdige bestandsoverdracht is ingeschakeld aan de gecontroleerde kant."),
|
||||
("Authentication Required", "Verificatie vereist"),
|
||||
("Authenticate", "Verificatie"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -645,7 +645,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Resume", "Продолжить"),
|
||||
("Invalid file name", "Неправильное имя файла"),
|
||||
("one-way-file-transfer-tip", "На управляемой стороне включена односторонняя передача файлов."),
|
||||
("Authentication Required", ""),
|
||||
("Authenticate", ""),
|
||||
("Authentication Required", "Требуется аутентификация"),
|
||||
("Authenticate", "Аутентификация"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -644,8 +644,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Parent directory", "父目錄"),
|
||||
("Resume", "繼續"),
|
||||
("Invalid file name", "無效文件名"),
|
||||
("one-way-file-transfer-tip", ""),
|
||||
("Authentication Required", ""),
|
||||
("Authenticate", ""),
|
||||
("one-way-file-transfer-tip", "被控端啟用了單向文件傳輸"),
|
||||
("Authentication Required", "需要身分驗證"),
|
||||
("Authenticate", "認證"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ fn main() {
|
||||
}
|
||||
common::test_rendezvous_server();
|
||||
common::test_nat_type();
|
||||
#[cfg(target_os = "android")]
|
||||
crate::common::check_software_update();
|
||||
common::global_clean();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user