Merge pull request #1181 from Heap-Hop/flutter_desktop_merge_master_mobile

Flutter desktop merge master mobile
This commit is contained in:
RustDesk 2022-08-04 18:17:49 +08:00 committed by GitHub
commit ab6a83e8b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 958 additions and 198 deletions

View File

@ -13,7 +13,7 @@ Juttele meidän kanssa: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](htt
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
Vielä yksi etätyöpöytäohjelmisto, ohjelmoitu Rust-kielellä. Toimii suoraan pakkauksesta, ei tarvitse asetuksia. Hallitset täysin tietojasi, ei tarvitse murehtia turvallisuutta. Voit käyttää meidän rendezvous/relay-palvelinta, [aseta omasi](https://rustdesk.com/server), tai [kirjoita oma rendezvous/relay-palvelin](https://github.com/rustdesk/rustdesk-server-demo).
Vielä yksi etätyöpöytäohjelmisto, ohjelmoitu Rust-kielellä. Toimii suoraan pakkauksesta, ei tarvitse asetusta. Hallitset täysin tietojasi, ei tarvitse murehtia turvallisuutta. Voit käyttää meidän rendezvous/relay-palvelinta, [aseta omasi](https://rustdesk.com/server), tai [kirjoittaa oma rendezvous/relay-palvelin](https://github.com/rustdesk/rustdesk-server-demo).
RustDesk toivottaa avustukset tervetulleiksi kaikilta. Katso lisätietoja [`CONTRIBUTING.md`](CONTRIBUTING.md) avun saamiseksi.
@ -45,9 +45,9 @@ Desktop-versiot käyttävät [sciter](https://sciter.com/) graafisena käyttöli
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static
- Linux/MacOS: vcpkg install libvpx libyuv opus
- aja `cargo run`
- suorita `cargo run`
## Kuinka rakentaa Linuxissa
## Kuinka rakentaa Linux:issa
### Ubuntu 18 (Debian 10)
@ -79,7 +79,7 @@ export VCPKG_ROOT=$HOME/vcpkg
vcpkg/vcpkg install libvpx libyuv opus
```
### Korjaa libvpx (Fedora-linux-versiota varten)
### Korjaa libvpx (Fedora)
```sh
cd vcpkg/buildtrees/libvpx/src
@ -107,7 +107,7 @@ VCPKG_ROOT=$HOME/vcpkg cargo run
### Vaihda Wayland-ympäristö X11 (Xorg)-ympäristöön
RustDesk ei tue Waylandia. Tarkista [tämä](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) asettamaan Xorg oletus GNOME-istuntona.
RustDesk ei tue Waylandia. Tarkista [tämä](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) asettamalla Xorg oletus GNOME-istuntoon.
## Kuinka rakennetaan Dockerin kanssa
@ -119,13 +119,13 @@ cd rustdesk
docker build -t "rustdesk-builder" .
```
Sitten, joka kerta kun sinun on rakennettava sovellus, aja seuraava komento:
Sitten, joka kerta kun sinun on rakennettava sovellus, suorita seuraava komento:
```sh
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
Huomaa, että ensimmäinen rakentaminen saattaa kestää pitempään ennen kuin riippuvuudet on siirretty välimuistiin, seuraavat rakentamiset ovat nopeampia. Lisäksi, jos sinun on määritettävä eri argumentteja rakentamiskomennolle, saatat tehdä sen niin, että komennon lopussa <OPTIONAL-ARGS>`-kohdassa. Esimerkiksi, jos haluat rakentaa optimoidun julkaisuversion, sinun on ajettava komento yllä siten, että sitä seuraa argumentti`--release`. Suoritettava tiedosto on saatavilla järjestelmäsi kohdehakemistossa, ja se voidaan suorittaa seuraavan kera:
Huomaa, että ensimmäinen rakentaminen saattaa kestää pitempään ennen kuin riippuvuudet on siirretty välimuistiin, seuraavat rakentamiset ovat nopeampia. Lisäksi, jos sinun on määritettävä eri väittämiä rakentamiskomennolle, saatat tehdä sen niin, että komennon lopussa <OPTIONAL-ARGS>`-kohdassa. Esimerkiksi, jos haluat rakentaa optimoidun julkaisuversion, sinun on ajettava komento yllä siten, että sitä seuraa väittämä`--release`. Suoritettava tiedosto on saatavilla järjestelmäsi kohdehakemistossa, ja se voidaan suorittaa seuraavan kera:
```sh
target/debug/rustdesk

View File

@ -32,7 +32,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 31
compileSdkVersion 32
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}

View File

@ -364,7 +364,9 @@ late FFI _globalFFI;
FFI get gFFI => _globalFFI;
Future<void> initGlobalFFI() async {
debugPrint("_globalFFI init");
_globalFFI = FFI();
debugPrint("_globalFFI init end");
// after `put`, can also be globally found by Get.find<FFI>();
Get.put(_globalFFI, permanent: true);
// trigger connection status updater

View File

@ -1,4 +1,4 @@
import 'package:dash_chat/dash_chat.dart';
import 'package:dash_chat_2/dash_chat_2.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/chat_model.dart';
@ -7,8 +7,6 @@ import 'package:provider/provider.dart';
import '../../models/model.dart';
import 'home_page.dart';
ChatPage chatPage = ChatPage();
class ChatPage extends StatelessWidget implements PageShape {
@override
final title = translate("Chat");
@ -26,7 +24,7 @@ class ChatPage extends StatelessWidget implements PageShape {
final id = entry.key;
final user = entry.value.chatUser;
return PopupMenuItem<int>(
child: Text("${user.name} ${user.uid}"),
child: Text("${user.firstName} ${user.id}"),
value: id,
);
}).toList();
@ -47,19 +45,24 @@ class ChatPage extends StatelessWidget implements PageShape {
return Stack(
children: [
DashChat(
inputContainerStyle: BoxDecoration(color: Colors.white70),
sendOnEnter: false,
// if true,reload keyboard everytime,need fix
onSend: (chatMsg) {
chatModel.send(chatMsg);
},
user: chatModel.me,
currentUser: chatModel.me,
messages:
chatModel.messages[chatModel.currentID]?.chatMessages ??
[],
// default scrollToBottom has bug https://github.com/fayeed/dash_chat/issues/53
scrollToBottom: false,
scrollController: chatModel.scroller,
messageOptions: MessageOptions(
showOtherUsersAvatar: false,
showTime: true,
messageDecorationBuilder: (_, __, ___) =>
defaultMessageDecoration(
color: MyTheme.accent80,
borderTopLeft: 8,
borderTopRight: 8,
borderBottomRight: 8,
borderBottomLeft: 8,
)),
),
chatModel.currentID == ChatModel.clientModeID
? SizedBox.shrink()
@ -71,7 +74,7 @@ class ChatPage extends StatelessWidget implements PageShape {
color: MyTheme.accent80),
SizedBox(width: 5),
Text(
"${currentUser.name ?? ""} ${currentUser.uid ?? ""}",
"${currentUser.firstName} ${currentUser.id}",
style: TextStyle(color: MyTheme.accent50),
),
],

View File

@ -29,6 +29,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
void initState() {
super.initState();
gFFI.connect(widget.id, isFileTransfer: true);
showLoading(translate('Connecting...'));
gFFI.ffiModel.updateEventListener(widget.id);
Wakelock.enable();
}

View File

@ -12,6 +12,8 @@ abstract class PageShape extends Widget {
final List<Widget> appBarActions = [];
}
final homeKey = GlobalKey<_HomePageState>();
class HomePage extends StatefulWidget {
HomePage({Key? key}) : super(key: key);
@ -23,12 +25,23 @@ class _HomePageState extends State<HomePage> {
var _selectedIndex = 0;
final List<PageShape> _pages = [];
void refreshPages() {
setState(() {
initPages();
});
}
@override
void initState() {
super.initState();
initPages();
}
void initPages() {
_pages.clear();
_pages.add(ConnectionPage());
if (isAndroid) {
_pages.addAll([chatPage, ServerPage()]);
_pages.addAll([ChatPage(), ServerPage()]);
}
_pages.add(SettingsPage());
}

View File

@ -595,6 +595,7 @@ class _RemotePageState extends State<RemotePage> {
child: Stack(children: [
ImagePaint(),
CursorPaint(),
QualityMonitor(),
getHelpTools(),
SizedBox(
width: 0,
@ -662,7 +663,7 @@ class _RemotePageState extends State<RemotePage> {
more.add(PopupMenuItem<String>(
child: Row(
children: ([
Container(width: 100.0, child: Text(translate('OS Password'))),
Text(translate('OS Password')),
TextButton(
style: flatButtonStyle,
onPressed: () {
@ -697,6 +698,13 @@ class _RemotePageState extends State<RemotePage> {
value: 'block-input'));
}
}
if (gFFI.ffiModel.permissions["restart"] != false &&
(pi.platform == "Linux" ||
pi.platform == "Windows" ||
pi.platform == "Mac OS")) {
more.add(PopupMenuItem<String>(
child: Text(translate('Restart Remote Device')), value: 'restart'));
}
() async {
var value = await showMenu(
context: context,
@ -730,6 +738,8 @@ class _RemotePageState extends State<RemotePage> {
}
} else if (value == 'reset_canvas') {
gFFI.cursorModel.reset();
} else if (value == 'restart') {
showRestartRemoteDevice(pi, widget.id);
}
}();
}
@ -952,6 +962,47 @@ class ImagePainter extends CustomPainter {
}
}
class QualityMonitor extends StatelessWidget {
@override
Widget build(BuildContext context) => ChangeNotifierProvider.value(
value: gFFI.qualityMonitorModel,
child: Consumer<QualityMonitorModel>(
builder: (context, qualityMonitorModel, child) => Positioned(
top: 10,
right: 10,
child: qualityMonitorModel.show
? Container(
padding: EdgeInsets.all(8),
color: MyTheme.canvasColor.withAlpha(120),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Speed: ${qualityMonitorModel.data.speed}",
style: TextStyle(color: MyTheme.grayBg),
),
Text(
"FPS: ${qualityMonitorModel.data.fps}",
style: TextStyle(color: MyTheme.grayBg),
),
Text(
"Delay: ${qualityMonitorModel.data.delay} ms",
style: TextStyle(color: MyTheme.grayBg),
),
Text(
"Target Bitrate: ${qualityMonitorModel.data.targetBitrate}kb",
style: TextStyle(color: MyTheme.grayBg),
),
Text(
"Codec: ${qualityMonitorModel.data.codecFormat}",
style: TextStyle(color: MyTheme.grayBg),
),
],
),
)
: SizedBox.shrink())));
}
CheckboxListTile getToggle(
void Function(void Function()) setState, option, name) {
return CheckboxListTile(
@ -960,6 +1011,9 @@ CheckboxListTile getToggle(
setState(() {
gFFI.setByName('toggle_option', option);
});
if (option == "show-quality-monitor") {
gFFI.qualityMonitorModel.checkShowQualityMonitor();
}
},
dense: true,
title: Text(translate(name)));
@ -1062,6 +1116,27 @@ void showOptions() {
}, clickMaskDismiss: true, backDismiss: true);
}
void showRestartRemoteDevice(PeerInfo pi, String id) async {
final res =
await DialogManager.show<bool>((setState, close) => CustomAlertDialog(
title: Row(children: [
Icon(Icons.warning_amber_sharp,
color: Colors.redAccent, size: 28),
SizedBox(width: 10),
Text(translate("Restart Remote Device")),
]),
content: Text(
"${translate('Are you sure you want to restart')} \n${pi.username}@${pi.hostname}($id) ?"),
actions: [
TextButton(
onPressed: () => close(), child: Text(translate("Cancel"))),
ElevatedButton(
onPressed: () => close(true), child: Text(translate("OK"))),
],
));
if (res == true) gFFI.setByName('restart_remote_device');
}
void showSetOSPassword(bool login) {
final controller = TextEditingController();
var password = gFFI.getByName('peer_option', "os-password");

View File

@ -200,7 +200,8 @@ class ServerInfo extends StatelessWidget {
Icon(Icons.warning_amber_sharp,
color: Colors.redAccent, size: 24),
SizedBox(width: 10),
Text(
Expanded(
child: Text(
translate("Service is not running"),
style: TextStyle(
fontFamily: 'WorkSans',
@ -208,7 +209,7 @@ class ServerInfo extends StatelessWidget {
fontSize: 18,
color: MyTheme.accent80,
),
)
))
],
)),
SizedBox(height: 5),
@ -316,30 +317,35 @@ class PermissionRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
SizedBox(
width: 140,
Expanded(
flex: 5,
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: Text(name,
style: TextStyle(fontSize: 16.0, color: MyTheme.accent50))),
SizedBox(
width: 50,
style:
TextStyle(fontSize: 16.0, color: MyTheme.accent50)))),
Expanded(
flex: 2,
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(isOk ? translate("ON") : translate("OFF"),
style: TextStyle(
fontSize: 16.0,
color: isOk ? Colors.green : Colors.grey)),
)
],
color: isOk ? Colors.green : Colors.grey))),
),
TextButton(
Expanded(
flex: 3,
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerRight,
child: TextButton(
onPressed: onPressed,
child: Text(
translate(isOk ? "CLOSE" : "OPEN"),
style: TextStyle(fontWeight: FontWeight.bold),
)),
const Divider(height: 0)
)))),
],
);
}

View File

@ -27,11 +27,11 @@ class SettingsPage extends StatefulWidget implements PageShape {
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
static const url = 'https://rustdesk.com/';
const url = 'https://rustdesk.com/';
final _hasIgnoreBattery = androidVersion >= 26;
var _ignoreBatteryOpt = false;
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
@ -147,6 +147,12 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
leading: Icon(Icons.cloud),
onPressed: (context) {
showServerSettings();
}),
SettingsTile.navigation(
title: Text(translate('Language')),
leading: Icon(Icons.translate),
onPressed: (context) {
showLanguageSettings();
})
]),
SettingsSection(
@ -186,6 +192,42 @@ void showServerSettings() {
showServerSettingsWithValue(id, relay, key, api);
}
void showLanguageSettings() {
try {
final langs = json.decode(gFFI.getByName('langs')) as List<dynamic>;
var lang = gFFI.getByName('local_option', 'lang');
DialogManager.show((setState, close) {
final setLang = (v) {
if (lang != v) {
setState(() {
lang = v;
});
final msg = Map()
..['name'] = 'lang'
..['value'] = v;
gFFI.setByName('local_option', json.encode(msg));
homeKey.currentState?.refreshPages();
Future.delayed(Duration(milliseconds: 200), close);
}
};
return CustomAlertDialog(
title: SizedBox.shrink(),
content: Column(
children: [
getRadio('Default', '', lang, setLang),
Divider(color: MyTheme.border),
] +
langs.map((e) {
final key = e[0] as String;
final name = e[1] as String;
return getRadio(name, key, lang, setLang);
}).toList(),
),
actions: []);
}, backDismiss: true, clickMaskDismiss: true);
} catch (_e) {}
}
void showAbout() {
DialogManager.show((setState, close) {
return CustomAlertDialog(

View File

@ -27,7 +27,7 @@ class DraggableChatWindow extends StatelessWidget {
height: height,
builder: (_, onPanUpdate) {
return isIOS
? chatPage
? ChatPage()
: Scaffold(
resizeToAvoidBottomInset: false,
appBar: CustomAppBar(
@ -68,7 +68,7 @@ class DraggableChatWindow extends StatelessWidget {
),
),
),
body: chatPage,
body: ChatPage(),
);
});
}

View File

@ -1,6 +1,6 @@
import 'dart:convert';
import 'package:dash_chat/dash_chat.dart';
import 'package:dash_chat_2/dash_chat_2.dart';
import 'package:flutter/material.dart';
import '../../mobile/widgets/overlay.dart';
@ -11,8 +11,8 @@ class MessageBody {
List<ChatMessage> chatMessages;
MessageBody(this.chatUser, this.chatMessages);
void add(ChatMessage cm) {
this.chatMessages.add(cm);
void insert(ChatMessage cm) {
this.chatMessages.insert(0, cm);
}
void clear() {
@ -24,19 +24,15 @@ class ChatModel with ChangeNotifier {
static final clientModeID = -1;
final ChatUser me = ChatUser(
uid: "",
name: "Me",
id: "",
firstName: "Me",
);
late final Map<int, MessageBody> _messages = Map()
..[clientModeID] = MessageBody(me, []);
final _scroller = ScrollController();
var _currentID = clientModeID;
ScrollController get scroller => _scroller;
Map<int, MessageBody> get messages => _messages;
int get currentID => _currentID;
@ -67,8 +63,8 @@ class ChatModel with ChangeNotifier {
"Failed to changeCurrentID,remote user doesn't exist");
}
final chatUser = ChatUser(
uid: client.peerId,
name: client.name,
id: client.peerId,
firstName: client.name,
);
_messages[id] = MessageBody(chatUser, []);
_currentID = id;
@ -85,48 +81,39 @@ class ChatModel with ChangeNotifier {
late final chatUser;
if (id == clientModeID) {
chatUser = ChatUser(
name: _ffi.target?.ffiModel.pi.username,
uid: _ffi.target?.getId(),
firstName: _ffi.target?.ffiModel.pi.username,
id: _ffi.target?.getId() ?? "",
);
} else {
final client = _ffi.target?.serverModel.clients[id];
if (client == null) {
return debugPrint("Failed to receive msg,user doesn't exist");
}
chatUser = ChatUser(uid: client.peerId, name: client.name);
chatUser = ChatUser(id: client.peerId, firstName: client.name);
}
if (!_messages.containsKey(id)) {
_messages[id] = MessageBody(chatUser, []);
}
_messages[id]!.add(ChatMessage(text: text, user: chatUser));
_messages[id]!.insert(
ChatMessage(text: text, user: chatUser, createdAt: DateTime.now()));
_currentID = id;
notifyListeners();
scrollToBottom();
}
scrollToBottom() {
Future.delayed(Duration(milliseconds: 500), () {
_scroller.animateTo(_scroller.position.maxScrollExtent,
duration: Duration(milliseconds: 200),
curve: Curves.fastLinearToSlowEaseIn);
});
}
send(ChatMessage message) {
if (message.text != null && message.text!.isNotEmpty) {
_messages[_currentID]?.add(message);
if (message.text.isNotEmpty) {
_messages[_currentID]?.insert(message);
if (_currentID == clientModeID) {
_ffi.target?.setByName("chat_client_mode", message.text!);
_ffi.target?.setByName("chat_client_mode", message.text);
} else {
final msg = Map()
..["id"] = _currentID
..["text"] = message.text!;
..["text"] = message.text;
_ffi.target?.setByName("chat_server_mode", jsonEncode(msg));
}
}
notifyListeners();
scrollToBottom();
}
close() {

View File

@ -323,6 +323,7 @@ class FileModel extends ChangeNotifier {
onClose() {
SmartDialog.dismiss();
jobReset();
// save config
Map<String, String> msgMap = Map();

View File

@ -73,7 +73,7 @@ class FfiModel with ChangeNotifier {
void updatePermission(Map<String, dynamic> evt) {
evt.forEach((k, v) {
if (k == 'name') return;
if (k == 'name' || k.isEmpty) return;
_permissions[k] = v == 'true';
});
print('$_permissions');
@ -224,6 +224,8 @@ class FfiModel with ChangeNotifier {
parent.target?.serverModel.onClientAuthorized(evt);
} else if (name == 'on_client_remove') {
parent.target?.serverModel.onClientRemove(evt);
} else if (name == 'update_quality_status') {
parent.target?.qualityMonitorModel.updateQualityStatus(evt);
}
};
platformFFI.setEventCallback(cb);
@ -256,6 +258,8 @@ class FfiModel with ChangeNotifier {
wrongPasswordDialog(id);
} else if (type == 'input-password') {
enterPasswordDialog(id);
} else if (type == 'restarting') {
showMsgBox(id, type, title, text, false, hasCancel: false);
} else {
var hasRetry = evt['hasRetry'] == 'true';
showMsgBox(id, type, title, text, hasRetry);
@ -264,8 +268,9 @@ class FfiModel with ChangeNotifier {
/// Show a message box with [type], [title] and [text].
void showMsgBox(
String id, String type, String title, String text, bool hasRetry) {
msgBox(type, title, text);
String id, String type, String title, String text, bool hasRetry,
{bool? hasCancel}) {
msgBox(type, title, text, hasCancel: hasCancel);
_timer?.cancel();
if (hasRetry) {
_timer = Timer(Duration(seconds: _reconnects), () {
@ -784,6 +789,47 @@ class CursorModel with ChangeNotifier {
}
}
class QualityMonitorData {
String? speed;
String? fps;
String? delay;
String? targetBitrate;
String? codecFormat;
}
class QualityMonitorModel with ChangeNotifier {
WeakReference<FFI> parent;
QualityMonitorModel(this.parent);
var _show = false;
final _data = QualityMonitorData();
bool get show => _show;
QualityMonitorData get data => _data;
checkShowQualityMonitor() {
final show =
gFFI.getByName('toggle_option', 'show-quality-monitor') == 'true';
if (_show != show) {
_show = show;
notifyListeners();
}
}
updateQualityStatus(Map<String, dynamic> evt) {
try {
if ((evt["speed"] as String).isNotEmpty) _data.speed = evt["speed"];
if ((evt["fps"] as String).isNotEmpty) _data.fps = evt["fps"];
if ((evt["delay"] as String).isNotEmpty) _data.delay = evt["delay"];
if ((evt["target_bitrate"] as String).isNotEmpty)
_data.targetBitrate = evt["target_bitrate"];
if ((evt["codec_format"] as String).isNotEmpty)
_data.codecFormat = evt["codec_format"];
notifyListeners();
} catch (e) {}
}
}
/// Mouse button enum.
enum MouseButtons { left, right, wheel }
@ -817,6 +863,7 @@ class FFI {
late final FileModel fileModel;
late final AbModel abModel;
late final UserModel userModel;
late final QualityMonitorModel qualityMonitorModel;
FFI() {
this.imageModel = ImageModel(WeakReference(this));
@ -828,6 +875,7 @@ class FFI {
this.fileModel = FileModel(WeakReference(this));
this.abModel = AbModel(WeakReference(this));
this.userModel = UserModel(WeakReference(this));
this.qualityMonitorModel = QualityMonitorModel(WeakReference(this));
}
/// Get the remote id for current client.

View File

@ -113,6 +113,27 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "8.4.0"
cached_network_image:
dependency: transitive
description:
name: cached_network_image
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.2.1"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
characters:
dependency: transitive
description:
@ -147,7 +168,7 @@ packages:
name: code_builder
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.0"
version: "4.2.0"
collection:
dependency: transitive
description:
@ -183,6 +204,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
csslib:
dependency: transitive
description:
name: csslib
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.17.2"
cupertino_icons:
dependency: "direct main"
description:
@ -197,13 +225,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.3"
dash_chat:
dash_chat_2:
dependency: "direct main"
description:
name: dash_chat
name: dash_chat_2
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.16"
version: "0.0.12"
desktop_multi_window:
dependency: "direct main"
description:
@ -351,6 +379,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_blurhash:
dependency: transitive
description:
name: flutter_blurhash
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.0"
flutter_breadcrumb:
dependency: "direct main"
description:
@ -358,6 +393,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
flutter_cache_manager:
dependency: transitive
description:
name: flutter_cache_manager
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.3.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
@ -394,7 +436,7 @@ packages:
name: flutter_smart_dialog
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.5.3+7"
version: "4.5.3+8"
flutter_test:
dependency: "direct dev"
description: flutter
@ -447,13 +489,20 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
html:
dependency: transitive
description:
name: html
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.15.0"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.13.4"
version: "0.13.5"
http_multi_server:
dependency: transitive
description:
@ -509,7 +558,7 @@ packages:
name: image_picker_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.5.0"
version: "2.6.1"
intl:
dependency: transitive
description:
@ -587,6 +636,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
octo_image:
dependency: transitive
description:
name: octo_image
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.2"
package_config:
dependency: transitive
description:
@ -656,14 +712,14 @@ packages:
name: path_provider_android
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.16"
version: "2.0.17"
path_provider_ios:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.10"
version: "2.0.11"
path_provider_linux:
dependency: transitive
description:
@ -740,7 +796,7 @@ packages:
name: provider
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.0"
version: "6.0.3"
pub_semver:
dependency: transitive
description:
@ -758,12 +814,10 @@ packages:
qr_code_scanner:
dependency: "direct main"
description:
path: "."
ref: fix_break_changes_platform
resolved-ref: "0feca6f15042c279ff575c559a3430df917b623d"
url: "https://github.com/Heap-Hop/qr_code_scanner.git"
source: git
version: "0.7.0"
name: qr_code_scanner
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
quiver:
dependency: transitive
description:
@ -771,6 +825,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
rxdart:
dependency: transitive
description:
name: rxdart
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.27.5"
screen_retriever:
dependency: transitive
description:
@ -847,7 +908,7 @@ packages:
name: shelf
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
version: "1.3.2"
shelf_web_socket:
dependency: transitive
description:
@ -881,6 +942,20 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.2"
sqflite:
dependency: transitive
description:
name: sqflite
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.3"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1+1"
stack_trace:
dependency: transitive
description:
@ -909,6 +984,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
synchronized:
dependency: transitive
description:
name: synchronized
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.0+2"
term_glyph:
dependency: transitive
description:
@ -937,13 +1019,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
transparent_image:
dependency: transitive
description:
name: transparent_image
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
tray_manager:
dependency: "direct main"
description:
@ -1035,6 +1110,41 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
video_player:
dependency: transitive
description:
name: video_player
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.5"
video_player_android:
dependency: transitive
description:
name: video_player_android
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.8"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.5"
video_player_platform_interface:
dependency: transitive
description:
name: video_player_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.1.3"
video_player_web:
dependency: transitive
description:
name: video_player_web
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.12"
visibility_detector:
dependency: "direct main"
description:

View File

@ -31,7 +31,7 @@ dependencies:
ffi: ^1.1.2
path_provider: ^2.0.2
external_path: ^1.0.1
provider: ^5.0.0
provider: ^6.0.3
tuple: ^2.0.0
wakelock: ^0.5.2
device_info_plus: ^3.2.3
@ -40,15 +40,12 @@ dependencies:
url_launcher: ^6.0.9
shared_preferences: ^2.0.6
toggle_switch: ^1.4.0
dash_chat: ^1.1.16
dash_chat_2: ^0.0.12
draggable_float_widget: ^0.0.2
settings_ui: ^2.0.2
flutter_breadcrumb: ^1.0.1
http: ^0.13.4
qr_code_scanner:
git:
url: https://github.com/Heap-Hop/qr_code_scanner.git
ref: fix_break_changes_platform
qr_code_scanner: ^1.0.0
zxing2: ^0.1.0
image_picker: ^0.8.5
image: ^3.1.3

View File

@ -64,6 +64,7 @@ message LoginRequest {
}
bool video_ack_required = 9;
uint64 session_id = 10;
string version = 11;
}
message ChatMessage { string text = 1; }

View File

@ -276,7 +276,7 @@ impl TransferJob {
show_hidden: bool,
is_remote: bool,
files: Vec<FileEntry>,
enable_override_detection: bool,
enable_overwrite_detection: bool,
) -> Self {
log::info!("new write {}", path);
let total_size = files.iter().map(|x| x.size as u64).sum();
@ -289,7 +289,7 @@ impl TransferJob {
is_remote,
files,
total_size,
enable_overwrite_detection: enable_override_detection,
enable_overwrite_detection,
..Default::default()
}
}
@ -301,7 +301,7 @@ impl TransferJob {
file_num: i32,
show_hidden: bool,
is_remote: bool,
enable_override_detection: bool,
enable_overwrite_detection: bool,
) -> ResultType<Self> {
log::info!("new read {}", path);
let files = get_recursive_files(&path, show_hidden)?;
@ -315,7 +315,7 @@ impl TransferJob {
is_remote,
files,
total_size,
enable_overwrite_detection: enable_override_detection,
enable_overwrite_detection,
..Default::default()
})
}

View File

@ -1033,10 +1033,6 @@ impl LoginConfigHandler {
msg.lock_after_session_end = BoolOption::Yes.into();
n += 1;
}
if self.get_toggle_option("privacy-mode") {
msg.privacy_mode = BoolOption::Yes.into();
n += 1;
}
if self.get_toggle_option("disable-audio") {
msg.disable_audio = BoolOption::Yes.into();
n += 1;
@ -1060,6 +1056,23 @@ impl LoginConfigHandler {
}
}
pub fn get_option_message_after_login(&self) -> Option<OptionMessage> {
if self.is_port_forward || self.is_file_transfer {
return None;
}
let mut n = 0;
let mut msg = OptionMessage::new();
if self.get_toggle_option("privacy-mode") {
msg.privacy_mode = BoolOption::Yes.into();
n += 1;
}
if n > 0 {
Some(msg)
} else {
None
}
}
/// Parse the image quality option.
/// Return [`ImageQuality`] if the option is valid, otherwise return `None`.
///
@ -1267,7 +1280,7 @@ impl LoginConfigHandler {
/// Create a [`Message`] for login.
fn create_login_msg(&self, password: Vec<u8>) -> Message {
#[cfg(any(target_os = "android", target_os = "ios"))]
let my_id = Config::get_id_or(crate::common::MOBILE_INFO1.lock().unwrap().clone());
let my_id = Config::get_id_or(crate::common::FLUTTER_INFO1.lock().unwrap().clone());
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let my_id = Config::get_id();
let mut lr = LoginRequest {
@ -1277,6 +1290,7 @@ impl LoginConfigHandler {
my_name: crate::username(),
option: self.get_option_message(true).into(),
session_id: self.session_id,
version: crate::VERSION.to_string(),
..Default::default()
};
if self.is_file_transfer {

View File

@ -3,7 +3,10 @@ use std::{
time::Instant,
};
use hbb_common::{log, message_proto::{VideoFrame, video_frame}};
use hbb_common::{
log,
message_proto::{video_frame, VideoFrame},
};
const MAX_LATENCY: i64 = 500;
const MIN_LATENCY: i64 = 100;
@ -89,3 +92,12 @@ impl ToString for CodecFormat {
}
}
}
#[derive(Debug, Default)]
pub struct QualityStatus {
pub speed: Option<String>,
pub fps: Option<i32>,
pub delay: Option<i32>,
pub target_bitrate: Option<i32>,
pub codec_format: Option<CodecFormat>,
}

View File

@ -441,7 +441,7 @@ pub fn username() -> String {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
return whoami::username().trim_end_matches('\0').to_owned();
#[cfg(any(target_os = "android", target_os = "ios"))]
return MOBILE_INFO2.lock().unwrap().clone();
return FLUTTER_INFO2.lock().unwrap().clone();
}
#[inline]

View File

@ -1,6 +1,9 @@
use std::{
collections::{HashMap, VecDeque},
sync::{Arc, Mutex, RwLock},
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex, RwLock,
},
};
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
@ -118,6 +121,9 @@ impl Session {
}
lc.set_option(name, value);
}
// TODO
// input_os_password
// restart_remote_device
/// Input the OS password.
pub fn input_os_password(&self, pass: String, activate: bool) {
@ -509,6 +515,26 @@ impl Session {
}
}
}
fn update_quality_status(&self, status: QualityStatus) {
const NULL: String = String::new();
self.push_event(
"update_quality_status",
vec![
("speed", &status.speed.map_or(NULL, |it| it)),
("fps", &status.fps.map_or(NULL, |it| it.to_string())),
("delay", &status.delay.map_or(NULL, |it| it.to_string())),
(
"target_bitrate",
&status.target_bitrate.map_or(NULL, |it| it.to_string()),
),
(
"codec_format",
&status.codec_format.map_or(NULL, |it| it.to_string()),
),
],
);
}
}
impl FileManager for Session {}
@ -603,9 +629,16 @@ impl Interface for Session {
}
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
if !t.from_client {
self.update_quality_status(QualityStatus {
delay: Some(t.last_delay as _),
target_bitrate: Some(t.target_bitrate as _),
..Default::default()
});
handle_test_delay(t, peer).await;
}
}
}
const MILLI1: Duration = Duration::from_millis(1);
@ -618,6 +651,9 @@ struct Connection {
write_jobs: Vec<fs::TransferJob>,
timer: Interval,
last_update_jobs_status: (Instant, HashMap<i32, u64>),
data_count: Arc<AtomicUsize>,
frame_count: Arc<AtomicUsize>,
video_format: CodecFormat,
}
impl Connection {
@ -650,6 +686,9 @@ impl Connection {
write_jobs: Vec::new(),
timer: time::interval(SEC30),
last_update_jobs_status: (Instant::now(), Default::default()),
data_count: Arc::new(AtomicUsize::new(0)),
frame_count: Arc::new(AtomicUsize::new(0)),
video_format: CodecFormat::Unknown,
};
let key = Config::get_option("key");
let token = Config::get_option("access_token");
@ -663,6 +702,9 @@ impl Connection {
("direct", &direct.to_string()),
],
);
let mut status_timer = time::interval(Duration::new(1, 0));
loop {
tokio::select! {
res = peer.next() => {
@ -675,14 +717,20 @@ impl Connection {
}
Ok(ref bytes) => {
last_recv_time = Instant::now();
conn.data_count.fetch_add(bytes.len(), Ordering::Relaxed);
if !conn.handle_msg_from_peer(bytes, &mut peer).await {
break
}
}
}
} else {
if session.lc.read().unwrap().restarting_remote_device {
log::info!("Restart remote device");
session.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip");
} else {
log::info!("Reset by the peer");
session.msgbox("error", "Connection Error", "Reset by the peer");
}
break;
}
}
@ -708,6 +756,16 @@ impl Connection {
conn.timer = time::interval_at(Instant::now() + SEC30, SEC30);
}
}
_ = status_timer.tick() => {
let speed = conn.data_count.swap(0, Ordering::Relaxed);
let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32);
let fps = conn.frame_count.swap(0, Ordering::Relaxed) as _;
conn.session.update_quality_status(QualityStatus {
speed:Some(speed),
fps:Some(fps),
..Default::default()
});
}
}
}
log::debug!("Exit io_loop of id={}", session.id);
@ -729,6 +787,14 @@ impl Connection {
if !self.first_frame {
self.first_frame = true;
}
let incomming_format = CodecFormat::from(&vf);
if self.video_format != incomming_format {
self.video_format = incomming_format.clone();
self.session.update_quality_status(QualityStatus {
codec_format: Some(incomming_format),
..Default::default()
})
};
if let Ok(true) = self.video_handler.handle_frame(vf) {
let stream = self.session.events2ui.read().unwrap();
stream.add(EventToUI::Rgba(ZeroCopyBuffer(
@ -937,6 +1003,7 @@ impl Connection {
Permission::Keyboard => "keyboard",
Permission::Clipboard => "clipboard",
Permission::Audio => "audio",
Permission::Restart => "restart",
_ => "",
},
&p.enabled.to_string(),
@ -1618,8 +1685,8 @@ pub mod connection_manager {
id,
file_num,
mut files,
overwrite_detection,
} => {
// in mobile, can_enable_override_detection is always true
WRITE_JOBS.lock().unwrap().push(fs::TransferJob::new_write(
id,
"".to_string(),
@ -1635,7 +1702,7 @@ pub mod connection_manager {
..Default::default()
})
.collect(),
true,
overwrite_detection,
));
}
ipc::FS::CancelWrite { id } => {

View File

@ -19,12 +19,14 @@ use crate::flutter::connection_manager::{self, get_clients_length, get_clients_s
use crate::flutter::{self, Session, SESSIONS};
use crate::start_server;
use crate::ui_interface;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use crate::ui_interface::{change_id, check_connect_status, is_ok_change_id};
use crate::ui_interface::{
change_id, check_connect_status, discover, forget_password, get_api_server, get_app_name,
get_async_job_status, get_connect_status, get_fav, get_id, get_lan_peers, get_license,
get_local_option, get_options, get_peer, get_peer_option, get_socks, get_sound_inputs,
get_uuid, get_version, has_rendezvous_service, is_ok_change_id, post_request, set_local_option,
set_options, set_peer_option, set_socks, store_fav, test_if_valid_server, using_public_server,
discover, forget_password, get_api_server, get_app_name, get_async_job_status,
get_connect_status, get_fav, get_id, get_lan_peers, get_license, get_local_option, get_options,
get_peer, get_peer_option, get_socks, get_sound_inputs, get_uuid, get_version,
has_rendezvous_service, post_request, set_local_option, set_options, set_peer_option,
set_socks, store_fav, test_if_valid_server, using_public_server,
};
fn initialize(app_dir: &str) {
@ -67,7 +69,10 @@ fn initialize(app_dir: &str) {
/// Return true if the app should continue running with UI(possibly Flutter), false if the app should exit.
#[no_mangle]
pub extern "C" fn rustdesk_core_main() -> bool {
crate::core_main::core_main()
#[cfg(not(any(target_os = "android", target_os = "ios")))]
return crate::core_main::core_main();
#[cfg(any(target_os = "android", target_os = "ios"))]
false
}
pub enum EventToUI {
@ -396,6 +401,7 @@ pub fn main_get_sound_inputs() -> Vec<String> {
}
pub fn main_change_id(new_id: String) {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
change_id(new_id)
}
@ -467,6 +473,7 @@ pub fn main_get_connect_status() -> String {
}
pub fn main_check_connect_status() {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
check_connect_status(true);
}
@ -643,25 +650,6 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co
// res = Session::get_option(arg);
// }
// }
"server_id" => {
res = ui_interface::get_id();
}
"temporary_password" => {
res = ui_interface::temporary_password();
}
"permanent_password" => {
res = ui_interface::permanent_password();
}
"connect_statue" => {
res = ONLINE
.lock()
.unwrap()
.values()
.max()
.unwrap_or(&0)
.clone()
.to_string();
}
// File Action
"get_home_dir" => {
res = fs::get_home_as_string();
@ -682,6 +670,33 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co
// }
// }
// Server Side
"local_option" => {
if let Ok(arg) = arg.to_str() {
res = LocalConfig::get_option(arg);
}
}
"langs" => {
res = crate::lang::LANGS.to_string();
}
"server_id" => {
res = ui_interface::get_id();
}
"temporary_password" => {
res = ui_interface::temporary_password();
}
"permanent_password" => {
res = ui_interface::permanent_password();
}
"connect_statue" => {
res = ONLINE
.lock()
.unwrap()
.values()
.max()
.unwrap_or(&0)
.clone()
.to_string();
}
#[cfg(not(any(target_os = "ios")))]
"clients_state" => {
res = get_clients_state();
@ -861,9 +876,22 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) {
// }
// }
// }
"local_option" => {
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
if let Some(name) = m.get("name") {
if let Some(value) = m.get("value") {
LocalConfig::set_option(name.to_owned(), value.to_owned());
}
}
}
}
// "input_os_password" => {
// Session::input_os_password(value.to_owned(), true);
// }
"restart_remote_device" => {
// TODO
// Session::restart_remote_device();
}
// // File Action
// "read_remote_dir" => {
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
@ -1048,8 +1076,13 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) {
crate::rendezvous_mediator::RendezvousMediator::restart();
}
"start_service" => {
#[cfg(target_os = "android")]
{
Config::set_option("stop-service".into(), "".into());
start_server(false);
crate::rendezvous_mediator::RendezvousMediator::restart();
}
#[cfg(not(target_os = "android"))]
std::thread::spawn(move || start_server(true));
}
#[cfg(target_os = "android")]
"close_conn" => {

View File

@ -60,6 +60,7 @@ pub enum FS {
id: i32,
file_num: i32,
files: Vec<(String, u64)>,
overwrite_detection: bool,
},
CancelWrite {
id: i32,

View File

@ -8,17 +8,18 @@ mod de;
mod en;
mod eo;
mod es;
mod hu;
mod fr;
mod hu;
mod id;
mod it;
mod ja;
mod pl;
mod ptbr;
mod ru;
mod sk;
mod tr;
mod tw;
mod vn;
mod pl;
lazy_static::lazy_static! {
pub static ref LANGS: Value =
@ -41,6 +42,7 @@ lazy_static::lazy_static! {
("tr", "Türkçe"),
("vn", "Tiếng Việt"),
("pl", "Polski"),
("ja", "日本語"),
]);
}
@ -87,16 +89,19 @@ pub fn translate_locale(name: String, locale: &str) -> String {
"sk" => sk::T.deref(),
"vn" => vn::T.deref(),
"pl" => pl::T.deref(),
"ja" => ja::T.deref(),
_ => en::T.deref(),
};
if let Some(v) = m.get(&name as &str) {
v.to_string()
} else {
if v.is_empty() {
if lang != "en" {
if let Some(v) = en::T.get(&name as &str) {
return v.to_string();
}
}
} else {
return v.to_string();
}
}
name
}
}

303
src/lang/ja.rs Normal file
View File

@ -0,0 +1,303 @@
lazy_static::lazy_static! {
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
[
("Status", "状態"),
("Your Desktop", "デスクトップ"),
("desk_tip", "このIDとパスワードであなたのデスクトップにアクセスできます。"),
("Password", "パスワード"),
("Ready", "準備完了"),
("Established", "接続完了"),
("connecting_status", "RuskDeskネットワークに接続中..."),
("Enable Service", "サービスを有効化"),
("Start Service", "サービスを開始"),
("Service is running", "サービスは動作中"),
("Service is not running", "サービスは動作していません"),
("not_ready_status", "準備できていません。接続を確認してください。"),
("Control Remote Desktop", "リモートのデスクトップを操作する"),
("Transfer File", "ファイルを転送"),
("Connect", "接続"),
("Recent Sessions", "最近のセッション"),
("Address Book", "アドレス帳"),
("Confirmation", "確認用"),
("TCP Tunneling", "TCPトンネリング"),
("Remove", "削除"),
("Refresh random password", "ランダムパスワードを再生成"),
("Set your own password", "自分のパスワードを設定"),
("Enable Keyboard/Mouse", "キーボード・マウスを有効化"),
("Enable Clipboard", "クリップボードを有効化"),
("Enable File Transfer", "ファイル転送を有効化"),
("Enable TCP Tunneling", "TCPトンネリングを有効化"),
("IP Whitelisting", "IPホワイトリスト"),
("ID/Relay Server", "認証・中継サーバー"),
("Stop service", "サービスを停止"),
("Change ID", "IDを変更"),
("Website", "公式サイト"),
("About", "情報"),
("Mute", "ミュート"),
("Audio Input", "音声入力デバイス"),
("Enhancements", "追加機能"),
("Hardware Codec", "ハードウェア コーデック"),
("Adaptive Bitrate", "アダプティブビットレート"),
("ID Server", "認証サーバー"),
("Relay Server", "中継サーバー"),
("API Server", "APIサーバー"),
("invalid_http", "http:// もしくは https:// から入力してください"),
("Invalid IP", "無効なIP"),
("id_change_tip", "使用できるのは大文字・小文字のアルファベット、数字、アンダースコア_のみです。初めの文字はアルファベットにする必要があります。6文字から16文字までです。"),
("Invalid format", "無効な形式"),
("server_not_support", "サーバー側でまだサポートされていません"),
("Not available", "利用不可"),
("Too frequent", "使用量が多すぎです"),
("Cancel", "キャンセル"),
("Skip", "スキップ"),
("Close", "閉じる"),
("Retry", "再試行"),
("OK", "OK"),
("Password Required", "パスワードが必要"),
("Please enter your password", "パスワードを入力してください"),
("Remember password", "パスワードを記憶する"),
("Wrong Password", "パスワードが間違っています"),
("Do you want to enter again?", "もう一度入力しますか?"),
("Connection Error", "接続エラー"),
("Error", "エラー"),
("Reset by the peer", "相手がリセットしました"),
("Connecting...", "接続中..."),
("Connection in progress. Please wait.", "接続中です。しばらくお待ちください。"),
("Please try 1 minute later", "1分後にもう一度お試しください"),
("Login Error", "ログインエラー"),
("Successful", "成功"),
("Connected, waiting for image...", "接続完了、画像を取得中..."),
("Name", "名前"),
("Type", "種類"),
("Modified", "最終更新"),
("Size", "サイズ"),
("Show Hidden Files", "隠しファイルを表示"),
("Receive", "受信"),
("Send", "送信"),
("Refresh File", "ファイルを更新"),
("Local", "ローカル"),
("Remote", "リモート"),
("Remote Computer", "リモート側コンピューター"),
("Local Computer", "ローカル側コンピューター"),
("Confirm Delete", "削除の確認"),
("Delete", "削除"),
("Properties", "プロパティ"),
("Multi Select", "複数選択"),
("Empty Directory", "空のディレクトリ"),
("Not an empty directory", "空ではないディレクトリ"),
("Are you sure you want to delete this file?", "本当にこのファイルを削除しますか?"),
("Are you sure you want to delete this empty directory?", "本当にこの空のディレクトリを削除しますか?"),
("Are you sure you want to delete the file of this directory?", "本当にこのディレクトリ内のファイルを削除しますか?"),
("Do this for all conflicts", "他のすべてにも適用する"),
("This is irreversible!", "この操作は元に戻せません!"),
("Deleting", "削除中"),
("files", "ファイル"),
("Waiting", "待機中"),
("Finished", "完了"),
("Speed", "速度"),
("Custom Image Quality", "画質を調整"),
("Privacy mode", "プライバシーモード"),
("Block user input", "ユーザーの入力をブロック"),
("Unblock user input", "ユーザーの入力を許可"),
("Adjust Window", "ウィンドウを調整"),
("Original", "オリジナル"),
("Shrink", "縮小"),
("Stretch", "伸縮"),
("Good image quality", "画質優先"),
("Balanced", "バランス"),
("Optimize reaction time", "速度優先"),
("Custom", "カスタム"),
("Show remote cursor", "リモート側のカーソルを表示"),
("Show quality monitor", "品質モニターを表示"),
("Disable clipboard", "クリップボードを無効化"),
("Lock after session end", "セッション終了後にロックする"),
("Insert", "送信"),
("Insert Lock", "ロック命令を送信"),
("Refresh", "更新"),
("ID does not exist", "IDが存在しません"),
("Failed to connect to rendezvous server", "ランデブーサーバーに接続できませんでした"),
("Please try later", "後でもう一度お試しください"),
("Remote desktop is offline", "リモート側デスクトップがオフラインです"),
("Key mismatch", "キーが一致しません"),
("Timeout", "タイムアウト"),
("Failed to connect to relay server", "中継サーバーに接続できませんでした"),
("Failed to connect via rendezvous server", "ランデブーサーバー経由で接続できませんでした"),
("Failed to connect via relay server", "中継サーバー経由で接続できませんでした"),
("Failed to make direct connection to remote desktop", "リモート側デスクトップと直接接続できませんでした"),
("Set Password", "パスワードを設定"),
("OS Password", "OSのパスワード"),
("install_tip", "RustDeskがUACの影響によりリモート側で正常に動作しない場合があります。UACを回避するには、下のボタンをクリックしてシステムにRustDeskをインストールしてください。"),
("Click to upgrade", "アップグレード"),
("Click to download", "ダウンロード"),
("Click to update", "アップデート"),
("Configure", "設定"),
("config_acc", "リモートからあなたのデスクトップを操作するには、RustDeskに「アクセシビリティ」権限を与える必要があります。"),
("config_screen", "リモートからあなたのデスクトップにアクセスするには、RustDeskに「画面収録」権限を与える必要があります。"),
("Installing ...", "インストール中..."),
("Install", "インストール"),
("Installation", "インストール"),
("Installation Path", "インストール先のパス"),
("Create start menu shortcuts", "スタートメニューにショートカットを作成する"),
("Create desktop icon", "デスクトップにアイコンを作成する"),
("agreement_tip", "インストールを開始することで、ライセンス条項に同意したとみなされます。"),
("Accept and Install", "同意してインストール"),
("End-user license agreement", "エンドユーザー ライセンス条項"),
("Generating ...", "生成中 ..."),
("Your installation is lower version.", "インストール済みのバージョンが古いです。"),
("not_close_tcp_tip", "トンネルを使用中はこのウィンドウを閉じないでください"),
("Listening ...", "リッスン中 ..."),
("Remote Host", "リモートのホスト"),
("Remote Port", "リモートのポート"),
("Action", "操作"),
("Add", "追加"),
("Local Port", "ローカルのポート"),
("setup_server_tip", "接続をより速くするには、自分のサーバーをセットアップしてください"),
("Too short, at least 6 characters.", "短すぎます。最低6文字です。"),
("The confirmation is not identical.", "確認用と一致しません。"),
("Permissions", "権限"),
("Accept", "承諾"),
("Dismiss", "無視"),
("Disconnect", "切断"),
("Allow using keyboard and mouse", "キーボード・マウスの使用を許可"),
("Allow using clipboard", "クリップボードの使用を許可"),
("Allow hearing sound", "サウンドの受信を許可"),
("Allow file copy and paste", "ファイルのコピーアンドペーストを許可"),
("Connected", "接続済み"),
("Direct and encrypted connection", "接続は暗号化され、直接つながっている"),
("Relayed and encrypted connection", "接続は暗号化され、中継されている"),
("Direct and unencrypted connection", "接続は暗号化されてなく、直接つながっている"),
("Relayed and unencrypted connection", "接続は暗号化されてなく、中継されている"),
("Enter Remote ID", "リモートのIDを入力"),
("Enter your password", "パスワードを入力"),
("Logging in...", "ログイン中..."),
("Enable RDP session sharing", "RDPセッション共有を有効化"),
("Auto Login", "自動ログイン"),
("Enable Direct IP Access", "直接IPアクセスを有効化"),
("Rename", "名前の変更"),
("Space", "スペース"),
("Create Desktop Shortcut", "デスクトップにショートカットを作成する"),
("Change Path", "パスを変更"),
("Create Folder", "フォルダを作成"),
("Please enter the folder name", "フォルダ名を入力してください"),
("Fix it", "修復"),
("Warning", "注意"),
("Login screen using Wayland is not supported", "Waylandを使用したログインスクリーンはサポートされていません"),
("Reboot required", "再起動が必要"),
("Unsupported display server ", "サポートされていないディスプレイサーバー"),
("x11 expected", "X11 が必要です"),
("Port", "ポート"),
("Settings", "設定"),
("Username", "ユーザー名"),
("Invalid port", "無効なポート"),
("Closed manually by the peer", "相手が手動で切断しました"),
("Enable remote configuration modification", "リモート設定変更を有効化"),
("Run without install", "インストールせずに実行"),
("Always connected via relay", "常に中継サーバー経由で接続"),
("Always connect via relay", "常に中継サーバー経由で接続"),
("whitelist_tip", "ホワイトリストに登録されたIPからのみ接続を許可します"),
("Login", "ログイン"),
("Logout", "ログアウト"),
("Tags", "タグ"),
("Search ID", "IDを検索"),
("Current Wayland display server is not supported", "現在のWaylandディスプレイサーバーはサポートされていません"),
("whitelist_sep", "カンマやセミコロン、空白、改行で区切ってください"),
("Add ID", "IDを追加"),
("Add Tag", "タグを追加"),
("Unselect all tags", "全てのタグを選択解除"),
("Network error", "ネットワークエラー"),
("Username missed", "ユーザー名がありません"),
("Password missed", "パスワードがありません"),
("Wrong credentials", "資格情報が間違っています"),
("Edit Tag", "タグを編集"),
("Unremember Password", "パスワードの記憶を解除"),
("Favorites", "お気に入り"),
("Add to Favorites", "お気に入りに追加"),
("Remove from Favorites", "お気に入りから削除"),
("Empty", ""),
("Invalid folder name", "無効なフォルダ名"),
("Socks5 Proxy", "SOCKS5プロキシ"),
("Hostname", "ホスト名"),
("Discovered", "探知済み"),
("install_daemon_tip", "起動時に開始するには、システムサービスをインストールする必要があります。"),
("Remote ID", "リモートのID"),
("Paste", "ペースト"),
("Paste here?", "ここにペースト?"),
("Are you sure to close the connection?", "本当に切断しますか?"),
("Download new version", "新しいバージョンをダウンロード"),
("Touch mode", "タッチモード"),
("Mouse mode", "マウスモード"),
("One-Finger Tap", "1本指でタップ"),
("Left Mouse", "マウス左クリック"),
("One-Long Tap", "1本指でロングタップ"),
("Two-Finger Tap", "2本指でタップ"),
("Right Mouse", "マウス右クリック"),
("One-Finger Move", "1本指でドラッグ"),
("Double Tap & Move", "2本指でタップ&ドラッグ"),
("Mouse Drag", "マウスドラッグ"),
("Three-Finger vertically", "3本指で縦方向"),
("Mouse Wheel", "マウスホイール"),
("Two-Finger Move", "2本指でドラッグ"),
("Canvas Move", "キャンバスの移動"),
("Pinch to Zoom", "ピンチしてズーム"),
("Canvas Zoom", "キャンバスのズーム"),
("Reset canvas", "キャンバスのリセット"),
("No permission of file transfer", "ファイル転送の権限がありません"),
("Note", "ノート"),
("Connection", "接続"),
("Share Screen", "画面を共有"),
("CLOSE", "閉じる"),
("OPEN", "開く"),
("Chat", "チャット"),
("Total", ""),
("items", "個のアイテム"),
("Selected", "選択済み"),
("Screen Capture", "画面キャプチャ"),
("Input Control", "入力操作"),
("Audio Capture", "音声キャプチャ"),
("File Connection", "ファイルの接続"),
("Screen Connection", "画面の接続"),
("Do you accept?", "承諾しますか?"),
("Open System Setting", "端末設定を開く"),
("How to get Android input permission?", "Androidの入力権限を取得するには"),
("android_input_permission_tip1", "このAndroid端末をリモートの端末からマウスやタッチで操作するには、RustDeskに「アクセシビリティ」サービスの使用を許可する必要があります。"),
("android_input_permission_tip2", "次の端末設定ページに進み、「インストール済みアプリ」から「RestDesk Input」をオンにしてください。"),
("android_new_connection_tip", "新しい操作リクエストが届きました。この端末を操作しようとしています。"),
("android_service_will_start_tip", "「画面キャプチャ」をオンにするとサービスが自動的に開始され、他の端末がこの端末への接続をリクエストできるようになります。"),
("android_stop_service_tip", "サービスを停止すると、現在確立されている接続が全て自動的に閉じられます。"),
("android_version_audio_tip", "現在のAndroidバージョンでは音声キャプチャはサポートされていません。Android 10以降にアップグレードしてください。"),
("android_start_service_tip", "「サービスを開始」をタップするか「画面キャプチャ」を開くと、画面共有サービスが開始されます。"),
("Account", "アカウント"),
("Overwrite", "上書き"),
("This file exists, skip or overwrite this file?", "このファイルは存在しています。スキップするか上書きしますか?"),
("Quit", "終了"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), // @TODO: Update url when someone translates the document
("Help", "ヘルプ"),
("Failed", "失敗"),
("Succeeded", "成功"),
("Someone turns on privacy mode, exit", "プライバシーモードがオンになりました。終了します。"),
("Unsupported", "サポートされていません"),
("Peer denied", "相手が拒否しました"),
("Please install plugins", "プラグインをインストールしてください"),
("Peer exit", "相手が終了しました"),
("Failed to turn off", "オフにできませんでした"),
("Turned off", "オフになりました"),
("In privacy mode", "プライバシーモード開始"),
("Out privacy mode", "プライバシーモード終了"),
("Language", "言語"),
("Keep RustDesk background service", "RustDesk バックグラウンドサービスを維持"),
("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"),
("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」の選択を外してください"),
("Connection not allowed", "接続が許可されていません"),
("Use temporary password", "使い捨てのパスワードを使用"),
("Use permanent password", "固定のパスワードを使用"),
("Use both passwords", "どちらのパスワードも使用"),
("Set permanent password", "固定のパスワードを設定"),
("Set temporary password length", "使い捨てのパスワードの長さを設定"),
("Enable Remote Restart", "リモートからの再起動を有効化"),
("Allow remote restart", "リモートからの再起動を許可"),
("Restart Remote Device", "リモートの端末を再起動"),
("Are you sure you want to restart", "本当に再起動しますか"),
("Restarting Remote Device", "リモート端末を再起動中"),
("remote_restarting_tip", "リモート端末は再起動中です。このメッセージボックスを閉じて、しばらくした後に固定のパスワードを使用して再接続してください。"),
].iter().cloned().collect();
}

View File

@ -5,7 +5,7 @@ use crate::clipboard_file::*;
use crate::common::update_clipboard;
use crate::video_service;
#[cfg(any(target_os = "android", target_os = "ios"))]
use crate::{common::MOBILE_INFO2, flutter::connection_manager::start_channel};
use crate::{common::FLUTTER_INFO2, flutter::connection_manager::start_channel};
use crate::{ipc, VERSION};
use hbb_common::{
config::Config,
@ -643,7 +643,7 @@ impl Connection {
}
#[cfg(target_os = "android")]
{
pi.hostname = MOBILE_INFO2.lock().unwrap().clone();
pi.hostname = FLUTTER_INFO2.lock().unwrap().clone();
pi.platform = "Android".into();
}
#[cfg(feature = "hwcodec")]
@ -1099,8 +1099,9 @@ impl Connection {
}
Some(file_action::Union::Send(s)) => {
let id = s.id;
let od =
can_enable_overwrite_detection(get_version_number(VERSION));
let od = can_enable_overwrite_detection(get_version_number(
&self.lr.version,
));
let path = s.path.clone();
match fs::TransferJob::new_read(
id,
@ -1123,6 +1124,11 @@ impl Connection {
}
}
Some(file_action::Union::Receive(r)) => {
// note: 1.1.10 introduced identical file detection, which breaks original logic of send/recv files
// whenever got send/recv request, check peer version to ensure old version of rustdesk
let od = can_enable_overwrite_detection(get_version_number(
&self.lr.version,
));
self.send_fs(ipc::FS::NewWrite {
path: r.path,
id: r.id,
@ -1133,6 +1139,7 @@ impl Connection {
.drain(..)
.map(|f| (f.name, f.modified_time))
.collect(),
overwrite_detection: od,
});
}
Some(file_action::Union::RemoveDir(d)) => {

View File

@ -160,8 +160,8 @@ impl ConnectionManager {
id,
file_num,
mut files,
overwrite_detection
} => {
let od = can_enable_overwrite_detection(get_version_number(VERSION));
// cm has no show_hidden context
// dummy remote, show_hidden, is_remote
write_jobs.push(fs::TransferJob::new_write(
@ -179,7 +179,7 @@ impl ConnectionManager {
..Default::default()
})
.collect(),
od,
overwrite_detection,
));
}
ipc::FS::CancelWrite { id } => {

View File

@ -23,10 +23,6 @@ use clipboard::{
get_rx_clip_client, server_clip_file,
};
use enigo::{self, Enigo, KeyboardControllable};
use hbb_common::fs::{
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
RemoveJobMeta,
};
use hbb_common::{
allow_err,
config::{Config, LocalConfig, PeerConfig},
@ -43,6 +39,13 @@ use hbb_common::{
Stream,
};
use hbb_common::{config::TransferSerde, fs::TransferJobMeta};
use hbb_common::{
fs::{
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
RemoveJobMeta,
},
get_version_number,
};
#[cfg(windows)]
use crate::clipboard_file::*;
@ -239,15 +242,6 @@ impl sciter::EventHandler for Handler {
}
}
#[derive(Debug, Default)]
struct QualityStatus {
speed: Option<String>,
fps: Option<i32>,
delay: Option<i32>,
target_bitrate: Option<i32>,
codec_format: Option<CodecFormat>,
}
impl Handler {
pub fn new(cmd: String, id: String, password: String, args: Vec<String>) -> Self {
let me = Self {
@ -638,8 +632,9 @@ impl Handler {
}
fn restart_remote_device(&mut self) {
self.lc.write().unwrap().restarting_remote_device = true;
let msg = self.lc.write().unwrap().restart_remote_device();
let mut lc = self.lc.write().unwrap();
lc.restarting_remote_device = true;
let msg = lc.restart_remote_device();
self.send(Data::Message(msg));
}
@ -2076,6 +2071,22 @@ impl Remote {
true
}
async fn send_opts_after_login(&self, peer: &mut Stream) {
if let Some(opts) = self
.handler
.lc
.read()
.unwrap()
.get_option_message_after_login()
{
let mut misc = Misc::new();
misc.set_option(opts);
let mut msg_out = Message::new();
msg_out.set_misc(misc);
allow_err!(peer.send(&msg_out).await);
}
}
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
match msg_in.union {
@ -2084,6 +2095,7 @@ impl Remote {
self.first_frame = true;
self.handler.call2("closeSuccess", &make_args!());
self.handler.call("adaptSize", &make_args!());
self.send_opts_after_login(peer).await;
}
let incomming_format = CodecFormat::from(&vf);
if self.video_format != incomming_format {
@ -2595,6 +2607,9 @@ impl Interface for Handler {
pi_sciter.set_item("hostname", pi.hostname.clone());
pi_sciter.set_item("platform", pi.platform.clone());
pi_sciter.set_item("sas_enabled", pi.sas_enabled);
if get_version_number(&pi.version) < get_version_number("1.1.10") {
self.call2("setPermission", &make_args!("restart", false));
}
if self.is_file_transfer() {
if pi.username.is_empty() {
self.on_error("No active console user logged on, please connect and logon first.");

View File

@ -32,10 +32,14 @@ lazy_static::lazy_static! {
pub static ref UI_STATUS : Arc<Mutex<Status>> = Arc::new(Mutex::new((0, false, 0, "".to_owned())));
pub static ref OPTIONS : Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(Config::get_options()));
pub static ref ASYNC_JOB_STATUS : Arc<Mutex<String>> = Default::default();
pub static ref SENDER : Mutex<mpsc::UnboundedSender<ipc::Data>> = Mutex::new(check_connect_status(true));
pub static ref TEMPORARY_PASSWD : Arc<Mutex<String>> = Arc::new(Mutex::new("".to_owned()));
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
lazy_static::lazy_static! {
pub static ref SENDER : Mutex<mpsc::UnboundedSender<ipc::Data>> = Mutex::new(check_connect_status(true));
}
pub fn recent_sessions_updated() -> bool {
let mut childs = CHILDS.lock().unwrap();
if childs.0 {
@ -47,7 +51,10 @@ pub fn recent_sessions_updated() -> bool {
}
pub fn get_id() -> String {
ipc::get_id()
#[cfg(any(target_os = "android", target_os = "ios"))]
return Config::get_id();
#[cfg(not(any(target_os = "android", target_os = "ios")))]
return ipc::get_id();
}
pub fn get_remote_id() -> String {
@ -132,7 +139,7 @@ pub fn get_license() -> String {
pub fn get_option(key: String) -> String {
#[cfg(any(target_os = "android", target_os = "ios"))]
return Config::get_option(arg);
return Config::get_option(&key);
#[cfg(not(any(target_os = "android", target_os = "ios")))]
return get_option_(&key);
}
@ -243,13 +250,16 @@ pub fn get_sound_inputs() -> Vec<String> {
}
pub fn set_options(m: HashMap<String, String>) {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
*OPTIONS.lock().unwrap() = m.clone();
ipc::set_options(m).ok();
}
}
pub fn set_option(key: String, value: String) {
#[cfg(any(target_os = "android", target_os = "ios"))]
Config::set_option(name.to_owned(), value.to_owned());
Config::set_option(key, value);
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
let mut options = OPTIONS.lock().unwrap();
@ -277,6 +287,10 @@ pub fn install_path() -> String {
}
pub fn get_socks() -> Vec<String> {
#[cfg(any(target_os = "android", target_os = "ios"))]
return Vec::new();
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
let s = ipc::get_socks();
match s {
None => Vec::new(),
@ -289,8 +303,10 @@ pub fn get_socks() -> Vec<String> {
}
}
}
}
pub fn set_socks(proxy: String, username: String, password: String) {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
ipc::set_socks(config::Socks5Server {
proxy,
username,
@ -357,6 +373,7 @@ pub fn get_mouse_time() -> f64 {
return res;
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn check_mouse_time() {
let sender = SENDER.lock().unwrap();
allow_err!(sender.send(ipc::Data::MouseMoveTime(0)));