74 Commits

90 changed files with 2279 additions and 1507 deletions

1
.gitignore vendored
View File

@@ -54,3 +54,4 @@ examples/**/target/
vcpkg_installed
flutter/lib/generated_plugin_registrant.dart
libsciter.dylib
flutter/web/

View File

@@ -283,6 +283,8 @@ def generate_control_file(version):
system2('/bin/rm -rf %s' % control_file_path)
content = """Package: rustdesk
Section: net
Priority: optional
Version: %s
Architecture: %s
Maintainer: rustdesk <info@rustdesk.com>

View File

@@ -1,20 +1,18 @@
<p align="center">
<img src="../res/logo-header.svg" alt="RustDesk - Ваша віддалена стільниця"><br>
<a href="#безкоштовні-загальнодоступні-сервери">Сервери</a> •
<a href="#публічні-сервери">Сервери</a> •
<a href="#кроки-для-збірки">Збирання</a> •
<a href="#як-зібрати-за-допомогою-docker">Docker</a> •
<a href="#структура-файлів">Структура</a> •
<a href="#знімки">Знімки</a><br>
[<a href="../README.md">English</a>] | [<a href="docs/README-CS.md">česky</a>] | [<a href="docs/README-ZH.md">中文</a>] | [<a href="docs/README-HU.md">Magyar</a>] | [<a href="docs/README-ES.md">Español</a>] | [<a href="docs/README-FA.md">فارسی</a>] | [<a href="docs/README-FR.md">Français</a>] | [<a href="docs/README-DE.md">Deutsch</a>] | [<a href="docs/README-PL.md">Polski</a>] | [<a href="docs/README-ID.md">Indonesian</a>] | [<a href="docs/README-FI.md">Suomi</a>] | [<a href="docs/README-ML.md">മലയാളം</a>] | [<a href="docs/README-JP.md">日本語</a>] | [<a href="docs/README-NL.md">Nederlands</a>] | [<a href="docs/README-IT.md">Italiano</a>] | [<a href="docs/README-RU.md">Русский</a>] | [<a href="docs/README-PTBR.md">Português (Brasil)</a>] | [<a href="docs/README-EO.md">Esperanto</a>] | [<a href="docs/README-KR.md">한국어</a>] | [<a href="docs/README-AR.md">العربي</a>] | [<a href="docs/README-VN.md">Tiếng Việt</a>] | [<a href="docs/README-DA.md">Dansk</a>] | [<a href="docs/README-GR.md">Ελληνικά</a>] | [<a href="docs/README-TR.md">Türkçe</a>]<br>
<b>Нам потрібна ваша допомога для перекладу цього README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">інтерфейсу</a> та <a href="https://github.com/rustdesk/doc.rustdesk.com">документації</a> RustDesk на вашу рідну мову</B>
<a href="#знімки-екрана">Знімки екрана</a><br>
[<a href="../README.md">English</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-DA.md">Dansk</a>] | [<a href="README-GR.md">Ελληνικά</a>] | [<a href="README-TR.md">Türkçe</a>]<br>
<b>Нам потрібна ваша допомога для перекладу цього README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">інтерфейсу</a> та <a href="https://github.com/rustdesk/doc.rustdesk.com">документації</a> RustDesk вашою рідною мовою</B>
</p>
Спілкування з нами: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
[![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Frustdesk%2Fbounties%3Fstatus%3Dopen)](https://console.algora.io/org/rustdesk/bounties?status=open)
Ще один застосунок для віддаленого керування стільницею, написаний на Rust. Працює з коробки, не потребує налаштування. Ви повністю контролюєте свої дані, не турбуючись про безпеку. Ви можете використовувати наш сервер ретрансляції, [налаштувати свій власний](https://rustdesk.com/server), або [написати свій власний сервер ретрансляції](https://github.com/rustdesk/rustdesk-server-demo).
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
@@ -61,19 +59,19 @@ RustDesk вітає внесок кожного. Ознайомтеся з [CONT
```sh
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libpam0g-dev
```
### openSUSE Tumbleweed
```sh
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel pam-devel
```
### Fedora 28 (CentOS 8)
```sh
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel gstreamer1-devel gstreamer1-plugins-base-devel pam-devel
```
### Arch (Manjaro)
@@ -158,18 +156,22 @@ target/release/rustdesk
- **[libs/clipboard](https://github.com/rustdesk/rustdesk/tree/master/libs/clipboard)**: реалізація копіювання та вставлення файлів для Windows, Linux, macOS.
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: графічний інтерфейс користувача
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: сервіси аудіо/буфера обміну/вводу/відео та мережевих підключень
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: однорангове з'єднання
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: комунікація з [rustdesk-server](https://github.com/rustdesk/rustdesk-server), очікування віддаленого прямого (обхід TCP NAT) або ретрансльованого з'єднання
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: однорангове зʼєднання
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: комунікація з [rustdesk-server](https://github.com/rustdesk/rustdesk-server), очікування віддаленого прямого (обхід TCP NAT) або ретрансльованого зʼєднання
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: специфічний для платформи код
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: код Flutter для мобільних пристроїв
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: JavaScript для Flutter веб клієнту
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: JavaScript для веб клієнта на Flutter
## Знімки
## Знімки екрана
![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png)
![Менеджер зʼєднань](https://github.com/rustdesk/rustdesk/assets/28412477/db82d4e7-c4bc-4823-8e6f-6af7eadf7651)
![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png)
![Підключення до ПК з Windows](https://github.com/rustdesk/rustdesk/assets/28412477/9baa91e9-3362-4d06-aa1a-7518edcbd7ea)
![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png)
![Передача файлів](https://github.com/rustdesk/rustdesk/assets/28412477/39511ad3-aa9a-4f8c-8947-1cce286a46ad)
![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png)
![Тунелювання TCP](https://github.com/rustdesk/rustdesk/assets/28412477/78e8708f-e87e-4570-8373-1360033ea6c5)
## [Публічні сервери](#публічні-сервери)
RustDesk підтримується безкоштовним європейським сервером, любʼязно наданим [Codext GmbH](https://codext.link/rustdesk?utm_source=github)

View File

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

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="-4 -4 32 32" width="24px" fill="#5f6368"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/></svg>

After

Width:  |  Height:  |  Size: 277 B

View File

@@ -30,6 +30,7 @@ import 'common/widgets/overlay.dart';
import 'mobile/pages/file_manager_page.dart';
import 'mobile/pages/remote_page.dart';
import 'desktop/pages/remote_page.dart' as desktop_remote;
import 'desktop/pages/file_manager_page.dart' as desktop_file_manager;
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
import 'models/model.dart';
import 'models/platform_model.dart';
@@ -554,7 +555,7 @@ class MyTheme {
return themeModeFromString(bind.mainGetLocalOption(key: kCommConfKeyTheme));
}
static void changeDarkMode(ThemeMode mode) async {
static Future<void> changeDarkMode(ThemeMode mode) async {
Get.changeThemeMode(mode);
if (desktopType == DesktopType.main || isAndroid || isIOS || isWeb) {
if (mode == ThemeMode.system) {
@@ -680,10 +681,12 @@ closeConnection({String? id}) {
overlays: SystemUiOverlay.values);
gFFI.chatModel.hideChatOverlay();
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
stateGlobal.isInMainPage = true;
}();
} else {
if (isWeb) {
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
stateGlobal.isInMainPage = true;
} else {
final controller = Get.find<DesktopTabController>();
controller.closeBy(id);
@@ -2035,6 +2038,8 @@ Future<bool> restoreWindowPosition(WindowType type,
return false;
}
var webInitialLink = "";
/// Initialize uni links for macos/windows
///
/// [Availability]
@@ -2051,7 +2056,12 @@ Future<bool> initUniLinks() async {
if (initialLink == null || initialLink.isEmpty) {
return false;
}
return handleUriLink(uriString: initialLink);
if (isWeb) {
webInitialLink = initialLink;
return false;
} else {
return handleUriLink(uriString: initialLink);
}
} catch (err) {
debugPrintStack(label: "$err");
return false;
@@ -2064,7 +2074,7 @@ Future<bool> initUniLinks() async {
///
/// Returns a [StreamSubscription] which can listen the uni links.
StreamSubscription? listenUniLinks({handleByFlutter = true}) {
if (isLinux) {
if (isLinux || isWeb) {
return null;
}
@@ -2368,20 +2378,35 @@ connect(BuildContext context, String id,
}
} else {
if (isFileTransfer) {
if (!await AndroidPermissionManager.check(kManageExternalStorage)) {
if (!await AndroidPermissionManager.request(kManageExternalStorage)) {
return;
if (isAndroid) {
if (!await AndroidPermissionManager.check(kManageExternalStorage)) {
if (!await AndroidPermissionManager.request(kManageExternalStorage)) {
return;
}
}
}
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => FileManagerPage(
id: id, password: password, isSharedPassword: isSharedPassword),
),
);
if (isWeb) {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) =>
desktop_file_manager.FileManagerPage(
id: id,
password: password,
isSharedPassword: isSharedPassword),
),
);
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => FileManagerPage(
id: id, password: password, isSharedPassword: isSharedPassword),
),
);
}
} else {
if (isWebDesktop) {
if (isWeb) {
Navigator.push(
context,
MaterialPageRoute(
@@ -2405,6 +2430,7 @@ connect(BuildContext context, String id,
);
}
}
stateGlobal.isInMainPage = false;
}
FocusScopeNode currentFocus = FocusScope.of(context);
@@ -3158,6 +3184,9 @@ importConfig(List<TextEditingController>? controllers, List<RxString>? errMsgs,
if (text != null && text.isNotEmpty) {
try {
final sc = ServerConfig.decode(text);
if (isWeb || isIOS) {
sc.relayServer = '';
}
if (sc.idServer.isNotEmpty) {
Future<bool> success = setServerConfig(controllers, errMsgs, sc);
success.then((value) {
@@ -3597,3 +3626,7 @@ List<SubWindowResizeEdge>? get subWindowManagerEnableResizeEdges => isWindows
SubWindowResizeEdge.topRight,
]
: null;
void earlyAssert() {
assert('\1' == '1');
}

View File

@@ -0,0 +1,38 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../common.dart';
Widget getConnectionPageTitle(BuildContext context, bool isWeb) {
return Row(
children: [
Expanded(
child: Row(
children: [
AutoSizeText(
translate('Control Remote Desktop'),
maxLines: 1,
style: Theme.of(context)
.textTheme
.titleLarge
?.merge(TextStyle(height: 1)),
).marginOnly(right: 4),
Tooltip(
waitDuration: Duration(milliseconds: 300),
message: translate(isWeb ? "web_id_input_tip" : "id_input_tip"),
child: Icon(
Icons.help_outline_outlined,
size: 16,
color: Theme.of(context)
.textTheme
.titleLarge
?.color
?.withOpacity(0.5),
),
),
],
)),
],
);
}

View File

@@ -58,27 +58,33 @@ class _PeerCardState extends State<_PeerCard>
stateGlobal.isPortrait.isTrue ? _buildPortrait() : _buildLandscape());
}
Widget gestureDetector({required Widget child}) {
final PeerTabModel peerTabModel = Provider.of(context);
final peer = super.widget.peer;
return GestureDetector(
onDoubleTap: peerTabModel.multiSelectionMode
? null
: () => widget.connect(context, peer.id),
onTap: () {
if (peerTabModel.multiSelectionMode) {
peerTabModel.select(peer);
} else {
if (isMobile) {
widget.connect(context, peer.id);
} else {
peerTabModel.select(peer);
}
}
},
onLongPress: () => peerTabModel.select(peer),
child: child);
}
Widget _buildPortrait() {
final peer = super.widget.peer;
final PeerTabModel peerTabModel = Provider.of(context);
return Card(
margin: EdgeInsets.symmetric(horizontal: 2),
child: GestureDetector(
onTap: () {
if (peerTabModel.multiSelectionMode) {
peerTabModel.select(peer);
} else {
if (!isWebDesktop) {
connectInPeerTab(context, peer, widget.tab);
}
}
},
onDoubleTap: isWebDesktop
? () => connectInPeerTab(context, peer, widget.tab)
: null,
onLongPress: () {
peerTabModel.select(peer);
},
child: gestureDetector(
child: Container(
padding: EdgeInsets.only(left: 12, top: 8, bottom: 8),
child: _buildPeerTile(context, peer, null)),
@@ -86,7 +92,6 @@ class _PeerCardState extends State<_PeerCard>
}
Widget _buildLandscape() {
final PeerTabModel peerTabModel = Provider.of(context);
final peer = super.widget.peer;
var deco = Rx<BoxDecoration?>(
BoxDecoration(
@@ -115,30 +120,21 @@ class _PeerCardState extends State<_PeerCard>
),
);
},
child: GestureDetector(
onDoubleTap:
peerTabModel.multiSelectionMode || peerTabModel.isShiftDown
? null
: () => widget.connect(context, peer.id),
onTap: () => peerTabModel.select(peer),
onLongPress: () => peerTabModel.select(peer),
child: gestureDetector(
child: Obx(() => peerCardUiType.value == PeerUiType.grid
? _buildPeerCard(context, peer, deco)
: _buildPeerTile(context, peer, deco))),
);
}
Widget _buildPeerTile(
BuildContext context, Peer peer, Rx<BoxDecoration?>? deco) {
hideUsernameOnCard ??=
bind.mainGetBuildinOption(key: kHideUsernameOnCard) == 'Y';
makeChild(bool isPortrait, Peer peer) {
final name = hideUsernameOnCard == true
? peer.hostname
: '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
final greyStyle = TextStyle(
fontSize: 11,
color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6));
makeChild(bool isPortrait) => Row(
return Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
@@ -210,6 +206,12 @@ class _PeerCardState extends State<_PeerCard>
)
],
);
}
Widget _buildPeerTile(
BuildContext context, Peer peer, Rx<BoxDecoration?>? deco) {
hideUsernameOnCard ??=
bind.mainGetBuildinOption(key: kHideUsernameOnCard) == 'Y';
final colors = _frontN(peer.tags, 25)
.map((e) => gFFI.abModel.getCurrentAbTagColor(e))
.toList();
@@ -220,21 +222,22 @@ class _PeerCardState extends State<_PeerCard>
? '${translate('Tags')}: ${peer.tags.join(', ')}'
: '',
child: Stack(children: [
Obx(() => deco == null
? makeChild(stateGlobal.isPortrait.isTrue)
: Container(
Obx(
() => deco == null
? makeChild(stateGlobal.isPortrait.isTrue, peer)
: Container(
foregroundDecoration: deco.value,
child: makeChild(stateGlobal.isPortrait.isTrue),
child: makeChild(stateGlobal.isPortrait.isTrue, peer),
),
),
),
if (colors.isNotEmpty)
Obx(()=> Positioned(
top: 2,
right: stateGlobal.isPortrait.isTrue ? 20 : 10,
child: CustomPaint(
painter: TagPainter(radius: 3, colors: colors),
),
))
Obx(() => Positioned(
top: 2,
right: stateGlobal.isPortrait.isTrue ? 20 : 10,
child: CustomPaint(
painter: TagPainter(radius: 3, colors: colors),
),
))
]),
);
}
@@ -876,7 +879,7 @@ class RecentPeerCard extends BasePeerCard {
BuildContext context) async {
final List<MenuEntryBase<String>> menuItems = [
_connectAction(context),
if (!isWeb) _transferFileAction(context),
_transferFileAction(context),
];
final List favs = (await bind.mainGetFav()).toList();
@@ -935,7 +938,7 @@ class FavoritePeerCard extends BasePeerCard {
BuildContext context) async {
final List<MenuEntryBase<String>> menuItems = [
_connectAction(context),
if (!isWeb) _transferFileAction(context),
_transferFileAction(context),
];
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context));
@@ -988,7 +991,7 @@ class DiscoveredPeerCard extends BasePeerCard {
BuildContext context) async {
final List<MenuEntryBase<String>> menuItems = [
_connectAction(context),
if (!isWeb) _transferFileAction(context),
_transferFileAction(context),
];
final List favs = (await bind.mainGetFav()).toList();
@@ -1041,7 +1044,7 @@ class AddressBookPeerCard extends BasePeerCard {
BuildContext context) async {
final List<MenuEntryBase<String>> menuItems = [
_connectAction(context),
if (!isWeb) _transferFileAction(context),
_transferFileAction(context),
];
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context));
@@ -1173,7 +1176,7 @@ class MyGroupPeerCard extends BasePeerCard {
BuildContext context) async {
final List<MenuEntryBase<String>> menuItems = [
_connectAction(context),
if (!isWeb) _transferFileAction(context),
_transferFileAction(context),
];
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context));
@@ -1259,54 +1262,53 @@ void _rdpDialog(String id) async {
],
).marginOnly(bottom: isDesktop ? 8 : 0),
Obx(() => Row(
children: [
stateGlobal.isPortrait.isFalse
? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 140),
child: Text(
"${translate('Username')}:",
textAlign: TextAlign.right,
).marginOnly(right: 10))
: SizedBox.shrink(),
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: isDesktop
? null
: translate('Username')),
controller: userController,
),
),
],
).marginOnly(bottom: stateGlobal.isPortrait.isFalse ? 8 : 0)),
Obx(() => Row(
children: [
stateGlobal.isPortrait.isFalse
? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 140),
child: Text(
"${translate('Password')}:",
textAlign: TextAlign.right,
).marginOnly(right: 10))
: SizedBox.shrink(),
Expanded(
child: Obx(() => TextField(
obscureText: secure.value,
maxLength: maxLength,
children: [
stateGlobal.isPortrait.isFalse
? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 140),
child: Text(
"${translate('Username')}:",
textAlign: TextAlign.right,
).marginOnly(right: 10))
: SizedBox.shrink(),
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: isDesktop
? null
: translate('Password'),
suffixIcon: IconButton(
onPressed: () => secure.value = !secure.value,
icon: Icon(secure.value
? Icons.visibility_off
: Icons.visibility))),
controller: passwordController,
)),
),
],
))
labelText:
isDesktop ? null : translate('Username')),
controller: userController,
),
),
],
).marginOnly(bottom: stateGlobal.isPortrait.isFalse ? 8 : 0)),
Obx(() => Row(
children: [
stateGlobal.isPortrait.isFalse
? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 140),
child: Text(
"${translate('Password')}:",
textAlign: TextAlign.right,
).marginOnly(right: 10))
: SizedBox.shrink(),
Expanded(
child: Obx(() => TextField(
obscureText: secure.value,
maxLength: maxLength,
decoration: InputDecoration(
labelText:
isDesktop ? null : translate('Password'),
suffixIcon: IconButton(
onPressed: () =>
secure.value = !secure.value,
icon: Icon(secure.value
? Icons.visibility_off
: Icons.visibility))),
controller: passwordController,
)),
),
],
))
],
),
),

View File

@@ -332,7 +332,12 @@ class _PeersViewState extends State<_PeersView>
_queryOnlines(false);
}
} else {
if (_isActive && (_queryCount < _maxQueryCount || !p)) {
final skipIfIsWeb =
isWeb && !(stateGlobal.isWebVisible && stateGlobal.isInMainPage);
final skipIfMobile =
(isAndroid || isIOS) && !stateGlobal.isInMainPage;
final skipIfNotActive = skipIfIsWeb || skipIfMobile || !_isActive;
if (!skipIfNotActive && (_queryCount < _maxQueryCount || !p)) {
if (now.difference(_lastQueryTime) >= _queryInterval) {
if (_curPeers.isNotEmpty) {
bind.queryOnlines(ids: _curPeers.toList(growable: false));

View File

@@ -201,7 +201,7 @@ const double kMinFps = 5;
const double kDefaultFps = 30;
const double kMaxFps = 120;
const double kMinQuality = 10;
const double kMinQuality = 5;
const double kDefaultQuality = 50;
const double kMaxQuality = 100;
const double kMaxMoreQuality = 2000;
@@ -571,4 +571,4 @@ extension WindowsTargetExt on int {
WindowsTarget get windowsVersion => getWindowsTarget(this);
}
const kCheckSoftwareUpdateFinish = 'check_software_update_finish';
const kCheckSoftwareUpdateFinish = 'check_software_update_finish';

View File

@@ -3,8 +3,8 @@
import 'dart:async';
import 'dart:convert';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/connection_page_title.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
@@ -323,36 +323,7 @@ class _ConnectionPageState extends State<ConnectionPage>
child: Ink(
child: Column(
children: [
Row(
children: [
Expanded(
child: Row(
children: [
AutoSizeText(
translate('Control Remote Desktop'),
maxLines: 1,
style: Theme.of(context)
.textTheme
.titleLarge
?.merge(TextStyle(height: 1)),
).marginOnly(right: 4),
Tooltip(
waitDuration: Duration(milliseconds: 300),
message: translate("id_input_tip"),
child: Icon(
Icons.help_outline_outlined,
size: 16,
color: Theme.of(context)
.textTheme
.titleLarge
?.color
?.withOpacity(0.5),
),
),
],
)),
],
).marginOnly(bottom: 15),
getConnectionPageTitle(context, false).marginOnly(bottom: 15),
Row(
children: [
Expanded(

View File

@@ -369,8 +369,8 @@ class _GeneralState extends State<_General> {
Widget theme() {
final current = MyTheme.getThemeModePreference().toShortString();
onChanged(String value) {
MyTheme.changeDarkMode(MyTheme.themeModeFromString(value));
onChanged(String value) async {
await MyTheme.changeDarkMode(MyTheme.themeModeFromString(value));
setState(() {});
}
@@ -1451,8 +1451,9 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
children: [
Obx(() => _LabeledTextField(context, 'ID Server', idController,
idErrMsg.value, enabled, secure)),
Obx(() => _LabeledTextField(context, 'Relay Server',
relayController, relayErrMsg.value, enabled, secure)),
if (!isWeb)
Obx(() => _LabeledTextField(context, 'Relay Server',
relayController, relayErrMsg.value, enabled, secure)),
Obx(() => _LabeledTextField(context, 'API Server',
apiController, apiErrMsg.value, enabled, secure)),
_LabeledTextField(

View File

@@ -17,6 +17,8 @@ import 'package:flutter_hbb/models/file_model.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:flutter_hbb/web/dummy.dart'
if (dart.library.html) 'package:flutter_hbb/web/web_unique.dart';
import '../../consts.dart';
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
@@ -55,14 +57,14 @@ class FileManagerPage extends StatefulWidget {
required this.id,
required this.password,
required this.isSharedPassword,
required this.tabController,
this.tabController,
this.forceRelay})
: super(key: key);
final String id;
final String? password;
final bool? isSharedPassword;
final bool? forceRelay;
final DesktopTabController tabController;
final DesktopTabController? tabController;
@override
State<StatefulWidget> createState() => _FileManagerPageState();
@@ -97,11 +99,14 @@ class _FileManagerPageState extends State<FileManagerPage>
if (!isLinux) {
WakelockPlus.enable();
}
if (isWeb) {
_ffi.ffiModel.updateEventListener(_ffi.sessionId, widget.id);
}
debugPrint("File manager page init success with id ${widget.id}");
_ffi.dialogManager.setOverlayState(_overlayKeyState);
// Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.tabController.onSelected?.call(widget.id);
widget.tabController?.onSelected?.call(widget.id);
});
WidgetsBinding.instance.addObserver(this);
}
@@ -140,10 +145,11 @@ class _FileManagerPageState extends State<FileManagerPage>
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: Row(
children: [
Flexible(
flex: 3,
child: dropArea(FileManagerView(
model.localController, _ffi, _mouseFocusScope))),
if (!isWeb)
Flexible(
flex: 3,
child: dropArea(FileManagerView(
model.localController, _ffi, _mouseFocusScope))),
Flexible(
flex: 3,
child: dropArea(FileManagerView(
@@ -192,7 +198,13 @@ class _FileManagerPageState extends State<FileManagerPage>
return Icon(Icons.delete_outline, color: color);
default:
return Transform.rotate(
angle: job.isRemoteToLocal ? pi : 0,
angle: isWeb
? job.isRemoteToLocal
? pi / 2
: pi / 2 * 3
: job.isRemoteToLocal
? pi
: 0,
child: Icon(Icons.arrow_forward_ios, color: color),
);
}
@@ -478,6 +490,9 @@ class _FileManagerViewState extends State<FileManagerView> {
}
Widget headTools() {
var uploadButtonTapPosition = RelativeRect.fill;
RxBool isUploadFolder =
(bind.mainGetLocalOption(key: 'upload-folder-button') == 'Y').obs;
return Container(
child: Column(
children: [
@@ -800,6 +815,66 @@ class _FileManagerViewState extends State<FileManagerView> {
],
),
),
if (isWeb)
Obx(() => ElevatedButton.icon(
style: ButtonStyle(
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
isLocal
? EdgeInsets.only(left: 10)
: EdgeInsets.only(right: 10)),
backgroundColor: MaterialStateProperty.all(
selectedItems.items.isEmpty
? MyTheme.accent80
: MyTheme.accent,
),
),
onPressed: () =>
{webselectFiles(is_folder: isUploadFolder.value)},
label: InkWell(
hoverColor: Colors.transparent,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
focusColor: Colors.transparent,
onTapDown: (e) {
final x = e.globalPosition.dx;
final y = e.globalPosition.dy;
uploadButtonTapPosition =
RelativeRect.fromLTRB(x, y, x, y);
},
onTap: () async {
final value = await showMenu<bool>(
context: context,
position: uploadButtonTapPosition,
items: [
PopupMenuItem<bool>(
value: false,
child: Text(translate('Upload files')),
),
PopupMenuItem<bool>(
value: true,
child: Text(translate('Upload folder')),
),
]);
if (value != null) {
isUploadFolder.value = value;
bind.mainSetLocalOption(
key: 'upload-folder-button',
value: value ? 'Y' : '');
webselectFiles(is_folder: value);
}
},
child: Icon(Icons.arrow_drop_down),
),
icon: Text(
translate(isUploadFolder.isTrue
? 'Upload folder'
: 'Upload files'),
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.white,
),
).marginOnly(left: 8),
)).marginOnly(left: 16),
Obx(() => ElevatedButton.icon(
style: ButtonStyle(
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
@@ -833,19 +908,22 @@ class _FileManagerViewState extends State<FileManagerView> {
: Colors.white,
),
)
: RotatedBox(
quarterTurns: 2,
child: SvgPicture.asset(
"assets/arrow.svg",
colorFilter: svgColor(selectedItems.items.isEmpty
? Theme.of(context).brightness ==
Brightness.light
? MyTheme.grayBg
: MyTheme.darkGray
: Colors.white),
alignment: Alignment.bottomRight,
),
),
: isWeb
? Offstage()
: RotatedBox(
quarterTurns: 2,
child: SvgPicture.asset(
"assets/arrow.svg",
colorFilter: svgColor(
selectedItems.items.isEmpty
? Theme.of(context).brightness ==
Brightness.light
? MyTheme.grayBg
: MyTheme.darkGray
: Colors.white),
alignment: Alignment.bottomRight,
),
),
label: isLocal
? SvgPicture.asset(
"assets/arrow.svg",
@@ -857,7 +935,7 @@ class _FileManagerViewState extends State<FileManagerView> {
: Colors.white),
)
: Text(
translate('Receive'),
translate(isWeb ? 'Download' : 'Receive'),
style: TextStyle(
color: selectedItems.items.isEmpty
? Theme.of(context).brightness ==
@@ -1020,7 +1098,7 @@ class _FileManagerViewState extends State<FileManagerView> {
if (!entry.isDrive &&
versionCmp(_ffi.ffiModel.pi.version, "1.3.0") >= 0)
mod_menu.PopupMenuItem(
child: Text("Rename"),
child: Text(translate("Rename")),
height: CustomPopupMenuTheme.height,
onTap: () {
controller.renameAction(entry, isLocal);

View File

@@ -436,6 +436,7 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
shadowColor: MyTheme.color(context).shadow,
borderRadius: borderRadius,
child: _DraggableShowHide(
id: widget.id,
sessionId: widget.ffi.sessionId,
dragging: _dragging,
fractionX: _fractionX,
@@ -478,8 +479,8 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
setFullscreen: _setFullscreen,
));
toolbarItems.add(_KeyboardMenu(id: widget.id, ffi: widget.ffi));
toolbarItems.add(_ChatMenu(id: widget.id, ffi: widget.ffi));
if (!isWeb) {
toolbarItems.add(_ChatMenu(id: widget.id, ffi: widget.ffi));
toolbarItems.add(_VoiceCallMenu(id: widget.id, ffi: widget.ffi));
}
if (!isWeb) toolbarItems.add(_RecordMenu());
@@ -1780,34 +1781,49 @@ class _ChatMenuState extends State<_ChatMenu> {
@override
Widget build(BuildContext context) {
return _IconSubmenuButton(
tooltip: 'Chat',
key: chatButtonKey,
svg: 'assets/chat.svg',
ffi: widget.ffi,
color: _ToolbarTheme.blueColor,
hoverColor: _ToolbarTheme.hoverBlueColor,
menuChildrenGetter: () => [textChat(), voiceCall()]);
if (isWeb) {
return buildTextChatButton();
} else {
return _IconSubmenuButton(
tooltip: 'Chat',
key: chatButtonKey,
svg: 'assets/chat.svg',
ffi: widget.ffi,
color: _ToolbarTheme.blueColor,
hoverColor: _ToolbarTheme.hoverBlueColor,
menuChildrenGetter: () => [textChat(), voiceCall()]);
}
}
buildTextChatButton() {
return _IconMenuButton(
assetName: 'assets/message_24dp_5F6368.svg',
tooltip: 'Text chat',
key: chatButtonKey,
onPressed: _textChatOnPressed,
color: _ToolbarTheme.blueColor,
hoverColor: _ToolbarTheme.hoverBlueColor,
);
}
textChat() {
return MenuButton(
child: Text(translate('Text chat')),
ffi: widget.ffi,
onPressed: () {
RenderBox? renderBox =
chatButtonKey.currentContext?.findRenderObject() as RenderBox?;
onPressed: _textChatOnPressed);
}
Offset? initPos;
if (renderBox != null) {
final pos = renderBox.localToGlobal(Offset.zero);
initPos = Offset(pos.dx, pos.dy + _ToolbarTheme.dividerHeight);
}
widget.ffi.chatModel.changeCurrentKey(
MessageKey(widget.ffi.id, ChatModel.clientModeID));
widget.ffi.chatModel.toggleChatOverlay(chatInitPos: initPos);
});
_textChatOnPressed() {
RenderBox? renderBox =
chatButtonKey.currentContext?.findRenderObject() as RenderBox?;
Offset? initPos;
if (renderBox != null) {
final pos = renderBox.localToGlobal(Offset.zero);
initPos = Offset(pos.dx, pos.dy + _ToolbarTheme.dividerHeight);
}
widget.ffi.chatModel
.changeCurrentKey(MessageKey(widget.ffi.id, ChatModel.clientModeID));
widget.ffi.chatModel.toggleChatOverlay(chatInitPos: initPos);
}
voiceCall() {
@@ -2218,6 +2234,7 @@ class RdoMenuButton<T> extends StatelessWidget {
}
class _DraggableShowHide extends StatefulWidget {
final String id;
final SessionID sessionId;
final RxDouble fractionX;
final RxBool dragging;
@@ -2229,6 +2246,7 @@ class _DraggableShowHide extends StatefulWidget {
const _DraggableShowHide({
Key? key,
required this.id,
required this.sessionId,
required this.fractionX,
required this.dragging,
@@ -2318,15 +2336,33 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
);
final isFullscreen = stateGlobal.fullscreen;
const double iconSize = 20;
buttonWrapper(VoidCallback? onPressed, Widget child,
{Color hoverColor = _ToolbarTheme.blueColor}) {
final bgColor = buttonStyle.backgroundColor?.resolve({});
return TextButton(
onPressed: onPressed,
child: child,
style: buttonStyle.copyWith(
backgroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.hovered)) {
return (bgColor ?? hoverColor).withOpacity(0.15);
}
return bgColor;
}),
),
);
}
final child = Row(
mainAxisSize: MainAxisSize.min,
children: [
_buildDraggable(context),
Obx(() => TextButton(
onPressed: () {
Obx(() => buttonWrapper(
() {
widget.setFullscreen(!isFullscreen.value);
},
child: Tooltip(
Tooltip(
message: translate(
isFullscreen.isTrue ? 'Exit Fullscreen' : 'Fullscreen'),
child: Icon(
@@ -2337,12 +2373,12 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
),
),
)),
if (!isMacOS)
if (!isMacOS && !isWebDesktop)
Obx(() => Offstage(
offstage: isFullscreen.isFalse,
child: TextButton(
onPressed: () => widget.setMinimize(),
child: Tooltip(
child: buttonWrapper(
widget.setMinimize,
Tooltip(
message: translate('Minimize'),
child: Icon(
Icons.remove,
@@ -2351,11 +2387,11 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
),
),
)),
TextButton(
onPressed: () => setState(() {
buttonWrapper(
() => setState(() {
widget.toolbarState.switchShow(widget.sessionId);
}),
child: Obx((() => Tooltip(
Obx((() => Tooltip(
message:
translate(show.isTrue ? 'Hide Toolbar' : 'Show Toolbar'),
child: Icon(
@@ -2364,6 +2400,25 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
),
))),
),
if (isWebDesktop)
Obx(() {
if (show.isTrue) {
return Offstage();
} else {
return buttonWrapper(
() => closeConnection(id: widget.id),
Tooltip(
message: translate('Close'),
child: Icon(
Icons.close,
size: iconSize,
color: _ToolbarTheme.redColor,
),
),
hoverColor: _ToolbarTheme.redColor,
).paddingOnly(left: iconSize / 2);
}
})
],
);
return TextButtonTheme(

View File

@@ -36,6 +36,7 @@ WindowType? kWindowType;
late List<String> kBootArgs;
Future<void> main(List<String> args) async {
earlyAssert();
WidgetsFlutterBinding.ensureInitialized();
debugPrint("launch args: $args");
@@ -161,7 +162,7 @@ void runMobileApp() async {
await Future.wait([gFFI.abModel.loadCache(), gFFI.groupModel.loadCache()]);
gFFI.userModel.refreshCurrentUser();
runApp(App());
if (!isWeb) await initUniLinks();
await initUniLinks();
}
void runMultiWindow(
@@ -444,7 +445,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
child: GetMaterialApp(
navigatorKey: globalKey,
debugShowCheckedModeBanner: false,
title: 'RustDesk',
title: isWeb
? '${bind.mainGetAppNameSync()} Web Client V2 (Preview)'
: bind.mainGetAppNameSync(),
theme: MyTheme.lightTheme,
darkTheme: MyTheme.darkTheme,
themeMode: MyTheme.currentThemeMode(),
@@ -475,7 +478,8 @@ class _AppState extends State<App> with WidgetsBindingObserver {
: (context, child) {
child = _keepScaleBuilder(context, child);
child = botToastBuilder(context, child);
if (isDesktop && desktopType == DesktopType.main) {
if ((isDesktop && desktopType == DesktopType.main) ||
isWebDesktop) {
child = keyListenerBuilder(context, child);
}
if (isLinux) {
@@ -503,7 +507,7 @@ _registerEventHandler() {
platformFFI.registerEventHandler('theme', 'theme', (evt) async {
String? dark = evt['dark'];
if (dark != null) {
MyTheme.changeDarkMode(MyTheme.themeModeFromString(dark));
await MyTheme.changeDarkMode(MyTheme.themeModeFromString(dark));
}
});
platformFFI.registerEventHandler('language', 'language', (_) async {

View File

@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:auto_size_text_field/auto_size_text_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import 'package:flutter_hbb/common/widgets/connection_page_title.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -211,6 +212,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
FocusNode fieldFocusNode,
VoidCallback onFieldSubmitted) {
fieldTextEditingController.text = _idController.text;
Get.put<TextEditingController>(
fieldTextEditingController);
fieldFocusNode.addListener(() async {
_idEmpty.value =
fieldTextEditingController.text.isEmpty;
@@ -349,9 +352,15 @@ class _ConnectionPageState extends State<ConnectionPage> {
),
),
);
final child = Column(children: [
if (isWebDesktop)
getConnectionPageTitle(context, true)
.marginOnly(bottom: 10, top: 15, left: 12),
w
]);
return Align(
alignment: Alignment.topCenter,
child: Container(constraints: kMobilePageConstraints, child: w));
child: Container(constraints: kMobilePageConstraints, child: child));
}
@override
@@ -361,6 +370,9 @@ class _ConnectionPageState extends State<ConnectionPage> {
if (Get.isRegistered<IDTextEditingController>()) {
Get.delete<IDTextEditingController>();
}
if (Get.isRegistered<TextEditingController>()) {
Get.delete<TextEditingController>();
}
if (!bind.isCustomClient()) {
platformFFI.unregisterEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish);

View File

@@ -6,6 +6,7 @@ import 'package:get/get.dart';
import '../../common.dart';
import '../../common/widgets/chat_page.dart';
import '../../models/platform_model.dart';
import '../../models/state_model.dart';
import 'connection_page.dart';
abstract class PageShape extends Widget {
@@ -159,14 +160,75 @@ class WebHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
stateGlobal.isInMainPage = true;
handleUnilink(context);
return Scaffold(
// backgroundColor: MyTheme.grayBg,
appBar: AppBar(
centerTitle: true,
title: Text(bind.mainGetAppNameSync()),
title: Text("${bind.mainGetAppNameSync()} (Preview)"),
actions: connectionPage.appBarActions,
),
body: connectionPage,
);
}
handleUnilink(BuildContext context) {
if (webInitialLink.isEmpty) {
return;
}
final link = webInitialLink;
webInitialLink = '';
final splitter = ["/#/", "/#", "#/", "#"];
var fakelink = '';
for (var s in splitter) {
if (link.contains(s)) {
var list = link.split(s);
if (list.length < 2 || list[1].isEmpty) {
return;
}
list.removeAt(0);
fakelink = "rustdesk://${list.join(s)}";
break;
}
}
if (fakelink.isEmpty) {
return;
}
final uri = Uri.tryParse(fakelink);
if (uri == null) {
return;
}
final args = urlLinkToCmdArgs(uri);
if (args == null || args.isEmpty) {
return;
}
bool isFileTransfer = false;
String? id;
String? password;
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case '--connect':
case '--play':
isFileTransfer = false;
id = args[i + 1];
i++;
break;
case '--file-transfer':
isFileTransfer = true;
id = args[i + 1];
i++;
break;
case '--password':
password = args[i + 1];
i++;
break;
default:
break;
}
}
if (id != null) {
connect(context, id, isFileTransfer: isFileTransfer, password: password);
}
}
}

View File

@@ -57,6 +57,9 @@ class _RemotePageState extends State<RemotePage> {
final TextEditingController _textController =
TextEditingController(text: initText);
// This timer is used to check the composing status of the soft keyboard.
// It is used for Android, Korean(and other similar) input method.
Timer? _composingTimer;
_RemotePageState(String id) {
initSharedStates(id);
@@ -104,6 +107,7 @@ class _RemotePageState extends State<RemotePage> {
_physicalFocusNode.dispose();
await gFFI.close();
_timer?.cancel();
_composingTimer?.cancel();
gFFI.dialogManager.dismissAll();
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
@@ -139,6 +143,7 @@ class _RemotePageState extends State<RemotePage> {
gFFI.ffiModel.pi.version.isNotEmpty) {
gFFI.invokeMethod("enable_soft_keyboard", false);
}
_composingTimer?.cancel();
} else {
_timer?.cancel();
_timer = Timer(kMobileDelaySoftKeyboardFocus, () {
@@ -155,9 +160,9 @@ class _RemotePageState extends State<RemotePage> {
var oldValue = _value;
_value = newValue;
var i = newValue.length - 1;
for (; i >= 0 && newValue[i] != '\1'; --i) {}
for (; i >= 0 && newValue[i] != '1'; --i) {}
var j = oldValue.length - 1;
for (; j >= 0 && oldValue[j] != '\1'; --j) {}
for (; j >= 0 && oldValue[j] != '1'; --j) {}
if (i < j) j = i;
var subNewValue = newValue.substring(j + 1);
var subOldValue = oldValue.substring(j + 1);
@@ -202,12 +207,19 @@ class _RemotePageState extends State<RemotePage> {
}
void _handleNonIOSSoftKeyboardInput(String newValue) {
_composingTimer?.cancel();
if (_textController.value.isComposingRangeValid) {
_composingTimer = Timer(Duration(milliseconds: 25), () {
_handleNonIOSSoftKeyboardInput(_textController.value.text);
});
return;
}
var oldValue = _value;
_value = newValue;
if (oldValue.isNotEmpty &&
newValue.isNotEmpty &&
oldValue[0] == '\1' &&
newValue[0] != '\1') {
oldValue[0] == '1' &&
newValue[0] != '1') {
// clipboard
oldValue = '';
}
@@ -242,10 +254,14 @@ class _RemotePageState extends State<RemotePage> {
}
}
// handle mobile virtual keyboard
void handleSoftKeyboardInput(String newValue) {
Future<void> handleSoftKeyboardInput(String newValue) async {
if (isIOS) {
_handleIOSSoftKeyboardInput(newValue);
// fix: TextFormField onChanged event triggered multiple times when Korean input
// https://github.com/rustdesk/rustdesk/pull/9644
await Future.delayed(const Duration(milliseconds: 10));
if (newValue != _textController.text) return;
_handleIOSSoftKeyboardInput(_textController.text);
} else {
_handleNonIOSSoftKeyboardInput(newValue);
}

View File

@@ -205,14 +205,15 @@ void showServerSettingsWithValue(
)
] +
[
TextFormField(
controller: relayCtrl,
decoration: InputDecoration(
labelText: translate('Relay Server'),
errorText: relayServerMsg.value.isEmpty
? null
: relayServerMsg.value),
)
if (isAndroid)
TextFormField(
controller: relayCtrl,
decoration: InputDecoration(
labelText: translate('Relay Server'),
errorText: relayServerMsg.value.isEmpty
? null
: relayServerMsg.value),
)
] +
[
TextFormField(

View File

@@ -235,13 +235,14 @@ class ChatModel with ChangeNotifier {
}
}
_isChatOverlayHide() => ((!isDesktop && chatIconOverlayEntry == null) ||
chatWindowOverlayEntry == null);
_isChatOverlayHide() =>
((!(isDesktop || isWebDesktop) && chatIconOverlayEntry == null) ||
chatWindowOverlayEntry == null);
toggleChatOverlay({Offset? chatInitPos}) {
if (_isChatOverlayHide()) {
gFFI.invokeMethod("enable_soft_keyboard", true);
if (!isDesktop) {
if (!(isDesktop || isWebDesktop)) {
showChatIconOverlay();
}
showChatWindowOverlay(chatInitPos: chatInitPos);

View File

@@ -7,6 +7,8 @@ import 'package:flutter_hbb/common/widgets/dialog.dart';
import 'package:flutter_hbb/utils/event_loop.dart';
import 'package:get/get.dart';
import 'package:path/path.dart' as path;
import 'package:flutter_hbb/web/dummy.dart'
if (dart.library.html) 'package:flutter_hbb/web/web_unique.dart';
import '../consts.dart';
import 'model.dart';
@@ -74,7 +76,7 @@ class FileModel {
Future<void> onReady() async {
await evtLoop.onReady();
await localController.onReady();
if (!isWeb) await localController.onReady();
await remoteController.onReady();
}
@@ -86,7 +88,7 @@ class FileModel {
}
Future<void> refreshAll() async {
await localController.refresh();
if (!isWeb) await localController.refresh();
await remoteController.refresh();
}
@@ -228,6 +230,33 @@ class FileModel {
);
}, useAnimation: false);
}
void onSelectedFiles(dynamic obj) {
localController.selectedItems.clear();
try {
int handleIndex = int.parse(obj['handleIndex']);
final file = jsonDecode(obj['file']);
var entry = Entry.fromJson(file);
entry.path = entry.name;
final otherSideData = remoteController.directoryData();
final toPath = otherSideData.directory.path;
final isWindows = otherSideData.options.isWindows;
final showHidden = otherSideData.options.showHidden;
final jobID = jobController.addTransferJob(entry, false);
webSendLocalFiles(
handleIndex: handleIndex,
actId: jobID,
path: entry.path,
to: PathUtil.join(toPath, entry.name, isWindows),
fileNum: 0,
includeHidden: showHidden,
isRemote: false,
);
} catch (e) {
debugPrint("Failed to decode onSelectedFiles: $e");
}
}
}
class DirectoryData {
@@ -462,7 +491,8 @@ class FileController {
to: PathUtil.join(toPath, from.name, isWindows),
fileNum: 0,
includeHidden: showHidden,
isRemote: isRemoteToLocal);
isRemote: isRemoteToLocal,
isDir: from.isDirectory);
debugPrint(
"path: ${from.path}, toPath: $toPath, to: ${PathUtil.join(toPath, from.name, isWindows)}");
}
@@ -489,7 +519,7 @@ class FileController {
} else if (item.isDirectory) {
title = translate("Not an empty directory");
dialogManager?.showLoading(translate("Waiting"));
final fd = await fileFetcher.fetchDirectoryRecursive(
final fd = await fileFetcher.fetchDirectoryRecursiveToRemove(
jobID, item.path, items.isLocal, true);
if (fd.path.isEmpty) {
fd.path = item.path;
@@ -809,7 +839,6 @@ class JobController {
job.speed = double.parse(evt['speed']);
job.finishedSize = int.parse(evt['finished_size']);
job.recvJobRes = true;
debugPrint("update job $id with $evt");
jobTable.refresh();
}
} catch (e) {
@@ -1116,11 +1145,11 @@ class FileFetcher {
}
}
Future<FileDirectory> fetchDirectoryRecursive(
Future<FileDirectory> fetchDirectoryRecursiveToRemove(
int actID, String path, bool isLocal, bool showHidden) async {
// TODO test Recursive is show hidden default?
try {
await bind.sessionReadDirRecursive(
await bind.sessionReadDirToRemoveRecursive(
sessionId: sessionId,
actId: actID,
path: path,

View File

@@ -4,6 +4,7 @@ import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:bot_toast/bot_toast.dart';
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -269,6 +270,8 @@ class FfiModel with ChangeNotifier {
var name = evt['name'];
if (name == 'msgbox') {
handleMsgBox(evt, sessionId, peerId);
} else if (name == 'toast') {
handleToast(evt, sessionId, peerId);
} else if (name == 'set_multiple_windows_session') {
handleMultipleWindowsSession(evt, sessionId, peerId);
} else if (name == 'peer_info') {
@@ -372,7 +375,7 @@ class FfiModel with ChangeNotifier {
} else if (name == 'plugin_option') {
handleOption(evt);
} else if (name == "sync_peer_hash_password_to_personal_ab") {
if (desktopType == DesktopType.main) {
if (desktopType == DesktopType.main || isWeb) {
final id = evt['id'];
final hash = evt['hash'];
if (id != null && hash != null) {
@@ -390,6 +393,10 @@ class FfiModel with ChangeNotifier {
handleFollowCurrentDisplay(evt, sessionId, peerId);
} else if (name == 'use_texture_render') {
_handleUseTextureRender(evt, sessionId, peerId);
} else if (name == "selected_files") {
if (isWeb) {
parent.target?.fileModel.onSelectedFiles(evt);
}
} else {
debugPrint('Event is not handled in the fixed branch: $name');
}
@@ -591,6 +598,37 @@ class FfiModel with ChangeNotifier {
}
}
handleToast(Map<String, dynamic> evt, SessionID sessionId, String peerId) {
final type = evt['type'] ?? 'info';
final text = evt['text'] ?? '';
final durMsc = evt['dur_msec'] ?? 2000;
final duration = Duration(milliseconds: durMsc);
if ((text).isEmpty) {
BotToast.showLoading(
duration: duration,
clickClose: true,
allowClick: true,
);
} else {
if (type.contains('error')) {
BotToast.showText(
contentColor: Colors.red,
text: translate(text),
duration: duration,
clickClose: true,
onlyOne: true,
);
} else {
BotToast.showText(
text: translate(text),
duration: duration,
clickClose: true,
onlyOne: true,
);
}
}
}
/// Show a message box with [type], [title] and [text].
showMsgBox(SessionID sessionId, String type, String title, String text,
String link, bool hasRetry, OverlayDialogManager dialogManager,
@@ -1187,6 +1225,27 @@ class ImageModel with ChangeNotifier {
clearImage() => _image = null;
bool _webDecodingRgba = false;
final List<Uint8List> _webRgbaList = List.empty(growable: true);
webOnRgba(int display, Uint8List rgba) async {
// deep copy needed, otherwise "instantiateCodec failed: TypeError: Cannot perform Construct on a detached ArrayBuffer"
_webRgbaList.add(Uint8List.fromList(rgba));
if (_webDecodingRgba) {
return;
}
_webDecodingRgba = true;
try {
while (_webRgbaList.isNotEmpty) {
final rgba2 = _webRgbaList.last;
_webRgbaList.clear();
await decodeAndUpdate(display, rgba2);
}
} catch (e) {
debugPrint('onRgba error: $e');
}
_webDecodingRgba = false;
}
onRgba(int display, Uint8List rgba) async {
try {
await decodeAndUpdate(display, rgba);

View File

@@ -48,6 +48,12 @@ class PlatformFFI {
static get isMain => instance._appType == kAppTypeMain;
static String getByName(String name, [String arg = '']) {
return '';
}
static void setByName(String name, [String value = '']) {}
static Future<String> getVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
return packageInfo.version;
@@ -276,4 +282,6 @@ class PlatformFFI {
void syncAndroidServiceAppDirConfigPath() {
invokeMethod(AndroidChannel.kSyncAppDirConfigPath, _dir);
}
void setFullscreenCallback(void Function(bool) fun) {}
}

View File

@@ -152,7 +152,7 @@ class PeerTabModel with ChangeNotifier {
// https://github.com/flutter/flutter/issues/101275#issuecomment-1604541700
// After onTap, the shift key should be pressed for a while when not in multiselection mode,
// because onTap is delayed when onDoubleTap is not null
if (isDesktop && !_isShiftDown) return;
if (isDesktop || isWebDesktop) return;
_multiSelectionMode = true;
}
final cached = _currentTabCachedPeers.map((e) => e.id).toList();

View File

@@ -6,3 +6,11 @@ final platformFFI = PlatformFFI.instance;
final localeName = PlatformFFI.localeName;
RustdeskImpl get bind => platformFFI.ffiBind;
String ffiGetByName(String name, [String arg = '']) {
return PlatformFFI.getByName(name, arg);
}
void ffiSetByName(String name, [String value = '']) {
PlatformFFI.setByName(name, value);
}

View File

@@ -19,6 +19,9 @@ class StateGlobal {
final RxBool showRemoteToolBar = false.obs;
final svcStatus = SvcStatus.notReady.obs;
final RxBool isFocused = false.obs;
// for mobile and web
bool isInMainPage = true;
bool isWebVisible = true;
final isPortrait = false.obs;
@@ -70,27 +73,40 @@ class StateGlobal {
if (_fullscreen.value != v) {
_fullscreen.value = v;
_showTabBar.value = !_fullscreen.value;
refreshResizeEdgeSize();
print(
"fullscreen: $fullscreen, resizeEdgeSize: ${_resizeEdgeSize.value}");
_windowBorderWidth.value = fullscreen.isTrue ? 0 : kWindowBorderWidth;
if (procWnd) {
final wc = WindowController.fromWindowId(windowId);
wc.setFullscreen(_fullscreen.isTrue).then((_) {
// https://github.com/leanflutter/window_manager/issues/131#issuecomment-1111587982
if (isWindows && !v) {
Future.delayed(Duration.zero, () async {
final frame = await wc.getFrame();
final newRect = Rect.fromLTWH(
frame.left, frame.top, frame.width + 1, frame.height + 1);
await wc.setFrame(newRect);
});
}
});
if (isWebDesktop) {
procFullscreenWeb();
} else {
procFullscreenNative(procWnd);
}
}
}
procFullscreenWeb() {
final isFullscreen = ffiGetByName('fullscreen') == 'Y';
String fullscreenValue = '';
if (isFullscreen && _fullscreen.isFalse) {
fullscreenValue = 'N';
} else if (!isFullscreen && fullscreen.isTrue) {
fullscreenValue = 'Y';
}
if (fullscreenValue.isNotEmpty) {
ffiSetByName('fullscreen', fullscreenValue);
}
}
procFullscreenNative(bool procWnd) {
refreshResizeEdgeSize();
print("fullscreen: $fullscreen, resizeEdgeSize: ${_resizeEdgeSize.value}");
_windowBorderWidth.value = fullscreen.isTrue ? 0 : kWindowBorderWidth;
if (procWnd) {
final wc = WindowController.fromWindowId(windowId);
wc.setFullscreen(_fullscreen.isTrue).then((_) {
// We remove the redraw (width + 1, height + 1), because this issue cannot be reproduced.
// https://github.com/rustdesk/rustdesk/issues/9675
});
}
}
refreshResizeEdgeSize() => _resizeEdgeSize.value = fullscreen.isTrue
? kFullScreenEdgeSize
: isMaximized.isTrue
@@ -109,7 +125,13 @@ class StateGlobal {
_inputSource = bind.mainGetInputSource();
}
StateGlobal._();
StateGlobal._() {
if (isWebDesktop) {
platformFFI.setFullscreenCallback((v) {
_fullscreen.value = v;
});
}
}
static final StateGlobal instance = StateGlobal._();
}

View File

@@ -1,12 +1,14 @@
// ignore_for_file: avoid_web_libraries_in_flutter
import 'dart:convert';
import 'dart:js_interop';
import 'dart:typed_data';
import 'dart:js';
import 'dart:html';
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/web/bridge.dart';
import 'package:flutter_hbb/common.dart';
@@ -28,7 +30,15 @@ class PlatformFFI {
context.callMethod('setByName', [name, value]);
}
PlatformFFI._();
PlatformFFI._() {
window.document.addEventListener(
'visibilitychange',
(event) => {
stateGlobal.isWebVisible =
window.document.visibilityState == 'visible'
});
}
static final PlatformFFI instance = PlatformFFI._();
static get localeName => window.navigator.language;
@@ -98,6 +108,10 @@ class PlatformFFI {
sessionId: sessionId, display: display, ptr: ptr);
Future<void> init(String appType) async {
Completer completer = Completer();
context["onInitFinished"] = () {
completer.complete();
};
context.callMethod('init');
version = getByName('version');
window.onContextMenu.listen((event) {
@@ -112,6 +126,7 @@ class PlatformFFI {
print('json.decode fail(): $e');
}
};
return completer.future;
}
void setEventCallback(void Function(Map<String, dynamic>) fun) {
@@ -157,4 +172,10 @@ class PlatformFFI {
// just for compilation
void syncAndroidServiceAppDirConfigPath() {}
void setFullscreenCallback(void Function(bool) fun) {
context["onFullscreenChanged"] = (bool v) {
fun(v);
};
}
}

View File

@@ -124,6 +124,9 @@ class RustDeskMultiWindowManager {
bool withScreenRect,
) async {
final windowController = await DesktopMultiWindow.createWindow(msg);
if (isWindows) {
windowController.setInitBackgroundColor(Colors.black);
}
final windowId = windowController.windowId;
if (!withScreenRect) {
windowController

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
Future<void> webselectFiles({required bool is_folder}) async {
throw UnimplementedError("webselectFiles");
}
Future<void> webSendLocalFiles(
{required int handleIndex,
required int actId,
required String path,
required String to,
required int fileNum,
required bool includeHidden,
required bool isRemote}) {
throw UnimplementedError("webSendLocalFiles");
}

View File

@@ -1,23 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
import 'package:flutter_hbb/mobile/pages/scan_page.dart';
import 'package:flutter_hbb/mobile/pages/settings_page.dart';
import 'package:provider/provider.dart';
import '../../common.dart';
import '../../common/widgets/login.dart';
import '../../models/model.dart';
class WebSettingsPage extends StatelessWidget {
const WebSettingsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
if (isWebDesktop) {
return _buildDesktopButton(context);
} else {
return _buildMobileMenu(context);
}
return _buildDesktopButton(context);
}
Widget _buildDesktopButton(BuildContext context) {
@@ -34,65 +23,4 @@ class WebSettingsPage extends StatelessWidget {
},
);
}
Widget _buildMobileMenu(BuildContext context) {
Provider.of<FfiModel>(context);
return PopupMenuButton<String>(
tooltip: "",
icon: const Icon(Icons.more_vert),
itemBuilder: (context) {
return (isIOS
? [
const PopupMenuItem(
value: "scan",
child: Icon(Icons.qr_code_scanner, color: Colors.black),
)
]
: <PopupMenuItem<String>>[]) +
[
PopupMenuItem(
value: "server",
child: Text(translate('ID/Relay Server')),
)
] +
[
PopupMenuItem(
value: "login",
child: Text(gFFI.userModel.userName.value.isEmpty
? translate("Login")
: '${translate("Logout")} (${gFFI.userModel.userName.value})'),
)
] +
[
PopupMenuItem(
value: "about",
child: Text(translate('About RustDesk')),
)
];
},
onSelected: (value) {
if (value == 'server') {
showServerSettings(gFFI.dialogManager);
}
if (value == 'about') {
showAbout(gFFI.dialogManager);
}
if (value == 'login') {
if (gFFI.userModel.userName.value.isEmpty) {
loginDialog();
} else {
logOutConfirmDialog();
}
}
if (value == 'scan') {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => ScanPage(),
),
);
}
});
}
}

View File

@@ -0,0 +1,30 @@
import 'dart:async';
import 'dart:convert';
import 'dart:js' as js;
Future<void> webselectFiles({required bool is_folder}) async {
return Future(
() => js.context.callMethod('setByName', ['select_files', is_folder]));
}
Future<void> webSendLocalFiles(
{required int handleIndex,
required int actId,
required String path,
required String to,
required int fileNum,
required bool includeHidden,
required bool isRemote}) {
return Future(() => js.context.callMethod('setByName', [
'send_local_files',
jsonEncode({
'id': actId,
'handle_index': handleIndex,
'path': path,
'to': to,
'file_num': fileNum,
'include_hidden': includeHidden,
'is_remote': isRemote,
})
]));
}

View File

@@ -335,7 +335,7 @@ packages:
description:
path: "."
ref: HEAD
resolved-ref: "80b063b9d4e015f62e17f42a5aa0b3d20a365926"
resolved-ref: "519350f1f40746798299e94786197d058353bac9"
url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window"
source: git
version: "0.1.0"

View File

@@ -156,7 +156,7 @@ pub trait TraitPixelBuffer {
#[cfg(not(any(target_os = "ios")))]
pub enum Frame<'a> {
PixelBuffer(PixelBuffer<'a>),
Texture(*mut c_void),
Texture((*mut c_void, usize)),
}
#[cfg(not(any(target_os = "ios")))]
@@ -164,7 +164,7 @@ impl Frame<'_> {
pub fn valid<'a>(&'a self) -> bool {
match self {
Frame::PixelBuffer(pixelbuffer) => !pixelbuffer.data().is_empty(),
Frame::Texture(texture) => !texture.is_null(),
Frame::Texture((texture, _)) => !texture.is_null(),
}
}
@@ -186,7 +186,7 @@ impl Frame<'_> {
pub enum EncodeInput<'a> {
YUV(&'a [u8]),
Texture(*mut c_void),
Texture((*mut c_void, usize)),
}
impl<'a> EncodeInput<'a> {
@@ -197,7 +197,7 @@ impl<'a> EncodeInput<'a> {
}
}
pub fn texture(&self) -> ResultType<*mut c_void> {
pub fn texture(&self) -> ResultType<(*mut c_void, usize)> {
match self {
Self::Texture(f) => Ok(*f),
_ => bail!("not texture frame"),

View File

@@ -101,7 +101,12 @@ impl EncoderApi for VRamEncoder {
frame: EncodeInput,
ms: i64,
) -> ResultType<hbb_common::message_proto::VideoFrame> {
let texture = frame.texture()?;
let (texture, rotation) = frame.texture()?;
if rotation != 0 {
// to-do: support rotation
// Both the encoder and display(w,h) information need to be changed.
bail!("rotation not supported");
}
let mut vf = VideoFrame::new();
let mut frames = Vec::new();
for frame in self

View File

@@ -253,7 +253,17 @@ impl Capturer {
pub fn frame<'a>(&'a mut self, timeout: UINT) -> io::Result<Frame<'a>> {
if self.output_texture {
Ok(Frame::Texture(self.get_texture(timeout)?))
let rotation = match self.display.rotation() {
DXGI_MODE_ROTATION_IDENTITY | DXGI_MODE_ROTATION_UNSPECIFIED => 0,
DXGI_MODE_ROTATION_ROTATE90 => 90,
DXGI_MODE_ROTATION_ROTATE180 => 180,
DXGI_MODE_ROTATION_ROTATE270 => 270,
_ => {
// Unsupported rotation, try anyway
0
}
};
Ok(Frame::Texture((self.get_texture(timeout)?, rotation)))
} else {
let width = self.width;
let height = self.height;

View File

@@ -14,15 +14,10 @@ case $1 in
rm /etc/systemd/system/rustdesk.service /usr/lib/systemd/system/rustdesk.service || true
# workaround temp dev build between 1.1.9 and 1.2.0
ubuntuVersion=$(grep -oP 'VERSION_ID="\K[\d]+' /etc/os-release | bc -l)
waylandSupportVersion=21
if [ "$ubuntuVersion" != "" ] && [ "$ubuntuVersion" -ge "$waylandSupportVersion" ]
serverUser=$(ps -ef | grep -E 'rustdesk +--server' | grep -v 'sudo ' | awk '{print $1}' | head -1)
if [ "$serverUser" != "" ] && [ "$serverUser" != "root" ]
then
serverUser=$(ps -ef | grep -E 'rustdesk +--server' | grep -v 'sudo ' | awk '{print $1}' | head -1)
if [ "$serverUser" != "" ] && [ "$serverUser" != "root" ]
then
systemctl --machine=${serverUser}@.host --user stop rustdesk >/dev/null 2>&1 || true
fi
systemctl --machine=${serverUser}@.host --user stop rustdesk >/dev/null 2>&1 || true
fi
rm /usr/lib/systemd/user/rustdesk.service >/dev/null 2>&1 || true
fi

View File

@@ -602,6 +602,7 @@ pub fn session_send_files(
file_num: i32,
include_hidden: bool,
is_remote: bool,
_is_dir: bool,
) {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
session.send_files(act_id, path, to, file_num, include_hidden, is_remote);
@@ -633,7 +634,7 @@ pub fn session_remove_file(
}
}
pub fn session_read_dir_recursive(
pub fn session_read_dir_to_remove_recursive(
session_id: SessionID,
act_id: i32,
path: String,

View File

@@ -121,6 +121,7 @@ pub struct ClipboardNonFile {
pub height: i32,
// message.proto: ClipboardFormat
pub format: i32,
pub special_name: String,
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

File diff suppressed because it is too large Load Diff

View File

@@ -310,7 +310,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Start the screen sharing service on boot, requires special permissions", "开机自动启动屏幕共享服务,此功能需要一些特殊权限。"),
("Connection not allowed", "对方不允许连接"),
("Legacy mode", "传统模式"),
("Map mode", "11 传输"),
("Map mode", "1:1 传输"),
("Translate mode", "翻译模式"),
("Use permanent password", "使用固定密码"),
("Use both passwords", "同时使用两种密码"),
@@ -563,7 +563,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Plug out all", "拔出所有"),
("True color (4:4:4)", "真彩模式4:4:4"),
("Enable blocking user input", "允许阻止用户输入"),
("id_input_tip", "可以输入 ID、直连 IP或域名和端口号<域名>:<端口号>)。\n要访问另一台服务器上的设备,请附加服务器地址(<ID>@<服务器地址>?key=<密钥>)。比如,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=。\n要访问公共服务器上的设备,请输入 \"<ID>@public\", 无需密钥。\n\n如果您想要在首次连接时,强制走中继连接,请在 ID 的后面添加 \"/r\",例如,\"9123456234/r\""),
("id_input_tip", "可以输入 ID、直连 IP或域名和端口号<域名>:<端口号>)。\n要访问另一台服务器上的设备,请附加服务器地址(<ID>@<服务器地址>?key=<密钥>)。比如,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=。\n要访问公共服务器上的设备,请输入 \"<ID>@public\"无需密钥。\n\n如果您想要在首次连接时,强制走中继连接,请在 ID 的后面添加 \"/r\",例如,\"9123456234/r\""),
("privacy_mode_impl_mag_tip", "模式 1"),
("privacy_mode_impl_virtual_display_tip", "模式 2"),
("Enter privacy mode", "进入隐私模式"),
@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "被控端启用了单向文件传输"),
("Authentication Required", "需要身份验证"),
("Authenticate", "认证"),
("web_id_input_tip", "可以输入同一个服务器内的 IDweb 客户端不支持直接 IP 访问。\n要访问另一台服务器上的设备,请附加服务器地址(<ID>@<服务器地址>?key=<密钥>)。比如,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=。\n要访问公共服务器上的设备,请输入 \"<ID>@public\",无需密钥。"),
("Download", "下载"),
("Upload folder", "上传文件夹"),
("Upload files", "上传文件"),
("Clipboard is synchronized", "剪贴板已同步"),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -563,7 +563,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Plug out all", "Alle ausschalten"),
("True color (4:4:4)", "True Color (4:4:4)"),
("Enable blocking user input", "Blockieren von Benutzereingaben aktivieren"),
("id_input_tip", "Sie können eine ID, eine direkte IP oder eine Domäne mit einem Port (<domain>:<port>) eingeben.\nWenn Sie auf ein Gerät auf einem anderen Server zugreifen möchten, fügen Sie bitte die Serveradresse (<id>@<server_address>?key=<key_value>) hinzu, zum Beispiel\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nWenn Sie auf ein Gerät auf einem öffentlichen Server zugreifen wollen, geben Sie bitte \"<id>@public\" ein. Der Schlüssel wird für öffentliche Server nicht benötigt.\n\nWenn Sie bei der ersten Verbindung die Verwendung einer Relay-Verbindung erzwingen wollen, fügen Sie \"/r\" am Ende der ID hinzu, zum Beispiel \"9123456234/r\"."),
("id_input_tip", "Sie können eine ID, eine direkte IP oder eine Domäne mit einem Port (<domain>:<port>) eingeben.\nWenn Sie auf ein Gerät auf einem anderen Server zugreifen wollen, fügen Sie bitte die Serveradresse (<id>@<server_address>?key=<key_value>) hinzu, zum Beispiel\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nWenn Sie auf ein Gerät auf einem öffentlichen Server zugreifen wollen, geben Sie bitte \"<id>@public\" ein. Der Schlüssel wird für öffentliche Server nicht benötigt.\n\nWenn Sie bei der ersten Verbindung die Verwendung einer Relay-Verbindung erzwingen wollen, fügen Sie \"/r\" am Ende der ID hinzu, zum Beispiel \"9123456234/r\"."),
("privacy_mode_impl_mag_tip", "Modus 1"),
("privacy_mode_impl_virtual_display_tip", "Modus 2"),
("Enter privacy mode", "Datenschutzmodus aktivieren"),
@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "Die einseitige Dateiübertragung ist auf der kontrollierten Seite aktiviert."),
("Authentication Required", "Authentifizierung erforderlich"),
("Authenticate", "Authentifizieren"),
("web_id_input_tip", "Sie können eine ID auf demselben Server eingeben, direkter IP-Zugriff wird im Web-Client nicht unterstützt.\nWenn Sie auf ein Gerät auf einem anderen Server zugreifen wollen, fügen Sie bitte die Serveradresse (<id>@<server_address>?key=<key_value>) hinzu, zum Beispiel\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nWenn Sie auf ein Gerät auf einem öffentlichen Server zugreifen wollen, geben Sie bitte \"<id>@public\" ein. Der Schlüssel wird für öffentliche Server nicht benötigt."),
("Download", "Herunterladen"),
("Upload folder", "Ordner hochladen"),
("Upload files", "Dateien hochladen"),
("Clipboard is synchronized", "Zwischenablage ist synchronisiert"),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -235,5 +235,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("network_error_tip", "Please check your network connection, then click retry."),
("enable-trusted-devices-tip", "Skip 2FA verification on trusted devices"),
("one-way-file-transfer-tip", "One-way file transfer is enabled on the controlled side."),
("web_id_input_tip", "You can input an ID in the same server, direct IP access is not supported in web client.\nIf you want to access a device on another server, please append the server address (<id>@<server_address>?key=<key_value>), for example,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nIf you want to access a device on a public server, please input \"<id>@public\", the key is not needed for public server."),
].iter().cloned().collect();
}

View File

@@ -6,11 +6,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("desk_tip", "Via aparato povas esti alirita kun tiu identigilo kaj pasvorto"),
("Password", "Pasvorto"),
("Ready", "Preta"),
("Established", ""),
("Established", "Establis"),
("connecting_status", "Konektante al la reto RustDesk..."),
("Enable service", "Ebligi servon"),
("Start service", "Starti servon"),
("Service is running", ""),
("Service is running", "La servo funkcias"),
("Service is not running", "La servo ne funkcias"),
("not_ready_status", "Ne preta, bonvolu kontroli la retkonekto"),
("Control Remote Desktop", "Kontroli foran aparaton"),
@@ -29,33 +29,33 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable TCP tunneling", "Ebligi tunelado TCP"),
("IP Whitelisting", "Listo de IP akceptataj"),
("ID/Relay Server", "Identigila/Relajsa servilo"),
("Import server config", "Enporti servilan agordon"),
("Export Server Config", ""),
("Import server config", "Importi servilan agordon"),
("Export Server Config", "Eksporti servilan agordon"),
("Import server configuration successfully", "Importi servilan agordon sukcese"),
("Export server configuration successfully", ""),
("Export server configuration successfully", "Eksporti servilan agordon sukcese"),
("Invalid server configuration", "Nevalida servila agordo"),
("Clipboard is empty", "La poŝo estas malplena"),
("Stop service", "Haltu servon"),
("Change ID", "Ŝanĝi identigilon"),
("Your new ID", ""),
("length %min% to %max%", ""),
("starts with a letter", ""),
("allowed characters", ""),
("Your new ID", "Via nova identigilo"),
("length %min% to %max%", "longeco %min% al %max%"),
("starts with a letter", "komencas kun letero"),
("allowed characters", "permesitaj signoj"),
("id_change_tip", "Nur la signoj a-z, A-Z, 0-9, _ (substreko) povas esti uzataj. La unua litero povas esti inter a-z, A-Z. La longeco devas esti inter 6 kaj 16."),
("Website", "Retejo"),
("About", "Pri"),
("Slogan_tip", ""),
("Privacy Statement", ""),
("Slogan_tip", "Farita kun koro en ĉi tiu ĥaosa mondo!"),
("Privacy Statement", "Deklaro Pri Privateco"),
("Mute", "Muta"),
("Build Date", ""),
("Version", ""),
("Home", ""),
("Audio Input", "Aŭdia enigo"),
("Enhancements", ""),
("Hardware Codec", ""),
("Adaptive bitrate", ""),
("Build Date", "konstruada dato"),
("Version", "Versio"),
("Home", "Hejmo"),
("Audio Input", "Aŭdia Enigo"),
("Enhancements", "Plibonigoj"),
("Hardware Codec", "Aparataro Kodeko"),
("Adaptive bitrate", "Adapta bitrapido"),
("ID Server", "Servilo de identigiloj"),
("Relay Server", "Relajsa servilo"),
("Relay Server", "Relajsa Servilo"),
("API Server", "Servilo de API"),
("invalid_http", "Devas komenci kun http:// aŭ https://"),
("Invalid IP", "IP nevalida"),
@@ -83,35 +83,35 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Successful", "Sukceso"),
("Connected, waiting for image...", "Konektita, atendante bildon..."),
("Name", "Nomo"),
("Type", ""),
("Type", "Tipo"),
("Modified", "Modifita"),
("Size", "Grandeco"),
("Show Hidden Files", "Montri kaŝitajn dosierojn"),
("Receive", "Akcepti"),
("Send", "Sendi"),
("Refresh File", ""),
("Local", ""),
("Remote", ""),
("Refresh File", "Aktualigu Dosieron"),
("Local", "Loka"),
("Remote", "Fora"),
("Remote Computer", "Fora komputilo"),
("Local Computer", "Loka komputilo"),
("Confirm Delete", "Konfermi la forigo"),
("Delete", ""),
("Properties", ""),
("Multi Select", ""),
("Select All", ""),
("Unselect All", ""),
("Empty Directory", ""),
("Not an empty directory", ""),
("Are you sure you want to delete this file?", "Ĉu vi vere volas forigi tiun dosieron?"),
("Are you sure you want to delete this empty directory?", ""),
("Are you sure you want to delete the file of this directory?", ""),
("Delete", "Forigi"),
("Properties", "Propraĵoj"),
("Multi Select", "Pluropa Elekto"),
("Select All", "Elektu Ĉiujn"),
("Unselect All", "Malelektu Ĉiujn"),
("Empty Directory", "Malplena Dosierujo"),
("Not an empty directory", "Ne Malplena Dosierujo"),
("Are you sure you want to delete this file?", "Ĉu vi certas, ke vi volas forigi ĉi tiun dosieron?"),
("Are you sure you want to delete this empty directory?", "Ĉu vi certas, ke vi volas forigi ĉi tiun malplenan dosierujon?"),
("Are you sure you want to delete the file of this directory?", "Ĉu vi certa. ke vi volas forigi la dosieron de ĉi tiu dosierujo"),
("Do this for all conflicts", "Same por ĉiuj konfliktoj"),
("This is irreversible!", ""),
("This is irreversible!", "Ĉi tio estas neinversigebla!"),
("Deleting", "Forigado"),
("files", "dosiero"),
("Waiting", "Atendante..."),
("Finished", "Finita"),
("Speed", ""),
("Speed", "Rapideco"),
("Custom Image Quality", "Agordi bildan kvaliton"),
("Privacy mode", "Modo privata"),
("Block user input", "Bloki uzanta enigo"),
@@ -127,7 +127,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Optimize reaction time", "Optimigi reakcia tempo"),
("Custom", ""),
("Show remote cursor", "Montri foran kursoron"),
("Show quality monitor", ""),
("Show quality monitor", "Montri kvalito monitoron"),
("Disable clipboard", "Malebligi poŝon"),
("Lock after session end", "Ŝlosi foran komputilon post malkonektado"),
("Insert", "Enmeti"),
@@ -170,8 +170,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Action", "Ago"),
("Add", "Aldoni"),
("Local Port", "Loka pordo"),
("Local Address", ""),
("Change Local Port", ""),
("Local Address", "Loka Adreso"),
("Change Local Port", "Ŝanĝi Loka Pordo"),
("setup_server_tip", "Se vi bezonas pli rapida konekcio, vi povas krei vian propran servilon"),
("Too short, at least 6 characters.", "Tro mallonga, almenaŭ 6 signoj."),
("The confirmation is not identical.", "Ambaŭ enigoj ne kongruas"),
@@ -203,23 +203,23 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Reboot required", "Restarto deviga"),
("Unsupported display server", "La aktuala bilda servilo ne estas subtenita"),
("x11 expected", "Bonvolu uzi x11"),
("Port", ""),
("Port", "Pordo"),
("Settings", "Agordoj"),
("Username", " Uzanta nomo"),
("Invalid port", "Pordo nevalida"),
("Closed manually by the peer", "Manuale fermita de la samtavolano"),
("Enable remote configuration modification", "Permesi foran redaktadon de la konfiguracio"),
("Run without install", "Plenumi sen instali"),
("Connect via relay", ""),
("Connect via relay", "Konekti per relajso"),
("Always connect via relay", "Ĉiam konekti per relajso"),
("whitelist_tip", "Nur la IP en la blanka listo povas kontroli mian komputilon"),
("Login", "Konekti"),
("Verify", ""),
("Remember me", ""),
("Trust this device", ""),
("Verification code", ""),
("verification_tip", ""),
("Logout", "Malkonekti"),
("Login", "Ensaluti"),
("Verify", "Kontrolis"),
("Remember me", "Memori min"),
("Trust this device", "Fidu ĉi tiun aparaton"),
("Verification code", "Konfirmkodo"),
("verification_tip", "Konfirmkodo estis sendita al la registrita retpoŝta adreso, enigu la konfirmkodon por daŭrigi ensaluti."),
("Logout", "Elsaluti"),
("Tags", "Etikedi"),
("Search ID", "Serĉi ID"),
("whitelist_sep", "Vi povas uzi komon, punktokomon, spacon aŭ linsalton kiel apartigilo"),
@@ -241,86 +241,86 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Socks5 Proxy", "Socks5 prokura servilo"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) prokura servilo"),
("Discovered", "Malkovritaj"),
("install_daemon_tip", ""),
("install_daemon_tip", "Por komenci ĉe ekŝargo, oni devas instali sisteman servon."),
("Remote ID", "Fora identigilo"),
("Paste", "Alglui"),
("Paste here?", ""),
("Paste here?", "Alglui ĉi tie?"),
("Are you sure to close the connection?", "Ĉu vi vere volas fermi la konekton?"),
("Download new version", "Elŝuti la novan version"),
("Touch mode", "Tuŝa modo"),
("Mouse mode", ""),
("One-Finger Tap", ""),
("Left Mouse", ""),
("One-Long Tap", ""),
("Two-Finger Tap", ""),
("Right Mouse", ""),
("One-Finger Move", ""),
("Double Tap & Move", ""),
("Mouse Drag", ""),
("Three-Finger vertically", ""),
("Mouse Wheel", ""),
("Two-Finger Move", ""),
("Canvas Move", ""),
("Pinch to Zoom", ""),
("Canvas Zoom", ""),
("Mouse mode", "musa modo"),
("One-Finger Tap", "Unufingra Frapeto"),
("Left Mouse", "Maldekstra Muso"),
("One-Long Tap", "Unulonga Frapeto"),
("Two-Finger Tap", "Dufingra Frapeto"),
("Right Mouse", "Deskra Muso"),
("One-Finger Move", "Unufingra Movo"),
("Double Tap & Move", "Duobla Frapeto & Movo"),
("Mouse Drag", "Muso Trenadi"),
("Three-Finger vertically", "Tri Figroj Vertikale"),
("Mouse Wheel", "Musa Rado"),
("Two-Finger Move", "Dufingra Movo"),
("Canvas Move", "Kanvasa Movo"),
("Pinch to Zoom", "Pinĉi al Zomo"),
("Canvas Zoom", "Kanvasa Zomo"),
("Reset canvas", "Restarigi kanvaso"),
("No permission of file transfer", "Neniu permeso de dosiertransigo"),
("Note", "Notu"),
("Connection", ""),
("Share Screen", ""),
("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_input_permission_tip1", ""),
("android_input_permission_tip2", ""),
("android_new_connection_tip", ""),
("android_service_will_start_tip", ""),
("android_stop_service_tip", ""),
("android_version_audio_tip", ""),
("android_start_service_tip", ""),
("android_permission_may_not_change_tip", ""),
("Account", ""),
("Overwrite", ""),
("This file exists, skip or overwrite this file?", ""),
("Quit", ""),
("Help", ""),
("Failed", ""),
("Succeeded", ""),
("Someone turns on privacy mode, exit", ""),
("Unsupported", ""),
("Peer denied", ""),
("Please install plugins", ""),
("Peer exit", ""),
("Failed to turn off", ""),
("Turned off", ""),
("Language", ""),
("Keep RustDesk background service", ""),
("Ignore Battery Optimizations", ""),
("android_open_battery_optimizations_tip", ""),
("Start on boot", ""),
("Start the screen sharing service on boot, requires special permissions", ""),
("Connection not allowed", ""),
("Connection", "Konekto"),
("Share Screen", "Kunhavigi Ekranon"),
("Chat", "Babilo"),
("Total", "Sumo"),
("items", "eroj"),
("Selected", "Elektita"),
("Screen Capture", "Ekrankapto"),
("Input Control", "Eniga Kontrolo"),
("Audio Capture", "Sonkontrolo"),
("File Connection", "Dosiero Konekto"),
("Screen Connection", "Ekrono konekto"),
("Do you accept?", "Ĉu vi akceptas?"),
("Open System Setting", "Malfermi Sistemajn Agordojn"),
("How to get Android input permission?", "Kiel akiri Android enigajn permesojn"),
("android_input_permission_tip1", "Por ke fora aparato regu vian Android-aparaton per muso aŭ tuŝo, vi devas permesi al RustDesk uzi la servon \"Alirebleco\"."),
("android_input_permission_tip2", "Bonvolu iri al la sekva paĝo de sistemaj agordoj, trovi kaj eniri [Instatajn Servojn], ŝalti la servon [RustDesk Enigo]."),
("android_new_connection_tip", "Nova kontrolpeto estis ricevita, kiu volas kontroli vian nunan aparaton."),
("android_service_will_start_tip", "Ŝalti \"Ekrankapto\" aŭtomate startos la servon, permesante al aliaj aparatoj peti konekton al via aparato."),
("android_stop_service_tip", "Fermante la servon aŭtomate fermos ĉiujn establitajn konektojn."),
("android_version_audio_tip", "La nuna versio da Android ne subtenas sonkapton, bonvolu ĝisdatigi al Android 10 aŭ pli alta."),
("android_start_service_tip", "Frapu [Komenci servo] aŭ ebligu la permeson de [Ekrankapto] por komenci la servon de kundivido de ekrano."),
("android_permission_may_not_change_tip", "Permesoj por establitaj konektoj neble estas ŝanĝitaj tuj ĝis rekonektitaj."),
("Account", "Konto"),
("Overwrite", "anstataŭigi"),
("This file exists, skip or overwrite this file?", "Ĉi tiu dosiero ekzistas, ĉu preterlasi aŭ anstataŭi ĉi tiun dosieron?"),
("Quit", "Forlasi"),
("Help", "Helpi"),
("Failed", "Malsukcesa"),
("Succeeded", "Sukcesa"),
("Someone turns on privacy mode, exit", "Iu ŝaltas modon privata, Eliro"),
("Unsupported", "Nesubtenata"),
("Peer denied", "Samulo rifuzita"),
("Please install plugins", "Bonvolu instali kromprogramojn"),
("Peer exit", "Samulo eliras"),
("Failed to turn off", "Malsukcesis malŝalti"),
("Turned off", "Malŝaltita"),
("Language", "Lingvo"),
("Keep RustDesk background service", "Tenu RustDesk fonan servon"),
("Ignore Battery Optimizations", "Ignoru Bateria Optimumigojn"),
("android_open_battery_optimizations_tip", "Se vi volas malŝalti ĉi tiun funkcion, bonvolu iri al la sekva paĝo de agordoj de la aplikaĵo de RustDesk, trovi kaj eniri [Baterio], Malmarku [Senrestrikta]"),
("Start on boot", "Komencu ĉe ekfunkciigo"),
("Start the screen sharing service on boot, requires special permissions", "Komencu la servon de kundivido de ekrano ĉe lanĉo, postulas specialajn permesojn"),
("Connection not allowed", "Konekto ne rajtas"),
("Legacy mode", ""),
("Map mode", ""),
("Translate mode", ""),
("Use permanent password", ""),
("Use both passwords", ""),
("Set permanent password", ""),
("Enable remote restart", ""),
("Restart remote device", ""),
("Are you sure you want to restart", ""),
("Restarting remote device", ""),
("remote_restarting_tip", ""),
("Copied", ""),
("Map mode", "Mapa modo"),
("Translate mode", "Traduki modo"),
("Use permanent password", "Uzu permanenta pasvorto"),
("Use both passwords", "Uzu ambaŭ pasvorto"),
("Set permanent password", "Starigi permanenta pasvorto"),
("Enable remote restart", "Permesi fora restartas"),
("Restart remote device", "Restartu fora aparato"),
("Are you sure you want to restart", "Ĉu vi certas, ke vi volas restarti"),
("Restarting remote device", "Restartas fora aparato"),
("remote_restarting_tip", "Fora aparato restartiĝas, bonvolu fermi ĉi tiun mesaĝkeston kaj rekonekti kun permanenta pasvorto post iom da tempo"),
("Copied", "Kopiita"),
("Exit Fullscreen", "Eliru Plenekranon"),
("Fullscreen", "Plenekrane"),
("Mobile Actions", "Poŝtelefonaj Agoj"),
@@ -330,8 +330,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ratio", "Proporcio"),
("Image Quality", "Bilda Kvalito"),
("Scroll Style", "Ruluma Stilo"),
("Show Toolbar", ""),
("Hide Toolbar", ""),
("Show Toolbar", "Montri Ilobreton"),
("Hide Toolbar", "Kaŝi Ilobreton"),
("Direct Connection", "Rekta Konekto"),
("Relay Connection", "Relajsa Konekto"),
("Secure Connection", "Sekura Konekto"),
@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "La transferencia en un sentido está habilitada en el lado controlado."),
("Authentication Required", "Se requiere autenticación"),
("Authenticate", "Autenticar"),
("web_id_input_tip", "Puedes introducir una ID en el mismo servidor, el cliente web no soporta acceso vía IP.\nSi quieres acceder a un dispositivo en otro servidor, por favor, agrega la dirección del servidor (<id>@<direccion_servidor>?clave=<clave_valor>), por ejemplo,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nSi quieres accedder a un dispositivo en un servidor público, por favor, introduce \"<id>@public\", la clave no es necesaria para el servidor público."),
("Download", "Descarga"),
("Upload folder", "Subir carpeta"),
("Upload files", "Subir archivos"),
("Clipboard is synchronized", "Portapapeles sincronizado"),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -2,8 +2,8 @@ lazy_static::lazy_static! {
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
[
("Status", "Status"),
("Your Desktop", "Desktop Anda"),
("desk_tip", "Desktop Anda dapat diakses dengan ID dan kata sandi ini."),
("Your Desktop", "Layar Utama"),
("desk_tip", "Layar kamu dapat diakses dengan ID dan kata sandi ini."),
("Password", "Kata sandi"),
("Ready", "Sudah siap"),
("Established", "Didirikan"),
@@ -12,17 +12,17 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Start service", "Mulai Layanan"),
("Service is running", "Layanan berjalan"),
("Service is not running", "Layanan tidak berjalan"),
("not_ready_status", "Belum siap. Silakan periksa koneksi Anda"),
("Control Remote Desktop", "Kontrol Remote Desktop"),
("Transfer file", "File Transfer"),
("Connect", "Hubungkan"),
("not_ready_status", "Belum siap digunakan. Silakan periksa koneksi"),
("Control Remote Desktop", "Kontrol PC dari jarak jauh"),
("Transfer file", "Transfer File"),
("Connect", "Sambungkan"),
("Recent sessions", "Sesi Terkini"),
("Address book", "Buku Alamat"),
("Confirmation", "Konfirmasi"),
("TCP tunneling", "Tunneling TCP"),
("Remove", "Hapus"),
("Refresh random password", "Perbarui kata sandi acak"),
("Set your own password", "Tetapkan kata sandi Anda"),
("Set your own password", "Tetapkan kata sandi"),
("Enable keyboard/mouse", "Aktifkan Keyboard/Mouse"),
("Enable clipboard", "Aktifkan Papan Klip"),
("Enable file transfer", "Aktifkan Transfer file"),
@@ -37,7 +37,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Clipboard is empty", "Papan klip kosong"),
("Stop service", "Hentikan Layanan"),
("Change ID", "Ubah ID"),
("Your new ID", "ID baru anda"),
("Your new ID", "ID baru"),
("length %min% to %max%", "panjang %min% s/d %max%"),
("starts with a letter", "Dimulai dengan huruf"),
("allowed characters", "Karakter yang dapat digunakan"),
@@ -69,10 +69,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Retry", "Coba lagi"),
("OK", "Oke"),
("Password Required", "Kata sandi tidak boleh kosong"),
("Please enter your password", "Silahkan masukkan kata sandi anda"),
("Please enter your password", "Silahkan masukkan kata sandi"),
("Remember password", "Ingat kata sandi"),
("Wrong Password", "Kata sandi Salah"),
("Do you want to enter again?", "Apakah anda ingin masuk lagi?"),
("Do you want to enter again?", "Apakah kamu ingin mencoba lagi?"),
("Connection Error", "Kesalahan koneksi"),
("Error", "Kesalahan"),
("Reset by the peer", "Direset oleh rekan"),
@@ -102,9 +102,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Unselect All", "Batalkan Pilihan Semua"),
("Empty Directory", "Folder Kosong"),
("Not an empty directory", "Folder tidak kosong"),
("Are you sure you want to delete this file?", "Apakah anda yakin untuk menghapus file ini?"),
("Are you sure you want to delete this empty directory?", "Apakah anda yakin untuk menghapus folder ini?"),
("Are you sure you want to delete the file of this directory?", "Apakah anda yakin untuk menghapus file dan folder ini?"),
("Are you sure you want to delete this file?", "Apakah kamu yakin untuk menghapus file ini?"),
("Are you sure you want to delete this empty directory?", "Apakah yakin yakin untuk menghapus folder ini?"),
("Are you sure you want to delete the file of this directory?", "Apakah yakin yakin untuk menghapus file dan folder ini?"),
("Do this for all conflicts", "Lakukan untuk semua konflik"),
("This is irreversible!", "Ini tidak dapat diubah!"),
("Deleting", "Menghapus"),
@@ -150,20 +150,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Click to download", "Klik untuk unduh"),
("Click to update", "Klik untuk memperbarui"),
("Configure", "Konfigurasi"),
("config_acc", "Untuk mengontrol Desktop Anda dari jarak jauh, Anda perlu memberikan izin \"Aksesibilitas\" RustDesk."),
("config_screen", "Untuk mengakses Desktop Anda dari jarak jauh, Anda perlu memberikan izin \"Perekaman Layar\" RustDesk."),
("config_acc", "Agar bisa mengontrol Desktopmu dari jarak jauh, Kamu harus memberikan izin \"Aksesibilitas\" untuk RustDesk."),
("config_screen", "Agar bisa mengakses Desktopmu dari jarak jauh, kamu harus memberikan izin \"Perekaman Layar\" untuk RustDesk."),
("Installing ...", "Menginstall"),
("Install", "Instal"),
("Installation", "Instalasi"),
("Installation Path", "Direktori Instalasi"),
("Create start menu shortcuts", "Buat pintasan start menu"),
("Create desktop icon", "Buat icon desktop"),
("agreement_tip", "Dengan memulai instalasi, Anda menerima perjanjian lisensi."),
("agreement_tip", "Dengan memulai proses instalasi, Kamu menerima perjanjian lisensi."),
("Accept and Install", "Terima dan Install"),
("End-user license agreement", "Perjanjian lisensi pengguna"),
("Generating ...", "Memproses..."),
("Your installation is lower version.", "Instalasi Anda adalah versi yang lebih rendah."),
("not_close_tcp_tip", "Jangan tutup jendela ini saat menggunakan tunnel"),
("Your installation is lower version.", "Kamu menggunakan versi instalasi yang lebih rendah."),
("not_close_tcp_tip", "Pastikan jendela ini tetap terbuka saat menggunakan tunnel."),
("Listening ...", "Menghubungkan..."),
("Remote Host", "Host Remote"),
("Remote Port", "Port Remote"),
@@ -172,24 +172,24 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Local Port", "Port Lokal"),
("Local Address", "Alamat lokal"),
("Change Local Port", "Ubah Port Lokal"),
("setup_server_tip", "Untuk mendapatkan koneksi yang lebih baik, disarankan untuk menginstal di server anda sendiri"),
("setup_server_tip", "Untuk koneksi yang lebih baik, silakan konfigurasi di server pribadi"),
("Too short, at least 6 characters.", "Terlalu pendek, setidaknya 6 karekter."),
("The confirmation is not identical.", "Konfirmasi tidak identik."),
("Permissions", "Perizinan"),
("Accept", "Terima"),
("Dismiss", "Hentikan"),
("Disconnect", "Terputus"),
("Enable file copy and paste", "Izinkan salin dan tempel file"),
("Enable file copy and paste", "Izinkan copy dan paste"),
("Connected", "Terhubung"),
("Direct and encrypted connection", "Koneksi langsung dan terenkripsi"),
("Relayed and encrypted connection", "Koneksi relay dan terenkripsi"),
("Direct and unencrypted connection", "Koneksi langsung dan tanpa enkripsi"),
("Relayed and unencrypted connection", "Koneksi relay dan tanpa enkripsi"),
("Enter Remote ID", "Masukkan ID Remote"),
("Enter your password", "Masukkan kata sandi anda"),
("Enter your password", "Masukkan kata sandi"),
("Logging in...", "Masuk..."),
("Enable RDP session sharing", "Aktifkan berbagi sesi RDP"),
("Auto Login", "Login Otomatis (Hanya berlaku jika Anda mengatur \"Kunci setelah sesi berakhir\")"),
("Auto Login", "Login Otomatis (Hanya berlaku jika sudah mengatur \"Kunci setelah sesi berakhir\")"),
("Enable direct IP access", "Aktifkan Akses IP Langsung"),
("Rename", "Ubah nama"),
("Space", "Spasi"),
@@ -199,7 +199,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Please enter the folder name", "Silahkan masukkan nama folder"),
("Fix it", "Perbaiki"),
("Warning", "Peringatan"),
("Login screen using Wayland is not supported", "Layar masuk menggunakan Wayland tidak didukung"),
("Login screen using Wayland is not supported", "Login screen dengan Wayland tidak didukung"),
("Reboot required", "Diperlukan boot ulang"),
("Unsupported display server", "Server tampilan tidak didukung "),
("x11 expected", "Diperlukan x11"),
@@ -241,11 +241,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Socks5 Proxy", "Proksi Socks5"),
("Socks5/Http(s) Proxy", "Proksi Socks5/Http(s)"),
("Discovered", "Telah ditemukan"),
("install_daemon_tip", "Untuk memulai saat boot, Anda perlu menginstal system service."),
("install_daemon_tip", "Untuk dapat berjalan saat sistem menyala, kamu perlu menginstal layanan sistem (system service/daemon)."),
("Remote ID", "ID Remote"),
("Paste", "Tempel"),
("Paste here?", "Tempel disini?"),
("Are you sure to close the connection?", "Apakah anda yakin akan menutup koneksi?"),
("Are you sure to close the connection?", "Apakah kamu yakin akan menutup koneksi?"),
("Download new version", "Unduh versi baru"),
("Touch mode", "Mode Layar Sentuh"),
("Mouse mode", "Mode Mouse"),
@@ -373,7 +373,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Write a message", "Tulis pesan"),
("Prompt", ""),
("Please wait for confirmation of UAC...", "Harap tunggu konfirmasi UAC"),
("elevated_foreground_window_tip", "Jendela remote desktop ini memerlukan hak akses khusus, jadi anda tidak bisa menggunakan mouse dan keyboard untuk sementara. Anda bisa meminta pihak pengguna yang diremote untuk menyembunyikan jendela ini atau klik tombol elevasi di jendela pengaturan koneksi. Untuk menghindari masalah ini, direkomendasikan untuk menginstall aplikasi secara permanen"),
("elevated_foreground_window_tip", "Jendela yang sedang aktif di remote desktop memerlukan hak istimewa yang lebih tinggi untuk beroperasi, sehingga mouse dan keyboard tidak dapat digunakan sementara waktu. Kamu bisa meminta pengguna jarak jauh untuk meminimalkan jendela saat ini, atau klik tombol elevasi di jendela manajemen koneksi. Untuk menghindari masalah ini, disarankan untuk menginstal software di perangkat remote secara permanen."),
("Disconnected", "Terputus"),
("Other", "Lainnya"),
("Confirm before closing multiple tabs", "Konfirmasi sebelum menutup banyak tab"),
@@ -394,9 +394,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept sessions via click", "Izinkan sesi dengan klik"),
("Accept sessions via both", "Izinkan sesi dengan keduanya"),
("Please wait for the remote side to accept your session request...", "Harap tunggu pihak pengguna remote untuk menerima permintaan sesi..."),
("One-time Password", "Kata sandi sekali pakai"),
("Use one-time password", "Gunakan kata sandi sekali pakai"),
("One-time password length", "Panjang kata sandi sekali pakai"),
("One-time Password", "Kata sandi sementara"),
("Use one-time password", "Gunakan kata sandi sementara"),
("One-time password length", "Panjang kata sandi sementara"),
("Request access to your device", "Permintaan akses ke perangkat ini"),
("Hide connection management window", "Sembunyikan jendela pengaturan koneksi"),
("hide_cm_tip", "Izinkan untuk menyembunyikan hanya jika menerima sesi melalui kata sandi dan menggunakan kata sandi permanen"),
@@ -569,83 +569,88 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enter privacy mode", "Masuk mode privasi"),
("Exit privacy mode", "Keluar mode privasi"),
("idd_not_support_under_win10_2004_tip", "Driver grafis yang Anda gunakan tidak kompatibel dengan versi Windows Anda dan memerlukan Windows 10 versi 2004 atau yang lebih baru"),
("input_source_1_tip", ""),
("input_source_2_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("More", ""),
("enable-2fa-title", ""),
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
("Multiple Windows sessions found", ""),
("Please select the session you want to connect to", ""),
("powered_by_me", ""),
("outgoing_only_desk_tip", ""),
("preset_password_warning", ""),
("Security Alert", ""),
("My address book", ""),
("Personal", ""),
("Owner", ""),
("Set shared password", ""),
("Exist in", ""),
("input_source_1_tip", "Sumber input 1"),
("input_source_2_tip", "Sumber input 2"),
("Swap control-command key", "Menukar tombol control-command"),
("swap-left-right-mouse", "Tukar fungsi tombol kiri dan kanan pada mouse"),
("2FA code", "Kode 2FA"),
("More", "Lainnya"),
("enable-2fa-title", "Aktifkan autentikasi 2FA"),
("enable-2fa-desc", "Silakan atur autentikator Anda sekarang. Anda dapat menggunakan aplikasi autentikator seperti Authy, Microsoft Authenticator, atau Google Authenticator di ponsel atau desktop Anda\n\nPindai kode QR dengan aplikasi Anda dan masukkan kode yang ditampilkan oleh aplikasi untuk mengaktifkan autentikasi 2FA."),
("wrong-2fa-code", "Tidak dapat memverifikasi kode. Pastikan bahwa kode dan pengaturan waktu lokal sudah sesuai"),
("enter-2fa-title", "Autentikasi dua faktor"),
("Email verification code must be 6 characters.", "Kode verifikasi email harus terdiri dari 6 karakter."),
("2FA code must be 6 digits.", "Kode 2FA harus terdiri dari 6 digit."),
("Multiple Windows sessions found", "Terdapat beberapa sesi Windows"),
("Please select the session you want to connect to", "Silakan pilih sesi yang ingin Anda sambungkan."),
("powered_by_me", "Didukung oleh RustDesk"),
("outgoing_only_desk_tip", "Ini adalah edisi yang sudah kustomisasi.\nAnda dapat terhubung ke perangkat lain, tetapi perangkat lain tidak dapat terhubung ke perangkat Anda."),
("preset_password_warning", "Edisi yang dikustomisasi ini dilengkapi dengan kata sandi bawaan. Siapa pun yang mengetahui kata sandi ini dapat memperoleh kontrol penuh atas perangkat Anda. Jika Anda tidak mengharapkan ini, segera hapus pemasangan aplikasi tersebut."),
("Security Alert", "Peringatan Keamanan"),
("My address book", "Daftar Kontak"),
("Personal", "Personal"),
("Owner", "Pemilik"),
("Set shared password", "Atus kata sandi kolaboratif"),
("Exist in", "Ada di"),
("Read-only", ""),
("Read/Write", ""),
("Full Control", ""),
("share_warning_tip", ""),
("share_warning_tip", "Informasi di atas bersifat publik dan dapat dilihat oleh orang lain."),
("Everyone", ""),
("ab_web_console_tip", ""),
("allow-only-conn-window-open-tip", ""),
("no_need_privacy_mode_no_physical_displays_tip", ""),
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
("ab_web_console_tip", "Detail Lain di Konsol Web"),
("allow-only-conn-window-open-tip", "Koneksi hanya diperbolehkan jika jendela RustDesk sedang terbuka."),
("no_need_privacy_mode_no_physical_displays_tip", "Karena tidak ada layar fisik, mode privasi tidak perlu diaktifkan."),
("Follow remote cursor", "Ikuti kursor yang terhubung"),
("Follow remote window focus", "Ikuti jendela remote yang sedang aktif"),
("default_proxy_tip", "Pengaturan standar untuk protokol dan port adalah Socks5 dan 1080."),
("no_audio_input_device_tip", "Perangkat input audio tidak terdeteksi."),
("Incoming", ""),
("Outgoing", ""),
("Clear Wayland screen selection", ""),
("clear_Wayland_screen_selection_tip", ""),
("confirm_clear_Wayland_screen_selection_tip", ""),
("android_new_voice_call_tip", ""),
("texture_render_tip", ""),
("Use texture rendering", ""),
("Clear Wayland screen selection", "Kosongkan pilihan layar Wayland"),
("clear_Wayland_screen_selection_tip", "Setelah mengosongkan pilihan layar, Kamu bisa memilih kembali layar untuk dibagi"),
("confirm_clear_Wayland_screen_selection_tip", "Kamu yakin ingin membersihkan pemilihan layar Wayland?"),
("android_new_voice_call_tip", "Kamu mendapatkan permintaan panggilan suara baru. Jika diterima, audio akan berubah menjadi komunikasi suara."),
("texture_render_tip", "Aktifkan rendering tekstur untuk membuat tampilan gambar lebih mulus. Kamu dapat menonaktifkan opsi ini jika terjadi masalah saat merender."),
("Use texture rendering", "Aktifkan rendering tekstur"),
("Floating window", ""),
("floating_window_tip", ""),
("Keep screen on", ""),
("Never", ""),
("During controlled", ""),
("floating_window_tip", "Untuk menjaga layanan/service RustDesk agar tetap aktif"),
("Keep screen on", "Biarkan layar tetap menyala"),
("Never", "Tidak pernah"),
("During controlled", "Dalam proses pengendalian"),
("During service is on", ""),
("Capture screen using DirectX", ""),
("Back", ""),
("Apps", ""),
("Volume up", ""),
("Volume down", ""),
("Capture screen using DirectX", "Rekam layar dengan DirectX"),
("Back", "Kembali"),
("Apps", "App"),
("Volume up", "Naikkan volume"),
("Volume down", "Turunkan volume"),
("Power", ""),
("Telegram bot", ""),
("enable-bot-tip", ""),
("enable-bot-desc", ""),
("cancel-2fa-confirm-tip", ""),
("cancel-bot-confirm-tip", ""),
("About RustDesk", ""),
("Send clipboard keystrokes", ""),
("network_error_tip", ""),
("Unlock with PIN", ""),
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
("Parent directory", ""),
("Resume", ""),
("Invalid file name", ""),
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("enable-bot-tip", "Jika fitur ini diaktifkan, Kamu dapat menerima kode 2FA dari bot, serta mendapatkan notifikasi tentang koneksi."),
("enable-bot-desc", "1. Buka chat dengan @BotFather.\n2. Kirim perintah \"/newbot\". Setelah menyelesaikan langkah ini, Kamu akan mendapatkan token\n3. Mulai percakapan dengan bot yang baru dibuat. Kirim pesan yang dimulai dengan garis miring (\"/\") seperti \"/hello\" untuk mengaktifkannya."),
("cancel-2fa-confirm-tip", "Apakah Kamu yakin ingin membatalkan 2FA?"),
("cancel-bot-confirm-tip", "Apakah Kamu yakin ingin membatalkan bot Telegram?"),
("About RustDesk", "Tentang RustDesk"),
("Send clipboard keystrokes", "Kirim keystrokes clipboard"),
("network_error_tip", "Periksa koneksi internet, lalu klik \"Coba lagi\"."),
("Unlock with PIN", "Buka menggunakan PIN"),
("Requires at least {} characters", "Memerlukan setidaknya {} karakter."),
("Wrong PIN", "PIN salah"),
("Set PIN", "Atur PIN"),
("Enable trusted devices", "Izinkan perangkat tepercaya"),
("Manage trusted devices", "Kelola perangkat tepercaya"),
("Platform", "Platform"),
("Days remaining", "Sisa hari"),
("enable-trusted-devices-tip", "Tidak memerlukan verifikasi 2FA pada perangkat tepercaya."),
("Parent directory", "Direktori utama"),
("Resume", "Lanjutkan"),
("Invalid file name", "Nama file tidak valid"),
("one-way-file-transfer-tip", "Transfer file satu arah (One-way) telah diaktifkan pada sisi yang dikendalikan."),
("Authentication Required", "Diperlukan autentikasi"),
("Authenticate", "Autentikasi"),
("web_id_input_tip", "Kamu bisa memasukkan ID pada server yang sama, akses IP langsung tidak didukung di klien web.\nJika Anda ingin mengakses perangkat di server lain, silakan tambahkan alamat server (<id>@<server_address>?key=<key_value>), contohnya:\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nUntuk mengakses perangkat di server publik, cukup masukkan \"<id>@public\", tanpa kunci/key."),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -369,7 +369,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop session recording", "Ferma registrazione sessione"),
("Enable recording session", "Abilita registrazione sessione"),
("Enable LAN discovery", "Abilita rilevamento LAN"),
("Deny LAN discovery", "Disabilita rilevamento LAN"),
("Deny LAN discovery", "Non effettuare rilevamento LAN"),
("Write a message", "Scrivi un messaggio"),
("Prompt", "Richiedi"),
("Please wait for confirmation of UAC...", "Attendi la conferma dell'UAC..."),
@@ -559,7 +559,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Big tiles", "Icone grandi"),
("Small tiles", "Icone piccole"),
("List", "Elenco"),
("Virtual display", "Scehrmo virtuale"),
("Virtual display", "Schermo virtuale"),
("Plug out all", "Scollega tutto"),
("True color (4:4:4)", "Colore reale (4:4:4)"),
("Enable blocking user input", "Abilita blocco input utente"),
@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "Sul lato controllato è abilitato il trasferimento file unidirezionale."),
("Authentication Required", "Richiesta autenticazione"),
("Authenticate", "Autentica"),
("web_id_input_tip", "È possibile inserire un ID nello stesso server, nel client web non è supportato l'accesso con IP diretto.\nSe vuoi accedere ad un dispositivo in un altro server, aggiungi l'indirizzo del server (<id>@<indirizzo_server>?key=<valore_chiave >), ad esempio,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nSe vuoi accedere ad un dispositivo in un server pubblico, inserisci \"<id>@public\", la chiave non è necessaria per il server pubblico."),
("Download", "Download"),
("Upload folder", "Cartella upload"),
("Upload files", "File upload"),
("Clipboard is synchronized", "Gli appunti sono sincronizzati"),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "단방향 파일 전송은 제어되는 쪽에서 활성화됩니다."),
("Authentication Required", "인증 필요함"),
("Authenticate", "인증"),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "Kontrolējamajā pusē ir iespējota vienvirziena failu pārsūtīšana."),
("Authentication Required", "Nepieciešama autentifikācija"),
("Authenticate", "Autentificēt"),
("web_id_input_tip", "Varat ievadīt ID tajā pašā serverī, tīmekļa klientā tiešā IP piekļuve netiek atbalstīta.\nJa vēlaties piekļūt ierīcei citā serverī, lūdzu, pievienojiet servera adresi (<id>@<server_address>?key=<key_value>), piemēram,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nJa vēlaties piekļūt ierīcei publiskajā serverī, lūdzu, ievadiet \"<id>@public\", publiskajam serverim atslēga nav nepieciešama."),
("Download", "Lejupielādēt"),
("Upload folder", "Augšupielādēt mapi"),
("Upload files", "Augšupielādēt failus"),
("Clipboard is synchronized", "Starpliktuve ir sinhronizēta"),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "Eenzijdige bestandsoverdracht is ingeschakeld aan de gecontroleerde kant."),
("Authentication Required", "Verificatie vereist"),
("Authenticate", "Verificatie"),
("web_id_input_tip", "Je kunt een ID invoeren op dezelfde server, directe IP-toegang wordt niet ondersteund in de webclient.\nAls je toegang wilt tot een apparaat op een andere server, voeg je het serveradres toe (<id>@<server_adres>?key=<key_value>), bijvoorbeeld,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nAls je toegang wilt krijgen tot een apparaat op een publieke server, voer dan \"<id>@public\" in, sleutel is niet nodig voor de publieke server."),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "На управляемой стороне включена односторонняя передача файлов."),
("Authentication Required", "Требуется аутентификация"),
("Authenticate", "Аутентификация"),
("web_id_input_tip", "Можно ввести ID на том же сервере, прямой доступ по IP в веб-клиенте не поддерживается.\nЕсли вы хотите получить доступ к устройству на другом сервере, добавьте адрес сервера (<id>@<адрес_сервера>?key=<ключ>), например,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЕсли вы хотите получить доступ к устройству на публичном сервере, введите \"<id>@public\", для публичного сервера ключ не нужен."),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -37,19 +37,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Clipboard is empty", "Odložišče je prazno"),
("Stop service", "Ustavi storitev"),
("Change ID", "Spremeni ID"),
("Your new ID", ""),
("length %min% to %max%", ""),
("starts with a letter", ""),
("allowed characters", ""),
("Your new ID", "Vaš nov ID"),
("length %min% to %max%", "dolžina od %min% do %max%"),
("starts with a letter", "začne se s črko"),
("allowed characters", "dovoljeni znaki"),
("id_change_tip", "Dovoljeni znaki so a-z, A-Z (brez šumnikov), 0-9 in _. Prvi znak mora biti črka, dolžina od 6 do 16 znakov."),
("Website", "Spletna stran"),
("About", "O programu"),
("Slogan_tip", ""),
("Privacy Statement", ""),
("Privacy Statement", "Izjava o zasebnosti"),
("Mute", "Izklopi zvok"),
("Build Date", ""),
("Version", ""),
("Home", ""),
("Build Date", "Datum graditve"),
("Version", "Različica"),
("Home", "Začetek"),
("Audio Input", "Avdio vhod"),
("Enhancements", "Izboljšave"),
("Hardware Codec", "Strojni kodek"),
@@ -210,27 +210,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the peer", "Povezavo ročno prekinil odjemalec"),
("Enable remote configuration modification", "Omogoči oddaljeno spreminjanje nastavitev"),
("Run without install", "Zaženi brez namestitve"),
("Connect via relay", ""),
("Connect via relay", "Poveži preko posrednika"),
("Always connect via relay", "Vedno poveži preko posrednika"),
("whitelist_tip", "Dostop je možen samo iz dovoljenih IPjev"),
("Login", "Prijavi"),
("Verify", ""),
("Remember me", ""),
("Trust this device", ""),
("Verification code", ""),
("verification_tip", ""),
("Verify", "Preveri"),
("Remember me", "Zapomni si me"),
("Trust this device", "Zaupaj tej napravi"),
("Verification code", "Koda za preverjanje"),
("verification_tip", "Kodo za preverjanje prejmete na registrirani e-poštni naslov"),
("Logout", "Odjavi"),
("Tags", "Oznake"),
("Search ID", "Išči ID"),
("whitelist_sep", "Naslovi ločeni z vejico, podpičjem, presledkom ali novo vrstico"),
("Add ID", "Dodaj ID"),
("Add Tag", "Dodaj oznako"),
("Unselect all tags", ""),
("Unselect all tags", "Odznači vse oznake"),
("Network error", "Omrežna napaka"),
("Username missed", "Up. ime izpuščeno"),
("Password missed", "Geslo izpuščeno"),
("Wrong credentials", "Napačne poverilnice"),
("The verification code is incorrect or has expired", ""),
("The verification code is incorrect or has expired", "Koda za preverjanje je napačna, ali pa je potekla"),
("Edit Tag", "Uredi oznako"),
("Forget Password", "Pozabi geslo"),
("Favorites", "Priljubljene"),
@@ -248,7 +248,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Are you sure to close the connection?", "Ali želite prekiniti povezavo?"),
("Download new version", "Prenesi novo različico"),
("Touch mode", "Način dotika"),
("Mouse mode", "Način mišle"),
("Mouse mode", "Način miške"),
("One-Finger Tap", "Tap z enim prstom"),
("Left Mouse", "Leva tipka miške"),
("One-Long Tap", "Dolg tap z enim prstom"),
@@ -286,8 +286,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_service_will_start_tip", "Z vklopom zajema zaslona se bo samodejno zagnala storitev, ki omogoča da oddaljene naprave pošljejo zahtevo za povezavo na vašo napravo."),
("android_stop_service_tip", "Z zaustavitvijo storitve bodo samodejno prekinjene vse oddaljene povezave."),
("android_version_audio_tip", "Trenutna različica Androida ne omogoča zajema zvoka. Za zajem zvoka nadgradite na Android 10 ali novejši."),
("android_start_service_tip", ""),
("android_permission_may_not_change_tip", ""),
("android_start_service_tip", "Tapnite [Zaženi storitev] ali pa omogočite pravico [Zajemanje zaslona] za zagon storitve deljenja zaslona."),
("android_permission_may_not_change_tip", "Pravic za že vzpostavljene povezave ne morete spremeniti brez ponovne vzpostavitve povezave."),
("Account", "Račun"),
("Overwrite", "Prepiši"),
("This file exists, skip or overwrite this file?", "Datoteka obstaja, izpusti ali prepiši?"),
@@ -306,8 +306,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Keep RustDesk background service", "Ohrani RustDeskovo storitev v ozadju"),
("Ignore Battery Optimizations", "Prezri optimizacije baterije"),
("android_open_battery_optimizations_tip", "Če želite izklopiti to možnost, pojdite v nastavitve aplikacije RustDesk, poiščite »Baterija« in izklopite »Neomejeno«"),
("Start on boot", ""),
("Start the screen sharing service on boot, requires special permissions", ""),
("Start on boot", "Zaženi ob vklopu"),
("Start the screen sharing service on boot, requires special permissions", "Zaženi storitev deljenja zaslona ob vklopu, zahteva posebna dovoljenja"),
("Connection not allowed", "Povezava ni dovoljena"),
("Legacy mode", "Stari način"),
("Map mode", "Način preslikave"),
@@ -330,8 +330,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Ratio", "Razmerje"),
("Image Quality", "Kakovost slike"),
("Scroll Style", "Način drsenja"),
("Show Toolbar", ""),
("Hide Toolbar", ""),
("Show Toolbar", "Prikaži orodno vrstico"),
("Hide Toolbar", "Skrij orodno vrstico"),
("Direct Connection", "Neposredna povezava"),
("Relay Connection", "Posredovana povezava"),
("Secure Connection", "Zavarovana povezava"),
@@ -342,7 +342,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Security", "Varnost"),
("Theme", "Tema"),
("Dark Theme", "Temna tema"),
("Light Theme", ""),
("Light Theme", "Svetla tema"),
("Dark", "Temna"),
("Light", "Svetla"),
("Follow System", "Sistemska"),
@@ -359,8 +359,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Audio Input Device", "Vhodna naprava za zvok"),
("Use IP Whitelisting", "Omogoči seznam dovoljenih IP naslovov"),
("Network", "Mreža"),
("Pin Toolbar", ""),
("Unpin Toolbar", ""),
("Pin Toolbar", "Pripni orodno vrstico"),
("Unpin Toolbar", "Odpni orodno vrstico"),
("Recording", "Snemanje"),
("Directory", "Imenik"),
("Automatically record incoming sessions", "Samodejno snemaj vhodne seje"),
@@ -409,243 +409,248 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by web console", "Ročno zaprto iz spletne konzole"),
("Local keyboard type", "Lokalna vrsta tipkovnice"),
("Select local keyboard type", "Izberite lokalno vrsto tipkovnice"),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
("config_microphone", ""),
("request_elevation_tip", ""),
("Wait", ""),
("Elevation Error", ""),
("Ask the remote user for authentication", ""),
("Choose this if the remote account is administrator", ""),
("Transmit the username and password of administrator", ""),
("still_click_uac_tip", ""),
("Request Elevation", ""),
("wait_accept_uac_tip", ""),
("Elevate successfully", ""),
("uppercase", ""),
("lowercase", ""),
("digit", ""),
("special character", ""),
("length>=8", ""),
("Weak", ""),
("Medium", ""),
("Strong", ""),
("Switch Sides", ""),
("Please confirm if you want to share your desktop?", ""),
("Display", ""),
("Default View Style", ""),
("Default Scroll Style", ""),
("Default Image Quality", ""),
("Default Codec", ""),
("Bitrate", ""),
("FPS", ""),
("Auto", ""),
("Other Default Options", ""),
("Voice call", ""),
("Text chat", ""),
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
("Restore", ""),
("Minimize", ""),
("Maximize", ""),
("Your Device", ""),
("empty_recent_tip", ""),
("empty_favorite_tip", ""),
("empty_lan_tip", ""),
("empty_address_book_tip", ""),
("eg: admin", ""),
("Empty Username", ""),
("Empty Password", ""),
("Me", ""),
("identical_file_tip", ""),
("show_monitors_tip", ""),
("View Mode", ""),
("login_linux_tip", ""),
("verify_rustdesk_password_tip", ""),
("remember_account_tip", ""),
("os_account_desk_tip", ""),
("OS Account", ""),
("another_user_login_title_tip", ""),
("another_user_login_text_tip", ""),
("xorg_not_found_title_tip", ""),
("xorg_not_found_text_tip", ""),
("no_desktop_title_tip", ""),
("no_desktop_text_tip", ""),
("No need to elevate", ""),
("System Sound", ""),
("Default", ""),
("New RDP", ""),
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", ""),
("Uninstall", ""),
("Update", ""),
("Enable", ""),
("Disable", ""),
("Options", ""),
("resolution_original_tip", ""),
("resolution_fit_local_tip", ""),
("resolution_custom_tip", ""),
("Collapse toolbar", ""),
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
("logout_tip", ""),
("Service", ""),
("Start", ""),
("Stop", ""),
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
("Open connection in new tab", ""),
("Move tab to new window", ""),
("Can not be empty", ""),
("Already exists", ""),
("Change Password", ""),
("Refresh Password", ""),
("ID", ""),
("Grid View", ""),
("List View", ""),
("Select", ""),
("Toggle Tags", ""),
("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
("Change Color", ""),
("Primary Color", ""),
("HSV Color", ""),
("Installation Successful!", ""),
("Installation failed!", ""),
("Reverse mouse wheel", ""),
("{} sessions", ""),
("scam_title", ""),
("scam_text1", ""),
("scam_text2", ""),
("Don't show again", ""),
("I Agree", ""),
("Decline", ""),
("Timeout in minutes", ""),
("auto_disconnect_option_tip", ""),
("Connection failed due to inactivity", ""),
("Check for software update on startup", ""),
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
("pull_group_failed_tip", ""),
("Filter by intersection", ""),
("Remove wallpaper during incoming sessions", ""),
("Test", ""),
("display_is_plugged_out_msg", ""),
("No displays", ""),
("Open in new window", ""),
("Show displays as individual windows", ""),
("Use all my displays for the remote session", ""),
("selinux_tip", ""),
("Change view", ""),
("Big tiles", ""),
("Small tiles", ""),
("List", ""),
("Virtual display", ""),
("Plug out all", ""),
("True color (4:4:4)", ""),
("Enable blocking user input", ""),
("id_input_tip", ""),
("privacy_mode_impl_mag_tip", ""),
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_under_win10_2004_tip", ""),
("input_source_1_tip", ""),
("input_source_2_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("More", ""),
("enable-2fa-title", ""),
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
("Multiple Windows sessions found", ""),
("Please select the session you want to connect to", ""),
("powered_by_me", ""),
("outgoing_only_desk_tip", ""),
("preset_password_warning", ""),
("Security Alert", ""),
("My address book", ""),
("Personal", ""),
("Owner", ""),
("Set shared password", ""),
("Exist in", ""),
("Read-only", ""),
("Read/Write", ""),
("Full Control", ""),
("share_warning_tip", ""),
("Everyone", ""),
("ab_web_console_tip", ""),
("allow-only-conn-window-open-tip", ""),
("no_need_privacy_mode_no_physical_displays_tip", ""),
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
("Incoming", ""),
("Outgoing", ""),
("Clear Wayland screen selection", ""),
("clear_Wayland_screen_selection_tip", ""),
("confirm_clear_Wayland_screen_selection_tip", ""),
("android_new_voice_call_tip", ""),
("texture_render_tip", ""),
("Use texture rendering", ""),
("Floating window", ""),
("floating_window_tip", ""),
("Keep screen on", ""),
("Never", ""),
("During controlled", ""),
("During service is on", ""),
("Capture screen using DirectX", ""),
("Back", ""),
("Apps", ""),
("Volume up", ""),
("Volume down", ""),
("Power", ""),
("Telegram bot", ""),
("enable-bot-tip", ""),
("enable-bot-desc", ""),
("cancel-2fa-confirm-tip", ""),
("cancel-bot-confirm-tip", ""),
("About RustDesk", ""),
("Send clipboard keystrokes", ""),
("network_error_tip", ""),
("Unlock with PIN", ""),
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
("Parent directory", ""),
("Resume", ""),
("Invalid file name", ""),
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("software_render_tip", "Če na Linuxu uporabljate Nvidino grafično kartico in se oddaljeno okno zapre takoj po vzpostavitvi povezave, lahko pomaga preklop na odprtokodni gonilnik Nouveau in uporaba programskega upodabljanja. Potreben je ponovni zagon programa."),
("Always use software rendering", "Vedno uporabi programsko upodabljanje"),
("config_input", "Za nadzor oddaljenega namizja s tipkovnico, rabi RustDesk pravico »Nadzor vnosa«."),
("config_microphone", "Za zajem zvoka, rabi RustDesk pravico »Snemanje zvoka«."),
("request_elevation_tip", "Lahko tudi zaprosite za dvig pravic, če je kdo na oddaljeni strani."),
("Wait", "Čakaj"),
("Elevation Error", "Napaka pri povzdigovanju"),
("Ask the remote user for authentication", "Vprašaj oddaljenega uporabnika za prijavo"),
("Choose this if the remote account is administrator", "Izberite to, če ima oddaljeni uporabnik skrbniške pravice"),
("Transmit the username and password of administrator", "Vnesite poverilnice za skrbnika"),
("still_click_uac_tip", "Oddaljeni uporabnik mora klikniti »Da« v oknu za nadzor uporabniškega računa."),
("Request Elevation", "Zahtevaj povzdig pravic"),
("wait_accept_uac_tip", "Počakajte na potrditev oddaljenega uporabnika v oknu za nadzor uporabniškega računa."),
("Elevate successfully", "Povzdig pravic uspešen"),
("uppercase", "velike črke"),
("lowercase", "male črke"),
("digit", "številke"),
("special character", "posebni znaki"),
("length>=8", "dolžina>=8"),
("Weak", "Šibko"),
("Medium", "Srednje"),
("Strong", "Močno"),
("Switch Sides", "Zamenjaj strani"),
("Please confirm if you want to share your desktop?", "Potrdite, če želite deliti vaše namizje"),
("Display", "Zaslon"),
("Default View Style", "Privzeti način prikaza"),
("Default Scroll Style", "Privzeti način drsenja"),
("Default Image Quality", "Privzeta kakovost slike"),
("Default Codec", "Privzeti kodek"),
("Bitrate", "Bitna hitrost"),
("FPS", "Sličice/sekundo"),
("Auto", "Samodejno"),
("Other Default Options", "Ostale privzete možnosti"),
("Voice call", "Glasovni klic"),
("Text chat", "Besedilni klepet"),
("Stop voice call", "Prekini glasovni klic"),
("relay_hint_tip", "Morda neposredna povezava ni možna; lahko se poikusite povezati preko posrednika. Če želite uporabiti posrednika ob prvem poizkusu vzpotavljanja povezave, lahko na konec IDja dodate »/r«, ali pa izberete možnost »Vedno poveži preko posrednika« v kartici nedavnih sej, če le-ta obstja."),
("Reconnect", "Ponovna povezava"),
("Codec", "Kodek"),
("Resolution", "Ločljivost"),
("No transfers in progress", "Trenutno ni prenosov"),
("Set one-time password length", "Nastavi dolžino enkratnega gesla"),
("RDP Settings", "Nastavitve za RDP"),
("Sort by", "Razvrsti po"),
("New Connection", "Nova povezava"),
("Restore", "Obnovi"),
("Minimize", "Minimiziraj"),
("Maximize", "Maksimiziraj"),
("Your Device", "Vaša naprava"),
("empty_recent_tip", "Oops, ni nedavnih sej.\nPripravite novo."),
("empty_favorite_tip", "Nimate še priljubljenih partnerjev?\nVzpostavite povezavo, in jo dodajte med priljubljene."),
("empty_lan_tip", "Nismo našli še nobenih partnerjev."),
("empty_address_book_tip", "Vaš adresar je prazen."),
("eg: admin", "npr. admin"),
("Empty Username", "Prazno uporabniško ime"),
("Empty Password", "Prazno geslo"),
("Me", "Jaz"),
("identical_file_tip", "Datoteka je enaka partnerjevi"),
("show_monitors_tip", "Prikaži monitorje v orodni vrstici"),
("View Mode", "Način prikazovanja"),
("login_linux_tip", "Prijaviti se morate v oddaljeni Linux račun in omogočiti namizno sejo X."),
("verify_rustdesk_password_tip", "Preveri geslo za RustDesk"),
("remember_account_tip", "Zapomni si ta račun"),
("os_account_desk_tip", "Ta račun se uporabi za prijavo v oddaljeni sistem in omogči namizno sejo v napravi brez monitorja."),
("OS Account", "Račun operacijskega sistema"),
("another_user_login_title_tip", "Prijavljen je že drug uporabnik"),
("another_user_login_text_tip", "Prekini"),
("xorg_not_found_title_tip", "Xorg ni najden"),
("xorg_not_found_text_tip", "Namestite Xorg"),
("no_desktop_title_tip", "Namizno okolje ni na voljo"),
("no_desktop_text_tip", "Namestite GNOME"),
("No need to elevate", "Povzdig pravic ni potreben"),
("System Sound", "Sistemski zvok"),
("Default", "Privzeto"),
("New RDP", "Nova RDP povezava"),
("Fingerprint", "Prstni odtis"),
("Copy Fingerprint", "Kopiraj prstni odtis"),
("no fingerprints", "ni prstnega odtisa"),
("Select a peer", "Izberite partnerja"),
("Select peers", "Izberite partnerje"),
("Plugins", "Vključki"),
("Uninstall", "Odstrani"),
("Update", "Posodobi"),
("Enable", "Omogoči"),
("Disable", "Onemogoči"),
("Options", "Možnosti"),
("resolution_original_tip", "Izvirna ločljivost"),
("resolution_fit_local_tip", "Prilagodi lokalni ločljivosti"),
("resolution_custom_tip", "Ločljivost po meri"),
("Collapse toolbar", "Strni orodno vrstico"),
("Accept and Elevate", "Sprejmi in povzdigni pravice"),
("accept_and_elevate_btn_tooltip", "Sprejmi povezavo in preko nadzora uporabniškera računa povišaj pravice"),
("clipboard_wait_response_timeout_tip", "Časovna omejitev pri kopiranju je potekla"),
("Incoming connection", "Dohodna povezava"),
("Outgoing connection", "Odhodna povezava"),
("Exit", "Izhod"),
("Open", "Odpri"),
("logout_tip", "Ali ste prepričani, da se želite odjaviti?"),
("Service", "Storitev"),
("Start", "Zaženi"),
("Stop", "Ustavi"),
("exceed_max_devices", "Dosegli ste največje dovoljeno število upravljanih naprav."),
("Sync with recent sessions", "Sinhroniziraj z nedavnimi sejami"),
("Sort tags", "Uredi oznake"),
("Open connection in new tab", "Odpri povezavo na novem zavihku"),
("Move tab to new window", "Premakni zavihek v novo okno"),
("Can not be empty", "Ne more biti prazno"),
("Already exists", "Že obstaja"),
("Change Password", "Spremeni geslo"),
("Refresh Password", "Osveži geslo"),
("ID", "ID"),
("Grid View", "Mrežni pogled"),
("List View", "Pogled seznama"),
("Select", "Izberi"),
("Toggle Tags", "Preklopi oznake"),
("pull_ab_failed_tip", "Adresarja ni bilo mogoče osvežiti"),
("push_ab_failed_tip", "Adresarja ni bilo mogoče poslati na strežnik"),
("synced_peer_readded_tip", "Naprave, ki so bile prisotne v nedavnih sejah bodo sinhronizirane z adresarjem."),
("Change Color", "Spremeni barvo"),
("Primary Color", "Osnovne barve"),
("HSV Color", "Barve HSV"),
("Installation Successful!", "Namestitev uspešna"),
("Installation failed!", "Namestitev ni uspela"),
("Reverse mouse wheel", "Obrni smer drsenja miškinega kolesca"),
("{} sessions", "{} sej"),
("scam_title", "Lahko gre za prevaro!"),
("scam_text1", "V primeru, da vas je nekdo, ki ga ne poznate in mu zaupate prosil, da uporabite RustDesk, prekinite klic in program zaprite."),
("scam_text2", "RustDesk omogoča popoln nadzor nad vašim računalnikom in telefonom, in se lahko uporabi za krajo vašega denarja ali pa zasebnih podatkov."),
("Don't show again", "Ne prikaži znova"),
("I Agree", "Strinjam se"),
("Decline", "Zavrni"),
("Timeout in minutes", "Časovna omejitev v minutah"),
("auto_disconnect_option_tip", "Samodejno prekini neaktivne seje"),
("Connection failed due to inactivity", "Povezava je bila prekinjena zaradi neaktivnosti"),
("Check for software update on startup", "Preveri za posodobitve ob zagonu"),
("upgrade_rustdesk_server_pro_to_{}_tip", "Prosimo, nadgradite RustDesk Server Pro na različico {} ali novejšo."),
("pull_group_failed_tip", "Osveževanje skupine ni uspelo"),
("Filter by intersection", "Filtriraj po preseku"),
("Remove wallpaper during incoming sessions", "Odstrani sliko ozadja ob dohodnih povezavah"),
("Test", "Test"),
("display_is_plugged_out_msg", "Zaslon je bil odklopljen, preklop na primarni zaslon."),
("No displays", "Ni zaslonov"),
("Open in new window", "Odpri v novem oknu"),
("Show displays as individual windows", "Prikaži zaslone kot ločena okna"),
("Use all my displays for the remote session", "Uporabi vse zaslone za oddaljeno sejo"),
("selinux_tip", "Na vaši napravi je omogčen SELinux, kar lahko povzroča težave pri oddaljenem nadzoru"),
("Change view", "Spremeni pogled"),
("Big tiles", "Velike ploščice"),
("Small tiles", "Majhne ploščice"),
("List", "Seznam"),
("Virtual display", "Navidezni zaslon"),
("Plug out all", "Odklopi vse"),
("True color (4:4:4)", "Popolne barve (4:4:4)"),
("Enable blocking user input", "Omogoči blokiranje vnosa"),
("id_input_tip", "Vnesete lahko ID, neposredni IP naslov, ali pa domeno in vrata (<domena>:<vrata>)\nČe želite dostopati do naprave na drugem strežniku, pripnite naslov strežnika (<id>@<naslov_strežnika>?key=<ključ>), npr. 9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nČe želite dostopati do naprave na javnem strežniku, vnesite »<id>@public«; ključ za javni strežnik ni potreben.\nČe želite vsiliti povezavo preko posrednika, pripnite »/r« na konec IDja, npr. »9123456234/r«."),
("privacy_mode_impl_mag_tip", "Način 1"),
("privacy_mode_impl_virtual_display_tip", "Način 2"),
("Enter privacy mode", "Vstopi v zasebni način"),
("Exit privacy mode", "Izstopi iz zasebnega načina"),
("idd_not_support_under_win10_2004_tip", "Posredni gonilnik ni podprt. Za uporabo rabite Windows 10 2004 ali novejšo različico."),
("input_source_1_tip", "Vir vnosa 1"),
("input_source_2_tip", "Vir vnosa 2"),
("Swap control-command key", "Zamenjaj tipki Ctrl-Command"),
("swap-left-right-mouse", "Zamenjaj levo in desno tipko miške"),
("2FA code", "Koda za dvostopenjsko preverjanje"),
("More", "Več"),
("enable-2fa-title", "Omogoči dvostopenjsko preverjanje"),
("enable-2fa-desc", "Pripravite vaš TOTP avtentikator. Uporabite lahko programe kot so Authy, Microsoft ali Google Authenticator, na vašem telefonu ali računalniku.\n\nZa omogočanje dvostopenjskega preverjanja, skenirajte QR kodo in vnesite kodo, ki jo prikaže aplikacija."),
("wrong-2fa-code", "Kode ni bilo mogoče preveriti. Preverite, da je koda pravilna, in da je nastavitev ure točna."),
("enter-2fa-title", "Dvostopenjsko preverjanje"),
("Email verification code must be 6 characters.", "E-poštna koda za preverjanje mora imeti 6 znakov."),
("2FA code must be 6 digits.", "Koda za dvostopenjsko preverjanje mora imeti 6 znakov."),
("Multiple Windows sessions found", "Najdenih je bilo več Windows sej"),
("Please select the session you want to connect to", "Izberite sejo, v katero se želite povezati"),
("powered_by_me", "Uporablja tehnologijo RustDesk"),
("outgoing_only_desk_tip", "To je prilagojena različica.\nLahko se povežete na druge naprave, druge naprave pa se k vam ne morejo povezati."),
("preset_password_warning", "Ta prilagojena različica ima prednastavljeno geslo. Kdorkoli, ki pozna to geslo, lahko prevzame popoln nadzor nad vašim računalnikom. Če tega niste pričakovali, takoj odstranite program."),
("Security Alert", "Varnostno opozorilo"),
("My address book", "Moj adresar"),
("Personal", "Osebni"),
("Owner", "Lastnik"),
("Set shared password", "Nastavi deljeno geslo"),
("Exist in", "Obstaja v"),
("Read-only", "Samo za branje"),
("Read/Write", "Branje/pisanje"),
("Full Control", "Popoln nadzor"),
("share_warning_tip", "Zgornja polja so deljena, in vidna vsem"),
("Everyone", "Vsi"),
("ab_web_console_tip", "Več na spletni konzoli"),
("allow-only-conn-window-open-tip", "Dovoli povezavo samo če je okno RustDeska odprto"),
("no_need_privacy_mode_no_physical_displays_tip", "Ni fizičnih zaslonov, zasebni način ni potreben"),
("Follow remote cursor", "Sledi oddaljenemu kazalcu"),
("Follow remote window focus", "Sledi oddaljenemu fokusu"),
("default_proxy_tip", "Privzeti protokol je Socks5 na vratih 1080"),
("no_audio_input_device_tip", "Ni bilo možno najti vhodne zvočne naprave"),
("Incoming", "Dohodno"),
("Outgoing", "Odhodno"),
("Clear Wayland screen selection", "Počisti izbiro Wayland zaslona"),
("clear_Wayland_screen_selection_tip", "Po čiščenju izbire Wayland zaslona lahko ponovno izberete zaslon za delitev"),
("confirm_clear_Wayland_screen_selection_tip", "Ali res želite počistiti izbiro Wayland zaslona?"),
("android_new_voice_call_tip", "Prejeli ste prošnjo za nov glasovni klic. Če sprejmete, bo zvok preklopljen na glasovno komunikacijo."),
("texture_render_tip", "Uporabi upodabljanje tekstur, za gladkejše slike. Izklopite, če imate težave pri upodabljanju."),
("Use texture rendering", "Uporabi upodabljanje tekstur"),
("Floating window", "Plavajoče okno"),
("floating_window_tip", "Pomaga pri RustDesk storitvi v ozadju"),
("Keep screen on", "Ohranite zaslon prižgan"),
("Never", "Nikoli"),
("During controlled", "Med nadzorom"),
("During service is on", "Med vklopljeno storitvijo"),
("Capture screen using DirectX", "Uporabi DirectX za zajem zaslona"),
("Back", "Nazaj"),
("Apps", "Aplikacije"),
("Volume up", "Glasneje"),
("Volume down", "Tišje"),
("Power", "Vklop/izklop"),
("Telegram bot", "Telegram bot"),
("enable-bot-tip", "Če vklopite to možnost, lahko dobite kodo za dvostopenjsko preverjanje od bota. Lahko se uporabi tudi za obveščanje o povezavi."),
("enable-bot-desc", "1. Odprite pogovor z @BotFather.\n2. Pošljite ukaz »/newbot« in prejeli boste žeton.\n3. Začnite pogovor z na novo narejenim botom. Pošljite sporočilo z desno poševnico (/) kot npr. »/hello« za aktivacijo."),
("cancel-2fa-confirm-tip", "Ali ste prepričani, da želite ukiniti dvostopenjsko preverjanje?"),
("cancel-bot-confirm-tip", "Ali ste prepričani, da želite ukiniti Telegram bota?"),
("About RustDesk", "O RustDesku"),
("Send clipboard keystrokes", "Vtipkaj vsebino odložišča"),
("network_error_tip", "Preverite vašo mrežno povezavo, nato kliknite Ponovi."),
("Unlock with PIN", "Odkleni s PINom"),
("Requires at least {} characters", "Potrebuje vsaj {} znakov."),
("Wrong PIN", "Napačen PIN"),
("Set PIN", "Nastavi PIN"),
("Enable trusted devices", "Omogoči zaupanja vredne naprave"),
("Manage trusted devices", "Upravljaj zaupanja vredne naprave"),
("Platform", "Platforma"),
("Days remaining", "Preostane dni"),
("enable-trusted-devices-tip", "Na zaupanja vrednih napravah ni potrebno dvostopenjsko preverjanje"),
("Parent directory", "Nadrejena mapa"),
("Resume", "Nadaljuj"),
("Invalid file name", "Neveljavno ime datoteke"),
("one-way-file-transfer-tip", "Enosmerni prenos datotek je omogočen na nadzorovani strani"),
("Authentication Required", "Potrebno je preverjanje pristnosti"),
("Authenticate", "Preverjanje pristnosti"),
("web_id_input_tip", "Vnesete lahko ID iz istega strežnika, neposredni dostop preko IP naslova v spletnem odjemalcu ni podprt.\nČe želite dostopati do naprave na drugem strežniku, pripnite naslov strežnika (<id>@<naslov_strežnika>?key=<ključ>), npr. 9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nČe želite dostopati do naprave na javnem strežniku, vnesite »<id>@public«; ključ za javni strežnik ni potreben."),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -310,7 +310,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Start the screen sharing service on boot, requires special permissions", "開機時啟動螢幕分享服務,需要特殊權限。"),
("Connection not allowed", "不允許連線"),
("Legacy mode", "傳統模式"),
("Map mode", "11 傳輸模式"),
("Map mode", "1:1 傳輸模式"),
("Translate mode", "翻譯模式"),
("Use permanent password", "使用固定密碼"),
("Use both passwords", "同時使用兩種密碼"),
@@ -563,7 +563,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Plug out all", "拔出所有"),
("True color (4:4:4)", "全彩模式4:4:4"),
("Enable blocking user input", "允許封鎖使用者輸入"),
("id_input_tip", "您可以輸入 ID、IP、或網域名稱+端口號(<網域名稱>:<端口號>)。\n如果您要存取位於其他伺服器上的設備請在ID之後添加伺服器地址<ID>@<伺服器地址>?key=<金鑰>\n例如9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=\n要存取公共伺服器上的設備,請輸入\"<id>@public\",不需輸入金鑰。\n\n如果您想要在第一次連線時,強制使用中繼連接,請在 ID 的末尾添加 \"/r\",例如,\"9123456234/r\""),
("id_input_tip", "您可以輸入 ID、IP、或網域名稱+通訊埠號(<網域名稱>:<通訊埠號>)。\n如果您要存取位於其他伺服器上的設備,請在 ID 之後添加伺服器地址(<ID>@<伺服器地址>?key=<金鑰>\n例如9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=\n要存取公共伺服器上的設備,請輸入\"<id>@public\",不需輸入金鑰。\n\n如果您想要在第一次連線時,強制使用中繼連接,請在 ID 的末尾添加 \"/r\",例如,\"9123456234/r\""),
("privacy_mode_impl_mag_tip", "模式 1"),
("privacy_mode_impl_virtual_display_tip", "模式 2"),
("Enter privacy mode", "進入隱私模式"),
@@ -602,7 +602,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("no_need_privacy_mode_no_physical_displays_tip", "沒有物理螢幕,沒必要使用隱私模式。"),
("Follow remote cursor", "跟隨遠端游標"),
("Follow remote window focus", "跟隨遠端視窗焦點"),
("default_proxy_tip", "預設代理協定及端口為 Socks5 和 1080"),
("default_proxy_tip", "預設代理協定及通訊埠為 Socks5 和 1080"),
("no_audio_input_device_tip", "未找到音訊輸入裝置"),
("Incoming", "連入"),
("Outgoing", "連出"),
@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "被控端啟用了單向文件傳輸"),
("Authentication Required", "需要身分驗證"),
("Authenticate", "認證"),
("web_id_input_tip", "您可以輸入同一個伺服器內的 IDWeb 客戶端不支援直接 IP 存取。\n如果您要存取位於其他伺服器上的設備,請在 ID 之後添加伺服器地址(<ID>@<伺服器地址>?key=<金鑰>\n例如9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=\n要存取公共伺服器上的設備,請輸入\"<id>@public\",不需輸入金鑰。"),
("Download", "下載"),
("Upload folder", "上傳資料夾"),
("Upload files", "上傳檔案"),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -351,7 +351,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable audio", "Увімкнути аудіо"),
("Unlock Network Settings", "Розблокувати мережеві налаштування"),
("Server", "Сервер"),
("Direct IP Access", "Прямий IP доступ"),
("Direct IP Access", "Прямий IP-доступ"),
("Proxy", "Проксі"),
("Apply", "Застосувати"),
("Disconnect all devices?", "Відʼєднати всі прилади?"),
@@ -469,12 +469,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("identical_file_tip", "Цей файл ідентичний з тим, що на вузлі"),
("show_monitors_tip", "Показувати монітори на панелі інструментів"),
("View Mode", "Режим перегляду"),
("login_linux_tip", "Вам необхідно залогуватися у віддалений обліковий запис Linux, щоб увімкнути стільничний сеанс X"),
("login_linux_tip", "Вам необхідно увійти у віддалений обліковий запис Linux, щоб увімкнути стільничний сеанс X"),
("verify_rustdesk_password_tip", "Перевірте пароль RustDesk"),
("remember_account_tip", "Запамʼятати цей обліковий запис"),
("os_account_desk_tip", "Цей обліковий запис використовується для входу до віддаленої ОС та вмикання сеансу стільниці в неграфічному режимі"),
("os_account_desk_tip", "Цей обліковий запис використовується для входу до віддаленої ОС та вмикання сеансу стільниці в режимі без графічного інтерфейсу"),
("OS Account", "Користувач ОС"),
("another_user_login_title_tip", "Інший користувач вже залогований"),
("another_user_login_title_tip", "Інший користувач вже в системі"),
("another_user_login_text_tip", "Відʼєднатися"),
("xorg_not_found_title_tip", "Xorg не знайдено"),
("xorg_not_found_text_tip", "Будь ласка, встановіть Xorg"),
@@ -506,7 +506,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Outgoing connection", "Вихідне підключення"),
("Exit", "Вийти"),
("Open", "Відкрити"),
("logout_tip", "Ви впевнені, що хочете вилогуватися?"),
("logout_tip", "Ви впевнені, що хочете вийти з системи?"),
("Service", "Служба"),
("Start", "Запустити"),
("Stop", "Зупинити"),
@@ -563,7 +563,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Plug out all", "Відключити все"),
("True color (4:4:4)", "Справжній колір (4:4:4)"),
("Enable blocking user input", "Блокувати введення для користувача"),
("id_input_tip", "Ви можете ввести ID, безпосередню IP, або ж домен з портом (<домен>:<порт>).\nЯкщо ви хочете отримати доступ до пристрою на іншому сервері, будь ласка, додайте адресу сервера (<id>@<адреса_сервера>?key=<значення_ключа>), наприклад,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЯкщо ви хочете отримати доступ до пристрою на публічному сервері, будь ласка, введіть \"<id>@public\", ключ для публічного сервера не потрібен."),
("id_input_tip", "Ви можете ввести ID, безпосередню IP, або ж домен з портом (<домен>:<порт>).\nЯкщо ви хочете отримати доступ до пристрою на іншому сервері, будь ласка, додайте адресу сервера (<id>@<адреса_сервера>?key=<значення_ключа>), наприклад,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЯкщо ви хочете отримати доступ до пристрою на публічному сервері, будь ласка, введіть \"<id>@public\", для публічного сервера ключ не потрібен."),
("privacy_mode_impl_mag_tip", "Режим 1"),
("privacy_mode_impl_virtual_display_tip", "Режим 2"),
("Enter privacy mode", "Увійти в режим конфіденційності"),
@@ -631,7 +631,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("cancel-bot-confirm-tip", "Ви впевнені, що хочете скасувати Telegram бота?"),
("About RustDesk", "Про Rustdesk"),
("Send clipboard keystrokes", "Надіслати вміст буфера обміну"),
("network_error_tip", "Будь ласка, перевірте ваше підключення до мережі та натисність \"Повторити\""),
("network_error_tip", "Будь ласка, перевірте ваше підключення до мережі та натисніть \"Повторити\""),
("Unlock with PIN", "Розблокування PIN-кодом"),
("Requires at least {} characters", "Потрібно щонайменше {} символів"),
("Wrong PIN", "Неправильний PIN-код"),
@@ -640,12 +640,17 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Manage trusted devices", "Керувати довіреними пристроями"),
("Platform", "Платформа"),
("Days remaining", "Залишилося днів"),
("enable-trusted-devices-tip", "Дозволити довіреним пристроям пропускати двофакторну автентифікацію?"),
("enable-trusted-devices-tip", "Пропускати двофакторну автентифікацію на довірених пристроях"),
("Parent directory", "Батьківський каталог"),
("Resume", "Продовжити"),
("Invalid file name", "Неправильне ім'я файлу"),
("one-way-file-transfer-tip", "На керованій стороні ввімкнено одностороннє передавання файлів."),
("Invalid file name", "Неправильна назва файлу"),
("one-way-file-transfer-tip", "На стороні, що керується, увімкнено односторонню передачу файлів."),
("Authentication Required", "Потрібна автентифікація"),
("Authenticate", "Автентифікувати"),
("web_id_input_tip", "Ви можете ввести ID з того самого серверу, прямий IP-доступ у веб-клієнті не підтримується.\nЯкщо ви хочете отримати доступ до пристрою на іншому сервері, будь ласка, додайте адресу сервера (<id>@<адреса_сервера>?key=<значення_ключа>), наприклад,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЯкщо ви хочете отримати доступ до пристрою на публічному сервері, будь ласка, введіть \"<id>@public\", для публічного сервера ключ не потрібен."),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -647,5 +647,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", ""),
("Authentication Required", ""),
("Authenticate", ""),
("web_id_input_tip", ""),
("Download", ""),
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
].iter().cloned().collect();
}

View File

@@ -505,7 +505,7 @@ fn child(su_user: Option<String>, args: Vec<String>) -> ResultType<()> {
command = format!("'{}'", quote_shell_arg(&command, false));
}
params.push(command);
std::env::set_var("LC_ALL", "C.UTF-8");
std::env::set_var("LC_ALL", "C");
if let Some(user) = &su_user {
let su_subcommand = params

View File

@@ -610,8 +610,15 @@ pub fn get_env_var(k: &str) -> String {
}
}
fn is_flatpak() -> bool {
std::path::PathBuf::from("/.flatpak-info").exists()
}
// Headless is enabled, always return true.
pub fn is_prelogin() -> bool {
if is_flatpak() {
return false;
}
let n = get_active_userid().len();
n < 4 && n > 1
}

View File

@@ -117,6 +117,7 @@ impl Handler {
format: ClipboardFormat::from_i32(c.format)
.unwrap_or(ClipboardFormat::Text)
.into(),
special_name: c.special_name,
..Default::default()
})
.collect(),

View File

@@ -175,6 +175,22 @@ impl LockModesHandler {
}
}
#[cfg(target_os = "linux")]
fn sleep_to_ensure_locked(v: bool, k: enigo::Key, en: &mut Enigo) {
if wayland_use_uinput() {
// Sleep at most 500ms to ensure the lock state is applied.
for _ in 0..50 {
std::thread::sleep(std::time::Duration::from_millis(10));
if en.get_key_state(k) == v {
break;
}
}
} else if wayland_use_rdp_input() {
// We can't call `en.get_key_state(k)` because there's no api for this.
std::thread::sleep(std::time::Duration::from_millis(50));
}
}
#[cfg(any(target_os = "windows", target_os = "linux"))]
fn new(key_event: &KeyEvent, is_numpad_key: bool) -> Self {
let mut en = ENIGO.lock().unwrap();
@@ -183,12 +199,15 @@ impl LockModesHandler {
let caps_lock_changed = event_caps_enabled != local_caps_enabled;
if caps_lock_changed {
en.key_click(enigo::Key::CapsLock);
#[cfg(target_os = "linux")]
Self::sleep_to_ensure_locked(event_caps_enabled, enigo::Key::CapsLock, &mut en);
}
let mut num_lock_changed = false;
let mut event_num_enabled = false;
if is_numpad_key {
let local_num_enabled = en.get_key_state(enigo::Key::NumLock);
let event_num_enabled = Self::is_modifier_enabled(key_event, ControlKey::NumLock);
event_num_enabled = Self::is_modifier_enabled(key_event, ControlKey::NumLock);
num_lock_changed = event_num_enabled != local_num_enabled;
} else if is_legacy_mode(key_event) {
#[cfg(target_os = "windows")]
@@ -199,6 +218,8 @@ impl LockModesHandler {
}
if num_lock_changed {
en.key_click(enigo::Key::NumLock);
#[cfg(target_os = "linux")]
Self::sleep_to_ensure_locked(event_num_enabled, enigo::Key::NumLock, &mut en);
}
Self {
@@ -236,6 +257,14 @@ impl LockModesHandler {
#[cfg(any(target_os = "windows", target_os = "linux"))]
impl Drop for LockModesHandler {
fn drop(&mut self) {
// Do not change led state if is Wayland uinput.
// Because there must be a delay to ensure the lock state is applied on Wayland uinput,
// which may affect the user experience.
#[cfg(target_os = "linux")]
if wayland_use_uinput() {
return;
}
let mut en = ENIGO.lock().unwrap();
if self.caps_lock_changed {
en.key_click(enigo::Key::CapsLock);
@@ -1633,12 +1662,13 @@ pub fn handle_key_(evt: &KeyEvent) {
let is_numpad_key = false;
#[cfg(any(target_os = "windows", target_os = "linux"))]
let is_numpad_key = crate::keyboard::is_numpad_rdev_key(&key);
_lock_mode_handler = Some(LockModesHandler::new_handler(evt, is_numpad_key));
_lock_mode_handler =
Some(LockModesHandler::new_handler(evt, is_numpad_key));
}
}
}
_ => {}
};
}
match evt.mode.enum_value() {
Ok(KeyboardMode::Map) => {

View File

@@ -431,8 +431,8 @@ pub mod service {
allow_err!(keyboard.emit(&[down_event]));
}
DataKeyboard::KeyUp(enigo::Key::Raw(code)) => {
let down_event = InputEvent::new(EventType::KEY, *code - 8, 0);
allow_err!(keyboard.emit(&[down_event]));
let up_event = InputEvent::new(EventType::KEY, *code - 8, 0);
allow_err!(keyboard.emit(&[up_event]));
}
DataKeyboard::KeyDown(key) => {
if let Ok((k, is_shift)) = map_key(key) {

View File

@@ -446,7 +446,7 @@ function handle_custom_image_quality() {
var extendedBitrate = bitrate > 100;
var maxRate = extendedBitrate ? 2000 : 100;
msgbox("custom-image-quality", "Custom Image Quality", "<div .form> \
<div><input #bitrate-slider type=\"hslider\" style=\"width: 50%\" name=\"bitrate\" max=\"" + maxRate + "\" min=\"10\" value=\"" + bitrate + "\"/ buddy=\"bitrate-buddy\"><b #bitrate-buddy>x</b>% Bitrate <button|checkbox #extended-slider .custom-event " + (extendedBitrate ? "checked" : "") + ">More</button></div> \
<div><input #bitrate-slider type=\"hslider\" style=\"width: 50%\" name=\"bitrate\" max=\"" + maxRate + "\" min=\"5\" value=\"" + bitrate + "\"/ buddy=\"bitrate-buddy\"><b #bitrate-buddy>x</b>% Bitrate <button|checkbox #extended-slider .custom-event " + (extendedBitrate ? "checked" : "") + ">More</button></div> \
</div>", "", function(res=null) {
if (!res) return;
if (res.id === "extended-slider") {

View File

@@ -512,6 +512,7 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
width: c.width,
height: c.height,
format: c.format.value(),
special_name: c.special_name,
});
}
allow_err!(self.stream.send(&Data::ClipboardNonFile(Some(("".to_owned(), main_data)))).await);