Merge remote-tracking branch 'rustdesk/master' into flutter_desktop
# Conflicts: # Cargo.lock # Cargo.toml # build.rs # flutter/.gitignore # flutter/lib/common.dart # flutter/lib/mobile/pages/remote_page.dart # flutter/lib/models/model.dart # flutter/lib/models/native_model.dart # flutter/lib/models/server_model.dart # flutter/pubspec.lock # flutter/pubspec.yaml # src/client.rs # src/client/file_trait.rs # src/flutter.rs # src/mobile_ffi.rs # src/ui.rs
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project ((English only, Please).
|
||||
title: ''
|
||||
labels: feature-request
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
30
.github/workflows/snap-ci.yml
vendored
@ -1,30 +0,0 @@
|
||||
name: Snap CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- README.md
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build-snap-master-package:
|
||||
runs-on: ubuntu-18.04
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Use Snapcraft
|
||||
uses: snapcore/action-build@v1
|
||||
id: build
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: rustdesk.snap
|
||||
path: ${{ steps.build.outputs.snap }}
|
||||
# - uses: snapcore/action-publish@v1
|
||||
# with:
|
||||
# store_login: ${{ secrets.SNAP_TOKEN }}
|
||||
# snap: ${{ steps.build.outputs.snap }}
|
||||
# release: edge
|
12
.github/workflows/winget.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
name: Publish to WinGet
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: windows-latest # action can only be run on windows
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@latest
|
||||
with:
|
||||
identifier: RustDesk.RustDesk
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
6
.gitignore
vendored
@ -10,14 +10,16 @@ src/version.rs
|
||||
*exe
|
||||
*tgz
|
||||
cert.pfx
|
||||
flutter_hbb
|
||||
*.bak
|
||||
*png
|
||||
*svg
|
||||
*jpg
|
||||
web_hbb
|
||||
sciter.dll
|
||||
**pdb
|
||||
src/bridge_generated.rs
|
||||
*deb
|
||||
rustdesk
|
||||
# appimage
|
||||
appimage/AppDir
|
||||
appimage/*.AppImage
|
||||
appimage/appimage-build
|
11
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustdesk"
|
||||
version = "1.2.0"
|
||||
version = "1.1.10"
|
||||
authors = ["rustdesk <info@rustdesk.com>"]
|
||||
edition = "2021"
|
||||
build= "build.rs"
|
||||
@ -19,6 +19,8 @@ path = "src/naming.rs"
|
||||
inline = []
|
||||
hbbs = []
|
||||
cli = []
|
||||
with_rc = ["simple_rc"]
|
||||
appimage = []
|
||||
use_samplerate = ["samplerate"]
|
||||
use_rubato = ["rubato"]
|
||||
use_dasp = ["dasp"]
|
||||
@ -81,6 +83,7 @@ winit = "0.25"
|
||||
winapi = { version = "0.3", features = ["winuser"] }
|
||||
winreg = "0.10"
|
||||
windows-service = "0.4"
|
||||
virtual_display = { path = "libs/virtual_display" }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc = "0.2"
|
||||
@ -105,7 +108,7 @@ jni = "0.19.0"
|
||||
flutter_rust_bridge = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge" }
|
||||
|
||||
[workspace]
|
||||
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display"]
|
||||
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/simple_rc"]
|
||||
|
||||
[package.metadata.winres]
|
||||
LegalCopyright = "Copyright © 2022 Purslane, Inc."
|
||||
@ -119,6 +122,7 @@ winapi = { version = "0.3", features = [ "winnt" ] }
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
hbb_common = { path = "libs/hbb_common" }
|
||||
simple_rc = { path = "libs/simple_rc", optional = true }
|
||||
flutter_rust_bridge_codegen = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge" }
|
||||
|
||||
[dev-dependencies]
|
||||
@ -133,10 +137,9 @@ osx_minimum_system_version = "10.14"
|
||||
resources = ["mac-tray.png"]
|
||||
|
||||
#https://github.com/johnthagen/min-sized-rust
|
||||
#!!! rembember call "strip target/release/rustdesk"
|
||||
# which reduce binary size a lot
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = 'abort'
|
||||
strip = true
|
||||
#opt-level = 'z' # only have smaller size after strip
|
||||
|
@ -1,5 +1,5 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
@ -7,17 +7,15 @@
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
@ -72,7 +60,7 @@ modification follow.
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
@ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
190
README-AR.md
Normal file
@ -0,0 +1,190 @@
|
||||
<p align="center">
|
||||
<img src="logo-header.svg" alt="RustDesk - Your remote desktop"><br>
|
||||
<a href="#free-public-servers">Servers</a> •
|
||||
<a href="#raw-steps-to-build">Build</a> •
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</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>]<br>
|
||||
<b> لغتك الأم, <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> و <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a>, README نحن بحاجة إلى مساعدتك لترجمة هذا </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)
|
||||
|
||||
.Rustبرنامج آخر لسطح المكتب عن بعد، مكتوب بـ
|
||||
يعمل خارج الصندوق، لا حاجة إلى إعدادات. لديك سيطرة كاملة على بياناتك، دون مخاوف بشأن الأمن. يمكنك استخدام خادم
|
||||
الخاص بنا rendezvous/relay
|
||||
[جهز لنفسك واحدا](https://rustdesk.com/server), أو
|
||||
[خاص بك rendezvous/relay أكتب خادم](https://github.com/rustdesk/rustdesk-server-demo).
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
|
||||
|
||||
لمساعدتك على ذلك [`CONTRIBUTING.md`](CONTRIBUTING.md) يرحب بمساهمة الجميع. اطلع على RustDesk.
|
||||
|
||||
[**؟ RustDesk كيفية يعمل**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
|
||||
|
||||
[**BINARY تنزيل**](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
## خوادم مفتوحة ومجانية
|
||||
|
||||
فيما يلي الخوادم التي تستخدمها مجانًا، وقد تتغير طوال الوقت. إذا لم تكن قريبًا من أحد هؤلاء، فقد تكون شبكتك بطيئة.
|
||||
| الموقع | المورد | المواصفات |
|
||||
| --------- | ------------- | ------------------ |
|
||||
| Seoul | AWS lightsail | 1 VCPU / 0.5GB RAM |
|
||||
| Singapore | Vultr | 1 VCPU / 1GB RAM |
|
||||
| Dallas | Vultr | 1 VCPU / 1GB RAM | |
|
||||
|
||||
## التبعيات
|
||||
|
||||
لواجهة المستخدم الرسومية [sciter](https://sciter.com/) نسخة سطح المكتب تستخدم
|
||||
بنفسك sciter dynamic library عليك تحميل
|
||||
|
||||
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
|
||||
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
|
||||
[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
|
||||
|
||||
Sciter إلى Flutter سنقوم بترحيل نسخة سطح المكتب من .Flutter تستخدم إصدارات الهاتف المحمول.
|
||||
|
||||
## خطوات البناء
|
||||
|
||||
- C++ build env و Rust development env قم بإعداد
|
||||
|
||||
- بطريقة صحيحة `VCPKG_ROOT` env variable وأعد [vcpkg](https://github.com/microsoft/vcpkg) ثبت
|
||||
|
||||
- Windows: `vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static`
|
||||
- Linux/MacOS: `vcpkg install libvpx libyuv opus`
|
||||
|
||||
- run `cargo run`
|
||||
|
||||
## [البناء](https://rustdesk.com/docs/en/dev/build/)
|
||||
|
||||
## Linux
|
||||
|
||||
### Ubuntu 18 (Debian 10)
|
||||
|
||||
```sh
|
||||
sudo apt install -y 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
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### Arch (Manjaro)
|
||||
|
||||
```sh
|
||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||
```
|
||||
|
||||
### pynput package تثبيت
|
||||
|
||||
```sh
|
||||
pip3 install pynput
|
||||
```
|
||||
|
||||
### vcpkg تثبيت
|
||||
|
||||
```sh
|
||||
git clone https://github.com/microsoft/vcpkg
|
||||
cd vcpkg
|
||||
git checkout 2021.12.01
|
||||
cd ..
|
||||
vcpkg/bootstrap-vcpkg.sh
|
||||
export VCPKG_ROOT=$HOME/vcpkg
|
||||
vcpkg/vcpkg install libvpx libyuv opus
|
||||
```
|
||||
|
||||
### Fix libvpx (For Fedora)
|
||||
|
||||
```sh
|
||||
cd vcpkg/buildtrees/libvpx/src
|
||||
cd *
|
||||
./configure
|
||||
sed -i 's/CFLAGS+=-I/CFLAGS+=-fPIC -I/g' Makefile
|
||||
sed -i 's/CXXFLAGS+=-I/CXXFLAGS+=-fPIC -I/g' Makefile
|
||||
make
|
||||
cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/
|
||||
cd
|
||||
```
|
||||
|
||||
### البناء
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source $HOME/.cargo/env
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
cd rustdesk
|
||||
mkdir -p target/debug
|
||||
wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so
|
||||
mv libsciter-gtk.so target/debug
|
||||
VCPKG_ROOT=$HOME/vcpkg cargo run
|
||||
```
|
||||
|
||||
### X11 (Xorg) إلى Wayland تغيير
|
||||
|
||||
افتراضية GNOME session ك Xorg إتبع [هذه](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) الخطوات لإعداد Wayland لا تدعم RustDesk
|
||||
|
||||
## Docker طريقة البناء باستخدام
|
||||
|
||||
ابدأ باستنساخ المستودع وبناء الكونتاينر:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
cd rustdesk
|
||||
docker build -t "rustdesk-builder" .
|
||||
```
|
||||
|
||||
ثم، في كل مرة تحتاج إلى بناء التطبيق، قم بتشغيل الأمر التالي:
|
||||
|
||||
```sh
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
لاحظ أن البناء الأول قد يستغرق وقتًا أطول قبل تخزين التبعيات، وسيكون البناء اللاحق أسرع. بالإضافة إلى ذلك، إذا كنت بحاجة إلى تحديد وسائط مختلفة لأمر البناء، فيمكنك القيام بذلك في نهاية الأمر بوضع
|
||||
`<OPTIONAL-ARGS>`
|
||||
على سبيل المثال، إذا كنت ترغب في بناء إصدار محسن، فستقوم بتشغيل الأمر أعلاه متبوعًا بـ
|
||||
`--release`
|
||||
:سيكون الملف القابل للتنفيذ الناتج متاحًا في مجلد تارغت، ويمكن تشغيله باستخدام
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
```
|
||||
|
||||
:أو في حال قمت ببناء إصدار محسن
|
||||
|
||||
```sh
|
||||
target/release/rustdesk
|
||||
```
|
||||
|
||||
RustDesk يرجى التأكد من أنك تنفذ هذه الأوامر من جذر مستودع
|
||||
وإلا فقد لا يتمكن التطبيق من العثور على الموارد المطلوبة. لاحظ أيضًا أن الأوامر الفرعية الأخرى مثل
|
||||
`install` أو `run`
|
||||
لا يتم دعمها حاليًا عبر هذه الطريقة لأنها ستقوم بتثبيت أو تشغيل البرنامج داخل الكونتاينر بدلاً من الهوست.
|
||||
|
||||
## هيكل الملف
|
||||
|
||||
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: وظائف لنقل الملفات، وبعض وظائف المرافق الأخرى tcp/udp، protobuf ترميز الفيديو، إعدادات
|
||||
|
||||
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: التقاط الشاشة
|
||||
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: التحكم في لوحة المفاتيح/الماوس الخاصة بكل منصة
|
||||
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: واجهة المستخدم الرسومية
|
||||
- **[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)**: أو المنقول عن بُعد (TCP hole punching) انتظر الاتصال المباشر [rustdesk-server](https://github.com/rustdesk/rustdesk-server) الإتصال ب
|
||||
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: رمز خاص بكل منصة
|
||||
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: رمز الهاتف المحمول
|
||||
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**:Flutter لعميل الويب الخاص ب Javascript
|
||||
|
||||
## لقطات
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png)
|
176
README-CS.md
Normal file
@ -0,0 +1,176 @@
|
||||
<p align="center">
|
||||
<img src="logo-header.svg" alt="RustDesk – vaše vzdálená plocha"><br>
|
||||
<a href="#free-public-servers">Servery</a> •
|
||||
<a href="#raw-steps-to-build">Sestavení ze zdrojových kódů</a> •
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Struktura</a> •
|
||||
<a href="#snapshot">Ukázky</a><br>
|
||||
[<a href="README-ZH.md">中文</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>]<br>
|
||||
<b>Potřebujeme Vaši pomoc s překláním textů tohoto ČTIMNE, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">uživatelského rozhraní aplikace RustDesk</a> a <a href="https://github.com/rustdesk/doc.rustdesk.com">dokumentace k ní</a> do vašeho jazyka</b>
|
||||
</p>
|
||||
|
||||
Dopisujte si s námi: [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)
|
||||
|
||||
Zase další software pro přístup k ploše na dálku, naprogramovaný v jazyce Rust. Funguje hned tak, jak je – není třeba žádného nastavování. Svá data máte ve svých rukách, bez obav o zabezpečení. Je možné používat námi poskytovaný propojovací/předávací (relay) server, [vytvořit si svůj vlastní](https://rustdesk.com/server), nebo [si dokonce svůj vlastní naprogramovat](https://github.com/rustdesk/rustdesk-server-demo), budete-li chtít.
|
||||
|
||||
Projekt RustDesk vítá přiložení ruky k dílu od každého. Jak začít se dozvíte z [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
||||
|
||||
[**Jak RustDesk funguje?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
|
||||
|
||||
[**STAHOVÁNÍ ZKOMPILOVANÝCH APLIKACÍ**](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
## Veřejné, zdarma službu nabízející servery
|
||||
|
||||
Níže jsou uvedeny servery zdarma k vašemu použití (údaje se mohou v čase měnit). Pokud se nenacházíte v oblastech světa poblíž nich, spojení může být pomalé.
|
||||
| umístění | dodavatel | parametry |
|
||||
| --------- | ------------- | ------------------ |
|
||||
| Soul | AWS lightsail | 1 VCPU / 0,5GB RAM |
|
||||
| Singapur | Vultr | 1 VCPU / 1GB RAM |
|
||||
| Dallas | Vultr | 1 VCPU / 1GB RAM | |
|
||||
|
||||
## Softwarové součásti, na kterých závisí
|
||||
|
||||
Varianta pro počítač používá pro grafické uživatelské rozhraní [sciter](https://sciter.com/) – stáhněte si potřebnou knihovnu.
|
||||
|
||||
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
|
||||
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
|
||||
[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
|
||||
|
||||
Varianta pro mobilní platformy používá aplikační rámec (framework) Flutter. Na tu také v budoucnu předěláme i variantu pro počítač.
|
||||
|
||||
## Stručně kroky pro sestavení ze zdrojových kódů
|
||||
|
||||
- Připravte si vývojové prostředí pro jazyky Rust a C++
|
||||
|
||||
- Nainstalujte [vcpkg](https://github.com/microsoft/vcpkg), a nastavte správně proměnnou prostsředí `VCPKG_ROOT`
|
||||
|
||||
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static
|
||||
- Linux/MacOS: vcpkg install libvpx libyuv opus
|
||||
|
||||
- spusťte `cargo run`
|
||||
|
||||
## [Sestavení ze zdrojových kódů](https://rustdesk.com/docs/en/dev/build/)
|
||||
|
||||
## Jak zkompilovat na Linuxu
|
||||
|
||||
### Ubuntu 18 (Debian 10)
|
||||
|
||||
```sh
|
||||
sudo apt install -y 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
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### Arch (Manjaro)
|
||||
|
||||
```sh
|
||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||
```
|
||||
|
||||
### Instalace balíčku pynput
|
||||
|
||||
```sh
|
||||
pip3 install pynput
|
||||
```
|
||||
|
||||
### Instalace vcpkg
|
||||
|
||||
```sh
|
||||
git clone https://github.com/microsoft/vcpkg
|
||||
cd vcpkg
|
||||
git checkout 2021.12.01
|
||||
cd ..
|
||||
vcpkg/bootstrap-vcpkg.sh
|
||||
export VCPKG_ROOT=$HOME/vcpkg
|
||||
vcpkg/vcpkg install libvpx libyuv opus
|
||||
```
|
||||
|
||||
### Oprava libvpx (pro Fedoru)
|
||||
|
||||
```sh
|
||||
cd vcpkg/buildtrees/libvpx/src
|
||||
cd *
|
||||
./configure
|
||||
sed -i 's/CFLAGS+=-I/CFLAGS+=-fPIC -I/g' Makefile
|
||||
sed -i 's/CXXFLAGS+=-I/CXXFLAGS+=-fPIC -I/g' Makefile
|
||||
make
|
||||
cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/
|
||||
cd
|
||||
```
|
||||
|
||||
### Sestavení
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source $HOME/.cargo/env
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
cd rustdesk
|
||||
mkdir -p target/debug
|
||||
wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so
|
||||
mv libsciter-gtk.so target/debug
|
||||
VCPKG_ROOT=$HOME/vcpkg cargo run
|
||||
```
|
||||
|
||||
### Změna z Wayland na X11 (Xorg)
|
||||
|
||||
RustDesk (zatím) nepodporuje zobrazovací server Wayland. Jak nastavit Xorg jako výchozí pro relace v prostředí GNOME naleznete [zde](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/).
|
||||
|
||||
## Jak sestavit prostřednictvím Docker kontejnerizace
|
||||
|
||||
Začněte tím, že si naklonujete tento repozitář a sestavíte docker kontejner:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
cd rustdesk
|
||||
docker build -t "rustdesk-builder" .
|
||||
```
|
||||
|
||||
Poté pokaždé, když bude třeba aplikaci sestavit, spusťte následující příkaz:
|
||||
|
||||
```sh
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Všimněte si, že prvotní sestavení může trvat déle (než se do mezipaměti uloží veškeré softwarové součásti, které jsou potřeba) – následná opakování už budou rychlejší. Dále, pokud potřebujete příkazu pro sestavení zadat nějaké argumenty, je možné je zapsat na konec příkazu na pozici `<OPTIONAL-ARGS>`. Například, pokud byste chtěli sestavit optimalizovaně pro vydání, spustili byste výše uvedený příkaz následovaný `--release`. Výsledný spustitelný soubor se objeví v cílové složce na vašem systému a bude ho možné spustit pomocí:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
```
|
||||
|
||||
Nebo, pokud spouštíte variantu pro vydání:
|
||||
|
||||
```sh
|
||||
target/release/rustdesk
|
||||
```
|
||||
|
||||
Zajistětě, abyste tyto příkazy spouštěli z kořene repozitáře s RustDesk, jinak aplikace nemusí být schopná nalézt potřebné prostředky (resources). Také si všimněte, že ostatní dílčí príkazy nástroje cargo, jako třeba `install` nebo `run` zatím nejsou prostřednictvím této metody podporovány, protože by vedly k instalaci či spuštění program uvnitř kontejneru namísto přímo v systému.
|
||||
|
||||
## Struktura souborů
|
||||
|
||||
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: kodek videa, nastavení, obalovaní tcp/udp, vyrovnávací paměť protokolu, funkce souborového systému pro přenos souborů a pár dalších podpůrných funkcí
|
||||
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: zachytávání obsahu obrazovky
|
||||
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: ovládání klávesnice/myši pro jednotlivé platformy
|
||||
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: grafické uživatelské rozhraní
|
||||
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: služby pro zvuk/schránku/zadávání/video a síťová spojení
|
||||
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: spouští připojení k protějšku
|
||||
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: komunikace s [rustdesk-server](https://github.com/rustdesk/rustdesk-server), očekávání vzdálených příméhých („proděrováváním“ TCP) nebo předávaných (relay) spojení
|
||||
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: zdrojové kódy, specifické pro jednotlivé platformy
|
||||
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: zdrojové kódy pro použití s aplikačním rámcem (framework) Flutter pro mobilní platformy
|
||||
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Javascript pro Flutter webový klient
|
||||
|
||||
## Ukázky
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png)
|
@ -5,7 +5,7 @@
|
||||
<a href="#auf-docker-kompilieren">Docker</a> •
|
||||
<a href="#dateistruktur">Dateistruktur</a> •
|
||||
<a href="#screenshots">Screenshots</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
<b>Wir brauchen deine Hilfe um diese README Datei zu verbessern und aktualisieren</b>
|
||||
</p>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#kiel-kompili-kun-docker">Docker</a> •
|
||||
<a href="#dosierstrukturo">Strukturo</a> •
|
||||
<a href="#ekrankopio">Ekrankopio</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
<b>Ni bezonas helpon traduki tiun README kaj <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">la interfacon</a> al via denaska lingvo</b>
|
||||
</p>
|
||||
|
||||
@ -125,7 +125,7 @@ Tiam, ĉiuj fojoj, kiuj vi bezonas kompili la programon, plenumu tiun komandon:
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Notu, ke la unua kompilado povas daŭri longe, antaŭ ke la dependantaĵoj estu kaŝitaj, sekvaj kompiladoj estos pli rapidaj. Aldone, se vi bezonas specifi diferentajn argumentojn por la kompilkomando, vi povas fari ĝin en la fine de la komando, en la posicio `<OPTIONAL-ARGS>`. Ekzemple, se vi volas kompili version de eldono optimigita, vi plenumus la komandon supre, kun `---release`. La plenumebla dosiero disponeblos en la cela dosierujo sur via sistemo, kaj povos esti plenumita kun:
|
||||
Notu, ke la unua kompilado povas daŭri longe, antaŭ ke la dependantaĵoj estu kaŝitaj, sekvaj kompiladoj estos pli rapidaj. Aldone, se vi bezonas specifi diferentajn argumentojn por la kompilkomando, vi povas fari ĝin en la fine de la komando, en la posicio `<OPTIONAL-ARGS>`. Ekzemple, se vi volas kompili version de eldono optimigita, vi plenumus la komandon supre, kun `--release`. La plenumebla dosiero disponeblos en la cela dosierujo sur via sistemo, kaj povos esti plenumita kun:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
|
20
README-ES.md
@ -5,11 +5,11 @@
|
||||
<a href="#como-compilar-con-docker">Docker</a> •
|
||||
<a href="#estructura-de-archivos">Estructura</a> •
|
||||
<a href="#captura-de-pantalla">Captura de pantalla</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</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>]<br>
|
||||
<b>Necesitamos tu ayuda para traducir este README a tu idioma</b>
|
||||
</p>
|
||||
|
||||
Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||
Chatea con nosotros: [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)
|
||||
|
||||
@ -23,9 +23,11 @@ RustDesk agradece la contribución de todo el mundo. Ve [`CONTRIBUTING.md`](CONT
|
||||
|
||||
A continuación se muestran los servidores que está utilizando de forma gratuita, puede cambiar en algún momento. Si no estás cerca de uno de ellos, tu red puede ser lenta.
|
||||
|
||||
- Seoul, AWS lightsail, 1 VCPU/0.5G RAM
|
||||
- Singapore, Vultr, 1 VCPU/1G RAM
|
||||
- Dallas, Vultr, 1 VCPU/1G RAM
|
||||
| Ubicación | Vendedor | Especificación |
|
||||
| --------- | ------------- | ------------------ |
|
||||
| Seoul | AWS lightsail | 1 VCPU / 0.5GB RAM |
|
||||
| Singapore | Vultr | 1 VCPU / 1GB RAM |
|
||||
| Dallas | Vultr | 1 VCPU / 1GB RAM | |
|
||||
|
||||
## Dependencies
|
||||
|
||||
@ -37,7 +39,7 @@ La versión Desktop usa [sciter](https://sciter.com/) para GUI, por favor bajate
|
||||
|
||||
## Pasos para compilar desde el inicio
|
||||
|
||||
- Prepara el entono de desarrollode Rust y el entorno de compilación de C++ y Rust.
|
||||
- Prepara el entono de desarrollo de Rust y el entorno de compilación de C++ y Rust.
|
||||
|
||||
- Instala [vcpkg](https://github.com/microsoft/vcpkg), y configura la variable de entono `VCPKG_ROOT` correctamente.
|
||||
|
||||
@ -78,7 +80,7 @@ export VCPKG_ROOT=$HOME/vcpkg
|
||||
vcpkg/vcpkg install libvpx libyuv opus
|
||||
```
|
||||
|
||||
### Soluciona libvpx (For Fedora)
|
||||
### Soluciona libvpx (Para Fedora)
|
||||
|
||||
```sh
|
||||
cd vcpkg/buildtrees/libvpx/src
|
||||
@ -124,7 +126,7 @@ Entonces, cada vez que necesites compilar una modificación, ejecuta el siguient
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Ten en cuenta que la primera compilación puede tardar más tiempo antes de que las dependencias se almacenen en la caché, las siguientes compilaciones serán más rápidas. Además, si necesitas especificar diferentes argumentos a la orden de compilación, puede hacerlo al final de la linea de comandos en el apartado`<OPTIONAL-ARGS>`. Por ejemplo, si desea compilar una versión optimizada para publicación, deberá ejecutar el comando anterior seguido de `---release`. El ejecutable resultante estará disponible en la carpeta de destino en su sistema, y puede ser ejecutado con:
|
||||
Ten en cuenta que la primera compilación puede tardar más tiempo antes de que las dependencias se almacenen en la caché, las siguientes compilaciones serán más rápidas. Además, si necesitas especificar diferentes argumentos a la orden de compilación, puede hacerlo al final de la linea de comandos en el apartado `<OPTIONAL-ARGS>`. Por ejemplo, si desea compilar una versión optimizada para publicación, deberá ejecutar el comando anterior seguido de `--release`. El ejecutable resultante estará disponible en la carpeta de destino en su sistema, y puede ser ejecutado con:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
@ -148,6 +150,8 @@ Por favor, asegurate de que estás ejecutando estos comandos desde la raíz del
|
||||
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: iniciar una conexión "peer to peer"
|
||||
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Comunicación con [rustdesk-server](https://github.com/rustdesk/rustdesk-server), esperar la conexión remota directa ("TCP hole punching") o conexión indirecta ("relayed")
|
||||
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: código específico de cada plataforma
|
||||
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter, código para moviles
|
||||
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Javascript para cliente web Flutter
|
||||
|
||||
## Captura de pantalla
|
||||
|
||||
|
178
README-FA.md
Normal file
@ -0,0 +1,178 @@
|
||||
<p align="center">
|
||||
<img src="logo-header.svg" alt="RustDesk - Your remote desktop"><br>
|
||||
<a dir="rtl" href="#اسکرین-شات-ها">اسنپ شات</a> •
|
||||
<a dir="rtl" href="#ساختار-پوشه-ها">ساختار</a> •
|
||||
<a dir="rtl" href="#نحوه-ساخت-با-داکر">داکر</a> •
|
||||
<a dir="rtl" href="#ساخت">ساخت</a> •
|
||||
<a dir="rtl" href="#سرورهای-عمومی-رایگان">سرور</a><br>
|
||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</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>]<br>
|
||||
‫<b>برای ترجمه این <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang"> RustDesk UI</a> ،README و <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> به زبان مادری شما به کمکتون نیاز داریم
|
||||
</p>
|
||||
|
||||
با ما گپ بزنید: [Reddit](https://www.reddit.com/r/rustdesk) | [Twitter](https://twitter.com/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV)
|
||||
|
||||
|
||||
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
|
||||
|
||||
یک نرم افزار دیگر کنترل دسکتاپ از راه دور، که با Rust نوشته شده است. راه اندازی سریع وبدون نیاز به تنظیمات. شما کنترل کاملی بر داده های خود دارید، بدون هیچ گونه نگرانی امنیتی.
|
||||
میتوانید از سرور rendezvous/relay ما استفاده کنید، [سرور خودتان را راهاندازی کنید](https://rustdesk.com/server) یا
|
||||
[ سرورrendezvous/relay خود را بنویسید](https://github.com/rustdesk/rustdesk).
|
||||
|
||||
‫راست دسک (RustDesk) از مشارکت همه استقبال می کند. برای راهنمایی جهت مشارکت به [`CONTRIBUTING.md`](CONTRIBUTING.md) مراجعه کنید.
|
||||
|
||||
[راست دسک چطور کار می کند؟](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
|
||||
|
||||
[دانلود باینری](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
## سرورهای عمومی رایگان
|
||||
|
||||
سرورهایی زیر را به صورت رایگان میتوانید استفاده می کنید. این لیست ممکن است در طول زمان تغییر کند. اگر به این سرورها نزدیک نیستید، ممکن است سرویس شما کند شود.
|
||||
| موقعیت | سرویس دهنده | مشخصات |
|
||||
| --------- | ------------- | ------------------ |
|
||||
| Seoul | AWS lightsail | 1 VCPU / 0.5GB RAM |
|
||||
| Singapore | Vultr | 1 VCPU / 1GB RAM |
|
||||
| Dallas | Vultr | 1 VCPU / 1GB RAM | |
|
||||
|
||||
## وابستگی ها
|
||||
|
||||
نسخههای دسکتاپ از [sciter](https://sciter.com/) برای رابط کاربری گرافیکی استفاده میکنند، لطفا کتابخانه پویا sciter را خودتان دانلود کنید.
|
||||
|
||||
[ویندوز](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
|
||||
[لینوکس](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
|
||||
[مک](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
|
||||
|
||||
نسخه های موبایل از Flutter استفاده می کنند. بعداً نسخه دسکتاپ را از Sciter به Flutter منتقل خواهیم کرد.
|
||||
|
||||
## مراحل بنیادین برای ساخت
|
||||
|
||||
‫- محیط توسعه نرم افزار Rust و محیط ساخت ++C خود را آماده کنید
|
||||
|
||||
‫- نرم افزار [vcpkg](https://github.com/microsoft/vcpkg) را نصب کنید و متغیر `VCPKG_ROOT` را به درستی تنظیم کنید:
|
||||
|
||||
- Windows: `vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static`
|
||||
- Linux/MacOS: `vcpkg install libvpx libyuv opus`
|
||||
|
||||
- run `cargo run`
|
||||
|
||||
## [ساخت](https://rustdesk.com/docs/en/dev/build/)
|
||||
|
||||
## نحوه ساخت بر روی لینوکس
|
||||
|
||||
### ساخت بر روی (Ubuntu 18 (Debian 10
|
||||
|
||||
```sh
|
||||
sudo apt install -y 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
|
||||
```
|
||||
|
||||
### ساخت بر روی (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
|
||||
```
|
||||
|
||||
### ساخت بر روی (Arch (Manjaro
|
||||
|
||||
```sh
|
||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||
```
|
||||
|
||||
### بسته pynput را نصب کنید
|
||||
|
||||
```sh
|
||||
pip3 install pynput
|
||||
```
|
||||
|
||||
### نرم افزار vcpkg را نصب کنید
|
||||
|
||||
```sh
|
||||
git clone https://github.com/microsoft/vcpkg
|
||||
cd vcpkg
|
||||
git checkout 2021.12.01
|
||||
cd ..
|
||||
vcpkg/bootstrap-vcpkg.sh
|
||||
export VCPKG_ROOT=$HOME/vcpkg
|
||||
vcpkg/vcpkg install libvpx libyuv opus
|
||||
```
|
||||
|
||||
### رفع ایراد libvpx (برای فدورا)
|
||||
|
||||
```sh
|
||||
cd vcpkg/buildtrees/libvpx/src
|
||||
cd *
|
||||
./configure
|
||||
sed -i 's/CFLAGS+=-I/CFLAGS+=-fPIC -I/g' Makefile
|
||||
sed -i 's/CXXFLAGS+=-I/CXXFLAGS+=-fPIC -I/g' Makefile
|
||||
make
|
||||
cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/
|
||||
cd
|
||||
```
|
||||
|
||||
### ساخت
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source $HOME/.cargo/env
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
cd rustdesk
|
||||
mkdir -p target/debug
|
||||
wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so
|
||||
mv libsciter-gtk.so target/debug
|
||||
VCPKG_ROOT=$HOME/vcpkg cargo run
|
||||
```
|
||||
|
||||
### تغییر Wayland به (X11 (Xorg
|
||||
|
||||
راست دسک از Wayland پشتیبانی نمی کند. برای جایگزنی Xorg به عنوان پیشفرض GNOM، [اینجا](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) را کلیک کنید.
|
||||
|
||||
## نحوه ساخت با داکر
|
||||
|
||||
این مخزن گیت را کلون کنید و کانتینر را به روش زیر بسازید
|
||||
|
||||
```sh
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
cd rustdesk
|
||||
docker build -t "rustdesk-builder" .
|
||||
```
|
||||
|
||||
سپس، هر بار که نیاز به ساخت اپلیکیشن داشتید، دستور زیر را اجرا کنید:
|
||||
|
||||
```sh
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
توجه داشته باشید که ساخت اول ممکن است قبل از کش شدن وابستگی ها بیشتر طول بکشد، دفعات بعدی سریعتر خواهند بود. علاوه بر این، اگر نیاز به تعیین آرگومان های مختلف برای دستور ساخت دارید، می توانید این کار را در انتهای دستور ساخت و از طریق `<OPTIONAL-ARGS>` انجام دهید. به عنوان مثال، اگر می خواهید یک نسخه نهایی بهینه سازی شده ایجاد کنید، دستور بالا را تایپ کنید و در انتها `release--` را اضافه کنید. فایل اجرایی به دست آمده در پوشه مقصد در سیستم شما در دسترس خواهد بود و می تواند با دستور:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
```
|
||||
|
||||
یا برای نسخه بهینه سازی شده دستور زیر را اجرا کنید:
|
||||
|
||||
```sh
|
||||
target/release/rustdesk
|
||||
```
|
||||
|
||||
لطفاً اطمینان حاصل کنید که این دستورات را از پوشه مخزن RustDesk اجرا می کنید، در غیر این صورت ممکن است برنامه نتواند منابع مورد نیاز را پیدا کند. همچنین توجه داشته باشید که سایر دستورات فرعی Cargo مانند `install` یا `run` در حال حاضر از طریق این روش پشتیبانی نمی شوند زیرا برنامه به جای سیستم عامل میزبان, در داخل کانتینر نصب و اجرا میشود.
|
||||
|
||||
## ساختار پوشه ها
|
||||
|
||||
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, config, tcp/udp wrapper, protobuf, fs functions for file transfer, and some other utility functions
|
||||
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: screen capture
|
||||
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: platform specific keyboard/mouse control
|
||||
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
|
||||
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: audio/clipboard/input/video services, and network connections
|
||||
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: start a peer connection
|
||||
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Communicate with [rustdesk-server](https://github.com/rustdesk/rustdesk-server), wait for remote direct (TCP hole punching) or relayed connection
|
||||
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: platform specific code
|
||||
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter code for mobile
|
||||
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Javascript for Flutter web client
|
||||
|
||||
## اسکرین شات ها
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png)
|
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Rakenne</a> •
|
||||
<a href="#snapshot">Tilannevedos</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
<b>Tarvitsemme apua tämän README-tiedoston kääntämiseksi äidinkielellesi</b>
|
||||
</p>
|
||||
|
||||
@ -125,7 +125,7 @@ Sitten, joka kerta kun sinun on rakennettava sovellus, aja seuraava komento:
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Huomaa, että ensimmäinen rakentaminen saattaa kestää pitempään ennen kuin riippuvuudet on siirretty välimuistiin, seuraavat rakentamiset ovat nopeampia. Lisäksi, jos sinun on määritettävä eri argumentteja rakentamiskomennolle, saatat tehdä sen niin, että komennon lopussa <OPTIONAL-ARGS>`-kohdassa. Esimerkiksi, jos haluat rakentaa optimoidun julkaisuversion, sinun on ajettava komento yllä siten, että sitä seuraa argumentti`---release`. Suoritettava tiedosto on saatavilla järjestelmäsi kohdehakemistossa, ja se voidaan suorittaa seuraavan kera:
|
||||
Huomaa, että ensimmäinen rakentaminen saattaa kestää pitempään ennen kuin riippuvuudet on siirretty välimuistiin, seuraavat rakentamiset ovat nopeampia. Lisäksi, jos sinun on määritettävä eri argumentteja rakentamiskomennolle, saatat tehdä sen niin, että komennon lopussa <OPTIONAL-ARGS>`-kohdassa. Esimerkiksi, jos haluat rakentaa optimoidun julkaisuversion, sinun on ajettava komento yllä siten, että sitä seuraa argumentti`--release`. Suoritettava tiedosto on saatavilla järjestelmäsi kohdehakemistossa, ja se voidaan suorittaa seuraavan kera:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#comment-construire-avec-docker">Docker</a> -
|
||||
<a href="#structure-du-projet">Structure</a> -
|
||||
<a href="#images">Images</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
<b>Nous avons besoin de votre aide pour traduire ce README dans votre langue maternelle</b>.
|
||||
</p>
|
||||
|
||||
@ -124,7 +124,7 @@ Ensuite, chaque fois que vous devez compiler le logiciel, exécutez la commande
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Notez que la première compilation peut prendre plus de temps avant que les dépendances ne soient mises en cache, les compilations suivantes seront plus rapides. De plus, si vous devez spécifier différents arguments à la commande de compilation, vous pouvez le faire à la fin de la commande à la position `<OPTIONAL-ARGS>`. Par exemple, si vous voulez compiler une version de release optimisée, vous devez exécuter la commande ci-dessus suivie de `---release`. L'exécutable résultant sera disponible dans le dossier cible sur votre système, et peut être lancé avec :
|
||||
Notez que la première compilation peut prendre plus de temps avant que les dépendances ne soient mises en cache, les compilations suivantes seront plus rapides. De plus, si vous devez spécifier différents arguments à la commande de compilation, vous pouvez le faire à la fin de la commande à la position `<OPTIONAL-ARGS>`. Par exemple, si vous voulez compiler une version de release optimisée, vous devez exécuter la commande ci-dessus suivie de `--release`. L'exécutable résultant sera disponible dans le dossier cible sur votre système, et peut être lancé avec :
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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>]<br>
|
||||
[<a href="README-ZH.md">中文</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>]<br>
|
||||
<b>Kami membutuhkan bantuan Anda untuk menerjemahkan README ini dan <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> ke bahasa asli anda</b>
|
||||
</p>
|
||||
|
||||
@ -125,7 +125,7 @@ Kemudian, setiap kali Anda perlu build aplikasi, jalankan perintah berikut:
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Perhatikan bahwa build pertama mungkin memerlukan waktu lebih lama sebelum dependensi di-cache, build berikutnya akan lebih cepat. Selain itu, jika Anda perlu menentukan argumen yang berbeda untuk perintah build, Anda dapat melakukannya di akhir perintah di posisi `<OPTIONAL-ARGS>`. Misalnya, jika Anda ingin membangun versi rilis yang dioptimalkan, Anda akan menjalankan perintah di atas diikuti oleh `---release`. Hasil eksekusi akan tersedia pada target folder di sistem anda, dan dapat dijalankan dengan:
|
||||
Perhatikan bahwa build pertama mungkin memerlukan waktu lebih lama sebelum dependensi di-cache, build berikutnya akan lebih cepat. Selain itu, jika Anda perlu menentukan argumen yang berbeda untuk perintah build, Anda dapat melakukannya di akhir perintah di posisi `<OPTIONAL-ARGS>`. Misalnya, jika Anda ingin membangun versi rilis yang dioptimalkan, Anda akan menjalankan perintah di atas diikuti oleh `--release`. Hasil eksekusi akan tersedia pada target folder di sistem anda, dan dapat dijalankan dengan:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#come-compilare-con-docker">Docker</a> •
|
||||
<a href="#struttura-dei-file">Struttura</a> •
|
||||
<a href="#screenshots">Screenshots</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
<b>Abbiamo bisogno del tuo aiuto per tradurre questo README e la <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> nella tua lingua nativa</b>
|
||||
</p>
|
||||
|
||||
@ -125,7 +125,7 @@ Quindi, ogni volta che devi compilare l'applicazione, esegui il comando seguente
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Tieni presente che la prima build potrebbe richiedere più tempo prima che le dipendenze vengano memorizzate nella cache, le build successive saranno più veloci. Inoltre, se hai bisogno di specificare argomenti diversi per il comando build, puoi farlo alla fine del comando nella posizione `<OPTIONAL-ARGS>`. Ad esempio, se si desidera creare una versione di rilascio ottimizzata, eseguire il comando sopra seguito da `---release`. L'eseguibile generato sarà creato nella cartella di destinazione del proprio sistema e può essere eseguito con:
|
||||
Tieni presente che la prima build potrebbe richiedere più tempo prima che le dipendenze vengano memorizzate nella cache, le build successive saranno più veloci. Inoltre, se hai bisogno di specificare argomenti diversi per il comando build, puoi farlo alla fine del comando nella posizione `<OPTIONAL-ARGS>`. Ad esempio, se si desidera creare una versione di rilascio ottimizzata, eseguire il comando sopra seguito da `--release`. L'eseguibile generato sarà creato nella cartella di destinazione del proprio sistema e può essere eseguito con:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
|
23
README-JP.md
@ -5,18 +5,23 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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>]<br>
|
||||
<b>このREADMEをあなたの母国語に翻訳するために、あなたの助けが必要です。</b>
|
||||
</p>
|
||||
|
||||
Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||
Chat with us: [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)
|
||||
|
||||
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)
|
||||
|
||||
RustDeskは誰からの貢献も歓迎します。 貢献するには [`CONTRIBUTING.md`](CONTRIBUTING.md) を参照してください。
|
||||
|
||||
[**RustDeskはどの様に動くのか?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
|
||||
|
||||
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
## 無料のパブリックサーバー
|
||||
@ -36,6 +41,8 @@ RustDeskは誰からの貢献も歓迎します。 貢献するには [`CONTRIBU
|
||||
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
|
||||
[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
|
||||
|
||||
モバイル版はFlutterを利用します。デスクトップ版もSciterからFlutterへマイグレーション予定です。
|
||||
|
||||
## ビルド手順
|
||||
|
||||
- Rust開発環境とC ++ビルド環境を準備します
|
||||
@ -47,6 +54,10 @@ RustDeskは誰からの貢献も歓迎します。 貢献するには [`CONTRIBU
|
||||
|
||||
- run `cargo run`
|
||||
|
||||
|
||||
|
||||
## [Build](https://rustdesk.com/docs/en/dev/build/)
|
||||
|
||||
## Linuxでのビルド手順
|
||||
|
||||
### Ubuntu 18 (Debian 10)
|
||||
@ -67,6 +78,12 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||
```
|
||||
|
||||
### Install pynput package
|
||||
|
||||
```sh
|
||||
pip3 install pynput
|
||||
```
|
||||
|
||||
### Install vcpkg
|
||||
|
||||
```sh
|
||||
@ -127,7 +144,7 @@ docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user
|
||||
```
|
||||
|
||||
なお、最初のビルドでは、依存関係がキャッシュされるまで時間がかかることがありますが、その後のビルドではより速くなります。さらに、ビルドコマンドに別の引数を指定する必要がある場合は、コマンドの最後にある `<OPTIONAL-ARGS>` の位置で指定することができます。例えば、最適化されたリリースバージョンをビルドしたい場合は、上記のコマンドの後に
|
||||
`---release` を実行します。できあがった実行ファイルは、システムのターゲット・フォルダに格納され、次のコマンドで実行できます。
|
||||
`--release` を実行します。できあがった実行ファイルは、システムのターゲット・フォルダに格納され、次のコマンドで実行できます。
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
|
21
README-KR.md
@ -5,19 +5,23 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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>]<br>
|
||||
<b>README를 모국어로 번역하기 위한 당신의 도움의 필요합니다.</b>
|
||||
</p>
|
||||
|
||||
Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||
Chat with us: [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)
|
||||
|
||||
Rust로 작성되었고, 설정없이 바로 사용할 수 있는 리모트 데스트탑 소프트웨어입니다. 자신의 데이터를 완전히 컨트롤할 수 있고, 보안의 염려도 없습니다. 우리의 rendezvous/relay 서버를 사용해도, [스스로 설정](https://rustdesk.com/server)하는 것도, [스스로 rendezvous/relay 서버를 작성할 수도 있습니다](https://github.com/rustdesk/rustdesk-server-demo).
|
||||
Rust로 작성되었고, 설정없이 바로 사용할 수 있는 원격 데스트탑 소프트웨어입니다. 자신의 데이터를 완전히 컨트롤할 수 있고, 보안의 염려도 없습니다. 우리의 rendezvous/relay 서버를 사용해도, [스스로 설정](https://rustdesk.com/server)하는 것도, [스스로 rendezvous/relay 서버를 작성할 수도 있습니다](https://github.com/rustdesk/rustdesk-server-demo).
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
|
||||
|
||||
RustDesk는 모든 기여를 환영합니다. 기여하고자 한다면 [`CONTRIBUTING.md`](CONTRIBUTING.md)를 참조해주세요.
|
||||
|
||||
[**RustDesk는 어떻게 작동하는가?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
|
||||
|
||||
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
## 무료 퍼블릭 서버
|
||||
@ -72,6 +76,12 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||
```
|
||||
|
||||
### Install pynput package
|
||||
|
||||
```sh
|
||||
pip3 install pynput
|
||||
```
|
||||
|
||||
### Install vcpkg
|
||||
|
||||
```sh
|
||||
@ -130,7 +140,7 @@ docker build -t "rustdesk-builder" .
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
첫 빌드에서는 의존관계가 캐시될 때까지 시간이 거릴 수 있습니다만, 이후의 빌드때는 빨라집니다. 더불어 빌드 커맨드에 다른 인수를 지정할 필요가 있다면, 커맨드 끝에 있는 `<OPTIONAL-ARGS>` 에 지정할 수 있습니다. 예를 들어 최적화된 출시 버전을 빌드하고 싶다면 이렇게 상기한 커맨드 뒤에 `---release` 를 붙여 실행합니다. 성공했다면 실행파일은 시스템 타겟 폴더에 담겨지고, 다음 커맨드로 실행할 수 있습니다.
|
||||
첫 빌드에서는 의존관계가 캐시될 때까지 시간이 걸릴 수 있습니다만, 이후의 빌드때는 빨라집니다. 더불어 빌드 커맨드에 다른 인수를 지정할 필요가 있다면, 커맨드 끝에 있는 `<OPTIONAL-ARGS>` 에 지정할 수 있습니다. 예를 들어 최적화된 출시 버전을 빌드하고 싶다면 이렇게 상기한 커맨드 뒤에 `--release` 를 붙여 실행합니다. 성공했다면 실행파일은 시스템 타겟 폴더에 담겨지고, 다음 커맨드로 실행할 수 있습니다.
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
@ -154,6 +164,8 @@ target/release/rustdesk
|
||||
- **[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 hole punching) 혹은 relayed 접속
|
||||
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: 플랫폼 고유의 코드
|
||||
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter code for mobile
|
||||
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Javascript for Flutter web client
|
||||
|
||||
## Snapshot
|
||||
|
||||
@ -164,3 +176,4 @@ target/release/rustdesk
|
||||
![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png)
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
<b>ഈ README നിങ്ങളുടെ മാതൃഭാഷയിലേക്ക് വിവർത്തനം ചെയ്യാൻ ഞങ്ങൾക്ക് നിങ്ങളുടെ സഹായം ആവശ്യമാണ്</b>
|
||||
</p>
|
||||
|
||||
@ -125,7 +125,7 @@ docker build -t "rustdesk-builder" .
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
ഡിപൻഡൻസികൾ കാഷെ ചെയ്യുന്നതിനുമുമ്പ് ആദ്യ ബിൽഡ് കൂടുതൽ സമയമെടുത്തേക്കാം, തുടർന്നുള്ള ബിൽഡുകൾ വേഗത്തിലാകും. കൂടാതെ, നിങ്ങൾക്ക് ബിൽഡ് കമാൻഡിലേക്ക് വ്യത്യസ്ത ആർഗ്യുമെന്റുകൾ വ്യക്തമാക്കണമെങ്കിൽ, കമാൻഡിന്റെ അവസാനം `<OPTIONAL-ARGS>` സ്ഥാനത്ത് നിങ്ങൾക്ക് അങ്ങനെ ചെയ്യാം. ഉദാഹരണത്തിന്, നിങ്ങൾ ഒരു ഒപ്റ്റിമൈസ് ചെയ്ത റിലീസ് പതിപ്പ് നിർമ്മിക്കാൻ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, മുകളിലുള്ള കമാൻഡ് തുടർന്ന് `---release` നിങ്ങൾ പ്രവർത്തിപ്പിക്കും. തത്ഫലമായുണ്ടാകുന്ന എക്സിക്യൂട്ടബിൾ നിങ്ങളുടെ സിസ്റ്റത്തിലെ ടാർഗെറ്റ് ഫോൾഡറിൽ ലഭ്യമാകും, കൂടാതെ ഇത് ഉപയോഗിച്ച് പ്രവർത്തിപ്പിക്കാം:
|
||||
ഡിപൻഡൻസികൾ കാഷെ ചെയ്യുന്നതിനുമുമ്പ് ആദ്യ ബിൽഡ് കൂടുതൽ സമയമെടുത്തേക്കാം, തുടർന്നുള്ള ബിൽഡുകൾ വേഗത്തിലാകും. കൂടാതെ, നിങ്ങൾക്ക് ബിൽഡ് കമാൻഡിലേക്ക് വ്യത്യസ്ത ആർഗ്യുമെന്റുകൾ വ്യക്തമാക്കണമെങ്കിൽ, കമാൻഡിന്റെ അവസാനം `<OPTIONAL-ARGS>` സ്ഥാനത്ത് നിങ്ങൾക്ക് അങ്ങനെ ചെയ്യാം. ഉദാഹരണത്തിന്, നിങ്ങൾ ഒരു ഒപ്റ്റിമൈസ് ചെയ്ത റിലീസ് പതിപ്പ് നിർമ്മിക്കാൻ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, മുകളിലുള്ള കമാൻഡ് തുടർന്ന് `--release` നിങ്ങൾ പ്രവർത്തിപ്പിക്കും. തത്ഫലമായുണ്ടാകുന്ന എക്സിക്യൂട്ടബിൾ നിങ്ങളുടെ സിസ്റ്റത്തിലെ ടാർഗെറ്റ് ഫോൾഡറിൽ ലഭ്യമാകും, കൂടാതെ ഇത് ഉപയോഗിച്ച് പ്രവർത്തിപ്പിക്കാം:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structuur</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
<b>We hebben je hulp nodig om deze README te vertalen naar jouw moedertaal</b>
|
||||
</p>
|
||||
|
||||
@ -125,7 +125,7 @@ Voer vervolgens de volgende commando's uit iedere keer dat je de applicatie opni
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Let op dat de eerste build langer kan duren omdat de dependencies nog niet zijn gecached; latere builds zullen sneller zijn. Als je extra command line arguments wilt toevoegen aan het build-commando, dan kun je dat doen aan het einde van de opdrachtregel in plaats van `<OPTIONAL-ARGS>`. Bijvoorbeeld: als je een geoptimaliseerde releaseversie wilt bouwen, draai dan het bovenstaande commando gevolgd door `---release`.
|
||||
Let op dat de eerste build langer kan duren omdat de dependencies nog niet zijn gecached; latere builds zullen sneller zijn. Als je extra command line arguments wilt toevoegen aan het build-commando, dan kun je dat doen aan het einde van de opdrachtregel in plaats van `<OPTIONAL-ARGS>`. Bijvoorbeeld: als je een geoptimaliseerde releaseversie wilt bouwen, draai dan het bovenstaande commando gevolgd door `--release`.
|
||||
|
||||
Het uitvoerbare bestand, in debug-modus, zal verschijnen in de target-map, en kan als volgt worden uitgevoerd:
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#jak-kompilować-za-pomocą-dockera">Docker</a> •
|
||||
<a href="#struktura-plików">Struktura</a> •
|
||||
<a href="#migawkisnapshoty">Snapshot</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
<b>Potrzebujemy twojej pomocy w tłumaczeniu README na twój ojczysty język</b>
|
||||
</p>
|
||||
|
||||
@ -125,7 +125,7 @@ Następnie, za każdym razem, gdy potrzebujesz skompilować aplikację, uruchom
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Zauważ, że pierwsza kompilacja może potrwać dłużej zanim zależności zostaną zbuforowane, kolejne będą szybsze. Dodatkowo, jeśli potrzebujesz określić inne argumenty dla polecenia budowania, możesz to zrobić na końcu komendy w miejscu `<OPTIONAL-ARGS>`. Na przykład, jeśli chciałbyś zbudować zoptymalizowaną wersję wydania, uruchomiłbyś powyższą komendę a następnie `---release`. Powstały plik wykonywalny będzie dostępny w folderze docelowym w twoim systemie, i może być uruchomiony z:
|
||||
Zauważ, że pierwsza kompilacja może potrwać dłużej zanim zależności zostaną zbuforowane, kolejne będą szybsze. Dodatkowo, jeśli potrzebujesz określić inne argumenty dla polecenia budowania, możesz to zrobić na końcu komendy w miejscu `<OPTIONAL-ARGS>`. Na przykład, jeśli chciałbyś zbudować zoptymalizowaną wersję wydania, uruchomiłbyś powyższą komendę a następnie `--release`. Powstały plik wykonywalny będzie dostępny w folderze docelowym w twoim systemie, i może być uruchomiony z:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#como-compilar-com-docker">Docker</a> •
|
||||
<a href="#estrutura-de-arquivos">Estrutura</a> •
|
||||
<a href="#screenshots">Screenshots</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
<b>Precisamos de sua ajuda para traduzir este README e a <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">UI do RustDesk</a> para sua língua nativa</b>
|
||||
</p>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
<b>Нам нужна ваша помощь для перевода этого README и <a href="https://github.com/rustdesk/rustdesk/tree/master/src/rustdesk/tree/master/src/lang">RustDesk UI</a> на ваш родной язык</B>
|
||||
</p>
|
||||
|
||||
@ -127,7 +127,7 @@ docker build -t "rustdesk-builder" .
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Обратите внимание, что первая сборка может занять больше времени, прежде чем зависимости будут кэшированы, но последующие сборки будут выполняться быстрее. Кроме того, если вам нужно указать другие аргументы для команды сборки, вы можете сделать это в конце команды в переменной `<OPTIONAL-ARGS>`. Например, если вы хотите создать оптимизированную версию, вы должны запустить приведенную выше команду и в конце строки добавить `---release`. Полученный исполняемый файл будет доступен в целевой папке вашей системы и может быть запущен с помощью:
|
||||
Обратите внимание, что первая сборка может занять больше времени, прежде чем зависимости будут кэшированы, но последующие сборки будут выполняться быстрее. Кроме того, если вам нужно указать другие аргументы для команды сборки, вы можете сделать это в конце команды в переменной `<OPTIONAL-ARGS>`. Например, если вы хотите создать оптимизированную версию, вы должны запустить приведенную выше команду и в конце строки добавить `--release`. Полученный исполняемый файл будет доступен в целевой папке вашей системы и может быть запущен с помощью:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#使用Docker编译">Docker</a> •
|
||||
<a href="#文件结构">结构</a> •
|
||||
<a href="#截图">截图</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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-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>]<br>
|
||||
[<a href="README-ZH.md">中文</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-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>]<br>
|
||||
</p>
|
||||
|
||||
Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||
@ -180,7 +180,7 @@ groupmod: cannot lock /etc/group; try again later.
|
||||
可以尝试把`-e PUID="$(id -u)" -e PGID="$(id -g)"`参数去掉。(出现这一问题的原因是容器中的 entrypoint 脚本中判定 uid 和 gid 与给定的环境变量不一致时会修改 user 的 uid 和 gid 重新运行,但是重新运行时取不到环境变量中的 uid 和 gid 了,会再次进入 uid 与 gid 与给定值不一致的逻辑分支)
|
||||
|
||||
请注意,第一次构建可能需要比较长的时间,因为需要缓存依赖项(国内网络经常出现拉取失败,可多尝试几次),后续构建会更快。此外,如果您需要为构建命令指定不同的参数,
|
||||
您可以在命令末尾的 `<OPTIONAL-ARGS>` 位置执行此操作。例如,如果你想构建一个优化的发布版本,你可以在命令后跟 `---release`。
|
||||
您可以在命令末尾的 `<OPTIONAL-ARGS>` 位置执行此操作。例如,如果你想构建一个优化的发布版本,你可以在命令后跟 `--release`。
|
||||
将在 target 下产生可执行程序,请通过以下方式运行调试版本:
|
||||
|
||||
```sh
|
||||
|
19
README.md
@ -5,21 +5,28 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</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>]<br>
|
||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</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>]<br>
|
||||
<b>We need your help to translate this README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> and <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> to your native language</b>
|
||||
</p>
|
||||
|
||||
Chat with us: [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)
|
||||
|
||||
Yet another remote desktop software, written in Rust. Works out of the box, no configuration required. You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/server), or [write your own rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo).
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
|
||||
|
||||
RustDesk welcomes contribution from everyone. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for help getting started.
|
||||
|
||||
[**How does RustDesk work?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
|
||||
|
||||
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Get it on F-Droid"
|
||||
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
|
||||
|
||||
## Free Public Servers
|
||||
|
||||
Below are the servers you are using for free, it may change along the time. If you are not close to one of these, your network may be slow.
|
||||
@ -72,6 +79,12 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||
```
|
||||
|
||||
### Install pynput package
|
||||
|
||||
```sh
|
||||
pip3 install pynput
|
||||
```
|
||||
|
||||
### Install vcpkg
|
||||
|
||||
```sh
|
||||
@ -130,7 +143,7 @@ Then, each time you need to build the application, run the following command:
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Note that the first build may take longer before dependencies are cached, subsequent builds will be faster. Additionally, if you need to specify different arguments to the build command, you may do so at the end of the command in the `<OPTIONAL-ARGS>` position. For instance, if you wanted to build an optimized release version, you would run the command above followed by `---release`. The resulting executable will be available in the target folder on your system, and can be run with:
|
||||
Note that the first build may take longer before dependencies are cached, subsequent builds will be faster. Additionally, if you need to specify different arguments to the build command, you may do so at the end of the command in the `<OPTIONAL-ARGS>` position. For instance, if you wanted to build an optimized release version, you would run the command above followed by `--release`. The resulting executable will be available in the target folder on your system, and can be run with:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
|
98
appimage/AppImageBuilder.yml
Normal file
@ -0,0 +1,98 @@
|
||||
# appimage-builder recipe see https://appimage-builder.readthedocs.io for details
|
||||
# Please build this AppImage on Ubuntu 18.04
|
||||
version: 1
|
||||
script:
|
||||
# Remove any previous build
|
||||
- rm -rf AppDir | true
|
||||
# Install application dependencies
|
||||
- pip3 install --upgrade pip && pip3 install --ignore-installed --prefix=/usr --root=AppDir -r ./requirements.txt
|
||||
# Download sciter.so
|
||||
- mkdir -p AppDir/usr/lib/rustdesk/
|
||||
- pushd AppDir/usr/lib/rustdesk && wget https://github.com/c-smile/sciter-sdk/raw/29a598b6d20220b93848b5e8abab704619296857/bin.lnx/x64/libsciter-gtk.so && popd
|
||||
# pynput_service.py
|
||||
- cp ../pynput_service.py ./AppDir/usr/lib/rustdesk
|
||||
# Build rustdesk
|
||||
- pushd .. && python3 inline-sciter.py && cargo build --features inline,appimage --release && popd
|
||||
- mkdir -p AppDir/usr/bin
|
||||
- cp ../target/release/rustdesk AppDir/usr/bin/rustdesk
|
||||
# Make usr and icons dirs
|
||||
- mkdir -p AppDir/usr/share/icons/hicolor/128x128 && cp ../128x128.png AppDir/usr/share/icons/hicolor/128x128/rustdesk.png
|
||||
- mkdir -p AppDir/usr/share/icons/hicolor/32x32 && cp ../32x32.png AppDir/usr/share/icons/hicolor/32x32/rustdesk.png
|
||||
- cp rustdesk.desktop AppDir/
|
||||
|
||||
AppDir:
|
||||
path: ./AppDir
|
||||
app_info:
|
||||
id: rustdesk
|
||||
name: RustDesk
|
||||
icon: rustdesk
|
||||
version: 1.1.10
|
||||
exec: usr/bin/rustdesk
|
||||
exec_args: $@
|
||||
apt:
|
||||
arch:
|
||||
- amd64
|
||||
allow_unauthenticated: true
|
||||
sources:
|
||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic main restricted
|
||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates main restricted
|
||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic universe
|
||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates universe
|
||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic multiverse
|
||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates multiverse
|
||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-backports main restricted
|
||||
universe multiverse
|
||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security main restricted
|
||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security universe
|
||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security multiverse
|
||||
include:
|
||||
- libgcc1:amd64
|
||||
- libgcrypt20:amd64
|
||||
- libgtk-3-0:amd64
|
||||
- liblz4-1:amd64
|
||||
- liblzma5:amd64
|
||||
- libpcre3:amd64
|
||||
- libpulse0:amd64
|
||||
- libsystemd0:amd64
|
||||
- libxau6:amd64
|
||||
- libxcb-randr0:amd64
|
||||
- libxdmcp6:amd64
|
||||
- libxdo3:amd64
|
||||
- libxext6:amd64
|
||||
- libxfixes3:amd64
|
||||
- libxinerama1:amd64
|
||||
- libxrender1:amd64
|
||||
- libxtst6:amd64
|
||||
- python3:amd64
|
||||
- python3-pkg-resources:amd64
|
||||
files:
|
||||
include: []
|
||||
exclude:
|
||||
- usr/share/man
|
||||
- usr/share/doc/*/README.*
|
||||
- usr/share/doc/*/changelog.*
|
||||
- usr/share/doc/*/NEWS.*
|
||||
- usr/share/doc/*/TODO.*
|
||||
runtime:
|
||||
env:
|
||||
PYTHONHOME: '${APPDIR}/usr'
|
||||
PYTHONPATH: '${APPDIR}/usr/lib/python3.6/site-packages'
|
||||
test:
|
||||
fedora-30:
|
||||
image: appimagecrafters/tests-env:fedora-30
|
||||
command: ./AppRun
|
||||
debian-stable:
|
||||
image: appimagecrafters/tests-env:debian-stable
|
||||
command: ./AppRun
|
||||
archlinux-latest:
|
||||
image: appimagecrafters/tests-env:archlinux-latest
|
||||
command: ./AppRun
|
||||
centos-7:
|
||||
image: appimagecrafters/tests-env:centos-7
|
||||
command: ./AppRun
|
||||
ubuntu-xenial:
|
||||
image: appimagecrafters/tests-env:ubuntu-xenial
|
||||
command: ./AppRun
|
||||
AppImage:
|
||||
arch: x86_64
|
||||
update-information: guess
|
20
appimage/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# How to build and run RustDesk in AppImage
|
||||
|
||||
Begin by installing `appimage-builder` and predependencies mentioned in official website.
|
||||
|
||||
Assume that `appimage-builder` is setup correctly, run commands below, `bash` or `zsh` is recommended:
|
||||
|
||||
```bash
|
||||
cd /path/to/rustdesk_root
|
||||
./build_appimage.py
|
||||
```
|
||||
|
||||
After a success package, you can see the message in console like:
|
||||
|
||||
```shell
|
||||
INFO:root:AppImage created successfully
|
||||
```
|
||||
|
||||
The AppImage package is shown in `./appimage/RustDesk-VERSION-TARGET_PLATFORM.AppImage`.
|
||||
|
||||
Note: AppImage version of rustdesk is an early version which requires more test. If you find problems, please open an issue.
|
1
appimage/requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
pynput
|
19
appimage/rustdesk.desktop
Normal file
@ -0,0 +1,19 @@
|
||||
[Desktop Entry]
|
||||
Version=1.1.10
|
||||
Name=RustDesk
|
||||
GenericName=Remote Desktop
|
||||
Comment=Remote Desktop
|
||||
Exec=rustdesk
|
||||
Icon=rustdesk
|
||||
Terminal=false
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=Other;
|
||||
Keywords=internet;
|
||||
Actions=new-window;
|
||||
|
||||
X-Desktop-File-Install-Version=0.23
|
||||
|
||||
[Desktop Action new-window]
|
||||
Name=Open a New Window
|
||||
|
125
build.py
@ -2,9 +2,11 @@
|
||||
|
||||
import os
|
||||
import platform
|
||||
import zlib
|
||||
from shutil import copy2
|
||||
import zipfile
|
||||
import urllib.request
|
||||
import shutil
|
||||
import hashlib
|
||||
import argparse
|
||||
|
||||
windows = platform.platform().startswith('Windows')
|
||||
osx = platform.platform().startswith('Darwin') or platform.platform().startswith("macOS")
|
||||
@ -20,7 +22,93 @@ def get_version():
|
||||
return ''
|
||||
|
||||
|
||||
def get_features(feature):
|
||||
available_features = {
|
||||
'IddDriver': {
|
||||
'zip_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.1/RustDeskIddDriver_x64.zip',
|
||||
'checksum_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.1'
|
||||
'/RustDeskIddDriver_x64.zip.checksum_md5',
|
||||
},
|
||||
'PrivacyMode': {
|
||||
'zip_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.1'
|
||||
'/TempTopMostWindow_x64.zip',
|
||||
'checksum_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.1'
|
||||
'/TempTopMostWindow_x64.zip.checksum_md5',
|
||||
}
|
||||
}
|
||||
apply_features = {}
|
||||
if not feature:
|
||||
return apply_features
|
||||
elif isinstance(feature, str) and feature.upper() == 'ALL':
|
||||
return available_features
|
||||
elif isinstance(feature, list):
|
||||
for feat in feature:
|
||||
if isinstance(feat, str) and feat.upper() == 'ALL':
|
||||
return available_features
|
||||
if feat in available_features:
|
||||
apply_features[feat] = available_features[feat]
|
||||
else:
|
||||
print(f'Unrecognized feature {feat}')
|
||||
return apply_features
|
||||
else:
|
||||
raise Exception(f'Unsupported features param {feature}')
|
||||
|
||||
|
||||
def make_parser():
|
||||
parser = argparse.ArgumentParser(description='Build script.')
|
||||
parser.add_argument(
|
||||
'-f',
|
||||
'--feature',
|
||||
dest='feature',
|
||||
metavar='N',
|
||||
type=str,
|
||||
nargs='+',
|
||||
default='',
|
||||
help='Integrate features, windows only.'
|
||||
'Available: IddDriver, PrivacyMode. Special value is "ALL" and empty "". Default is empty.')
|
||||
return parser
|
||||
|
||||
|
||||
def download_extract_features(features, res_dir):
|
||||
for (feat, feat_info) in features.items():
|
||||
print(f'{feat} download begin')
|
||||
checksum_md5_response = urllib.request.urlopen(feat_info['checksum_url'])
|
||||
checksum_md5 = checksum_md5_response.read().decode('utf-8').split()[0]
|
||||
download_filename = feat_info['zip_url'].split('/')[-1]
|
||||
filename, _headers = urllib.request.urlretrieve(feat_info['zip_url'], download_filename)
|
||||
md5 = hashlib.md5(open(filename, 'rb').read()).hexdigest()
|
||||
if checksum_md5 != md5:
|
||||
raise Exception(f'{feat} download failed')
|
||||
print(f'{feat} download end. extract bein')
|
||||
zip_file = zipfile.ZipFile(filename)
|
||||
zip_list = zip_file.namelist()
|
||||
for f in zip_list:
|
||||
zip_file.extract(f, res_dir)
|
||||
zip_file.close()
|
||||
os.remove(download_filename)
|
||||
print(f'{feat} extract end')
|
||||
|
||||
|
||||
def build_windows(feature):
|
||||
features = get_features(feature)
|
||||
if not features:
|
||||
os.system('cargo build --release --features inline')
|
||||
else:
|
||||
print(f'Build with features {list(features.keys())}')
|
||||
res_dir = 'resources'
|
||||
if os.path.isdir(res_dir) and not os.path.islink(res_dir):
|
||||
shutil.rmtree(res_dir)
|
||||
elif os.path.exists(res_dir):
|
||||
raise Exception(f'Find file {res_dir}, not a directory')
|
||||
os.makedirs(res_dir, exist_ok=True)
|
||||
download_extract_features(features, res_dir)
|
||||
os.system('cargo build --release --features inline,with_rc')
|
||||
|
||||
|
||||
def main():
|
||||
parser = make_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
os.system("cp Cargo.toml Cargo.toml.bk")
|
||||
os.system("cp src/main.rs src/main.rs.bk")
|
||||
if windows:
|
||||
@ -35,39 +123,40 @@ def main():
|
||||
os.system('git checkout src/ui/common.tis')
|
||||
version = get_version()
|
||||
if windows:
|
||||
os.system('cargo build --release --features inline')
|
||||
build_windows(args.feature)
|
||||
# os.system('upx.exe target/release/rustdesk.exe')
|
||||
os.system('mv target/release/rustdesk.exe target/release/RustDesk.exe')
|
||||
pa = os.environ.get('P')
|
||||
if pa:
|
||||
os.system('signtool sign /a /v /p %s /debug /f .\\cert.pfx /t http://timestamp.digicert.com target\\release\\rustdesk.exe'%pa)
|
||||
os.system(f'signtool sign /a /v /p {pa} /debug /f .\\cert.pfx /t http://timestamp.digicert.com '
|
||||
'target\\release\\rustdesk.exe')
|
||||
else:
|
||||
print('Not signed')
|
||||
os.system('cp -rf target/release/RustDesk.exe rustdesk-%s-putes.exe'%version)
|
||||
print('Not signed')
|
||||
os.system(f'cp -rf target/release/RustDesk.exe rustdesk-{version}-setdown.exe')
|
||||
elif os.path.isfile('/usr/bin/pacman'):
|
||||
os.system('cargo build --release --features inline')
|
||||
os.system('git checkout src/ui/common.tis')
|
||||
os.system('strip target/release/rustdesk')
|
||||
os.system("sed -i 's/pkgver=.*/pkgver=%s/g' PKGBUILD"%version)
|
||||
os.system("sed -i 's/pkgver=.*/pkgver=%s/g' PKGBUILD" % version)
|
||||
# pacman -S -needed base-devel
|
||||
os.system('HBB=`pwd` makepkg -f')
|
||||
os.system('mv rustdesk-%s-0-x86_64.pkg.tar.zst rustdesk-%s-manjaro-arch.pkg.tar.zst'%(version, version))
|
||||
os.system('mv rustdesk-%s-0-x86_64.pkg.tar.zst rustdesk-%s-manjaro-arch.pkg.tar.zst' % (version, version))
|
||||
# pacman -U ./rustdesk.pkg.tar.zst
|
||||
elif os.path.isfile('/usr/bin/yum'):
|
||||
os.system('cargo build --release --features inline')
|
||||
os.system('strip target/release/rustdesk')
|
||||
os.system("sed -i 's/Version: .*/Version: %s/g' rpm.spec"%version)
|
||||
os.system("sed -i 's/Version: .*/Version: %s/g' rpm.spec" % version)
|
||||
os.system('HBB=`pwd` rpmbuild -ba rpm.spec')
|
||||
os.system('mv $HOME/rpmbuild/RPMS/x86_64/rustdesk-%s-0.x86_64.rpm ./rustdesk-%s-fedora28-centos8.rpm'%(version, version))
|
||||
os.system('mv $HOME/rpmbuild/RPMS/x86_64/rustdesk-%s-0.x86_64.rpm ./rustdesk-%s-fedora28-centos8.rpm' % (
|
||||
version, version))
|
||||
# yum localinstall rustdesk.rpm
|
||||
elif os.path.isfile('/usr/bin/zypper'):
|
||||
os.system('cargo build --release --features inline')
|
||||
os.system('strip target/release/rustdesk')
|
||||
os.system("sed -i 's/Version: .*/Version: %s/g' rpm-suse.spec"%version)
|
||||
os.system("sed -i 's/Version: .*/Version: %s/g' rpm-suse.spec" % version)
|
||||
os.system('HBB=`pwd` rpmbuild -ba rpm-suse.spec')
|
||||
os.system('mv $HOME/rpmbuild/RPMS/x86_64/rustdesk-%s-0.x86_64.rpm ./rustdesk-%s-suse.rpm'%(version, version))
|
||||
os.system('mv $HOME/rpmbuild/RPMS/x86_64/rustdesk-%s-0.x86_64.rpm ./rustdesk-%s-suse.rpm' % (version, version))
|
||||
# yum localinstall rustdesk.rpm
|
||||
|
||||
else:
|
||||
os.system('cargo bundle --release --features inline')
|
||||
if osx:
|
||||
@ -86,7 +175,7 @@ def main():
|
||||
</dict>"""))
|
||||
pa = os.environ.get('P')
|
||||
if pa:
|
||||
os.system('''
|
||||
os.system('''
|
||||
# buggy: rcodesign sign ... path/*, have to sign one by one
|
||||
#rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/rustdesk
|
||||
#rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/libsciter.dylib
|
||||
@ -96,9 +185,9 @@ codesign -s "Developer ID Application: {0}" --force --options runtime ./target/
|
||||
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app
|
||||
'''.format(pa))
|
||||
os.system('create-dmg target/release/bundle/osx/RustDesk.app')
|
||||
os.rename('RustDesk %s.dmg'%version, 'rustdesk-%s.dmg'%version)
|
||||
os.rename('RustDesk %s.dmg' % version, 'rustdesk-%s.dmg' % version)
|
||||
if pa:
|
||||
os.system('''
|
||||
os.system('''
|
||||
#rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./rustdesk-{1}.dmg
|
||||
codesign -s "Developer ID Application: {0}" --force --options runtime ./rustdesk-{1}.dmg
|
||||
# https://pyoxidizer.readthedocs.io/en/latest/apple_codesign_rcodesign.html
|
||||
@ -106,7 +195,7 @@ rcodesign notarize --api-issuer 69a6de7d-2907-47e3-e053-5b8c7c11a4d1 --api-key 9
|
||||
# verify: spctl -a -t exec -v /Applications/RustDesk.app
|
||||
'''.format(pa, version))
|
||||
else:
|
||||
print('Not signed')
|
||||
print('Not signed')
|
||||
else:
|
||||
os.system('mv target/release/bundle/deb/rustdesk*.deb ./rustdesk.deb')
|
||||
os.system('dpkg-deb -R rustdesk.deb tmpdeb')
|
||||
@ -122,7 +211,7 @@ rcodesign notarize --api-issuer 69a6de7d-2907-47e3-e053-5b8c7c11a4d1 --api-key 9
|
||||
md5_file('usr/share/rustdesk/files/pynput_service.py')
|
||||
md5_file('usr/lib/rustdesk/libsciter-gtk.so')
|
||||
os.system('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/')
|
||||
os.rename('rustdesk.deb', 'rustdesk-%s.deb'%version)
|
||||
os.rename('rustdesk.deb', 'rustdesk-%s.deb' % version)
|
||||
os.system("mv Cargo.toml.bk Cargo.toml")
|
||||
os.system("mv src/main.rs.bk src/main.rs")
|
||||
|
||||
|
16
build.rs
@ -27,6 +27,20 @@ fn build_manifest() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(windows, feature = "with_rc"))]
|
||||
fn build_rc_source() {
|
||||
use simple_rc::{generate_with_conf, Config, ConfigItem};
|
||||
generate_with_conf(&Config {
|
||||
outfile: "src/rc.rs".to_owned(),
|
||||
confs: vec![ConfigItem {
|
||||
inc: "resources".to_owned(),
|
||||
exc: vec![],
|
||||
suppressed_front: "resources".to_owned(),
|
||||
}],
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn install_oboe() {
|
||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
if target_os != "android" {
|
||||
@ -89,6 +103,8 @@ fn main() {
|
||||
gen_flutter_rust_bridge();
|
||||
// return;
|
||||
// }
|
||||
#[cfg(all(windows, feature = "with_rc"))]
|
||||
build_rc_source();
|
||||
#[cfg(all(windows, feature = "inline"))]
|
||||
build_manifest();
|
||||
#[cfg(windows)]
|
||||
|
23
build_appimage.py
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/python3
|
||||
import os
|
||||
|
||||
def get_version():
|
||||
with open("Cargo.toml") as fh:
|
||||
for line in fh:
|
||||
if line.startswith("version"):
|
||||
return line.replace("version", "").replace("=", "").replace('"', '').strip()
|
||||
return ''
|
||||
|
||||
if __name__ == '__main__':
|
||||
# check version
|
||||
version = get_version()
|
||||
os.chdir("appimage")
|
||||
os.system("sed -i 's/^Version=.*/Version=%s/g' rustdesk.desktop" % version)
|
||||
os.system("sed -i 's/^ version: .*/ version: %s/g' AppImageBuilder.yml" % version)
|
||||
# build appimage
|
||||
ret = os.system("appimage-builder --recipe AppImageBuilder.yml --skip-test")
|
||||
if ret == 0:
|
||||
print("RustDesk AppImage build success :)")
|
||||
print("Check AppImage in '/path/to/rustdesk/appimage/RustDesk-VERSION-TARGET_PLATFORM.AppImage'")
|
||||
else:
|
||||
print("RustDesk AppImage build failed :(")
|
11
fastlane/metadata/android/en-US/full_description.txt
Normal file
@ -0,0 +1,11 @@
|
||||
An open-source remote desktop application, the open source TeamViewer alternative.
|
||||
Source code: https://github.com/rustdesk/rustdesk
|
||||
Doc: https://rustdesk.com/docs/en/manual/mobile/
|
||||
|
||||
In order for a remote device to control your Android device via mouse or touch, you need to allow RustDesk to use the "Accessibility" service, RustDesk uses AccessibilityService API to implement Addroid remote control.
|
||||
|
||||
In addtion to remote control, you can also transfer files between Android devices and PCs easily with RustDesk.
|
||||
|
||||
You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, or self-hosting, or write your own rendezvous/relay server. Self-hosting server is free and open source: https://github.com/rustdesk/rustdesk-server
|
||||
|
||||
Please download and install desktop version from: https://rustdesk.com, then you can access and control your desktop from your mobile, or control your mobile from desktop.
|
BIN
fastlane/metadata/android/en-US/images/icon.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
Normal file
After Width: | Height: | Size: 323 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
Normal file
After Width: | Height: | Size: 425 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
Normal file
After Width: | Height: | Size: 131 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 623 KiB |
After Width: | Height: | Size: 384 KiB |
After Width: | Height: | Size: 276 KiB |
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
An open-source remote desktop application, the open source TeamViewer alternative.
|
12
fastlane/metadata/android/zh-CN/full_description.txt
Normal file
@ -0,0 +1,12 @@
|
||||
开源远程桌面应用,开源 TeamViewer 替代方案。
|
||||
源代码:https://github.com/rustdesk/rustdesk
|
||||
文档:https://rustdesk.com/docs/en/manual/mobile/
|
||||
|
||||
为了让远程设备通过鼠标或触摸控制您的 Android 设备,您需要允许 RustDesk 使用“Accessibility”服务,RustDesk 使用 AccessibilityService API 来实现 Addroid 远程控制。
|
||||
|
||||
除了远程控制,您还可以使用 RustDesk 在 Android 设备和 PC 之间轻松传输文件。
|
||||
|
||||
您完全掌控数据,不用担心安全问题。您可以使用我们的注册/中继服务器,或者自建,亦或者开发您的版本。
|
||||
自托管服务器是免费和开源的:https://github.com/rustdesk/rustdesk-server
|
||||
|
||||
请从:https://rustdesk.com 下载并安装桌面版,然后您可以通过手机访问和控制您的桌面,或从桌面控制您的手机。
|
1
fastlane/metadata/android/zh-CN/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
开源远程桌面应用,开源 TeamViewer 替代方案
|
3
flutter/.gitignore
vendored
@ -55,3 +55,6 @@ macos/Flutter/GeneratedPluginRegistrant.swift
|
||||
windows/flutter/generated_plugin_registrant.cc
|
||||
windows/flutter/generated_plugin_registrant.h
|
||||
windows/flutter/generated_plugins.cmake
|
||||
flutter_export_environment.sh
|
||||
Flutter-Generated.xcconfig
|
||||
key.jks
|
||||
|
@ -9,6 +9,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<!--<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />-->
|
||||
|
||||
<application
|
||||
|
@ -1,5 +1,11 @@
|
||||
package com.carriez.flutter_hbb
|
||||
|
||||
/**
|
||||
* Handle remote input and dispatch android gesture
|
||||
*
|
||||
* Inspired by [droidVNC-NG] https://github.com/bk138/droidVNC-NG
|
||||
*/
|
||||
|
||||
import android.accessibilityservice.AccessibilityService
|
||||
import android.accessibilityservice.GestureDescription
|
||||
import android.content.Context
|
||||
@ -32,12 +38,6 @@ class InputService : AccessibilityService() {
|
||||
get() = ctx != null
|
||||
}
|
||||
|
||||
private external fun init(ctx: Context)
|
||||
|
||||
init {
|
||||
System.loadLibrary("rustdesk")
|
||||
}
|
||||
|
||||
private val logTag = "input service"
|
||||
private var leftIsDown = false
|
||||
private var touchPath = Path()
|
||||
@ -50,9 +50,8 @@ class InputService : AccessibilityService() {
|
||||
private val wheelActionsQueue = LinkedList<GestureDescription>()
|
||||
private var isWheelActionsPolling = false
|
||||
|
||||
@Keep
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
fun rustMouseInput(mask: Int, _x: Int, _y: Int) {
|
||||
fun onMouseInput(mask: Int, _x: Int, _y: Int) {
|
||||
val x = if (_x < 0) {
|
||||
0
|
||||
} else {
|
||||
@ -207,12 +206,15 @@ class InputService : AccessibilityService() {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
ctx = this
|
||||
Log.d(logTag, "onServiceConnected!")
|
||||
init(this)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
ctx = null
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent?) {}
|
||||
|
@ -1,5 +1,12 @@
|
||||
package com.carriez.flutter_hbb
|
||||
|
||||
/**
|
||||
* Handle events from flutter
|
||||
* Request MediaProjection permission
|
||||
*
|
||||
* Inspired by [droidVNC-NG] https://github.com/bk138/droidVNC-NG
|
||||
*/
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
|
@ -1,8 +1,11 @@
|
||||
package com.carriez.flutter_hbb
|
||||
|
||||
/**
|
||||
* Capture screen,get video and audio,send to rust.
|
||||
* Handle notification
|
||||
* Dispatch notifications
|
||||
*
|
||||
* Inspired by [droidVNC-NG] https://github.com/bk138/droidVNC-NG
|
||||
*/
|
||||
package com.carriez.flutter_hbb
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
@ -69,10 +72,32 @@ class MainService : Service() {
|
||||
System.loadLibrary("rustdesk")
|
||||
}
|
||||
|
||||
@Keep
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
fun rustMouseInput(mask: Int, x: Int, y: Int) {
|
||||
// turn on screen with LIFT_DOWN when screen off
|
||||
if (!powerManager.isInteractive && mask == LIFT_DOWN) {
|
||||
if (wakeLock.isHeld) {
|
||||
Log.d(logTag,"Turn on Screen, WakeLock release")
|
||||
wakeLock.release()
|
||||
}
|
||||
Log.d(logTag,"Turn on Screen")
|
||||
wakeLock.acquire(5000)
|
||||
} else {
|
||||
InputService.ctx?.onMouseInput(mask,x,y)
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
fun rustGetByName(name: String): String {
|
||||
return when (name) {
|
||||
"screen_size" -> "${SCREEN_INFO.width}:${SCREEN_INFO.height}"
|
||||
"screen_size" -> {
|
||||
JSONObject().apply {
|
||||
put("width",SCREEN_INFO.width)
|
||||
put("height",SCREEN_INFO.height)
|
||||
put("scale",SCREEN_INFO.scale)
|
||||
}.toString()
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
@ -130,6 +155,9 @@ class MainService : Service() {
|
||||
private var serviceLooper: Looper? = null
|
||||
private var serviceHandler: Handler? = null
|
||||
|
||||
private val powerManager: PowerManager by lazy { applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager }
|
||||
private val wakeLock: PowerManager.WakeLock by lazy { powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "rustdesk:wakelock")}
|
||||
|
||||
// jvm call rust
|
||||
private external fun init(ctx: Context)
|
||||
private external fun startServer()
|
||||
@ -191,10 +219,6 @@ class MainService : Service() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
InputService.ctx?.disableSelf()
|
||||
}
|
||||
InputService.ctx = null
|
||||
checkMediaPermission()
|
||||
super.onDestroy()
|
||||
}
|
||||
@ -383,10 +407,6 @@ class MainService : Service() {
|
||||
|
||||
mediaProjection = null
|
||||
checkMediaPermission()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
InputService.ctx?.disableSelf()
|
||||
}
|
||||
InputService.ctx = null
|
||||
stopForeground(true)
|
||||
stopSelf()
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
$ANDROID_NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-strip android/app/src/main/jniLibs/arm64-v8a/*
|
||||
flutter build apk --target-platform android-arm64 --release --obfuscate --split-debug-info ./split-debug-info
|
||||
flutter build appbundle --target-platform android-arm64 --release --obfuscate --split-debug-info ./split-debug-info
|
||||
flutter build apk --target-platform android-arm64,android-arm --release --obfuscate --split-debug-info ./split-debug-info
|
||||
flutter build apk ---split-per-abi --target-platform android-arm64,android-arm --release --obfuscate --split-debug-info ./split-debug-info
|
||||
flutter build appbundle --target-platform android-arm64,android-arm --release --obfuscate --split-debug-info ./split-debug-info
|
||||
|
||||
# build in linux
|
||||
# $ANDROID_NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip android/app/src/main/jniLibs/arm64-v8a/*
|
||||
|
125
flutter/build_android_deps.sh
Executable file
@ -0,0 +1,125 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Build libyuv / opus / libvpx / oboe for Android
|
||||
# Required:
|
||||
# 1. set VCPKG_ROOT / ANDROID_NDK path environment variables
|
||||
# 2. vcpkg initialized
|
||||
# 3. ndk, version: 22 (if ndk < 22 you need to change LD as `export LD=$TOOLCHAIN/bin/$NDK_LLVM_TARGET-ld`)
|
||||
|
||||
if [ -z "$ANDROID_NDK" ]; then
|
||||
echo "Failed! Please set ANDROID_NDK"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$VCPKG_ROOT" ]; then
|
||||
echo "Failed! Please set VCPKG_ROOT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
API_LEVEL="21"
|
||||
|
||||
# NDK llvm toolchain
|
||||
HOST_TAG="linux-x86_64" # current platform, set as `ls $ANDROID_NDK/toolchains/llvm/prebuilt/`
|
||||
TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG
|
||||
|
||||
function build {
|
||||
ANDROID_ABI=$1
|
||||
VCPKG_TARGET=$2
|
||||
NDK_LLVM_TARGET=$3
|
||||
LIBVPX_TARGET=$4
|
||||
|
||||
PREFIX=$VCPKG_ROOT/installed/$VCPKG_TARGET/
|
||||
|
||||
# 1
|
||||
echo "*** [$ANDROID_ABI][Start] Build opus / libyuv from vcpkg"
|
||||
export ANDROID_NDK_HOME=$ANDROID_NDK
|
||||
pushd $VCPKG_ROOT
|
||||
$VCPKG_ROOT/vcpkg install opus --triplet $VCPKG_TARGET
|
||||
$VCPKG_ROOT/vcpkg install libyuv --triplet $VCPKG_TARGET
|
||||
popd
|
||||
echo "*** [$ANDROID_ABI][Finished] Build opus / libyuv from vcpkg"
|
||||
|
||||
# 2
|
||||
echo "*** [$ANDROID_ABI][Start] Build libvpx"
|
||||
pushd build/libvpx
|
||||
export AR=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-ar
|
||||
export AS=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-as
|
||||
export LD=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-ld.gold # if ndk < 22, use aarch64-linux-android-ld
|
||||
export RANLIB=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-ranlib
|
||||
export STRIP=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-strip
|
||||
|
||||
if [ $NDK_LLVM_TARGET == "arm-linux-androideabi" ]
|
||||
then
|
||||
export CC=$TOOLCHAIN/bin/armv7a-linux-androideabi${API_LEVEL}-clang
|
||||
export CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi${API_LEVEL}-clang++
|
||||
else
|
||||
export CC=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}${API_LEVEL}-clang
|
||||
export CXX=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}${API_LEVEL}-clang++
|
||||
fi
|
||||
make clean
|
||||
./configure --target=$LIBVPX_TARGET \
|
||||
--enable-pic --disable-vp8 \
|
||||
--disable-webm-io \
|
||||
--disable-unit-tests \
|
||||
--disable-examples \
|
||||
--disable-libyuv \
|
||||
--disable-postproc \
|
||||
--disable-vp8 \
|
||||
--disable-tools \
|
||||
--disable-docs \
|
||||
--prefix=$PREFIX
|
||||
make -j5
|
||||
make install
|
||||
|
||||
popd
|
||||
echo "*** [$ANDROID_ABI][Finished] Build libvpx"
|
||||
|
||||
# 3
|
||||
echo "*** [$ANDROID_ABI][Start] Build oboe"
|
||||
pushd build/oboe
|
||||
make clean
|
||||
cmake -DBUILD_SHARED_LIBS=true \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DANDROID_TOOLCHAIN=clang \
|
||||
-DANDROID_STL=c++_shared \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
|
||||
-DCMAKE_INSTALL_PREFIX=$PREFIX \
|
||||
-DANDROID_ABI=$ANDROID_ABI \
|
||||
-DANDROID_PLATFORM=android-$API_LEVEL
|
||||
make -j5
|
||||
make install
|
||||
mv $PREFIX/lib/$ANDROID_ABI/liboboe.a $PREFIX/lib/
|
||||
popd
|
||||
echo "*** [$ANDROID_ABI][Finished] Build oboe"
|
||||
|
||||
echo "*** [$ANDROID_ABI][All Finished]"
|
||||
}
|
||||
|
||||
git clone -b v1.11.0 --depth=1 https://github.com/webmproject/libvpx.git build/libvpx
|
||||
git clone -b 1.6.1 --depth=1 https://github.com/google/oboe build/oboe
|
||||
patch -N -d build/oboe -p1 < ../src/oboe.patch
|
||||
|
||||
# VCPKG_TARGET ANDROID_ABI
|
||||
# arm64-android arm64-v8a
|
||||
# arm-android armeabi-v7a
|
||||
# x64-android x86_64
|
||||
# x86-android x86
|
||||
|
||||
# NDK_LLVM_TARGET
|
||||
# aarch64-linux-android
|
||||
# arm-linux-androideabi
|
||||
# x86_64-linux-android
|
||||
# i686-linux-android
|
||||
|
||||
# LIBVPX_TARGET :
|
||||
# arm64-android-gcc
|
||||
# armv7-android-gcc
|
||||
# x86_64-android-gcc
|
||||
# x86-android-gcc
|
||||
|
||||
# args: ANDROID_ABI VCPKG_TARGET NDK_LLVM_TARGET LIBVPX_TARGET
|
||||
build arm64-v8a arm64-android aarch64-linux-android arm64-android-gcc
|
||||
build armeabi-v7a arm-android arm-linux-androideabi armv7-android-gcc
|
||||
|
||||
# rm -rf build/libvpx
|
||||
# rm -rf build/oboe
|
@ -236,7 +236,7 @@ class AccessibilityListener extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
onPointerUp: (evt) {
|
||||
if (evt.size == 1 && GestureBinding.instance != null) {
|
||||
if (evt.size == 1) {
|
||||
GestureBinding.instance.handlePointerEvent(PointerUpEvent(
|
||||
pointer: evt.pointer + offset,
|
||||
size: 0.1,
|
||||
@ -246,7 +246,7 @@ class AccessibilityListener extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
onPointerMove: (evt) {
|
||||
if (evt.size == 1 && GestureBinding.instance != null) {
|
||||
if (evt.size == 1) {
|
||||
GestureBinding.instance.handlePointerEvent(PointerMoveEvent(
|
||||
pointer: evt.pointer + offset,
|
||||
size: 0.1,
|
||||
|
@ -113,8 +113,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
||||
: InkWell(
|
||||
onTap: () async {
|
||||
final url = _updateUrl + '.apk';
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
if (await canLaunchUrl(Uri.parse(url))) {
|
||||
await launchUrl(Uri.parse(url));
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
@ -275,7 +275,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
||||
? []
|
||||
: [
|
||||
PopupMenuItem<String>(
|
||||
child: Text(translate('File transfer')), value: 'file')
|
||||
child: Text(translate('Transfer File')), value: 'file')
|
||||
]),
|
||||
elevation: 8,
|
||||
);
|
||||
|
@ -35,6 +35,7 @@ class _RemotePageState extends State<RemotePage> {
|
||||
String _value = '';
|
||||
double _scale = 1;
|
||||
double _mouseScrollIntegral = 0; // mouse scroll speed controller
|
||||
Orientation? _currentOrientation;
|
||||
|
||||
var _more = true;
|
||||
var _fn = false;
|
||||
@ -127,7 +128,7 @@ class _RemotePageState extends State<RemotePage> {
|
||||
common < oldValue.length &&
|
||||
common < newValue.length &&
|
||||
newValue[common] == oldValue[common];
|
||||
++common);
|
||||
++common) {}
|
||||
for (i = 0; i < oldValue.length - common; ++i) {
|
||||
gFFI.inputKey('VK_BACK');
|
||||
}
|
||||
@ -260,12 +261,22 @@ class _RemotePageState extends State<RemotePage> {
|
||||
color: Colors.black,
|
||||
child: isWebDesktop
|
||||
? getBodyForDesktopWithListener(keyboard)
|
||||
: SafeArea(
|
||||
child: Container(
|
||||
: SafeArea(child:
|
||||
OrientationBuilder(builder: (ctx, orientation) {
|
||||
if (_currentOrientation != orientation) {
|
||||
debugPrint("on orientation changed");
|
||||
Timer(Duration(milliseconds: 200), () {
|
||||
resetMobileActionsOverlay();
|
||||
_currentOrientation = orientation;
|
||||
FFI.canvasModel.updateViewStyle();
|
||||
});
|
||||
}
|
||||
return Container(
|
||||
color: MyTheme.canvasColor,
|
||||
child: _isPhysicalMouse
|
||||
? getBodyForMobile()
|
||||
: getBodyForMobileWithGesture())));
|
||||
: getBodyForMobileWithGesture());
|
||||
})));
|
||||
})
|
||||
],
|
||||
))),
|
||||
|
@ -204,7 +204,7 @@ class _PermissionCheckerState extends State<PermissionChecker> {
|
||||
serverModel.toggleService),
|
||||
PermissionRow(translate("Input Control"), serverModel.inputOk,
|
||||
serverModel.toggleInput),
|
||||
PermissionRow(translate("File Transfer"), serverModel.fileOk,
|
||||
PermissionRow(translate("Transfer File"), serverModel.fileOk,
|
||||
serverModel.toggleFile),
|
||||
hasAudioPermission
|
||||
? PermissionRow(translate("Audio Capture"), serverModel.audioOk,
|
||||
|
@ -70,8 +70,8 @@ class _SettingsState extends State<SettingsPage> {
|
||||
tiles: [
|
||||
SettingsTile.navigation(
|
||||
onPressed: (context) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
if (await canLaunchUrl(Uri.parse(url))) {
|
||||
await launchUrl(Uri.parse(url));
|
||||
}
|
||||
},
|
||||
title: Text(translate("Version: ") + version),
|
||||
@ -107,8 +107,8 @@ void showAbout() {
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
const url = 'https://rustdesk.com/';
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
if (await canLaunchUrl(Uri.parse(url))) {
|
||||
await launchUrl(Uri.parse(url));
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
@ -151,7 +151,7 @@ fetch('http://localhost:21114/api/login', {
|
||||
'uuid': gFFI.getByName('uuid')
|
||||
};
|
||||
try {
|
||||
final response = await http.post(Uri.parse('${url}/api/login'),
|
||||
final response = await http.post(Uri.parse('$url/api/login'),
|
||||
headers: {"Content-Type": "application/json"}, body: json.encode(body));
|
||||
return parseResp(response.body);
|
||||
} catch (e) {
|
||||
@ -189,7 +189,7 @@ void refreshCurrentUser() async {
|
||||
'uuid': gFFI.getByName('uuid')
|
||||
};
|
||||
try {
|
||||
final response = await http.post(Uri.parse('${url}/api/currentUser'),
|
||||
final response = await http.post(Uri.parse('$url/api/currentUser'),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer $token"
|
||||
@ -215,7 +215,7 @@ void logout() async {
|
||||
'uuid': gFFI.getByName('uuid')
|
||||
};
|
||||
try {
|
||||
await http.post(Uri.parse('${url}/api/logout'),
|
||||
await http.post(Uri.parse('$url/api/logout'),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer $token"
|
||||
@ -245,7 +245,7 @@ String getUrl() {
|
||||
url = 'http://${tmp[0]}:$port';
|
||||
}
|
||||
} else {
|
||||
url = 'http://${url}:21114';
|
||||
url = 'http://$url:21114';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -594,10 +594,7 @@ class _TapTracker {
|
||||
required this.entry,
|
||||
required Duration doubleTapMinTime,
|
||||
required this.gestureSettings,
|
||||
}) : assert(doubleTapMinTime != null),
|
||||
assert(event != null),
|
||||
assert(event.buttons != null),
|
||||
pointer = event.pointer,
|
||||
}) : pointer = event.pointer,
|
||||
_initialGlobalPosition = event.position,
|
||||
initialButtons = event.buttons,
|
||||
_doubleTapMinTimeCountdown =
|
||||
@ -643,7 +640,7 @@ class _TapTracker {
|
||||
/// CountdownZoned tracks whether the specified duration has elapsed since
|
||||
/// creation, honoring [Zone].
|
||||
class _CountdownZoned {
|
||||
_CountdownZoned({required Duration duration}) : assert(duration != null) {
|
||||
_CountdownZoned({required Duration duration}) {
|
||||
Timer(duration, _onTimeout);
|
||||
}
|
||||
|
||||
|
@ -228,6 +228,12 @@ class DraggableMobileActions extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
resetMobileActionsOverlay() {
|
||||
if (mobileActionsOverlayEntry == null) return;
|
||||
hideMobileActionsOverlay();
|
||||
showMobileActionsOverlay();
|
||||
}
|
||||
|
||||
showMobileActionsOverlay() {
|
||||
if (mobileActionsOverlayEntry != null) return;
|
||||
if (globalKey.currentContext == null ||
|
||||
|
@ -621,15 +621,7 @@ class FileFetcher {
|
||||
|
||||
tryCompleteTask(String? msg, String? isLocalStr) {
|
||||
if (msg == null || isLocalStr == null) return;
|
||||
late final isLocal;
|
||||
late final tasks;
|
||||
if (isLocalStr == "true") {
|
||||
isLocal = true;
|
||||
} else if (isLocalStr == "false") {
|
||||
isLocal = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final fd = FileDirectory.fromJson(jsonDecode(msg));
|
||||
if (fd.id > 0) {
|
||||
|
@ -229,6 +229,7 @@ class FfiModel with ChangeNotifier {
|
||||
}
|
||||
|
||||
void handleSwitchDisplay(Map<String, dynamic> evt) {
|
||||
final oldOrientation = _display.width > _display.height;
|
||||
var old = _pi.currentDisplay;
|
||||
_pi.currentDisplay = int.parse(evt['display']);
|
||||
_display.x = double.parse(evt['x']);
|
||||
@ -237,6 +238,11 @@ class FfiModel with ChangeNotifier {
|
||||
_display.height = int.parse(evt['height']);
|
||||
if (old != _pi.currentDisplay)
|
||||
parent.target?.cursorModel.updateDisplayOrigin(_display.x, _display.y);
|
||||
|
||||
// remote is mobile, and orientation changed
|
||||
if ((_display.width > _display.height) != oldOrientation) {
|
||||
gFFI.canvasModel.updateViewStyle();
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,6 @@ class ServerModel with ChangeNotifier {
|
||||
* 2. check config
|
||||
* audio true by default (if permission on) (false default < Android 10)
|
||||
* file true by default (if permission on)
|
||||
* input false by default (it need turning on manually everytime)
|
||||
*/
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
// ignore_for_file: avoid_web_libraries_in_flutter
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:js';
|
||||
|
2
flutter/ndk_arm.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
cargo ndk --platform 21 --target armv7-linux-androideabi build --release
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers
|
||||
version: 1.1.10+27
|
||||
version: 1.1.10-1+28
|
||||
|
||||
environment:
|
||||
sdk: ">=2.16.1"
|
||||
|
@ -10,4 +10,8 @@ fn main() {
|
||||
enigo.key_down(Key::Layout('.')).ok();
|
||||
enigo.key_up(Key::Layout('.'));
|
||||
enigo.key_up(Key::Shift);
|
||||
enigo.key_down(Key::Shift).ok();
|
||||
enigo.key_down(Key::Layout('-')).ok();
|
||||
enigo.key_up(Key::Layout('-'));
|
||||
enigo.key_up(Key::Shift);
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ extern crate objc;
|
||||
mod win;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use win::Enigo;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use win::ENIGO_INPUT_EXTRA_VALUE;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
@ -491,7 +491,11 @@ fn start_pynput_service(rx: mpsc::Receiver<(PyMsg, bool)>) {
|
||||
if !std::path::Path::new(&py).exists() {
|
||||
py = "/usr/lib/rustdesk/pynput_service.py".to_owned();
|
||||
if !std::path::Path::new(&py).exists() {
|
||||
log::error!("{} not exits", py);
|
||||
// enigo libs, not rustdesk root project, so skip using appimage features
|
||||
py = std::env::var("APPDIR").unwrap_or("".to_string()) + "/usr/lib/rustdesk/pynput_service.py";
|
||||
if !std::path::Path::new(&py).exists() {
|
||||
log::error!("{} not exists", py);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -338,7 +338,7 @@ impl KeyboardControllable for Enigo {
|
||||
|
||||
fn key_click(&mut self, key: Key) {
|
||||
let keycode = self.key_to_keycode(key);
|
||||
if keycode == 0 {
|
||||
if keycode == u16::MAX {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -355,7 +355,7 @@ impl KeyboardControllable for Enigo {
|
||||
|
||||
fn key_down(&mut self, key: Key) -> crate::ResultType {
|
||||
let code = self.key_to_keycode(key);
|
||||
if code == 0 {
|
||||
if code == u16::MAX {
|
||||
return Err("".into());
|
||||
}
|
||||
if let Some(src) = self.event_source.as_ref() {
|
||||
@ -489,13 +489,18 @@ impl Enigo {
|
||||
Key::Layout(c) => self.map_key_board(c),
|
||||
|
||||
Key::Super | Key::Command | Key::Windows | Key::Meta => kVK_Command,
|
||||
_ => 0,
|
||||
_ => u16::MAX,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn map_key_board(&mut self, ch: char) -> CGKeyCode {
|
||||
let mut code = 0;
|
||||
// no idea why below char not working with shift, https://github.com/rustdesk/rustdesk/issues/406#issuecomment-1145157327
|
||||
// seems related to numpad char
|
||||
if ch == '-' || ch == '=' || ch == '.' || ch == '/' || (ch >= '0' && ch <= '9') {
|
||||
return self.map_key_board_en(ch);
|
||||
}
|
||||
let mut code = u16::MAX;
|
||||
unsafe {
|
||||
let (keyboard, layout) = get_layout();
|
||||
if !keyboard.is_null() && !layout.is_null() {
|
||||
@ -504,10 +509,10 @@ impl Enigo {
|
||||
let name = get_string(name_ref as _);
|
||||
if let Some(name) = name {
|
||||
if let Some(m) = self.char_to_vkey_map.get(&name) {
|
||||
code = *m.get(&ch).unwrap_or(&0);
|
||||
code = *m.get(&ch).unwrap_or(&u16::MAX);
|
||||
} else {
|
||||
let m = get_map(&name, layout);
|
||||
code = *m.get(&ch).unwrap_or(&0);
|
||||
code = *m.get(&ch).unwrap_or(&u16::MAX);
|
||||
self.char_to_vkey_map.insert(name.clone(), m);
|
||||
}
|
||||
}
|
||||
@ -517,9 +522,14 @@ impl Enigo {
|
||||
CFRelease(keyboard);
|
||||
}
|
||||
}
|
||||
if code > 0 {
|
||||
if code != u16::MAX {
|
||||
return code;
|
||||
}
|
||||
self.map_key_board_en(ch)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn map_key_board_en(&mut self, ch: char) -> CGKeyCode {
|
||||
match ch {
|
||||
'a' => kVK_ANSI_A,
|
||||
'b' => kVK_ANSI_B,
|
||||
@ -568,7 +578,7 @@ impl Enigo {
|
||||
'.' => kVK_ANSI_Period,
|
||||
'/' => kVK_ANSI_Slash,
|
||||
'`' => kVK_ANSI_Grave,
|
||||
_ => 0,
|
||||
_ => u16::MAX,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,3 +79,4 @@ pub const EVK_MULTIPLY: u16 = 0x6A;
|
||||
pub const EVK_SUBTRACT: u16 = 0x6D;
|
||||
pub const EVK_DECIMAL: u16 = 0x6E;
|
||||
pub const EVK_DIVIDE: u16 = 0x6F;
|
||||
pub const EVK_PERIOD: u16 = 0xBE;
|
||||
|
@ -2,3 +2,4 @@ mod win_impl;
|
||||
|
||||
pub mod keycodes;
|
||||
pub use self::win_impl::Enigo;
|
||||
pub use self::win_impl::ENIGO_INPUT_EXTRA_VALUE;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use winapi;
|
||||
|
||||
use self::winapi::ctypes::c_int;
|
||||
use self::winapi::shared::{minwindef::*, windef::*};
|
||||
use self::winapi::shared::{basetsd::ULONG_PTR, minwindef::*, windef::*};
|
||||
use self::winapi::um::winbase::*;
|
||||
use self::winapi::um::winuser::*;
|
||||
|
||||
@ -18,6 +18,9 @@ extern "system" {
|
||||
pub struct Enigo;
|
||||
static mut LAYOUT: HKL = std::ptr::null_mut();
|
||||
|
||||
/// The dwExtraInfo value in keyboard and mouse structure that used in SendInput()
|
||||
pub const ENIGO_INPUT_EXTRA_VALUE: ULONG_PTR = 100;
|
||||
|
||||
fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
|
||||
let mut input = INPUT {
|
||||
type_: INPUT_MOUSE,
|
||||
@ -28,7 +31,7 @@ fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
|
||||
mouseData: data,
|
||||
dwFlags: flags,
|
||||
time: 0,
|
||||
dwExtraInfo: 0,
|
||||
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
|
||||
})
|
||||
},
|
||||
};
|
||||
@ -56,7 +59,7 @@ fn keybd_event(flags: u32, vk: u16, scan: u16) -> DWORD {
|
||||
wScan: scan,
|
||||
dwFlags: flags,
|
||||
time: 0,
|
||||
dwExtraInfo: 0,
|
||||
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
|
||||
})
|
||||
},
|
||||
};
|
||||
@ -376,7 +379,7 @@ impl Enigo {
|
||||
let keycode_and_shiftstate = unsafe { VkKeyScanExW(chr as _, LAYOUT) };
|
||||
if keycode_and_shiftstate == (EVK_DECIMAL as i16) && chr == '.' {
|
||||
// a workaround of italian keyboard shift + '.' issue
|
||||
unsafe { VkKeyScanW(chr as _) as _ }
|
||||
EVK_PERIOD as _
|
||||
} else {
|
||||
keycode_and_shiftstate as _
|
||||
}
|
||||
|
@ -65,6 +65,10 @@ message LoginRequest {
|
||||
|
||||
message ChatMessage { string text = 1; }
|
||||
|
||||
message Features {
|
||||
bool privacy_mode = 1;
|
||||
}
|
||||
|
||||
message PeerInfo {
|
||||
string username = 1;
|
||||
string hostname = 2;
|
||||
@ -74,6 +78,7 @@ message PeerInfo {
|
||||
bool sas_enabled = 6;
|
||||
string version = 7;
|
||||
int32 conn_id = 8;
|
||||
Features features = 9;
|
||||
}
|
||||
|
||||
message LoginResponse {
|
||||
@ -442,11 +447,6 @@ message OptionMessage {
|
||||
BoolOption enable_file_transfer = 9;
|
||||
}
|
||||
|
||||
message OptionResponse {
|
||||
OptionMessage opt = 1;
|
||||
string error = 2;
|
||||
}
|
||||
|
||||
message TestDelay {
|
||||
int64 time = 1;
|
||||
bool from_client = 2;
|
||||
@ -469,6 +469,44 @@ message AudioFrame {
|
||||
int64 timestamp = 2;
|
||||
}
|
||||
|
||||
message BackNotification {
|
||||
// no need to consider block input by someone else
|
||||
enum BlockInputState {
|
||||
StateUnknown = 1;
|
||||
OnSucceeded = 2;
|
||||
OnFailed = 3;
|
||||
OffSucceeded = 4;
|
||||
OffFailed = 5;
|
||||
}
|
||||
enum PrivacyModeState {
|
||||
StateUnknown = 1;
|
||||
// Privacy mode on by someone else
|
||||
OnByOther = 2;
|
||||
// Privacy mode is not supported on the remote side
|
||||
NotSupported = 3;
|
||||
// Privacy mode on by self
|
||||
OnSucceeded = 4;
|
||||
// Privacy mode on by self, but denied
|
||||
OnFailedDenied = 5;
|
||||
// Some plugins are not found
|
||||
OnFailedPlugin = 6;
|
||||
// Privacy mode on by self, but failed
|
||||
OnFailed = 7;
|
||||
// Privacy mode off by self
|
||||
OffSucceeded = 8;
|
||||
// Ctrl + P
|
||||
OffByPeer = 9;
|
||||
// Privacy mode off by self, but failed
|
||||
OffFailed = 10;
|
||||
OffUnknown = 11;
|
||||
}
|
||||
|
||||
oneof union {
|
||||
PrivacyModeState privacy_mode_state = 1;
|
||||
BlockInputState block_input_state = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message Misc {
|
||||
oneof union {
|
||||
ChatMessage chat_message = 4;
|
||||
@ -478,8 +516,8 @@ message Misc {
|
||||
AudioFormat audio_format = 8;
|
||||
string close_reason = 9;
|
||||
bool refresh_video = 10;
|
||||
OptionResponse option_response = 11;
|
||||
bool video_received = 12;
|
||||
BackNotification back_notification = 13;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ pub const RENDEZVOUS_TIMEOUT: u64 = 12_000;
|
||||
pub const CONNECT_TIMEOUT: u64 = 18_000;
|
||||
pub const REG_INTERVAL: i64 = 12_000;
|
||||
pub const COMPRESS_LEVEL: i32 = 3;
|
||||
const SERIAL: i32 = 1;
|
||||
const SERIAL: i32 = 3;
|
||||
// 128x128
|
||||
#[cfg(target_os = "macos")] // 128x128 on 160x160 canvas, then shrink to 128, mac looks better with padding
|
||||
pub const ICON: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAyVBMVEUAAAAAcf8Acf8Acf8Acv8Acf8Acf8Acf8Acf8AcP8Acf8Ab/8AcP8Acf////8AaP/z+f/o8v/k7v/5/v/T5f8AYP/u9v/X6f+hx/+Kuv95pP8Aef/B1/+TwP9xoP8BdP/g6P+Irv9ZmP8Bgf/E3f98q/9sn/+01f+Es/9nm/9Jif8hhv8off/M4P+syP+avP86iP/c7f+xy/9yqf9Om/9hk/9Rjv+60P99tv9fpf88lv8yjf8Tgf8deP+kvP8BiP8NeP8hkP80gP8oj2VLAAAADXRSTlMA7o7qLvnaxZ1FOxYPjH9HWgAABHJJREFUeNrtm+tW4jAQgBfwuu7MtIUWsOUiCCioIIgLiqvr+z/UHq/LJKVkmwTcc/r9E2nzlU4mSTP9lpGRkZGR8VX5cZjfL+yCEXYL+/nDH//U/Pd8DgyTy39Xbv7oIAcWyB0cqbW/sweW2NtRaj8H1sgpGOwUIAH7Bkd7YJW9dXFwAJY5WNP/cmCZQnJvzIN18on5LwfWySXlxEPYAIcad8D6PdiHDbCfIFCADVBIENiFDbCbIACKPPXrZ+cP8E6/0znvP4EymgIEravIRcTxu8HxNSJ60a8W0AYECKrlAN+YwAthCd9wm1Ug6wKzIn5SgRduXfwkqDasCjx0XFzi9PV6zwNcIuhcWBOg+ikySq8C9UD4dEKWBCoOcspvAuLHTo9sCDQiFPHotRM48j8G5gVur1FdAN2uaYEuiz7xFsgEJ2RUoMUakXuBTHHoGxQYOBhHjeUBAefEnMAowFhaLBOKuOemBBbxLRQrH2PBCgMvNCPQGMeevTb9zLrPxz2Mo+QbEaijzPUcOOHMQZkKGRAIPem39+bypREMPTkQW/oCfk866zAkiIFG4yIKRE/aAnfiSd0WrORY6pFdXQEqi9mvAQm0RIOSnoCcZ8vJoz3diCnjRk+g8VP4/fuQDJ2Lxr6WwG0gXs9aTpDzW0vgDBlVUpixR8gYk44AD8FrUKHr8JQJGgIDnoDqoALxmWPQSi9AVVzm8gKUuEPGr/QCvptwJkbSYT/TC4S8C96DGjTj86aHtAI0x2WaBIq0eSYYpRa4EsdWVVwWu9O0Aj6f6dyBMnwEraeOgSYu0wZlauzA47QCbT7DgAQSE+hZWoEBF/BBmWOewNMK3BsSqKUW4MGcWqCSVmDkbvkXGKQOwg6PAUO9oL3xXhA20yaiCjuwYygRVQlUOTWTCf2SuNJTxeFjgaHByGuAIvd8ItdPLTDhS7IuqEE1YSKVOgbayLhSFQhMzYh8hwfBs1r7c505YVIQYEdNoKwxK06MJiyrpUFHiF0NAfCQUVHoiRclIXJIR6C2fqG37pBHvcWpgwzvAtYwkR5UGV2e42UISdBJETl3mg8ouo54Rcnti1/vaT+iuUQBt500Cgo4U10BeHSkk57FB0JjWkKRMWgLUA0lLodtImAQdaMiiri3+gIAPZQoutHNsgKF1aaDMhMyIdBf8Th+Bh8MTjGWCpl5Wv43tDmnF+IUVMrcZgRoiAxhtrloYizNkZaAnF5leglbNhj0wYCAbCDvGb0mP4nib7O7ZlcYQ2m1gPtIZgVgGNNMeaVAaWR+57TrqgtUnm3sHQ+kYeE6fufUubG1ez50FXbPnWgBlgSABmN3TTcsRl2yWkHRrwbiunvk/W2+Mg1hPZplPDeXRbZzStFH15s1QIVd3UImP5z/bHpeeQLvRJ7XLFUffQIlCvqlXETQbgN9/rlYABGosv+Vi9m2Xs639YLGrZd0br+odetlvdsvbN56abfd4vbCzv9Q3v/ygoOV21A4OPpfXvH4Ai+5ZGRkZGRkbJA/t/I0QMzoMiEAAAAASUVORK5CYII=
|
||||
@ -271,7 +271,7 @@ impl Config {
|
||||
|
||||
fn file_(suffix: &str) -> PathBuf {
|
||||
let name = format!("{}{}", *APP_NAME.read().unwrap(), suffix);
|
||||
Self::path(name).with_extension("toml")
|
||||
Config::with_extension(Self::path(name))
|
||||
}
|
||||
|
||||
pub fn get_home() -> PathBuf {
|
||||
@ -687,6 +687,16 @@ impl Config {
|
||||
lock.store();
|
||||
true
|
||||
}
|
||||
|
||||
fn with_extension(path: PathBuf) -> PathBuf {
|
||||
let ext = path.extension();
|
||||
if let Some(ext) = ext {
|
||||
let ext = format!("{}.toml", ext.to_string_lossy());
|
||||
path.with_extension(&ext)
|
||||
} else {
|
||||
path.with_extension("toml")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PEERS: &str = "peers";
|
||||
@ -716,7 +726,7 @@ impl PeerConfig {
|
||||
|
||||
fn path(id: &str) -> PathBuf {
|
||||
let path: PathBuf = [PEERS, id].iter().collect();
|
||||
Config::path(path).with_extension("toml")
|
||||
Config::with_extension(Config::path(path))
|
||||
}
|
||||
|
||||
pub fn peers() -> Vec<(String, SystemTime, PeerConfig)> {
|
||||
|
@ -195,7 +195,7 @@ pub fn is_file_exists(file_path: &str) -> bool {
|
||||
|
||||
#[inline]
|
||||
pub fn can_enable_overwrite_detection(version: i64) -> bool {
|
||||
version >= get_version_number("1.2.0")
|
||||
version >= get_version_number("1.1.10")
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -11,7 +11,10 @@ use tokio_socks::{IntoTargetAddr, TargetAddr};
|
||||
|
||||
fn to_socket_addr(host: &str) -> ResultType<SocketAddr> {
|
||||
use std::net::ToSocketAddrs;
|
||||
host.to_socket_addrs()?.next().context("Failed to solve")
|
||||
host.to_socket_addrs()?
|
||||
.filter(|x| x.is_ipv4())
|
||||
.next()
|
||||
.context("Failed to solve")
|
||||
}
|
||||
|
||||
pub fn get_target_addr(host: &str) -> ResultType<TargetAddr<'static>> {
|
||||
@ -60,8 +63,9 @@ pub async fn connect_tcp<'t, T: IntoTargetAddr<'t>>(
|
||||
.await
|
||||
} else {
|
||||
let addr = std::net::ToSocketAddrs::to_socket_addrs(&target_addr)?
|
||||
.filter(|x| x.is_ipv4())
|
||||
.next()
|
||||
.context("Invalid target addr")?;
|
||||
.context("Invalid target addr, no valid ipv4 address can be resolved.")?;
|
||||
Ok(FramedStream::new(addr, local, ms_timeout).await?)
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ impl FramedSocket {
|
||||
|
||||
#[allow(clippy::never_loop)]
|
||||
pub async fn new_reuse<T: std::net::ToSocketAddrs>(addr: T) -> ResultType<Self> {
|
||||
for addr in addr.to_socket_addrs()? {
|
||||
for addr in addr.to_socket_addrs()?.filter(|x| x.is_ipv4()) {
|
||||
let socket = new_socket(addr, true, 0)?.into_udp_socket();
|
||||
return Ok(Self::Direct(UdpFramed::new(
|
||||
UdpSocket::from_std(socket)?,
|
||||
@ -61,7 +61,7 @@ impl FramedSocket {
|
||||
addr: T,
|
||||
buf_size: usize,
|
||||
) -> ResultType<Self> {
|
||||
for addr in addr.to_socket_addrs()? {
|
||||
for addr in addr.to_socket_addrs()?.filter(|x| x.is_ipv4()) {
|
||||
return Ok(Self::Direct(UdpFramed::new(
|
||||
UdpSocket::from_std(new_socket(addr, false, buf_size)?.into_udp_socket())?,
|
||||
BytesCodec::new(),
|
||||
|
@ -17,17 +17,19 @@ block = "0.1"
|
||||
cfg-if = "1.0"
|
||||
libc = "0.2"
|
||||
num_cpus = "1.13"
|
||||
lazy_static = "1.4"
|
||||
|
||||
[dependencies.winapi]
|
||||
version = "0.3"
|
||||
default-features = true
|
||||
features = ["dxgi", "dxgi1_2", "dxgi1_5", "d3d11", "winuser"]
|
||||
features = ["dxgi", "dxgi1_2", "dxgi1_5", "d3d11", "winuser", "winerror", "errhandlingapi", "libloaderapi"]
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_logger = "0.10"
|
||||
jni = "0.19"
|
||||
lazy_static = "1.4"
|
||||
log = "0.4"
|
||||
serde_json = "1.0"
|
||||
|
||||
[target.'cfg(not(target_os = "android"))'.dev-dependencies]
|
||||
repng = "0.2"
|
||||
|
105
libs/scrap/examples/capture_mag.rs
Normal file
@ -0,0 +1,105 @@
|
||||
extern crate repng;
|
||||
extern crate scrap;
|
||||
|
||||
use std::fs::File;
|
||||
|
||||
#[cfg(windows)]
|
||||
use scrap::CapturerMag;
|
||||
use scrap::{i420_to_rgb, Display};
|
||||
|
||||
fn main() {
|
||||
let n = Display::all().unwrap().len();
|
||||
for i in 0..n {
|
||||
#[cfg(windows)]
|
||||
record(i);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_display(i: usize) -> Display {
|
||||
Display::all().unwrap().remove(i)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn record(i: usize) {
|
||||
for d in Display::all().unwrap() {
|
||||
println!("{:?} {} {}", d.origin(), d.width(), d.height());
|
||||
}
|
||||
|
||||
let display = get_display(i);
|
||||
let (w, h) = (display.width(), display.height());
|
||||
|
||||
{
|
||||
let mut capture_mag =
|
||||
CapturerMag::new(display.origin(), display.width(), display.height(), false)
|
||||
.expect("Couldn't begin capture.");
|
||||
let wnd_cls = "";
|
||||
let wnd_name = "RustDeskPrivacyWindow";
|
||||
if false == capture_mag.exclude(wnd_cls, wnd_name).unwrap() {
|
||||
println!("No window found for cls {} name {}", wnd_cls, wnd_name);
|
||||
} else {
|
||||
println!("Filter window for cls {} name {}", wnd_cls, wnd_name);
|
||||
}
|
||||
|
||||
let frame = capture_mag.frame(0).unwrap();
|
||||
println!("Capture data len: {}, Saving...", frame.len());
|
||||
|
||||
let mut bitflipped = Vec::with_capacity(w * h * 4);
|
||||
let stride = frame.len() / h;
|
||||
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let i = stride * y + 4 * x;
|
||||
bitflipped.extend_from_slice(&[frame[i + 2], frame[i + 1], frame[i], 255]);
|
||||
}
|
||||
}
|
||||
// Save the image.
|
||||
let name = format!("capture_mag_{}_1.png", i);
|
||||
repng::encode(
|
||||
File::create(name.clone()).unwrap(),
|
||||
w as u32,
|
||||
h as u32,
|
||||
&bitflipped,
|
||||
)
|
||||
.unwrap();
|
||||
println!("Image saved to `{}`.", name);
|
||||
}
|
||||
|
||||
{
|
||||
let mut capture_mag =
|
||||
CapturerMag::new(display.origin(), display.width(), display.height(), true)
|
||||
.expect("Couldn't begin capture.");
|
||||
let wnd_cls = "";
|
||||
let wnd_title = "RustDeskPrivacyWindow";
|
||||
if false == capture_mag.exclude(wnd_cls, wnd_title).unwrap() {
|
||||
println!("No window found for cls {} title {}", wnd_cls, wnd_title);
|
||||
} else {
|
||||
println!("Filter window for cls {} title {}", wnd_cls, wnd_title);
|
||||
}
|
||||
|
||||
let buffer = capture_mag.frame(0).unwrap();
|
||||
println!("Capture data len: {}, Saving...", buffer.len());
|
||||
|
||||
let mut frame = Default::default();
|
||||
i420_to_rgb(w, h, &buffer, &mut frame);
|
||||
|
||||
let mut bitflipped = Vec::with_capacity(w * h * 4);
|
||||
let stride = frame.len() / h;
|
||||
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let i = stride * y + 3 * x;
|
||||
bitflipped.extend_from_slice(&[frame[i], frame[i + 1], frame[i + 2], 255]);
|
||||
}
|
||||
}
|
||||
let name = format!("capture_mag_{}_2.png", i);
|
||||
repng::encode(
|
||||
File::create(name.clone()).unwrap(),
|
||||
w as u32,
|
||||
h as u32,
|
||||
&bitflipped,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("Image saved to `{}`.", name);
|
||||
}
|
||||
}
|
@ -46,8 +46,7 @@ fn record(i: usize) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
println!("Captured! Saving...");
|
||||
println!("Captured data len: {}, Saving...", buffer.len());
|
||||
|
||||
// Flip the BGRA image into a RGBA image.
|
||||
|
||||
@ -96,8 +95,7 @@ fn record(i: usize) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
println!("Captured! Saving...");
|
||||
println!("Captured data len: {}, Saving...", buffer.len());
|
||||
|
||||
let mut frame = Default::default();
|
||||
i420_to_rgb(w, h, &buffer, &mut frame);
|
||||
|
@ -17,7 +17,6 @@ use std::time::{Duration, Instant};
|
||||
lazy_static! {
|
||||
static ref JVM: RwLock<Option<JavaVM>> = RwLock::new(None);
|
||||
static ref MAIN_SERVICE_CTX: RwLock<Option<GlobalRef>> = RwLock::new(None); // MainService -> video service / audio service / info
|
||||
static ref INPUT_CTX: RwLock<Option<GlobalRef>> = RwLock::new(None);
|
||||
static ref VIDEO_RAW: Mutex<FrameRaw> = Mutex::new(FrameRaw::new("video", MAX_VIDEO_FRAME_TIMEOUT));
|
||||
static ref AUDIO_RAW: Mutex<FrameRaw> = Mutex::new(FrameRaw::new("audio", MAX_AUDIO_FRAME_TIMEOUT));
|
||||
}
|
||||
@ -148,25 +147,10 @@ pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_init(
|
||||
*MAIN_SERVICE_CTX.write().unwrap() = Some(context);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_carriez_flutter_1hbb_InputService_init(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
ctx: JObject,
|
||||
) {
|
||||
log::debug!("InputService init from java");
|
||||
let jvm = env.get_java_vm().unwrap();
|
||||
|
||||
*JVM.write().unwrap() = Some(jvm);
|
||||
|
||||
let context = env.new_global_ref(ctx).unwrap();
|
||||
*INPUT_CTX.write().unwrap() = Some(context);
|
||||
}
|
||||
|
||||
pub fn call_input_service_mouse_input(mask: i32, x: i32, y: i32) -> JniResult<()> {
|
||||
pub fn call_main_service_mouse_input(mask: i32, x: i32, y: i32) -> JniResult<()> {
|
||||
if let (Some(jvm), Some(ctx)) = (
|
||||
JVM.read().unwrap().as_ref(),
|
||||
INPUT_CTX.read().unwrap().as_ref(),
|
||||
MAIN_SERVICE_CTX.read().unwrap().as_ref(),
|
||||
) {
|
||||
let env = jvm.attach_current_thread_as_daemon()?;
|
||||
env.call_method(
|
||||
|
@ -1,11 +1,13 @@
|
||||
use crate::android::ffi::*;
|
||||
use crate::rgba_to_i420;
|
||||
use lazy_static::lazy_static;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::sync::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
static ref SCREEN_SIZE: Mutex<(u16, u16)> = Mutex::new((0, 0));
|
||||
static ref SCREEN_SIZE: Mutex<(u16, u16, u16)> = Mutex::new((0, 0, 0)); // (width, height, scale)
|
||||
}
|
||||
|
||||
pub struct Capturer {
|
||||
@ -65,9 +67,7 @@ impl Display {
|
||||
pub fn primary() -> io::Result<Display> {
|
||||
let mut size = SCREEN_SIZE.lock().unwrap();
|
||||
if size.0 == 0 || size.1 == 0 {
|
||||
let (w, h) = get_size().unwrap_or((0, 0));
|
||||
size.0 = w;
|
||||
size.1 = h;
|
||||
*size = get_size().unwrap_or_default();
|
||||
}
|
||||
Ok(Display {
|
||||
default: true,
|
||||
@ -111,19 +111,33 @@ impl Display {
|
||||
|
||||
pub fn refresh_size() {
|
||||
let mut size = SCREEN_SIZE.lock().unwrap();
|
||||
let (w, h) = get_size().unwrap_or((0, 0));
|
||||
size.0 = w;
|
||||
size.1 = h;
|
||||
*size = get_size().unwrap_or_default();
|
||||
}
|
||||
|
||||
// Big android screen size will be shrinked, to improve performance when screen-capturing and encoding
|
||||
// e.g 2280x1080 size will be set to 1140x540, and `scale` is 2
|
||||
// need to multiply by `4` (2*2) when compute the bitrate
|
||||
pub fn fix_quality() -> u16 {
|
||||
let scale = SCREEN_SIZE.lock().unwrap().2;
|
||||
if scale <= 0 {
|
||||
1
|
||||
} else {
|
||||
scale * scale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_size() -> Option<(u16, u16)> {
|
||||
fn get_size() -> Option<(u16, u16, u16)> {
|
||||
let res = call_main_service_get_by_name("screen_size").ok()?;
|
||||
if res.len() > 0 {
|
||||
let mut sp = res.split(":");
|
||||
let w = sp.next()?.parse::<u16>().ok()?;
|
||||
let h = sp.next()?.parse::<u16>().ok()?;
|
||||
return Some((w, h));
|
||||
if let Ok(json) = serde_json::from_str::<HashMap<String, Value>>(&res) {
|
||||
if let (Some(Value::Number(w)), Some(Value::Number(h)), Some(Value::Number(scale))) =
|
||||
(json.get("width"), json.get("height"), json.get("scale"))
|
||||
{
|
||||
let w = w.as_i64()? as _;
|
||||
let h = h.as_i64()? as _;
|
||||
let scale = scale.as_i64()? as _;
|
||||
return Some((w, h, scale));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -111,3 +111,32 @@ impl Display {
|
||||
self.origin() == (0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CapturerMag {
|
||||
inner: dxgi::mag::CapturerMag,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CapturerMag {
|
||||
pub fn is_supported() -> bool {
|
||||
dxgi::mag::CapturerMag::is_supported()
|
||||
}
|
||||
|
||||
pub fn new(origin: (i32, i32), width: usize, height: usize, use_yuv: bool) -> io::Result<Self> {
|
||||
Ok(CapturerMag {
|
||||
inner: dxgi::mag::CapturerMag::new(origin, width, height, use_yuv)?,
|
||||
data: Vec::new(),
|
||||
})
|
||||
}
|
||||
pub fn exclude(&mut self, cls: &str, name: &str) -> io::Result<bool> {
|
||||
self.inner.exclude(cls, name)
|
||||
}
|
||||
// ((x, y), w, h)
|
||||
pub fn get_rect(&self) -> ((i32, i32), usize, usize) {
|
||||
self.inner.get_rect()
|
||||
}
|
||||
pub fn frame<'a>(&'a mut self, _timeout_ms: u32) -> io::Result<Frame<'a>> {
|
||||
self.inner.frame(&mut self.data)?;
|
||||
Ok(Frame(&self.data))
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ cfg_if! {
|
||||
} else if #[cfg(dxgi)] {
|
||||
mod dxgi;
|
||||
pub use self::dxgi::*;
|
||||
} else if #[cfg(android)] {
|
||||
} else if #[cfg(target_os = "android")] {
|
||||
mod android;
|
||||
pub use self::android::*;
|
||||
}else {
|
||||
@ -36,9 +36,7 @@ mod vpx;
|
||||
|
||||
#[inline]
|
||||
pub fn would_block_if_equal(old: &mut Vec<u128>, b: &[u8]) -> std::io::Result<()> {
|
||||
let b = unsafe {
|
||||
std::slice::from_raw_parts::<u128>(b.as_ptr() as _, b.len() / 16)
|
||||
};
|
||||
let b = unsafe { std::slice::from_raw_parts::<u128>(b.as_ptr() as _, b.len() / 16) };
|
||||
if b == &old[..] {
|
||||
return Err(std::io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
|
662
libs/scrap/src/dxgi/mag.rs
Normal file
@ -0,0 +1,662 @@
|
||||
// logic from webrtc -- https://github.com/shiguredo/libwebrtc/blob/main/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
|
||||
use lazy_static;
|
||||
use std::{
|
||||
ffi::CString,
|
||||
io::{Error, ErrorKind, Result},
|
||||
mem::size_of,
|
||||
sync::Mutex,
|
||||
};
|
||||
use winapi::{
|
||||
shared::{
|
||||
basetsd::SIZE_T,
|
||||
guiddef::{IsEqualGUID, GUID},
|
||||
minwindef::{BOOL, DWORD, FALSE, FARPROC, HINSTANCE, HMODULE, HRGN, TRUE, UINT},
|
||||
ntdef::{LONG, NULL},
|
||||
windef::{HWND, RECT},
|
||||
winerror::ERROR_CLASS_ALREADY_EXISTS,
|
||||
},
|
||||
um::{
|
||||
errhandlingapi::GetLastError,
|
||||
libloaderapi::{FreeLibrary, GetModuleHandleExA, GetProcAddress, LoadLibraryExA},
|
||||
winuser::*,
|
||||
},
|
||||
};
|
||||
|
||||
pub const MW_FILTERMODE_EXCLUDE: u32 = 0;
|
||||
pub const MW_FILTERMODE_INCLUDE: u32 = 1;
|
||||
pub const GET_MODULE_HANDLE_EX_FLAG_PIN: u32 = 1;
|
||||
pub const GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT: u32 = 2;
|
||||
pub const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 4;
|
||||
pub const LOAD_LIBRARY_AS_DATAFILE: u32 = 2;
|
||||
pub const LOAD_WITH_ALTERED_SEARCH_PATH: u32 = 8;
|
||||
pub const LOAD_IGNORE_CODE_AUTHZ_LEVEL: u32 = 16;
|
||||
pub const LOAD_LIBRARY_AS_IMAGE_RESOURCE: u32 = 32;
|
||||
pub const LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE: u32 = 64;
|
||||
pub const LOAD_LIBRARY_REQUIRE_SIGNED_TARGET: u32 = 128;
|
||||
pub const LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR: u32 = 256;
|
||||
pub const LOAD_LIBRARY_SEARCH_APPLICATION_DIR: u32 = 512;
|
||||
pub const LOAD_LIBRARY_SEARCH_USER_DIRS: u32 = 1024;
|
||||
pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 2048;
|
||||
pub const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: u32 = 4096;
|
||||
pub const LOAD_LIBRARY_SAFE_CURRENT_DIRS: u32 = 8192;
|
||||
pub const LOAD_LIBRARY_SEARCH_SYSTEM32_NO_FORWARDER: u32 = 16384;
|
||||
pub const LOAD_LIBRARY_OS_INTEGRITY_CONTINUITY: u32 = 32768;
|
||||
|
||||
extern "C" {
|
||||
pub static GUID_WICPixelFormat32bppRGBA: GUID;
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref MAG_BUFFER: Mutex<(bool, Vec<u8>)> = Default::default();
|
||||
}
|
||||
|
||||
pub type REFWICPixelFormatGUID = *const GUID;
|
||||
pub type WICPixelFormatGUID = GUID;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct tagMAGIMAGEHEADER {
|
||||
pub width: UINT,
|
||||
pub height: UINT,
|
||||
pub format: WICPixelFormatGUID,
|
||||
pub stride: UINT,
|
||||
pub offset: UINT,
|
||||
pub cbSize: SIZE_T,
|
||||
}
|
||||
pub type MAGIMAGEHEADER = tagMAGIMAGEHEADER;
|
||||
pub type PMAGIMAGEHEADER = *mut tagMAGIMAGEHEADER;
|
||||
|
||||
// Function types
|
||||
pub type MagImageScalingCallback = ::std::option::Option<
|
||||
unsafe extern "C" fn(
|
||||
hwnd: HWND,
|
||||
srcdata: *mut ::std::os::raw::c_void,
|
||||
srcheader: MAGIMAGEHEADER,
|
||||
destdata: *mut ::std::os::raw::c_void,
|
||||
destheader: MAGIMAGEHEADER,
|
||||
unclipped: RECT,
|
||||
clipped: RECT,
|
||||
dirty: HRGN,
|
||||
) -> BOOL,
|
||||
>;
|
||||
|
||||
extern "C" {
|
||||
pub fn MagShowSystemCursor(fShowCursor: BOOL) -> BOOL;
|
||||
}
|
||||
pub type MagInitializeFunc = ::std::option::Option<unsafe extern "C" fn() -> BOOL>;
|
||||
pub type MagUninitializeFunc = ::std::option::Option<unsafe extern "C" fn() -> BOOL>;
|
||||
pub type MagSetWindowSourceFunc =
|
||||
::std::option::Option<unsafe extern "C" fn(hwnd: HWND, rect: RECT) -> BOOL>;
|
||||
pub type MagSetWindowFilterListFunc = ::std::option::Option<
|
||||
unsafe extern "C" fn(
|
||||
hwnd: HWND,
|
||||
dwFilterMode: DWORD,
|
||||
count: ::std::os::raw::c_int,
|
||||
pHWND: *mut HWND,
|
||||
) -> BOOL,
|
||||
>;
|
||||
pub type MagSetImageScalingCallbackFunc = ::std::option::Option<
|
||||
unsafe extern "C" fn(hwnd: HWND, callback: MagImageScalingCallback) -> BOOL,
|
||||
>;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct MagInterface {
|
||||
init_succeeded: bool,
|
||||
lib_handle: HINSTANCE,
|
||||
pub mag_initialize_func: MagInitializeFunc,
|
||||
pub mag_uninitialize_func: MagUninitializeFunc,
|
||||
pub set_window_source_func: MagSetWindowSourceFunc,
|
||||
pub set_window_filter_list_func: MagSetWindowFilterListFunc,
|
||||
pub set_image_scaling_callback_func: MagSetImageScalingCallbackFunc,
|
||||
}
|
||||
|
||||
// NOTE: MagInitialize and MagUninitialize should not be called in global init and uninit.
|
||||
// If so, strange errors occur.
|
||||
impl MagInterface {
|
||||
fn new() -> Result<Self> {
|
||||
let mut s = MagInterface {
|
||||
init_succeeded: false,
|
||||
lib_handle: NULL as _,
|
||||
mag_initialize_func: None,
|
||||
mag_uninitialize_func: None,
|
||||
set_window_source_func: None,
|
||||
set_window_filter_list_func: None,
|
||||
set_image_scaling_callback_func: None,
|
||||
};
|
||||
s.init_succeeded = false;
|
||||
unsafe {
|
||||
if GetSystemMetrics(SM_CMONITORS) != 1 {
|
||||
// Do not try to use the magnifier in multi-screen setup (where the API
|
||||
// crashes sometimes).
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
"Magnifier capturer cannot work on multi-screen system.",
|
||||
));
|
||||
}
|
||||
|
||||
// load lib
|
||||
let lib_file_name = "Magnification.dll";
|
||||
let lib_file_name_c = CString::new(lib_file_name).unwrap();
|
||||
s.lib_handle = LoadLibraryExA(
|
||||
lib_file_name_c.as_ptr() as _,
|
||||
NULL,
|
||||
LOAD_WITH_ALTERED_SEARCH_PATH,
|
||||
);
|
||||
if s.lib_handle.is_null() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"Failed to LoadLibraryExA {}, error: {}",
|
||||
lib_file_name,
|
||||
GetLastError()
|
||||
),
|
||||
));
|
||||
};
|
||||
|
||||
// load functions
|
||||
s.mag_initialize_func = Some(std::mem::transmute(Self::load_func(
|
||||
s.lib_handle,
|
||||
"MagInitialize",
|
||||
)?));
|
||||
s.mag_uninitialize_func = Some(std::mem::transmute(Self::load_func(
|
||||
s.lib_handle,
|
||||
"MagUninitialize",
|
||||
)?));
|
||||
s.set_window_source_func = Some(std::mem::transmute(Self::load_func(
|
||||
s.lib_handle,
|
||||
"MagSetWindowSource",
|
||||
)?));
|
||||
s.set_window_filter_list_func = Some(std::mem::transmute(Self::load_func(
|
||||
s.lib_handle,
|
||||
"MagSetWindowFilterList",
|
||||
)?));
|
||||
s.set_image_scaling_callback_func = Some(std::mem::transmute(Self::load_func(
|
||||
s.lib_handle,
|
||||
"MagSetImageScalingCallback",
|
||||
)?));
|
||||
|
||||
// MagInitialize
|
||||
if let Some(init_func) = s.mag_initialize_func {
|
||||
if FALSE == init_func() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Failed to MagInitialize, error: {}", GetLastError()),
|
||||
));
|
||||
} else {
|
||||
s.init_succeeded = true;
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
"Unreachable, mag_initialize_func should not be none",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
unsafe fn load_func(lib_module: HMODULE, func_name: &str) -> Result<FARPROC> {
|
||||
let func_name_c = CString::new(func_name).unwrap();
|
||||
let func = GetProcAddress(lib_module, func_name_c.as_ptr() as _);
|
||||
if func.is_null() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"Failed to GetProcAddress {}, error: {}",
|
||||
func_name,
|
||||
GetLastError()
|
||||
),
|
||||
));
|
||||
}
|
||||
Ok(func)
|
||||
}
|
||||
|
||||
pub(super) fn uninit(&mut self) {
|
||||
if self.init_succeeded {
|
||||
if let Some(uninit_func) = self.mag_uninitialize_func {
|
||||
unsafe {
|
||||
if FALSE == uninit_func() {
|
||||
println!("Failed MagUninitialize {}", GetLastError())
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.lib_handle.is_null() {
|
||||
unsafe {
|
||||
if FALSE == FreeLibrary(self.lib_handle) {
|
||||
println!("Failed FreeLibrary {}", GetLastError())
|
||||
}
|
||||
}
|
||||
self.lib_handle = NULL as _;
|
||||
}
|
||||
}
|
||||
self.init_succeeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MagInterface {
|
||||
fn drop(&mut self) {
|
||||
self.uninit();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CapturerMag {
|
||||
mag_interface: MagInterface,
|
||||
host_window: HWND,
|
||||
magnifier_window: HWND,
|
||||
|
||||
magnifier_host_class: CString,
|
||||
host_window_name: CString,
|
||||
magnifier_window_class: CString,
|
||||
magnifier_window_name: CString,
|
||||
|
||||
rect: RECT,
|
||||
width: usize,
|
||||
height: usize,
|
||||
|
||||
use_yuv: bool,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Drop for CapturerMag {
|
||||
fn drop(&mut self) {
|
||||
self.destroy_windows();
|
||||
self.mag_interface.uninit();
|
||||
}
|
||||
}
|
||||
|
||||
impl CapturerMag {
|
||||
pub(crate) fn is_supported() -> bool {
|
||||
MagInterface::new().is_ok()
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
origin: (i32, i32),
|
||||
width: usize,
|
||||
height: usize,
|
||||
use_yuv: bool,
|
||||
) -> Result<Self> {
|
||||
unsafe {
|
||||
let x = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||
let y = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
||||
let w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
let h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
if !(origin.0 == x as _ && origin.1 == y as _ && width == w as _ && height == h as _) {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"Failed Check screen rect ({}, {}, {} , {}) to ({}, {}, {}, {})",
|
||||
origin.0,
|
||||
origin.1,
|
||||
origin.0 + width as i32,
|
||||
origin.1 + height as i32,
|
||||
x,
|
||||
y,
|
||||
x + w,
|
||||
y + h
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let mut s = Self {
|
||||
mag_interface: MagInterface::new()?,
|
||||
host_window: 0 as _,
|
||||
magnifier_window: 0 as _,
|
||||
magnifier_host_class: CString::new("ScreenCapturerWinMagnifierHost")?,
|
||||
host_window_name: CString::new("MagnifierHost")?,
|
||||
magnifier_window_class: CString::new("Magnifier")?,
|
||||
magnifier_window_name: CString::new("MagnifierWindow")?,
|
||||
rect: RECT {
|
||||
left: origin.0 as _,
|
||||
top: origin.1 as _,
|
||||
right: origin.0 + width as LONG,
|
||||
bottom: origin.1 + height as LONG,
|
||||
},
|
||||
width,
|
||||
height,
|
||||
use_yuv,
|
||||
data: Vec::new(),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut instance = 0 as HMODULE;
|
||||
if 0 == GetModuleHandleExA(
|
||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
|
||||
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
DefWindowProcA as _,
|
||||
&mut instance as _,
|
||||
) {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Failed to GetModuleHandleExA, error: {}", GetLastError()),
|
||||
));
|
||||
}
|
||||
|
||||
// Register the host window class. See the MSDN documentation of the
|
||||
// Magnification API for more infomation.
|
||||
let wcex = WNDCLASSEXA {
|
||||
cbSize: size_of::<WNDCLASSEXA>() as _,
|
||||
style: 0,
|
||||
lpfnWndProc: Some(DefWindowProcA),
|
||||
cbClsExtra: 0,
|
||||
cbWndExtra: 0,
|
||||
hInstance: instance,
|
||||
hIcon: 0 as _,
|
||||
hCursor: LoadCursorA(NULL as _, IDC_ARROW as _),
|
||||
hbrBackground: 0 as _,
|
||||
lpszClassName: s.magnifier_host_class.as_ptr() as _,
|
||||
lpszMenuName: 0 as _,
|
||||
hIconSm: 0 as _,
|
||||
};
|
||||
|
||||
// Ignore the error which may happen when the class is already registered.
|
||||
if 0 == RegisterClassExA(&wcex) {
|
||||
let code = GetLastError();
|
||||
if code != ERROR_CLASS_ALREADY_EXISTS {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Failed to RegisterClassExA, error: {}", code),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Create the host window.
|
||||
s.host_window = CreateWindowExA(
|
||||
WS_EX_LAYERED,
|
||||
s.magnifier_host_class.as_ptr(),
|
||||
s.host_window_name.as_ptr(),
|
||||
WS_POPUP,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
NULL as _,
|
||||
NULL as _,
|
||||
instance,
|
||||
NULL,
|
||||
);
|
||||
if s.host_window.is_null() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"Failed to CreateWindowExA host_window, error: {}",
|
||||
GetLastError()
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
// Create the magnifier control.
|
||||
s.magnifier_window = CreateWindowExA(
|
||||
0,
|
||||
s.magnifier_window_class.as_ptr(),
|
||||
s.magnifier_window_name.as_ptr(),
|
||||
WS_CHILD | WS_VISIBLE,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
s.host_window,
|
||||
NULL as _,
|
||||
instance,
|
||||
NULL,
|
||||
);
|
||||
if s.magnifier_window.is_null() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"Failed CreateWindowA magnifier_window, error: {}",
|
||||
GetLastError()
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
// Hide the host window.
|
||||
let _ = ShowWindow(s.host_window, SW_HIDE);
|
||||
|
||||
// Set the scaling callback to receive captured image.
|
||||
if let Some(set_callback_func) = s.mag_interface.set_image_scaling_callback_func {
|
||||
if FALSE
|
||||
== set_callback_func(
|
||||
s.magnifier_window,
|
||||
Some(Self::on_gag_image_scaling_callback),
|
||||
)
|
||||
{
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"Failed to MagSetImageScalingCallback, error: {}",
|
||||
GetLastError()
|
||||
),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
"Unreachable, set_image_scaling_callback_func should not be none",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
pub(crate) fn exclude(&mut self, cls: &str, name: &str) -> Result<bool> {
|
||||
let name_c = CString::new(name).unwrap();
|
||||
unsafe {
|
||||
let mut hwnd = if cls.len() == 0 {
|
||||
FindWindowExA(NULL as _, NULL as _, NULL as _, name_c.as_ptr())
|
||||
} else {
|
||||
let cls_c = CString::new(cls).unwrap();
|
||||
FindWindowExA(NULL as _, NULL as _, cls_c.as_ptr(), name_c.as_ptr())
|
||||
};
|
||||
|
||||
if hwnd.is_null() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if let Some(set_window_filter_list_func) =
|
||||
self.mag_interface.set_window_filter_list_func
|
||||
{
|
||||
if FALSE
|
||||
== set_window_filter_list_func(
|
||||
self.magnifier_window,
|
||||
MW_FILTERMODE_EXCLUDE,
|
||||
1,
|
||||
&mut hwnd,
|
||||
)
|
||||
{
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"Failed MagSetWindowFilterList for cls {} name {}, err: {}",
|
||||
cls,
|
||||
name,
|
||||
GetLastError()
|
||||
),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
"Unreachable, MagSetWindowFilterList should not be none",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub(crate) fn get_rect(&self) -> ((i32, i32), usize, usize) {
|
||||
(
|
||||
(self.rect.left as _, self.rect.top as _),
|
||||
self.width as _,
|
||||
self.height as _,
|
||||
)
|
||||
}
|
||||
|
||||
fn clear_data() {
|
||||
let mut lock = MAG_BUFFER.lock().unwrap();
|
||||
lock.0 = false;
|
||||
lock.1.clear();
|
||||
}
|
||||
|
||||
pub(crate) fn frame(&mut self, data: &mut Vec<u8>) -> Result<()> {
|
||||
Self::clear_data();
|
||||
|
||||
unsafe {
|
||||
let x = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||
let y = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
||||
let w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
let h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
if !(self.rect.left == x as _
|
||||
&& self.rect.top == y as _
|
||||
&& self.rect.right == (x + w) as _
|
||||
&& self.rect.bottom == (y + h) as _)
|
||||
{
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"Failed Check screen rect ({}, {}, {} , {}) to ({}, {}, {}, {})",
|
||||
self.rect.left,
|
||||
self.rect.top,
|
||||
self.rect.right,
|
||||
self.rect.bottom,
|
||||
x,
|
||||
y,
|
||||
x + w,
|
||||
y + h
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
if FALSE
|
||||
== SetWindowPos(
|
||||
self.magnifier_window,
|
||||
HWND_TOP,
|
||||
self.rect.left,
|
||||
self.rect.top,
|
||||
self.rect.right,
|
||||
self.rect.bottom,
|
||||
0,
|
||||
)
|
||||
{
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"Failed SetWindowPos (x, y, w , h) - ({}, {}, {}, {}), error {}",
|
||||
self.rect.left,
|
||||
self.rect.top,
|
||||
self.rect.right,
|
||||
self.rect.bottom,
|
||||
GetLastError()
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
// on_gag_image_scaling_callback will be called and fill in the
|
||||
// frame before set_window_source_func_ returns.
|
||||
if let Some(set_window_source_func) = self.mag_interface.set_window_source_func {
|
||||
if FALSE == set_window_source_func(self.magnifier_window, self.rect) {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Failed to MagSetWindowSource, error: {}", GetLastError()),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
"Unreachable, set_window_source_func should not be none",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let mut lock = MAG_BUFFER.lock().unwrap();
|
||||
if !lock.0 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
"No data captured by magnifier",
|
||||
));
|
||||
}
|
||||
|
||||
if self.use_yuv {
|
||||
self.data.resize(lock.1.len(), 0);
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(&mut lock.1[0], &mut self.data[0], self.data.len());
|
||||
}
|
||||
crate::common::bgra_to_i420(
|
||||
self.width as usize,
|
||||
self.height as usize,
|
||||
&self.data,
|
||||
data,
|
||||
);
|
||||
} else {
|
||||
data.resize(lock.1.len(), 0);
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(&mut lock.1[0], &mut data[0], data.len());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy_windows(&mut self) {
|
||||
if !self.magnifier_window.is_null() {
|
||||
unsafe {
|
||||
if FALSE == DestroyWindow(self.magnifier_window) {
|
||||
//
|
||||
println!("Failed DestroyWindow magnifier window {}", GetLastError())
|
||||
}
|
||||
}
|
||||
}
|
||||
self.magnifier_window = NULL as _;
|
||||
|
||||
if !self.host_window.is_null() {
|
||||
unsafe {
|
||||
if FALSE == DestroyWindow(self.host_window) {
|
||||
//
|
||||
println!("Failed DestroyWindow host window {}", GetLastError())
|
||||
}
|
||||
}
|
||||
}
|
||||
self.host_window = NULL as _;
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_gag_image_scaling_callback(
|
||||
_hwnd: HWND,
|
||||
srcdata: *mut ::std::os::raw::c_void,
|
||||
srcheader: MAGIMAGEHEADER,
|
||||
_destdata: *mut ::std::os::raw::c_void,
|
||||
_destheader: MAGIMAGEHEADER,
|
||||
_unclipped: RECT,
|
||||
_clipped: RECT,
|
||||
_dirty: HRGN,
|
||||
) -> BOOL {
|
||||
Self::clear_data();
|
||||
|
||||
if !IsEqualGUID(&srcheader.format, &GUID_WICPixelFormat32bppRGBA) {
|
||||
// log warning?
|
||||
return FALSE;
|
||||
}
|
||||
let mut lock = MAG_BUFFER.lock().unwrap();
|
||||
lock.1.resize(srcheader.cbSize, 0);
|
||||
std::ptr::copy_nonoverlapping(srcdata as _, &mut lock.1[0], srcheader.cbSize);
|
||||
lock.0 = true;
|
||||
TRUE
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut capture_mag = CapturerMag::new((0, 0), 1920, 1080, false).unwrap();
|
||||
capture_mag.exclude("", "RustDeskPrivacyWindow").unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(1000 * 10));
|
||||
let mut data = Vec::new();
|
||||
capture_mag.frame(&mut data).unwrap();
|
||||
println!("capture data len: {}", data.len());
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
use std::{io, mem, ptr, slice};
|
||||
pub mod gdi;
|
||||
pub use gdi::CapturerGDI;
|
||||
pub mod mag;
|
||||
|
||||
use winapi::{
|
||||
shared::{
|
||||
|
@ -14,13 +14,13 @@ pub mod quartz;
|
||||
#[cfg(x11)]
|
||||
pub mod x11;
|
||||
|
||||
#[cfg(all(x11, feature="wayland"))]
|
||||
#[cfg(all(x11, feature = "wayland"))]
|
||||
pub mod wayland;
|
||||
|
||||
#[cfg(dxgi)]
|
||||
pub mod dxgi;
|
||||
|
||||
#[cfg(android)]
|
||||
#[cfg(target_os = "android")]
|
||||
pub mod android;
|
||||
|
||||
mod common;
|
||||
|
13
libs/simple_rc/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "simple_rc"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde_derive = "1.0"
|
||||
serde = "1.0"
|
||||
walkdir = "2"
|
||||
confy = { git = "https://github.com/open-trade/confy" }
|
||||
hbb_common = { path = "../hbb_common" }
|
23
libs/simple_rc/examples/generate.rs
Normal file
@ -0,0 +1,23 @@
|
||||
extern crate simple_rc;
|
||||
|
||||
use simple_rc::*;
|
||||
|
||||
fn main() {
|
||||
{
|
||||
const CONF_FILE: &str = "simple_rc.toml";
|
||||
generate(CONF_FILE).unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
generate_with_conf(&Config {
|
||||
outfile: "src/rc.rs".to_owned(),
|
||||
confs: vec![ConfigItem {
|
||||
inc: "D:/projects/windows/RustDeskTempTopMostWindow/x64/Release/xxx".to_owned(),
|
||||
// exc: vec!["*.dll".to_owned(), "*.exe".to_owned()],
|
||||
exc: vec![],
|
||||
suppressed_front: "D:/projects/windows".to_owned(),
|
||||
}],
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
12
libs/simple_rc/simple_rc.toml
Normal file
@ -0,0 +1,12 @@
|
||||
# The output source file
|
||||
outfile = "src/rc.rs"
|
||||
|
||||
# The resource config list.
|
||||
[[confs]]
|
||||
# The file or director to integrate.
|
||||
inc = "D:/projects/windows/RustDeskTempTopMostWindow/x64/Release/xxx"
|
||||
# The exclusions.
|
||||
exc = ["*.dll", "*.exe"]
|
||||
# The front path that will ignore for extracting.
|
||||
# The following config will make base output path to be "RustDeskTempTopMostWindow/x64/Release/xxx".
|
||||
suppressed_front = "D:/projects/windows"
|
208
libs/simple_rc/src/lib.rs
Normal file
@ -0,0 +1,208 @@
|
||||
use hbb_common::{bail, ResultType};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
//mod rc;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
|
||||
pub struct ConfigItem {
|
||||
// include directory or file
|
||||
pub inc: String,
|
||||
// exclude files
|
||||
pub exc: Vec<String>,
|
||||
// out_path = origin_path - suppressed_front
|
||||
pub suppressed_front: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
|
||||
pub struct Config {
|
||||
// output source file
|
||||
pub outfile: String,
|
||||
// config items
|
||||
pub confs: Vec<ConfigItem>,
|
||||
}
|
||||
|
||||
pub fn get_outin_files<'a>(item: &'a ConfigItem) -> ResultType<HashMap<String, String>> {
|
||||
let mut outin_filemap = HashMap::new();
|
||||
|
||||
for entry in WalkDir::new(&item.inc).follow_links(true) {
|
||||
let path = entry?.into_path();
|
||||
if path.is_file() {
|
||||
let mut exclude = false;
|
||||
for excfile in item.exc.iter() {
|
||||
if excfile.starts_with("*.") {
|
||||
if let Some(ext) = path.extension().and_then(|x| x.to_str()) {
|
||||
if excfile.ends_with(&format!(".{}", ext)) {
|
||||
exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if path.ends_with(Path::new(excfile)) {
|
||||
exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if exclude {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut suppressed_front = item.suppressed_front.clone();
|
||||
if !suppressed_front.is_empty() && suppressed_front.ends_with('/') {
|
||||
suppressed_front.push('/');
|
||||
}
|
||||
let outpath = path.strip_prefix(Path::new(&suppressed_front))?;
|
||||
let outfile = if outpath.is_absolute() {
|
||||
match outpath
|
||||
.file_name()
|
||||
.and_then(|f| f.to_str())
|
||||
.map(|f| f.to_string())
|
||||
{
|
||||
None => {
|
||||
bail!("Failed to get filename of {}", outpath.display());
|
||||
}
|
||||
Some(s) => s,
|
||||
}
|
||||
} else {
|
||||
match outpath.to_str() {
|
||||
None => {
|
||||
bail!("Failed to convert {} to string", outpath.display());
|
||||
}
|
||||
// Simple replace \ to / here.
|
||||
// A better way is to use lib [path-slash](https://github.com/rhysd/path-slash)
|
||||
Some(s) => s.to_string().replace("\\", "/"),
|
||||
}
|
||||
};
|
||||
let infile = match path.canonicalize()?.to_str() {
|
||||
None => {
|
||||
bail!("Failed to get file path of {}", path.display());
|
||||
}
|
||||
Some(s) => s.to_string(),
|
||||
};
|
||||
if let Some(_) = outin_filemap.insert(outfile.clone(), infile) {
|
||||
bail!("outfile {} is set before", outfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(outin_filemap)
|
||||
}
|
||||
|
||||
pub fn generate(conf_file: &str) -> ResultType<()> {
|
||||
let conf = confy::load_path(conf_file)?;
|
||||
generate_with_conf(&conf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_with_conf<'a>(conf: &'a Config) -> ResultType<()> {
|
||||
let mut outfile = File::create(&conf.outfile)?;
|
||||
|
||||
outfile.write(
|
||||
br##"use hbb_common::{bail, ResultType};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::prelude::*,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
"##,
|
||||
)?;
|
||||
|
||||
outfile.write(b"#[allow(dead_code)]\n")?;
|
||||
outfile.write(b"pub fn extract_resources(root_path: &str) -> ResultType<()> {\n")?;
|
||||
outfile.write(b" let mut resources: Vec<(&str, &[u8])> = Vec::new();\n")?;
|
||||
|
||||
let mut outin_files = HashMap::new();
|
||||
for item in conf.confs.iter() {
|
||||
for (o, i) in get_outin_files(item)?.into_iter() {
|
||||
if let Some(_) = outin_files.insert(o.clone(), i) {
|
||||
bail!("outfile {} is set before", o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut count = 1;
|
||||
for (o, i) in outin_files.iter() {
|
||||
let mut infile = File::open(&i)?;
|
||||
let mut buffer = Vec::<u8>::new();
|
||||
infile.read_to_end(&mut buffer)?;
|
||||
|
||||
let var_outfile = format!("outfile_{}", count);
|
||||
let var_outdata = format!("outdata_{}", count);
|
||||
|
||||
write!(outfile, " let {} = \"{}\";\n", var_outfile, o)?;
|
||||
write!(outfile, " let {}: &[u8] = &[\n ", var_outdata)?;
|
||||
|
||||
let mut line_num = 20;
|
||||
for v in buffer {
|
||||
if line_num == 0 {
|
||||
write!(outfile, "\n ")?;
|
||||
line_num = 20;
|
||||
}
|
||||
write!(outfile, "{:#04x}, ", v)?;
|
||||
line_num -= 1;
|
||||
}
|
||||
write!(outfile, "\n ];\n")?;
|
||||
|
||||
write!(
|
||||
outfile,
|
||||
" resources.push(({}, &{}));\n",
|
||||
var_outfile, var_outdata
|
||||
)?;
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
outfile.write(b" do_extract(root_path, resources)?;\n")?;
|
||||
outfile.write(b" Ok(())\n")?;
|
||||
outfile.write(b"}\n")?;
|
||||
|
||||
outfile.write(
|
||||
br##"
|
||||
#[allow(dead_code)]
|
||||
fn do_extract(root_path: &str, resources: Vec<(&str, &[u8])>) -> ResultType<()> {
|
||||
let mut root_path = root_path.replace("\\", "/");
|
||||
if !root_path.ends_with('/') {
|
||||
root_path.push('/');
|
||||
}
|
||||
let root_path = Path::new(&root_path);
|
||||
for (outfile, data) in resources {
|
||||
let outfile_path = root_path.join(outfile);
|
||||
match outfile_path.parent().and_then(|p| p.to_str()) {
|
||||
None => {
|
||||
bail!("Failed to get parent of {}", outfile_path.display());
|
||||
}
|
||||
Some(p) => {
|
||||
fs::create_dir_all(p)?;
|
||||
let mut of = File::create(outfile_path)?;
|
||||
of.write_all(data)?;
|
||||
of.flush()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
"##,
|
||||
)?;
|
||||
|
||||
outfile.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = 2 + 2;
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_extract() {
|
||||
// use super::*;
|
||||
// rc::extract_resources("D:").unwrap();
|
||||
// }
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
#[cfg(windows)]
|
||||
use virtual_display::win10::{idd, DRIVER_INSTALL_PATH};
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::{
|
||||
ffi::{CStr, CString},
|
||||
ffi::CStr,
|
||||
io::{self, Read},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
fn prompt_input() -> u8 {
|
||||
println!("Press key execute:");
|
||||
println!(" 1. 'x' 1. exit");
|
||||
@ -24,6 +26,7 @@ fn prompt_input() -> u8 {
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn plug_in(index: idd::UINT, edid: idd::UINT) {
|
||||
println!("Plug in monitor begin");
|
||||
if idd::FALSE == idd::MonitorPlugIn(index, edid, 25) {
|
||||
@ -48,6 +51,7 @@ unsafe fn plug_in(index: idd::UINT, edid: idd::UINT) {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn plug_out(index: idd::UINT) {
|
||||
println!("Plug out monitor begin");
|
||||
if idd::FALSE == idd::MonitorPlugOut(index) {
|
||||
@ -58,81 +62,91 @@ unsafe fn plug_out(index: idd::UINT) {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let abs_path = Path::new(DRIVER_INSTALL_PATH).canonicalize().unwrap();
|
||||
let full_inf_path = abs_path.to_str().unwrap();
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let abs_path = Path::new(DRIVER_INSTALL_PATH).canonicalize().unwrap();
|
||||
|
||||
unsafe {
|
||||
let invalid_device = 0 as idd::HSWDEVICE;
|
||||
let mut h_sw_device = invalid_device;
|
||||
let full_inf_path = CString::new(full_inf_path).unwrap().into_raw();
|
||||
loop {
|
||||
match prompt_input() as char {
|
||||
'x' => break,
|
||||
'i' => {
|
||||
println!("Install or update driver begin");
|
||||
let mut reboot_required = idd::FALSE;
|
||||
if idd::InstallUpdate(full_inf_path, &mut reboot_required) == idd::FALSE {
|
||||
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
|
||||
} else {
|
||||
println!(
|
||||
"Install or update driver done, reboot is {} required",
|
||||
if reboot_required == idd::FALSE {
|
||||
"not"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
);
|
||||
unsafe {
|
||||
let invalid_device = 0 as idd::HSWDEVICE;
|
||||
let mut h_sw_device = invalid_device;
|
||||
|
||||
let full_inf_path: Vec<u16> = abs_path
|
||||
.to_string_lossy()
|
||||
.as_ref()
|
||||
.encode_utf16()
|
||||
.chain(Some(0).into_iter())
|
||||
.collect();
|
||||
|
||||
loop {
|
||||
match prompt_input() as char {
|
||||
'x' => break,
|
||||
'i' => {
|
||||
println!("Install or update driver begin, {}", abs_path.display());
|
||||
let mut reboot_required = idd::FALSE;
|
||||
if idd::InstallUpdate(full_inf_path.as_ptr() as _, &mut reboot_required)
|
||||
== idd::FALSE
|
||||
{
|
||||
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
|
||||
} else {
|
||||
println!(
|
||||
"Install or update driver done, reboot is {} required",
|
||||
if reboot_required == idd::FALSE {
|
||||
"not"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
'u' => {
|
||||
println!("Uninstall driver begin");
|
||||
let mut reboot_required = idd::FALSE;
|
||||
if idd::Uninstall(full_inf_path, &mut reboot_required) == idd::FALSE {
|
||||
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
|
||||
} else {
|
||||
println!(
|
||||
"Uninstall driver done, reboot is {} required",
|
||||
if reboot_required == idd::FALSE {
|
||||
"not"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
);
|
||||
'u' => {
|
||||
println!("Uninstall driver begin {}", abs_path.display());
|
||||
let mut reboot_required = idd::FALSE;
|
||||
if idd::Uninstall(full_inf_path.as_ptr() as _, &mut reboot_required)
|
||||
== idd::FALSE
|
||||
{
|
||||
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
|
||||
} else {
|
||||
println!(
|
||||
"Uninstall driver done, reboot is {} required",
|
||||
if reboot_required == idd::FALSE {
|
||||
"not"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
'c' => {
|
||||
println!("Create device begin");
|
||||
if h_sw_device != invalid_device {
|
||||
println!("Device created before");
|
||||
continue;
|
||||
'c' => {
|
||||
println!("Create device begin");
|
||||
if h_sw_device != invalid_device {
|
||||
println!("Device created before");
|
||||
continue;
|
||||
}
|
||||
if idd::FALSE == idd::DeviceCreate(&mut h_sw_device) {
|
||||
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
|
||||
idd::DeviceClose(h_sw_device);
|
||||
h_sw_device = invalid_device;
|
||||
} else {
|
||||
println!("Create device done");
|
||||
}
|
||||
}
|
||||
if idd::FALSE == idd::DeviceCreate(&mut h_sw_device) {
|
||||
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
|
||||
'd' => {
|
||||
println!("Close device begin");
|
||||
idd::DeviceClose(h_sw_device);
|
||||
h_sw_device = invalid_device;
|
||||
} else {
|
||||
println!("Create device done");
|
||||
println!("Close device done");
|
||||
}
|
||||
'1' => plug_in(0, 0),
|
||||
'2' => plug_in(1, 0),
|
||||
'3' => plug_in(2, 0),
|
||||
'4' => plug_out(0),
|
||||
'5' => plug_out(1),
|
||||
'6' => plug_out(2),
|
||||
_ => {}
|
||||
}
|
||||
'd' => {
|
||||
println!("Close device begin");
|
||||
idd::DeviceClose(h_sw_device);
|
||||
h_sw_device = invalid_device;
|
||||
println!("Close device done");
|
||||
}
|
||||
'1' => plug_in(0, 0),
|
||||
'2' => plug_in(1, 0),
|
||||
'3' => plug_in(2, 0),
|
||||
'4' => plug_out(0),
|
||||
'5' => plug_out(1),
|
||||
'6' => plug_out(2),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if !full_inf_path.is_null() {
|
||||
let _ = CString::from_raw(full_inf_path);
|
||||
}
|
||||
|
||||
idd::DeviceClose(h_sw_device);
|
||||
idd::DeviceClose(h_sw_device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
pub mod win10;
|
||||
|
||||
use hbb_common::{bail, lazy_static, ResultType};
|
||||
use std::{ffi::CString, path::Path, sync::Mutex};
|
||||
use std::{path::Path, sync::Mutex};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
// If device is uninstalled though "Device Manager" Window.
|
||||
@ -33,16 +33,24 @@ pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> {
|
||||
bail!("{} not exists", install_path)
|
||||
}
|
||||
|
||||
let _full_install_path = match abs_path.to_str() {
|
||||
Some(p) => CString::new(p)?.into_raw(),
|
||||
None => bail!("{} not exists", install_path),
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
{
|
||||
// Device must be created before install driver.
|
||||
// https://github.com/fufesou/RustDeskIddDriver/issues/1
|
||||
if let Err(e) = create_device() {
|
||||
bail!("{}", e);
|
||||
}
|
||||
|
||||
let full_install_path: Vec<u16> = abs_path
|
||||
.to_string_lossy()
|
||||
.as_ref()
|
||||
.encode_utf16()
|
||||
.chain(Some(0).into_iter())
|
||||
.collect();
|
||||
|
||||
let mut reboot_required_tmp = win10::idd::FALSE;
|
||||
if win10::idd::InstallUpdate(_full_install_path, &mut reboot_required_tmp)
|
||||
if win10::idd::InstallUpdate(full_install_path.as_ptr() as _, &mut reboot_required_tmp)
|
||||
== win10::idd::FALSE
|
||||
{
|
||||
bail!("{}", win10::get_last_msg()?);
|
||||
@ -65,16 +73,18 @@ pub fn uninstall_driver(_reboot_required: &mut bool) -> ResultType<()> {
|
||||
bail!("{} not exists", install_path)
|
||||
}
|
||||
|
||||
let _full_install_path = match abs_path.to_str() {
|
||||
Some(p) => CString::new(p)?.into_raw(),
|
||||
None => bail!("{} not exists", install_path),
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
{
|
||||
let full_install_path: Vec<u16> = abs_path
|
||||
.to_string_lossy()
|
||||
.as_ref()
|
||||
.encode_utf16()
|
||||
.chain(Some(0).into_iter())
|
||||
.collect();
|
||||
|
||||
let mut reboot_required_tmp = win10::idd::FALSE;
|
||||
if win10::idd::Uninstall(_full_install_path, &mut reboot_required_tmp)
|
||||
if win10::idd::Uninstall(full_install_path.as_ptr() as _, &mut reboot_required_tmp)
|
||||
== win10::idd::FALSE
|
||||
{
|
||||
bail!("{}", win10::get_last_msg()?);
|
||||
|
@ -64,14 +64,14 @@ const char* GetLastMsg()
|
||||
return g_lastMsg;
|
||||
}
|
||||
|
||||
BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired)
|
||||
BOOL InstallUpdate(LPCWSTR fullInfPath, PBOOL rebootRequired)
|
||||
{
|
||||
SetLastMsg("Sucess");
|
||||
|
||||
// UpdateDriverForPlugAndPlayDevices may return FALSE while driver was successfully installed...
|
||||
if (FALSE == UpdateDriverForPlugAndPlayDevices(
|
||||
// UpdateDriverForPlugAndPlayDevicesW may return FALSE while driver was successfully installed...
|
||||
if (FALSE == UpdateDriverForPlugAndPlayDevicesW(
|
||||
NULL,
|
||||
_T("RustDeskIddDriver"), // match hardware id in the inf file
|
||||
L"RustDeskIddDriver", // match hardware id in the inf file
|
||||
fullInfPath,
|
||||
INSTALLFLAG_FORCE
|
||||
// | INSTALLFLAG_NONINTERACTIVE // INSTALLFLAG_NONINTERACTIVE may cause error 0xe0000247
|
||||
@ -82,7 +82,7 @@ BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired)
|
||||
DWORD error = GetLastError();
|
||||
if (error != 0)
|
||||
{
|
||||
SetLastMsg("UpdateDriverForPlugAndPlayDevices failed, last error 0x%x\n", error);
|
||||
SetLastMsg("UpdateDriverForPlugAndPlayDevicesW failed, last error 0x%x\n", error);
|
||||
if (g_printMsg)
|
||||
{
|
||||
printf(g_lastMsg);
|
||||
@ -94,11 +94,11 @@ BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL Uninstall(LPCTSTR fullInfPath, PBOOL rebootRequired)
|
||||
BOOL Uninstall(LPCWSTR fullInfPath, PBOOL rebootRequired)
|
||||
{
|
||||
SetLastMsg("Sucess");
|
||||
|
||||
if (FALSE == DiUninstallDriver(
|
||||
if (FALSE == DiUninstallDriverW(
|
||||
NULL,
|
||||
fullInfPath,
|
||||
0,
|
||||
@ -108,7 +108,7 @@ BOOL Uninstall(LPCTSTR fullInfPath, PBOOL rebootRequired)
|
||||
DWORD error = GetLastError();
|
||||
if (error != 0)
|
||||
{
|
||||
SetLastMsg("DiUninstallDriver failed, last error 0x%x\n", error);
|
||||
SetLastMsg("DiUninstallDriverW failed, last error 0x%x\n", error);
|
||||
if (g_printMsg)
|
||||
{
|
||||
printf(g_lastMsg);
|
||||
|
@ -1,65 +1,32 @@
|
||||
from pynput.keyboard import Key, Controller
|
||||
from pynput.keyboard._xorg import KeyCode
|
||||
from pynput._util.xorg import display_manager
|
||||
import Xlib
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
from Xlib.ext.xtest import fake_input
|
||||
from Xlib import X
|
||||
import Xlib
|
||||
|
||||
KeyCode._from_symbol("\0") # test
|
||||
|
||||
|
||||
class MyController(Controller):
|
||||
def _handle(self, key, is_press):
|
||||
"""Resolves a key identifier and sends a keyboard event.
|
||||
:param event: The *X* keyboard event.
|
||||
:param int keysym: The keysym to handle.
|
||||
"""
|
||||
event = Xlib.display.event.KeyPress if is_press \
|
||||
else Xlib.display.event.KeyRelease
|
||||
keysym = self._keysym(key)
|
||||
keycode = self._display.keysym_to_keycode(keysym)
|
||||
|
||||
# Make sure to verify that the key was resolved
|
||||
if keysym is None:
|
||||
raise self.InvalidKeyException(key)
|
||||
with display_manager(self._display) as dm:
|
||||
Xlib.ext.xtest.fake_input(
|
||||
dm,
|
||||
Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease,
|
||||
keycode)
|
||||
|
||||
# If the key has a virtual key code, use that immediately with
|
||||
# fake_input; fake input,being an X server extension, has access to
|
||||
# more internal state that we do
|
||||
if key.vk is not None:
|
||||
with display_manager(self._display) as dm:
|
||||
Xlib.ext.xtest.fake_input(
|
||||
dm,
|
||||
Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease,
|
||||
dm.keysym_to_keycode(key.vk))
|
||||
|
||||
# Otherwise use XSendEvent; we need to use this in the general case to
|
||||
# work around problems with keyboard layouts
|
||||
else:
|
||||
try:
|
||||
keycode, shift_state = self.keyboard_mapping[keysym]
|
||||
with self.modifiers as modifiers:
|
||||
alt_gr = Key.alt_gr in modifiers
|
||||
if alt_gr:
|
||||
self._send_key(event, keycode, shift_state)
|
||||
else:
|
||||
with display_manager(self._display) as dm:
|
||||
Xlib.ext.xtest.fake_input(
|
||||
dm,
|
||||
Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease,
|
||||
keycode)
|
||||
|
||||
except KeyError:
|
||||
with self._borrow_lock:
|
||||
keycode, index, count = self._borrows[keysym]
|
||||
self._send_key(
|
||||
event,
|
||||
keycode,
|
||||
index_to_shift(self._display, index))
|
||||
count += 1 if is_press else -1
|
||||
self._borrows[keysym] = (keycode, index, count)
|
||||
|
||||
# Notify any running listeners
|
||||
self._emit('_on_fake_event', key, is_press)
|
||||
|
||||
keyboard = MyController()
|
||||
|
||||
@ -77,7 +44,7 @@ server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
server.bind(server_address)
|
||||
server.listen(1)
|
||||
clientsocket, address = server.accept()
|
||||
os.system('chmod a+rw %s'%server_address)
|
||||
os.system('chmod a+rw %s' % server_address)
|
||||
print("Got pynput connection")
|
||||
|
||||
|
||||
@ -121,4 +88,3 @@ def loop():
|
||||
loop()
|
||||
clientsocket.close()
|
||||
server.close()
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Version=1.2.0
|
||||
Name=RustDesk
|
||||
GenericName=Remote Desktop
|
||||
Comment=Remote Desktop
|
||||
|
11
src/cli.rs
@ -21,7 +21,7 @@ impl Session {
|
||||
pub fn new(id: &str, sender: mpsc::UnboundedSender<Data>) -> Self {
|
||||
let mut password = "".to_owned();
|
||||
if PeerConfig::load(id).password.is_empty() {
|
||||
password = rpassword::read_password_from_tty(Some("Enter password: ")).unwrap();
|
||||
password = rpassword::prompt_password("Enter password: ").unwrap();
|
||||
}
|
||||
let session = Self {
|
||||
id: id.to_owned(),
|
||||
@ -47,7 +47,7 @@ impl Interface for Session {
|
||||
.ok();
|
||||
} else if msgtype == "re-input-password" {
|
||||
log::error!("{}: {}", title, text);
|
||||
let pass = rpassword::read_password_from_tty(Some("Enter password: ")).unwrap();
|
||||
let pass = rpassword::prompt_password("Enter password: ").unwrap();
|
||||
self.sender.send(Data::Login((pass, true))).ok();
|
||||
} else if msgtype.contains("error") {
|
||||
log::error!("{}: {}: {}", msgtype, title, text);
|
||||
@ -76,6 +76,10 @@ impl Interface for Session {
|
||||
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
|
||||
handle_test_delay(t, peer).await;
|
||||
}
|
||||
|
||||
fn send(&self, data: Data) {
|
||||
self.sender.send(data).ok();
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
@ -85,6 +89,7 @@ pub async fn start_one_port_forward(
|
||||
remote_host: String,
|
||||
remote_port: i32,
|
||||
key: String,
|
||||
token: String,
|
||||
) {
|
||||
crate::common::test_rendezvous_server();
|
||||
crate::common::test_nat_type();
|
||||
@ -92,7 +97,7 @@ pub async fn start_one_port_forward(
|
||||
let handler = Session::new(&id, sender);
|
||||
handler.lc.write().unwrap().port_forward = (remote_host, remote_port);
|
||||
if let Err(err) =
|
||||
crate::port_forward::listen(handler.id.clone(), port, handler.clone(), receiver, &key).await
|
||||
crate::port_forward::listen(handler.id.clone(), port, handler.clone(), receiver, &key, &token).await
|
||||
{
|
||||
log::error!("Failed to listen on {}: {}", port, err);
|
||||
}
|
||||
|
@ -795,6 +795,7 @@ pub struct LoginConfigHandler {
|
||||
pub port_forward: (String, i32),
|
||||
pub version: i64,
|
||||
pub conn_id: i32,
|
||||
features: Option<Features>,
|
||||
}
|
||||
|
||||
impl Deref for LoginConfigHandler {
|
||||
@ -924,11 +925,11 @@ impl LoginConfigHandler {
|
||||
})
|
||||
.into();
|
||||
} else if name == "privacy-mode" {
|
||||
config.privacy_mode = !config.privacy_mode;
|
||||
// try toggle privacy mode
|
||||
option.privacy_mode = (if config.privacy_mode {
|
||||
BoolOption::Yes
|
||||
} else {
|
||||
BoolOption::No
|
||||
} else {
|
||||
BoolOption::Yes
|
||||
})
|
||||
.into();
|
||||
} else if name == "enable-file-transfer" {
|
||||
@ -1068,6 +1069,14 @@ impl LoginConfigHandler {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_privacy_mode_supported(&self) -> bool {
|
||||
if let Some(features) = &self.features {
|
||||
features.privacy_mode
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`Message`] for refreshing video.
|
||||
pub fn refresh() -> Message {
|
||||
let mut misc = Misc::new();
|
||||
@ -1166,6 +1175,7 @@ impl LoginConfigHandler {
|
||||
if !pi.version.is_empty() {
|
||||
self.version = hbb_common::get_version_number(&pi.version);
|
||||
}
|
||||
self.features = pi.features.into_option();
|
||||
let serde = PeerInfoSerde {
|
||||
username,
|
||||
hostname: pi.hostname.clone(),
|
||||
|
@ -6,7 +6,7 @@ pub trait FileManager: Interface {
|
||||
fs::get_home_as_string()
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
|
||||
fn read_dir(&self, path: String, include_hidden: bool) -> sciter::Value {
|
||||
match fs::read_dir(&fs::get_path(&path), include_hidden) {
|
||||
Err(_) => sciter::Value::null(),
|
||||
@ -19,7 +19,7 @@ pub trait FileManager: Interface {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
|
||||
fn read_dir(&self, path: &str, include_hidden: bool) -> String {
|
||||
use crate::flutter::make_fd_to_json;
|
||||
match fs::read_dir(&fs::get_path(path), include_hidden) {
|
||||
|