mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-01-18 07:43:01 +08:00
Merge master
This commit is contained in:
parent
07debe8363
commit
3ff2f60fb7
14
README-FI.md
14
README-FI.md
@ -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)
|
[![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.
|
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
|
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static
|
||||||
- Linux/MacOS: vcpkg install libvpx libyuv opus
|
- Linux/MacOS: vcpkg install libvpx libyuv opus
|
||||||
|
|
||||||
- aja `cargo run`
|
- suorita `cargo run`
|
||||||
|
|
||||||
## Kuinka rakentaa Linuxissa
|
## Kuinka rakentaa Linux:issa
|
||||||
|
|
||||||
### Ubuntu 18 (Debian 10)
|
### Ubuntu 18 (Debian 10)
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ export VCPKG_ROOT=$HOME/vcpkg
|
|||||||
vcpkg/vcpkg install libvpx libyuv opus
|
vcpkg/vcpkg install libvpx libyuv opus
|
||||||
```
|
```
|
||||||
|
|
||||||
### Korjaa libvpx (Fedora-linux-versiota varten)
|
### Korjaa libvpx (Fedora)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd vcpkg/buildtrees/libvpx/src
|
cd vcpkg/buildtrees/libvpx/src
|
||||||
@ -107,7 +107,7 @@ VCPKG_ROOT=$HOME/vcpkg cargo run
|
|||||||
|
|
||||||
### Vaihda Wayland-ympäristö X11 (Xorg)-ympäristöön
|
### 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
|
## Kuinka rakennetaan Dockerin kanssa
|
||||||
|
|
||||||
@ -119,13 +119,13 @@ cd rustdesk
|
|||||||
docker build -t "rustdesk-builder" .
|
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
|
```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
|
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
|
```sh
|
||||||
target/debug/rustdesk
|
target/debug/rustdesk
|
||||||
|
@ -32,7 +32,7 @@ apply plugin: 'kotlin-android'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 31
|
compileSdkVersion 32
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
}
|
}
|
||||||
|
@ -119,9 +119,9 @@ class DialogManager {
|
|||||||
|
|
||||||
static Future<T?> show<T>(DialogBuilder builder,
|
static Future<T?> show<T>(DialogBuilder builder,
|
||||||
{bool clickMaskDismiss = false,
|
{bool clickMaskDismiss = false,
|
||||||
bool backDismiss = false,
|
bool backDismiss = false,
|
||||||
String? tag,
|
String? tag,
|
||||||
bool useAnimation = true}) async {
|
bool useAnimation = true}) async {
|
||||||
final t;
|
final t;
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
t = tag;
|
t = tag;
|
||||||
@ -146,10 +146,11 @@ class DialogManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CustomAlertDialog extends StatelessWidget {
|
class CustomAlertDialog extends StatelessWidget {
|
||||||
CustomAlertDialog({required this.title,
|
CustomAlertDialog(
|
||||||
required this.content,
|
{required this.title,
|
||||||
required this.actions,
|
required this.content,
|
||||||
this.contentPadding});
|
required this.actions,
|
||||||
|
this.contentPadding});
|
||||||
|
|
||||||
final Widget title;
|
final Widget title;
|
||||||
final Widget content;
|
final Widget content;
|
||||||
@ -162,7 +163,7 @@ class CustomAlertDialog extends StatelessWidget {
|
|||||||
scrollable: true,
|
scrollable: true,
|
||||||
title: title,
|
title: title,
|
||||||
contentPadding:
|
contentPadding:
|
||||||
EdgeInsets.symmetric(horizontal: contentPadding ?? 25, vertical: 10),
|
EdgeInsets.symmetric(horizontal: contentPadding ?? 25, vertical: 10),
|
||||||
content: content,
|
content: content,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
@ -361,7 +362,9 @@ late FFI _globalFFI;
|
|||||||
FFI get gFFI => _globalFFI;
|
FFI get gFFI => _globalFFI;
|
||||||
|
|
||||||
Future<void> initGlobalFFI() async {
|
Future<void> initGlobalFFI() async {
|
||||||
|
debugPrint("_globalFFI init");
|
||||||
_globalFFI = FFI();
|
_globalFFI = FFI();
|
||||||
|
debugPrint("_globalFFI init end");
|
||||||
// after `put`, can also be globally found by Get.find<FFI>();
|
// after `put`, can also be globally found by Get.find<FFI>();
|
||||||
Get.put(_globalFFI, permanent: true);
|
Get.put(_globalFFI, permanent: true);
|
||||||
await _globalFFI.ffiModel.init();
|
await _globalFFI.ffiModel.init();
|
||||||
@ -369,4 +372,4 @@ Future<void> initGlobalFFI() async {
|
|||||||
await _globalFFI.bind.mainCheckConnectStatus();
|
await _globalFFI.bind.mainCheckConnectStatus();
|
||||||
// global shared preference
|
// global shared preference
|
||||||
await Get.putAsync(() => SharedPreferences.getInstance());
|
await Get.putAsync(() => SharedPreferences.getInstance());
|
||||||
}
|
}
|
||||||
|
@ -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/material.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/models/chat_model.dart';
|
import 'package:flutter_hbb/models/chat_model.dart';
|
||||||
@ -7,8 +7,6 @@ import 'package:provider/provider.dart';
|
|||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
|
|
||||||
ChatPage chatPage = ChatPage();
|
|
||||||
|
|
||||||
class ChatPage extends StatelessWidget implements PageShape {
|
class ChatPage extends StatelessWidget implements PageShape {
|
||||||
@override
|
@override
|
||||||
final title = translate("Chat");
|
final title = translate("Chat");
|
||||||
@ -26,7 +24,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
final id = entry.key;
|
final id = entry.key;
|
||||||
final user = entry.value.chatUser;
|
final user = entry.value.chatUser;
|
||||||
return PopupMenuItem<int>(
|
return PopupMenuItem<int>(
|
||||||
child: Text("${user.name} ${user.uid}"),
|
child: Text("${user.firstName} ${user.id}"),
|
||||||
value: id,
|
value: id,
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
@ -47,19 +45,24 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
DashChat(
|
DashChat(
|
||||||
inputContainerStyle: BoxDecoration(color: Colors.white70),
|
|
||||||
sendOnEnter: false,
|
|
||||||
// if true,reload keyboard everytime,need fix
|
|
||||||
onSend: (chatMsg) {
|
onSend: (chatMsg) {
|
||||||
chatModel.send(chatMsg);
|
chatModel.send(chatMsg);
|
||||||
},
|
},
|
||||||
user: chatModel.me,
|
currentUser: chatModel.me,
|
||||||
messages:
|
messages:
|
||||||
chatModel.messages[chatModel.currentID]?.chatMessages ??
|
chatModel.messages[chatModel.currentID]?.chatMessages ??
|
||||||
[],
|
[],
|
||||||
// default scrollToBottom has bug https://github.com/fayeed/dash_chat/issues/53
|
messageOptions: MessageOptions(
|
||||||
scrollToBottom: false,
|
showOtherUsersAvatar: false,
|
||||||
scrollController: chatModel.scroller,
|
showTime: true,
|
||||||
|
messageDecorationBuilder: (_, __, ___) =>
|
||||||
|
defaultMessageDecoration(
|
||||||
|
color: MyTheme.accent80,
|
||||||
|
borderTopLeft: 8,
|
||||||
|
borderTopRight: 8,
|
||||||
|
borderBottomRight: 8,
|
||||||
|
borderBottomLeft: 8,
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
chatModel.currentID == ChatModel.clientModeID
|
chatModel.currentID == ChatModel.clientModeID
|
||||||
? SizedBox.shrink()
|
? SizedBox.shrink()
|
||||||
@ -71,7 +74,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
color: MyTheme.accent80),
|
color: MyTheme.accent80),
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
Text(
|
Text(
|
||||||
"${currentUser.name ?? ""} ${currentUser.uid ?? ""}",
|
"${currentUser.firstName} ${currentUser.id}",
|
||||||
style: TextStyle(color: MyTheme.accent50),
|
style: TextStyle(color: MyTheme.accent50),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -29,6 +29,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
gFFI.connect(widget.id, isFileTransfer: true);
|
gFFI.connect(widget.id, isFileTransfer: true);
|
||||||
|
showLoading(translate('Connecting...'));
|
||||||
gFFI.ffiModel.updateEventListener(widget.id);
|
gFFI.ffiModel.updateEventListener(widget.id);
|
||||||
Wakelock.enable();
|
Wakelock.enable();
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ abstract class PageShape extends Widget {
|
|||||||
final List<Widget> appBarActions = [];
|
final List<Widget> appBarActions = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final homeKey = GlobalKey<_HomePageState>();
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
HomePage({Key? key}) : super(key: key);
|
HomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@ -23,12 +25,23 @@ class _HomePageState extends State<HomePage> {
|
|||||||
var _selectedIndex = 0;
|
var _selectedIndex = 0;
|
||||||
final List<PageShape> _pages = [];
|
final List<PageShape> _pages = [];
|
||||||
|
|
||||||
|
void refreshPages() {
|
||||||
|
setState(() {
|
||||||
|
initPages();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
initPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initPages() {
|
||||||
|
_pages.clear();
|
||||||
_pages.add(ConnectionPage());
|
_pages.add(ConnectionPage());
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
_pages.addAll([chatPage, ServerPage()]);
|
_pages.addAll([ChatPage(), ServerPage()]);
|
||||||
}
|
}
|
||||||
_pages.add(SettingsPage());
|
_pages.add(SettingsPage());
|
||||||
}
|
}
|
||||||
|
@ -595,6 +595,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
child: Stack(children: [
|
child: Stack(children: [
|
||||||
ImagePaint(),
|
ImagePaint(),
|
||||||
CursorPaint(),
|
CursorPaint(),
|
||||||
|
QualityMonitor(),
|
||||||
getHelpTools(),
|
getHelpTools(),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 0,
|
width: 0,
|
||||||
@ -662,7 +663,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
more.add(PopupMenuItem<String>(
|
more.add(PopupMenuItem<String>(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: ([
|
children: ([
|
||||||
Container(width: 100.0, child: Text(translate('OS Password'))),
|
Text(translate('OS Password')),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: flatButtonStyle,
|
style: flatButtonStyle,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -697,6 +698,13 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
value: 'block-input'));
|
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 {
|
() async {
|
||||||
var value = await showMenu(
|
var value = await showMenu(
|
||||||
context: context,
|
context: context,
|
||||||
@ -730,6 +738,8 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
}
|
}
|
||||||
} else if (value == 'reset_canvas') {
|
} else if (value == 'reset_canvas') {
|
||||||
gFFI.cursorModel.reset();
|
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(
|
CheckboxListTile getToggle(
|
||||||
void Function(void Function()) setState, option, name) {
|
void Function(void Function()) setState, option, name) {
|
||||||
return CheckboxListTile(
|
return CheckboxListTile(
|
||||||
@ -960,6 +1011,9 @@ CheckboxListTile getToggle(
|
|||||||
setState(() {
|
setState(() {
|
||||||
gFFI.setByName('toggle_option', option);
|
gFFI.setByName('toggle_option', option);
|
||||||
});
|
});
|
||||||
|
if (option == "show-quality-monitor") {
|
||||||
|
gFFI.qualityMonitorModel.checkShowQualityMonitor();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text(translate(name)));
|
title: Text(translate(name)));
|
||||||
@ -1062,6 +1116,27 @@ void showOptions() {
|
|||||||
}, clickMaskDismiss: true, backDismiss: true);
|
}, 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) {
|
void showSetOSPassword(bool login) {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
var password = gFFI.getByName('peer_option', "os-password");
|
var password = gFFI.getByName('peer_option', "os-password");
|
||||||
|
@ -200,7 +200,8 @@ class ServerInfo extends StatelessWidget {
|
|||||||
Icon(Icons.warning_amber_sharp,
|
Icon(Icons.warning_amber_sharp,
|
||||||
color: Colors.redAccent, size: 24),
|
color: Colors.redAccent, size: 24),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
Text(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
translate("Service is not running"),
|
translate("Service is not running"),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: 'WorkSans',
|
fontFamily: 'WorkSans',
|
||||||
@ -208,7 +209,7 @@ class ServerInfo extends StatelessWidget {
|
|||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
color: MyTheme.accent80,
|
color: MyTheme.accent80,
|
||||||
),
|
),
|
||||||
)
|
))
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
@ -316,30 +317,35 @@ class PermissionRow extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Expanded(
|
||||||
children: [
|
flex: 5,
|
||||||
SizedBox(
|
child: FittedBox(
|
||||||
width: 140,
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(name,
|
child: Text(name,
|
||||||
style: TextStyle(fontSize: 16.0, color: MyTheme.accent50))),
|
style:
|
||||||
SizedBox(
|
TextStyle(fontSize: 16.0, color: MyTheme.accent50)))),
|
||||||
width: 50,
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
child: Text(isOk ? translate("ON") : translate("OFF"),
|
child: Text(isOk ? translate("ON") : translate("OFF"),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
color: isOk ? Colors.green : Colors.grey)),
|
color: isOk ? Colors.green : Colors.grey))),
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
TextButton(
|
Expanded(
|
||||||
onPressed: onPressed,
|
flex: 3,
|
||||||
child: Text(
|
child: FittedBox(
|
||||||
translate(isOk ? "CLOSE" : "OPEN"),
|
fit: BoxFit.scaleDown,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
alignment: Alignment.centerRight,
|
||||||
)),
|
child: TextButton(
|
||||||
const Divider(height: 0)
|
onPressed: onPressed,
|
||||||
|
child: Text(
|
||||||
|
translate(isOk ? "CLOSE" : "OPEN"),
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
)))),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,11 @@ class SettingsPage extends StatefulWidget implements PageShape {
|
|||||||
_SettingsState createState() => _SettingsState();
|
_SettingsState createState() => _SettingsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
const url = 'https://rustdesk.com/';
|
||||||
static const url = 'https://rustdesk.com/';
|
final _hasIgnoreBattery = androidVersion >= 26;
|
||||||
final _hasIgnoreBattery = androidVersion >= 26;
|
var _ignoreBatteryOpt = false;
|
||||||
var _ignoreBatteryOpt = false;
|
|
||||||
|
|
||||||
|
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -147,6 +147,12 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
leading: Icon(Icons.cloud),
|
leading: Icon(Icons.cloud),
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
showServerSettings();
|
showServerSettings();
|
||||||
|
}),
|
||||||
|
SettingsTile.navigation(
|
||||||
|
title: Text(translate('Language')),
|
||||||
|
leading: Icon(Icons.translate),
|
||||||
|
onPressed: (context) {
|
||||||
|
showLanguageSettings();
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
@ -186,6 +192,42 @@ void showServerSettings() {
|
|||||||
showServerSettingsWithValue(id, relay, key, api);
|
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() {
|
void showAbout() {
|
||||||
DialogManager.show((setState, close) {
|
DialogManager.show((setState, close) {
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
|
@ -27,7 +27,7 @@ class DraggableChatWindow extends StatelessWidget {
|
|||||||
height: height,
|
height: height,
|
||||||
builder: (_, onPanUpdate) {
|
builder: (_, onPanUpdate) {
|
||||||
return isIOS
|
return isIOS
|
||||||
? chatPage
|
? ChatPage()
|
||||||
: Scaffold(
|
: Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
appBar: CustomAppBar(
|
appBar: CustomAppBar(
|
||||||
@ -68,7 +68,7 @@ class DraggableChatWindow extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: chatPage,
|
body: ChatPage(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:convert';
|
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 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../../mobile/widgets/overlay.dart';
|
import '../../mobile/widgets/overlay.dart';
|
||||||
@ -11,8 +11,8 @@ class MessageBody {
|
|||||||
List<ChatMessage> chatMessages;
|
List<ChatMessage> chatMessages;
|
||||||
MessageBody(this.chatUser, this.chatMessages);
|
MessageBody(this.chatUser, this.chatMessages);
|
||||||
|
|
||||||
void add(ChatMessage cm) {
|
void insert(ChatMessage cm) {
|
||||||
this.chatMessages.add(cm);
|
this.chatMessages.insert(0, cm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
@ -24,19 +24,15 @@ class ChatModel with ChangeNotifier {
|
|||||||
static final clientModeID = -1;
|
static final clientModeID = -1;
|
||||||
|
|
||||||
final ChatUser me = ChatUser(
|
final ChatUser me = ChatUser(
|
||||||
uid: "",
|
id: "",
|
||||||
name: "Me",
|
firstName: "Me",
|
||||||
);
|
);
|
||||||
|
|
||||||
late final Map<int, MessageBody> _messages = Map()
|
late final Map<int, MessageBody> _messages = Map()
|
||||||
..[clientModeID] = MessageBody(me, []);
|
..[clientModeID] = MessageBody(me, []);
|
||||||
|
|
||||||
final _scroller = ScrollController();
|
|
||||||
|
|
||||||
var _currentID = clientModeID;
|
var _currentID = clientModeID;
|
||||||
|
|
||||||
ScrollController get scroller => _scroller;
|
|
||||||
|
|
||||||
Map<int, MessageBody> get messages => _messages;
|
Map<int, MessageBody> get messages => _messages;
|
||||||
|
|
||||||
int get currentID => _currentID;
|
int get currentID => _currentID;
|
||||||
@ -67,8 +63,8 @@ class ChatModel with ChangeNotifier {
|
|||||||
"Failed to changeCurrentID,remote user doesn't exist");
|
"Failed to changeCurrentID,remote user doesn't exist");
|
||||||
}
|
}
|
||||||
final chatUser = ChatUser(
|
final chatUser = ChatUser(
|
||||||
uid: client.peerId,
|
id: client.peerId,
|
||||||
name: client.name,
|
firstName: client.name,
|
||||||
);
|
);
|
||||||
_messages[id] = MessageBody(chatUser, []);
|
_messages[id] = MessageBody(chatUser, []);
|
||||||
_currentID = id;
|
_currentID = id;
|
||||||
@ -85,48 +81,39 @@ class ChatModel with ChangeNotifier {
|
|||||||
late final chatUser;
|
late final chatUser;
|
||||||
if (id == clientModeID) {
|
if (id == clientModeID) {
|
||||||
chatUser = ChatUser(
|
chatUser = ChatUser(
|
||||||
name: _ffi.target?.ffiModel.pi.username,
|
firstName: _ffi.target?.ffiModel.pi.username,
|
||||||
uid: _ffi.target?.getId(),
|
id: _ffi.target?.getId() ?? "",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final client = _ffi.target?.serverModel.clients[id];
|
final client = _ffi.target?.serverModel.clients[id];
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
return debugPrint("Failed to receive msg,user doesn't exist");
|
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)) {
|
if (!_messages.containsKey(id)) {
|
||||||
_messages[id] = MessageBody(chatUser, []);
|
_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;
|
_currentID = id;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
scrollToBottom();
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToBottom() {
|
|
||||||
Future.delayed(Duration(milliseconds: 500), () {
|
|
||||||
_scroller.animateTo(_scroller.position.maxScrollExtent,
|
|
||||||
duration: Duration(milliseconds: 200),
|
|
||||||
curve: Curves.fastLinearToSlowEaseIn);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
send(ChatMessage message) {
|
send(ChatMessage message) {
|
||||||
if (message.text != null && message.text!.isNotEmpty) {
|
if (message.text.isNotEmpty) {
|
||||||
_messages[_currentID]?.add(message);
|
_messages[_currentID]?.insert(message);
|
||||||
if (_currentID == clientModeID) {
|
if (_currentID == clientModeID) {
|
||||||
_ffi.target?.setByName("chat_client_mode", message.text!);
|
_ffi.target?.setByName("chat_client_mode", message.text);
|
||||||
} else {
|
} else {
|
||||||
final msg = Map()
|
final msg = Map()
|
||||||
..["id"] = _currentID
|
..["id"] = _currentID
|
||||||
..["text"] = message.text!;
|
..["text"] = message.text;
|
||||||
_ffi.target?.setByName("chat_server_mode", jsonEncode(msg));
|
_ffi.target?.setByName("chat_server_mode", jsonEncode(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
scrollToBottom();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
|
@ -318,6 +318,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
|
jobReset();
|
||||||
|
|
||||||
// save config
|
// save config
|
||||||
Map<String, String> msgMap = Map();
|
Map<String, String> msgMap = Map();
|
||||||
|
@ -84,7 +84,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
|
|
||||||
void updatePermission(Map<String, dynamic> evt) {
|
void updatePermission(Map<String, dynamic> evt) {
|
||||||
evt.forEach((k, v) {
|
evt.forEach((k, v) {
|
||||||
if (k == 'name') return;
|
if (k == 'name' || k.isEmpty) return;
|
||||||
_permissions[k] = v == 'true';
|
_permissions[k] = v == 'true';
|
||||||
});
|
});
|
||||||
print('$_permissions');
|
print('$_permissions');
|
||||||
@ -235,6 +235,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
parent.target?.serverModel.onClientAuthorized(evt);
|
parent.target?.serverModel.onClientAuthorized(evt);
|
||||||
} else if (name == 'on_client_remove') {
|
} else if (name == 'on_client_remove') {
|
||||||
parent.target?.serverModel.onClientRemove(evt);
|
parent.target?.serverModel.onClientRemove(evt);
|
||||||
|
} else if (name == 'update_quality_status') {
|
||||||
|
parent.target?.qualityMonitorModel.updateQualityStatus(evt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
platformFFI.setEventCallback(cb);
|
platformFFI.setEventCallback(cb);
|
||||||
@ -267,6 +269,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
wrongPasswordDialog(id);
|
wrongPasswordDialog(id);
|
||||||
} else if (type == 'input-password') {
|
} else if (type == 'input-password') {
|
||||||
enterPasswordDialog(id);
|
enterPasswordDialog(id);
|
||||||
|
} else if (type == 'restarting') {
|
||||||
|
showMsgBox(id, type, title, text, false, hasCancel: false);
|
||||||
} else {
|
} else {
|
||||||
var hasRetry = evt['hasRetry'] == 'true';
|
var hasRetry = evt['hasRetry'] == 'true';
|
||||||
showMsgBox(id, type, title, text, hasRetry);
|
showMsgBox(id, type, title, text, hasRetry);
|
||||||
@ -275,8 +279,9 @@ class FfiModel with ChangeNotifier {
|
|||||||
|
|
||||||
/// Show a message box with [type], [title] and [text].
|
/// Show a message box with [type], [title] and [text].
|
||||||
void showMsgBox(
|
void showMsgBox(
|
||||||
String id, String type, String title, String text, bool hasRetry) {
|
String id, String type, String title, String text, bool hasRetry,
|
||||||
msgBox(type, title, text);
|
{bool? hasCancel}) {
|
||||||
|
msgBox(type, title, text, hasCancel: hasCancel);
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
if (hasRetry) {
|
if (hasRetry) {
|
||||||
_timer = Timer(Duration(seconds: _reconnects), () {
|
_timer = Timer(Duration(seconds: _reconnects), () {
|
||||||
@ -798,6 +803,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.
|
/// Mouse button enum.
|
||||||
enum MouseButtons { left, right, wheel }
|
enum MouseButtons { left, right, wheel }
|
||||||
|
|
||||||
@ -831,6 +877,7 @@ class FFI {
|
|||||||
late final FileModel fileModel;
|
late final FileModel fileModel;
|
||||||
late final AbModel abModel;
|
late final AbModel abModel;
|
||||||
late final UserModel userModel;
|
late final UserModel userModel;
|
||||||
|
late final QualityMonitorModel qualityMonitorModel;
|
||||||
|
|
||||||
FFI() {
|
FFI() {
|
||||||
this.imageModel = ImageModel(WeakReference(this));
|
this.imageModel = ImageModel(WeakReference(this));
|
||||||
@ -842,6 +889,7 @@ class FFI {
|
|||||||
this.fileModel = FileModel(WeakReference(this));
|
this.fileModel = FileModel(WeakReference(this));
|
||||||
this.abModel = AbModel(WeakReference(this));
|
this.abModel = AbModel(WeakReference(this));
|
||||||
this.userModel = UserModel(WeakReference(this));
|
this.userModel = UserModel(WeakReference(this));
|
||||||
|
this.qualityMonitorModel = QualityMonitorModel(WeakReference(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
static FFI newFFI() {
|
static FFI newFFI() {
|
||||||
|
@ -113,6 +113,27 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.4.0"
|
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:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -147,7 +168,7 @@ packages:
|
|||||||
name: code_builder
|
name: code_builder
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.2.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -183,6 +204,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.2"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -197,13 +225,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.3"
|
version: "2.2.3"
|
||||||
dash_chat:
|
dash_chat_2:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dash_chat
|
name: dash_chat_2
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.16"
|
version: "0.0.12"
|
||||||
desktop_multi_window:
|
desktop_multi_window:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -351,6 +379,13 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
flutter_breadcrumb:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -358,6 +393,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
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:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -394,7 +436,7 @@ packages:
|
|||||||
name: flutter_smart_dialog
|
name: flutter_smart_dialog
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.3+7"
|
version: "4.5.3+8"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -447,13 +489,20 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.0"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.4"
|
version: "0.13.5"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -509,7 +558,7 @@ packages:
|
|||||||
name: image_picker_platform_interface
|
name: image_picker_platform_interface
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0"
|
version: "2.6.1"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -587,6 +636,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
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:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -656,14 +712,14 @@ packages:
|
|||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.16"
|
version: "2.0.17"
|
||||||
path_provider_ios:
|
path_provider_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_ios
|
name: path_provider_ios
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.10"
|
version: "2.0.11"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -740,7 +796,7 @@ packages:
|
|||||||
name: provider
|
name: provider
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "6.0.3"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -758,12 +814,10 @@ packages:
|
|||||||
qr_code_scanner:
|
qr_code_scanner:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
name: qr_code_scanner
|
||||||
ref: fix_break_changes_platform
|
url: "https://pub.flutter-io.cn"
|
||||||
resolved-ref: "0feca6f15042c279ff575c559a3430df917b623d"
|
source: hosted
|
||||||
url: "https://github.com/Heap-Hop/qr_code_scanner.git"
|
version: "1.0.0"
|
||||||
source: git
|
|
||||||
version: "0.7.0"
|
|
||||||
quiver:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -771,6 +825,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
|
rxdart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rxdart
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.27.5"
|
||||||
screen_retriever:
|
screen_retriever:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -847,7 +908,7 @@ packages:
|
|||||||
name: shelf
|
name: shelf
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.2"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -881,6 +942,20 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
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:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -909,6 +984,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
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:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -937,13 +1019,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
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:
|
tray_manager:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1035,6 +1110,41 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
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:
|
visibility_detector:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -31,7 +31,7 @@ dependencies:
|
|||||||
ffi: ^1.1.2
|
ffi: ^1.1.2
|
||||||
path_provider: ^2.0.2
|
path_provider: ^2.0.2
|
||||||
external_path: ^1.0.1
|
external_path: ^1.0.1
|
||||||
provider: ^5.0.0
|
provider: ^6.0.3
|
||||||
tuple: ^2.0.0
|
tuple: ^2.0.0
|
||||||
wakelock: ^0.5.2
|
wakelock: ^0.5.2
|
||||||
device_info_plus: ^3.2.3
|
device_info_plus: ^3.2.3
|
||||||
@ -40,15 +40,12 @@ dependencies:
|
|||||||
url_launcher: ^6.0.9
|
url_launcher: ^6.0.9
|
||||||
shared_preferences: ^2.0.6
|
shared_preferences: ^2.0.6
|
||||||
toggle_switch: ^1.4.0
|
toggle_switch: ^1.4.0
|
||||||
dash_chat: ^1.1.16
|
dash_chat_2: ^0.0.12
|
||||||
draggable_float_widget: ^0.0.2
|
draggable_float_widget: ^0.0.2
|
||||||
settings_ui: ^2.0.2
|
settings_ui: ^2.0.2
|
||||||
flutter_breadcrumb: ^1.0.1
|
flutter_breadcrumb: ^1.0.1
|
||||||
http: ^0.13.4
|
http: ^0.13.4
|
||||||
qr_code_scanner:
|
qr_code_scanner: ^1.0.0
|
||||||
git:
|
|
||||||
url: https://github.com/Heap-Hop/qr_code_scanner.git
|
|
||||||
ref: fix_break_changes_platform
|
|
||||||
zxing2: ^0.1.0
|
zxing2: ^0.1.0
|
||||||
image_picker: ^0.8.5
|
image_picker: ^0.8.5
|
||||||
image: ^3.1.3
|
image: ^3.1.3
|
||||||
|
@ -64,6 +64,7 @@ message LoginRequest {
|
|||||||
}
|
}
|
||||||
bool video_ack_required = 9;
|
bool video_ack_required = 9;
|
||||||
uint64 session_id = 10;
|
uint64 session_id = 10;
|
||||||
|
string version = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ChatMessage { string text = 1; }
|
message ChatMessage { string text = 1; }
|
||||||
|
@ -276,7 +276,7 @@ impl TransferJob {
|
|||||||
show_hidden: bool,
|
show_hidden: bool,
|
||||||
is_remote: bool,
|
is_remote: bool,
|
||||||
files: Vec<FileEntry>,
|
files: Vec<FileEntry>,
|
||||||
enable_override_detection: bool,
|
enable_overwrite_detection: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
log::info!("new write {}", path);
|
log::info!("new write {}", path);
|
||||||
let total_size = files.iter().map(|x| x.size as u64).sum();
|
let total_size = files.iter().map(|x| x.size as u64).sum();
|
||||||
@ -289,7 +289,7 @@ impl TransferJob {
|
|||||||
is_remote,
|
is_remote,
|
||||||
files,
|
files,
|
||||||
total_size,
|
total_size,
|
||||||
enable_overwrite_detection: enable_override_detection,
|
enable_overwrite_detection,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,7 +301,7 @@ impl TransferJob {
|
|||||||
file_num: i32,
|
file_num: i32,
|
||||||
show_hidden: bool,
|
show_hidden: bool,
|
||||||
is_remote: bool,
|
is_remote: bool,
|
||||||
enable_override_detection: bool,
|
enable_overwrite_detection: bool,
|
||||||
) -> ResultType<Self> {
|
) -> ResultType<Self> {
|
||||||
log::info!("new read {}", path);
|
log::info!("new read {}", path);
|
||||||
let files = get_recursive_files(&path, show_hidden)?;
|
let files = get_recursive_files(&path, show_hidden)?;
|
||||||
@ -315,7 +315,7 @@ impl TransferJob {
|
|||||||
is_remote,
|
is_remote,
|
||||||
files,
|
files,
|
||||||
total_size,
|
total_size,
|
||||||
enable_overwrite_detection: enable_override_detection,
|
enable_overwrite_detection,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1033,10 +1033,6 @@ impl LoginConfigHandler {
|
|||||||
msg.lock_after_session_end = BoolOption::Yes.into();
|
msg.lock_after_session_end = BoolOption::Yes.into();
|
||||||
n += 1;
|
n += 1;
|
||||||
}
|
}
|
||||||
if self.get_toggle_option("privacy-mode") {
|
|
||||||
msg.privacy_mode = BoolOption::Yes.into();
|
|
||||||
n += 1;
|
|
||||||
}
|
|
||||||
if self.get_toggle_option("disable-audio") {
|
if self.get_toggle_option("disable-audio") {
|
||||||
msg.disable_audio = BoolOption::Yes.into();
|
msg.disable_audio = BoolOption::Yes.into();
|
||||||
n += 1;
|
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.
|
/// Parse the image quality option.
|
||||||
/// Return [`ImageQuality`] if the option is valid, otherwise return `None`.
|
/// Return [`ImageQuality`] if the option is valid, otherwise return `None`.
|
||||||
///
|
///
|
||||||
@ -1277,6 +1290,7 @@ impl LoginConfigHandler {
|
|||||||
my_name: crate::username(),
|
my_name: crate::username(),
|
||||||
option: self.get_option_message(true).into(),
|
option: self.get_option_message(true).into(),
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
|
version: crate::VERSION.to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if self.is_file_transfer {
|
if self.is_file_transfer {
|
||||||
|
@ -3,7 +3,10 @@ use std::{
|
|||||||
time::Instant,
|
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 MAX_LATENCY: i64 = 500;
|
||||||
const MIN_LATENCY: i64 = 100;
|
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>,
|
||||||
|
}
|
||||||
|
283
src/flutter.rs
283
src/flutter.rs
@ -1,6 +1,9 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
sync::{Arc, Mutex, RwLock},
|
sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc, Mutex, RwLock,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
|
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
|
||||||
@ -114,6 +117,9 @@ impl Session {
|
|||||||
}
|
}
|
||||||
lc.set_option(name, value);
|
lc.set_option(name, value);
|
||||||
}
|
}
|
||||||
|
// TODO
|
||||||
|
// input_os_password
|
||||||
|
// restart_remote_device
|
||||||
|
|
||||||
/// Input the OS password.
|
/// Input the OS password.
|
||||||
pub fn input_os_password(&self, pass: String, activate: bool) {
|
pub fn input_os_password(&self, pass: String, activate: bool) {
|
||||||
@ -505,6 +511,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 {}
|
impl FileManager for Session {}
|
||||||
@ -599,7 +625,14 @@ impl Interface for Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
|
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
|
||||||
handle_test_delay(t, peer).await;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,6 +647,9 @@ struct Connection {
|
|||||||
write_jobs: Vec<fs::TransferJob>,
|
write_jobs: Vec<fs::TransferJob>,
|
||||||
timer: Interval,
|
timer: Interval,
|
||||||
last_update_jobs_status: (Instant, HashMap<i32, u64>),
|
last_update_jobs_status: (Instant, HashMap<i32, u64>),
|
||||||
|
data_count: Arc<AtomicUsize>,
|
||||||
|
frame_count: Arc<AtomicUsize>,
|
||||||
|
video_format: CodecFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
@ -646,6 +682,9 @@ impl Connection {
|
|||||||
write_jobs: Vec::new(),
|
write_jobs: Vec::new(),
|
||||||
timer: time::interval(SEC30),
|
timer: time::interval(SEC30),
|
||||||
last_update_jobs_status: (Instant::now(), Default::default()),
|
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 key = Config::get_option("key");
|
||||||
let token = Config::get_option("access_token");
|
let token = Config::get_option("access_token");
|
||||||
@ -659,6 +698,9 @@ impl Connection {
|
|||||||
("direct", &direct.to_string()),
|
("direct", &direct.to_string()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut status_timer = time::interval(Duration::new(1, 0));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
res = peer.next() => {
|
res = peer.next() => {
|
||||||
@ -671,14 +713,20 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
Ok(ref bytes) => {
|
Ok(ref bytes) => {
|
||||||
last_recv_time = Instant::now();
|
last_recv_time = Instant::now();
|
||||||
|
conn.data_count.fetch_add(bytes.len(), Ordering::Relaxed);
|
||||||
if !conn.handle_msg_from_peer(bytes, &mut peer).await {
|
if !conn.handle_msg_from_peer(bytes, &mut peer).await {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log::info!("Reset by the peer");
|
if session.lc.read().unwrap().restarting_remote_device {
|
||||||
session.msgbox("error", "Connection Error", "Reset by the peer");
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -704,6 +752,16 @@ impl Connection {
|
|||||||
conn.timer = time::interval_at(Instant::now() + SEC30, SEC30);
|
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);
|
log::debug!("Exit io_loop of id={}", session.id);
|
||||||
@ -725,6 +783,14 @@ impl Connection {
|
|||||||
if !self.first_frame {
|
if !self.first_frame {
|
||||||
self.first_frame = true;
|
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) {
|
if let Ok(true) = self.video_handler.handle_frame(vf) {
|
||||||
let stream = self.session.events2ui.read().unwrap();
|
let stream = self.session.events2ui.read().unwrap();
|
||||||
stream.add(EventToUI::Rgba(ZeroCopyBuffer(
|
stream.add(EventToUI::Rgba(ZeroCopyBuffer(
|
||||||
@ -786,113 +852,114 @@ impl Connection {
|
|||||||
vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
|
vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some(message::Union::FileResponse(fr)) => match fr.union {
|
Some(message::Union::FileResponse(fr)) => {
|
||||||
Some(file_response::Union::Dir(fd)) => {
|
match fr.union {
|
||||||
let mut entries = fd.entries.to_vec();
|
Some(file_response::Union::Dir(fd)) => {
|
||||||
if self.session.peer_platform() == "Windows" {
|
let mut entries = fd.entries.to_vec();
|
||||||
fs::transform_windows_path(&mut entries);
|
if self.session.peer_platform() == "Windows" {
|
||||||
}
|
fs::transform_windows_path(&mut entries);
|
||||||
let id = fd.id;
|
}
|
||||||
self.session.push_event(
|
let id = fd.id;
|
||||||
"file_dir",
|
self.session.push_event(
|
||||||
vec![("value", &make_fd_to_json(fd)), ("is_local", "false")],
|
"file_dir",
|
||||||
);
|
vec![("value", &make_fd_to_json(fd)), ("is_local", "false")],
|
||||||
if let Some(job) = fs::get_job(id, &mut self.write_jobs) {
|
);
|
||||||
job.set_files(entries);
|
if let Some(job) = fs::get_job(id, &mut self.write_jobs) {
|
||||||
}
|
job.set_files(entries);
|
||||||
}
|
|
||||||
Some(file_response::Union::Block(block)) => {
|
|
||||||
if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
|
||||||
if let Err(_err) = job.write(block, None).await {
|
|
||||||
// to-do: add "skip" for writing job
|
|
||||||
}
|
}
|
||||||
self.update_jobs_status();
|
|
||||||
}
|
}
|
||||||
}
|
Some(file_response::Union::Block(block)) => {
|
||||||
Some(file_response::Union::Done(d)) => {
|
if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
||||||
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
|
if let Err(_err) = job.write(block, None).await {
|
||||||
job.modify_time();
|
// to-do: add "skip" for writing job
|
||||||
fs::remove_job(d.id, &mut self.write_jobs);
|
}
|
||||||
|
self.update_jobs_status();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.handle_job_status(d.id, d.file_num, None);
|
Some(file_response::Union::Done(d)) => {
|
||||||
}
|
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
|
||||||
Some(file_response::Union::Error(e)) => {
|
job.modify_time();
|
||||||
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
fs::remove_job(d.id, &mut self.write_jobs);
|
||||||
}
|
}
|
||||||
Some(file_response::Union::Digest(digest)) => {
|
self.handle_job_status(d.id, d.file_num, None);
|
||||||
if digest.is_upload {
|
}
|
||||||
if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) {
|
Some(file_response::Union::Error(e)) => {
|
||||||
if let Some(file) = job.files().get(digest.file_num as usize) {
|
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
||||||
let read_path = get_string(&job.join(&file.name));
|
}
|
||||||
let overwrite_strategy = job.default_overwrite_strategy();
|
Some(file_response::Union::Digest(digest)) => {
|
||||||
if let Some(overwrite) = overwrite_strategy {
|
if digest.is_upload {
|
||||||
let req = FileTransferSendConfirmRequest {
|
if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) {
|
||||||
id: digest.id,
|
if let Some(file) = job.files().get(digest.file_num as usize) {
|
||||||
file_num: digest.file_num,
|
let read_path = get_string(&job.join(&file.name));
|
||||||
union: Some(if overwrite {
|
let overwrite_strategy = job.default_overwrite_strategy();
|
||||||
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
if let Some(overwrite) = overwrite_strategy {
|
||||||
} else {
|
let req = FileTransferSendConfirmRequest {
|
||||||
file_transfer_send_confirm_request::Union::Skip(
|
id: digest.id,
|
||||||
true,
|
file_num: digest.file_num,
|
||||||
)
|
union: Some(if overwrite {
|
||||||
}),
|
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
||||||
..Default::default()
|
} else {
|
||||||
};
|
file_transfer_send_confirm_request::Union::Skip(
|
||||||
job.confirm(&req);
|
true,
|
||||||
let msg = new_send_confirm(req);
|
)
|
||||||
allow_err!(peer.send(&msg).await);
|
}),
|
||||||
} else {
|
..Default::default()
|
||||||
self.handle_override_file_confirm(
|
};
|
||||||
digest.id,
|
job.confirm(&req);
|
||||||
digest.file_num,
|
let msg = new_send_confirm(req);
|
||||||
read_path,
|
allow_err!(peer.send(&msg).await);
|
||||||
true,
|
} else {
|
||||||
);
|
self.handle_override_file_confirm(
|
||||||
|
digest.id,
|
||||||
|
digest.file_num,
|
||||||
|
read_path,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) {
|
||||||
if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) {
|
if let Some(file) = job.files().get(digest.file_num as usize) {
|
||||||
if let Some(file) = job.files().get(digest.file_num as usize) {
|
let write_path = get_string(&job.join(&file.name));
|
||||||
let write_path = get_string(&job.join(&file.name));
|
let overwrite_strategy = job.default_overwrite_strategy();
|
||||||
let overwrite_strategy = job.default_overwrite_strategy();
|
match fs::is_write_need_confirmation(&write_path, &digest) {
|
||||||
match fs::is_write_need_confirmation(&write_path, &digest) {
|
Ok(res) => match res {
|
||||||
Ok(res) => match res {
|
DigestCheckResult::IsSame => {
|
||||||
DigestCheckResult::IsSame => {
|
let msg= new_send_confirm(FileTransferSendConfirmRequest {
|
||||||
let msg= new_send_confirm(FileTransferSendConfirmRequest {
|
|
||||||
id: digest.id,
|
id: digest.id,
|
||||||
file_num: digest.file_num,
|
file_num: digest.file_num,
|
||||||
union: Some(file_transfer_send_confirm_request::Union::Skip(true)),
|
union: Some(file_transfer_send_confirm_request::Union::Skip(true)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.session.send_msg(msg);
|
|
||||||
}
|
|
||||||
DigestCheckResult::NeedConfirm(digest) => {
|
|
||||||
if let Some(overwrite) = overwrite_strategy {
|
|
||||||
let msg = new_send_confirm(
|
|
||||||
FileTransferSendConfirmRequest {
|
|
||||||
id: digest.id,
|
|
||||||
file_num: digest.file_num,
|
|
||||||
union: Some(if overwrite {
|
|
||||||
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
|
||||||
} else {
|
|
||||||
file_transfer_send_confirm_request::Union::Skip(true)
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.session.send_msg(msg);
|
self.session.send_msg(msg);
|
||||||
} else {
|
|
||||||
self.handle_override_file_confirm(
|
|
||||||
digest.id,
|
|
||||||
digest.file_num,
|
|
||||||
write_path.to_string(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
DigestCheckResult::NeedConfirm(digest) => {
|
||||||
DigestCheckResult::NoSuchFile => {
|
if let Some(overwrite) = overwrite_strategy {
|
||||||
let msg = new_send_confirm(
|
let msg = new_send_confirm(
|
||||||
|
FileTransferSendConfirmRequest {
|
||||||
|
id: digest.id,
|
||||||
|
file_num: digest.file_num,
|
||||||
|
union: Some(if overwrite {
|
||||||
|
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
||||||
|
} else {
|
||||||
|
file_transfer_send_confirm_request::Union::Skip(true)
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.session.send_msg(msg);
|
||||||
|
} else {
|
||||||
|
self.handle_override_file_confirm(
|
||||||
|
digest.id,
|
||||||
|
digest.file_num,
|
||||||
|
write_path.to_string(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DigestCheckResult::NoSuchFile => {
|
||||||
|
let msg = new_send_confirm(
|
||||||
FileTransferSendConfirmRequest {
|
FileTransferSendConfirmRequest {
|
||||||
id: digest.id,
|
id: digest.id,
|
||||||
file_num: digest.file_num,
|
file_num: digest.file_num,
|
||||||
@ -900,19 +967,20 @@ impl Connection {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.session.send_msg(msg);
|
self.session.send_msg(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
println!("error recving digest: {}", err);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
println!("error recving digest: {}", err);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
|
||||||
Some(message::Union::Misc(misc)) => match misc.union {
|
Some(message::Union::Misc(misc)) => match misc.union {
|
||||||
Some(misc::Union::AudioFormat(f)) => {
|
Some(misc::Union::AudioFormat(f)) => {
|
||||||
self.audio_handler.handle_format(f); //
|
self.audio_handler.handle_format(f); //
|
||||||
@ -931,6 +999,7 @@ impl Connection {
|
|||||||
Permission::Keyboard => "keyboard",
|
Permission::Keyboard => "keyboard",
|
||||||
Permission::Clipboard => "clipboard",
|
Permission::Clipboard => "clipboard",
|
||||||
Permission::Audio => "audio",
|
Permission::Audio => "audio",
|
||||||
|
Permission::Restart => "restart",
|
||||||
_ => "",
|
_ => "",
|
||||||
},
|
},
|
||||||
&p.enabled.to_string(),
|
&p.enabled.to_string(),
|
||||||
@ -1608,8 +1677,8 @@ pub mod connection_manager {
|
|||||||
id,
|
id,
|
||||||
file_num,
|
file_num,
|
||||||
mut files,
|
mut files,
|
||||||
|
overwrite_detection,
|
||||||
} => {
|
} => {
|
||||||
// in mobile, can_enable_override_detection is always true
|
|
||||||
WRITE_JOBS.lock().unwrap().push(fs::TransferJob::new_write(
|
WRITE_JOBS.lock().unwrap().push(fs::TransferJob::new_write(
|
||||||
id,
|
id,
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
@ -1625,7 +1694,7 @@ pub mod connection_manager {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
true,
|
overwrite_detection,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ipc::FS::CancelWrite { id } => {
|
ipc::FS::CancelWrite { id } => {
|
||||||
|
@ -644,25 +644,6 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co
|
|||||||
// res = Session::get_option(arg);
|
// 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
|
// File Action
|
||||||
"get_home_dir" => {
|
"get_home_dir" => {
|
||||||
res = fs::get_home_as_string();
|
res = fs::get_home_as_string();
|
||||||
@ -683,6 +664,33 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// Server Side
|
// 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")))]
|
#[cfg(not(any(target_os = "ios")))]
|
||||||
"clients_state" => {
|
"clients_state" => {
|
||||||
res = get_clients_state();
|
res = get_clients_state();
|
||||||
@ -862,9 +870,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" => {
|
// "input_os_password" => {
|
||||||
// Session::input_os_password(value.to_owned(), true);
|
// Session::input_os_password(value.to_owned(), true);
|
||||||
// }
|
// }
|
||||||
|
"restart_remote_device" => {
|
||||||
|
// TODO
|
||||||
|
// Session::restart_remote_device();
|
||||||
|
}
|
||||||
// // File Action
|
// // File Action
|
||||||
// "read_remote_dir" => {
|
// "read_remote_dir" => {
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
||||||
|
@ -60,6 +60,7 @@ pub enum FS {
|
|||||||
id: i32,
|
id: i32,
|
||||||
file_num: i32,
|
file_num: i32,
|
||||||
files: Vec<(String, u64)>,
|
files: Vec<(String, u64)>,
|
||||||
|
overwrite_detection: bool,
|
||||||
},
|
},
|
||||||
CancelWrite {
|
CancelWrite {
|
||||||
id: i32,
|
id: i32,
|
||||||
|
21
src/lang.rs
21
src/lang.rs
@ -8,17 +8,18 @@ mod de;
|
|||||||
mod en;
|
mod en;
|
||||||
mod eo;
|
mod eo;
|
||||||
mod es;
|
mod es;
|
||||||
mod hu;
|
|
||||||
mod fr;
|
mod fr;
|
||||||
|
mod hu;
|
||||||
mod id;
|
mod id;
|
||||||
mod it;
|
mod it;
|
||||||
|
mod ja;
|
||||||
|
mod pl;
|
||||||
mod ptbr;
|
mod ptbr;
|
||||||
mod ru;
|
mod ru;
|
||||||
mod sk;
|
mod sk;
|
||||||
mod tr;
|
mod tr;
|
||||||
mod tw;
|
mod tw;
|
||||||
mod vn;
|
mod vn;
|
||||||
mod pl;
|
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref LANGS: Value =
|
pub static ref LANGS: Value =
|
||||||
@ -41,6 +42,7 @@ lazy_static::lazy_static! {
|
|||||||
("tr", "Türkçe"),
|
("tr", "Türkçe"),
|
||||||
("vn", "Tiếng Việt"),
|
("vn", "Tiếng Việt"),
|
||||||
("pl", "Polski"),
|
("pl", "Polski"),
|
||||||
|
("ja", "日本語"),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,16 +89,19 @@ pub fn translate_locale(name: String, locale: &str) -> String {
|
|||||||
"sk" => sk::T.deref(),
|
"sk" => sk::T.deref(),
|
||||||
"vn" => vn::T.deref(),
|
"vn" => vn::T.deref(),
|
||||||
"pl" => pl::T.deref(),
|
"pl" => pl::T.deref(),
|
||||||
|
"ja" => ja::T.deref(),
|
||||||
_ => en::T.deref(),
|
_ => en::T.deref(),
|
||||||
};
|
};
|
||||||
if let Some(v) = m.get(&name as &str) {
|
if let Some(v) = m.get(&name as &str) {
|
||||||
v.to_string()
|
if v.is_empty() {
|
||||||
} else {
|
if lang != "en" {
|
||||||
if lang != "en" {
|
if let Some(v) = en::T.get(&name as &str) {
|
||||||
if let Some(v) = en::T.get(&name as &str) {
|
return v.to_string();
|
||||||
return v.to_string();
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return v.to_string();
|
||||||
}
|
}
|
||||||
name
|
|
||||||
}
|
}
|
||||||
|
name
|
||||||
}
|
}
|
||||||
|
303
src/lang/ja.rs
Normal file
303
src/lang/ja.rs
Normal 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();
|
||||||
|
}
|
@ -1099,8 +1099,9 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
Some(file_action::Union::Send(s)) => {
|
Some(file_action::Union::Send(s)) => {
|
||||||
let id = s.id;
|
let id = s.id;
|
||||||
let od =
|
let od = can_enable_overwrite_detection(get_version_number(
|
||||||
can_enable_overwrite_detection(get_version_number(VERSION));
|
&self.lr.version,
|
||||||
|
));
|
||||||
let path = s.path.clone();
|
let path = s.path.clone();
|
||||||
match fs::TransferJob::new_read(
|
match fs::TransferJob::new_read(
|
||||||
id,
|
id,
|
||||||
@ -1123,6 +1124,11 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(file_action::Union::Receive(r)) => {
|
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 {
|
self.send_fs(ipc::FS::NewWrite {
|
||||||
path: r.path,
|
path: r.path,
|
||||||
id: r.id,
|
id: r.id,
|
||||||
@ -1133,6 +1139,7 @@ impl Connection {
|
|||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|f| (f.name, f.modified_time))
|
.map(|f| (f.name, f.modified_time))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
overwrite_detection: od,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some(file_action::Union::RemoveDir(d)) => {
|
Some(file_action::Union::RemoveDir(d)) => {
|
||||||
|
@ -160,8 +160,8 @@ impl ConnectionManager {
|
|||||||
id,
|
id,
|
||||||
file_num,
|
file_num,
|
||||||
mut files,
|
mut files,
|
||||||
|
overwrite_detection
|
||||||
} => {
|
} => {
|
||||||
let od = can_enable_overwrite_detection(get_version_number(VERSION));
|
|
||||||
// cm has no show_hidden context
|
// cm has no show_hidden context
|
||||||
// dummy remote, show_hidden, is_remote
|
// dummy remote, show_hidden, is_remote
|
||||||
write_jobs.push(fs::TransferJob::new_write(
|
write_jobs.push(fs::TransferJob::new_write(
|
||||||
@ -179,7 +179,7 @@ impl ConnectionManager {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
od,
|
overwrite_detection,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ipc::FS::CancelWrite { id } => {
|
ipc::FS::CancelWrite { id } => {
|
||||||
|
@ -23,10 +23,6 @@ use clipboard::{
|
|||||||
get_rx_clip_client, server_clip_file,
|
get_rx_clip_client, server_clip_file,
|
||||||
};
|
};
|
||||||
use enigo::{self, Enigo, KeyboardControllable};
|
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::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
config::{Config, LocalConfig, PeerConfig},
|
config::{Config, LocalConfig, PeerConfig},
|
||||||
@ -43,6 +39,13 @@ use hbb_common::{
|
|||||||
Stream,
|
Stream,
|
||||||
};
|
};
|
||||||
use hbb_common::{config::TransferSerde, fs::TransferJobMeta};
|
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)]
|
#[cfg(windows)]
|
||||||
use crate::clipboard_file::*;
|
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 {
|
impl Handler {
|
||||||
pub fn new(cmd: String, id: String, password: String, args: Vec<String>) -> Self {
|
pub fn new(cmd: String, id: String, password: String, args: Vec<String>) -> Self {
|
||||||
let me = Self {
|
let me = Self {
|
||||||
@ -638,8 +632,9 @@ impl Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn restart_remote_device(&mut self) {
|
fn restart_remote_device(&mut self) {
|
||||||
self.lc.write().unwrap().restarting_remote_device = true;
|
let mut lc = self.lc.write().unwrap();
|
||||||
let msg = self.lc.write().unwrap().restart_remote_device();
|
lc.restarting_remote_device = true;
|
||||||
|
let msg = lc.restart_remote_device();
|
||||||
self.send(Data::Message(msg));
|
self.send(Data::Message(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2076,6 +2071,22 @@ impl Remote {
|
|||||||
true
|
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 {
|
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
|
||||||
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
|
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
|
||||||
match msg_in.union {
|
match msg_in.union {
|
||||||
@ -2084,6 +2095,7 @@ impl Remote {
|
|||||||
self.first_frame = true;
|
self.first_frame = true;
|
||||||
self.handler.call2("closeSuccess", &make_args!());
|
self.handler.call2("closeSuccess", &make_args!());
|
||||||
self.handler.call("adaptSize", &make_args!());
|
self.handler.call("adaptSize", &make_args!());
|
||||||
|
self.send_opts_after_login(peer).await;
|
||||||
}
|
}
|
||||||
let incomming_format = CodecFormat::from(&vf);
|
let incomming_format = CodecFormat::from(&vf);
|
||||||
if self.video_format != incomming_format {
|
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("hostname", pi.hostname.clone());
|
||||||
pi_sciter.set_item("platform", pi.platform.clone());
|
pi_sciter.set_item("platform", pi.platform.clone());
|
||||||
pi_sciter.set_item("sas_enabled", pi.sas_enabled);
|
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 self.is_file_transfer() {
|
||||||
if pi.username.is_empty() {
|
if pi.username.is_empty() {
|
||||||
self.on_error("No active console user logged on, please connect and logon first.");
|
self.on_error("No active console user logged on, please connect and logon first.");
|
||||||
|
Loading…
Reference in New Issue
Block a user