mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-12-12 18:29:10 +08:00
Merge branch 'rustdesk:master' into master-ar
This commit is contained in:
commit
a470bf127e
10
.github/workflows/flutter-build.yml
vendored
10
.github/workflows/flutter-build.yml
vendored
@ -22,7 +22,7 @@ env:
|
||||
# vcpkg version: 2023.04.15
|
||||
# for multiarch gcc compatibility
|
||||
VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1"
|
||||
VERSION: "1.2.2"
|
||||
VERSION: "1.2.3"
|
||||
NDK_VERSION: "r25c"
|
||||
#signing keys env variable checks
|
||||
ANDROID_SIGNING_KEY: '${{ secrets.ANDROID_SIGNING_KEY }}'
|
||||
@ -766,7 +766,7 @@ jobs:
|
||||
if: ${{ inputs.upload-artifact }}
|
||||
needs: [generate-bridge-linux, build-vcpkg-deps-linux]
|
||||
name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
|
||||
runs-on: [self-hosted]
|
||||
runs-on: [self-hosted, Linux, ARM64]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@ -898,7 +898,7 @@ jobs:
|
||||
export DEFAULT_FEAT=linux_headless
|
||||
fi
|
||||
export CARGO_INCREMENTAL=0
|
||||
cargo build --jobs 1 --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release
|
||||
cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@master
|
||||
@ -910,7 +910,7 @@ jobs:
|
||||
if: ${{ inputs.upload-artifact }}
|
||||
needs: [build-vcpkg-deps-linux]
|
||||
name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
|
||||
runs-on: [self-hosted]
|
||||
runs-on: [self-hosted, Linux, ARM64]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@ -1015,7 +1015,7 @@ jobs:
|
||||
if ${{ matrix.job.enable-headless }}; then
|
||||
export DEFAULT_FEAT=linux_headless
|
||||
fi
|
||||
cargo build --jobs 1 --features inline,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release --bins
|
||||
cargo build --features inline,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release --bins
|
||||
# package
|
||||
mkdir -p ./Release
|
||||
mv ./target/release/rustdesk ./Release/rustdesk
|
||||
|
2
.github/workflows/flutter-tag.yml
vendored
2
.github/workflows/flutter-tag.yml
vendored
@ -15,4 +15,4 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
upload-artifact: true
|
||||
upload-tag: "1.2.2"
|
||||
upload-tag: "1.2.3"
|
||||
|
2
.github/workflows/history.yml
vendored
2
.github/workflows/history.yml
vendored
@ -10,7 +10,7 @@ env:
|
||||
# vcpkg version: 2022.05.10
|
||||
# for multiarch gcc compatibility
|
||||
VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1"
|
||||
VERSION: "1.2.2"
|
||||
VERSION: "1.2.3"
|
||||
|
||||
jobs:
|
||||
build-for-history-windows:
|
||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5124,7 +5124,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustdesk"
|
||||
version = "1.2.2"
|
||||
version = "1.2.3"
|
||||
dependencies = [
|
||||
"android_logger",
|
||||
"arboard",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustdesk"
|
||||
version = "1.2.2"
|
||||
version = "1.2.3"
|
||||
authors = ["rustdesk <info@rustdesk.com>"]
|
||||
edition = "2021"
|
||||
build= "build.rs"
|
||||
|
@ -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="docs/README-UA.md">Українська</a>] | [<a href="docs/README-CS.md">česky</a>] | [<a href="docs/README-ZH.md">中文</a>] | [<a href="docs/README-HU.md">Magyar</a>] | [<a href="docs/README-ES.md">Español</a>] | [<a href="docs/README-FA.md">فارسی</a>] | [<a href="docs/README-FR.md">Français</a>] | [<a href="docs/README-DE.md">Deutsch</a>] | [<a href="docs/README-PL.md">Polski</a>] | [<a href="docs/README-ID.md">Indonesian</a>] | [<a href="docs/README-FI.md">Suomi</a>] | [<a href="docs/README-ML.md">മലയാളം</a>] | [<a href="docs/README-JP.md">日本語</a>] | [<a href="docs/README-NL.md">Nederlands</a>] | [<a href="docs/README-IT.md">Italiano</a>] | [<a href="docs/README-RU.md">Русский</a>] | [<a href="docs/README-PTBR.md">Português (Brasil)</a>] | [<a href="docs/README-EO.md">Esperanto</a>] | [<a href="docs/README-KR.md">한국어</a>] | [<a href="docs/README-AR.md">العربي</a>] | [<a href="docs/README-VN.md">Tiếng Việt</a>] | [<a href="docs/README-DA.md">Dansk</a>] | [<a href="docs/README-GR.md">Ελληνικά</a>]<br>
|
||||
[<a href="docs/README-UA.md">Українська</a>] | [<a href="docs/README-CS.md">česky</a>] | [<a href="docs/README-ZH.md">中文</a>] | [<a href="docs/README-HU.md">Magyar</a>] | [<a href="docs/README-ES.md">Español</a>] | [<a href="docs/README-FA.md">فارسی</a>] | [<a href="docs/README-FR.md">Français</a>] | [<a href="docs/README-DE.md">Deutsch</a>] | [<a href="docs/README-PL.md">Polski</a>] | [<a href="docs/README-ID.md">Indonesian</a>] | [<a href="docs/README-FI.md">Suomi</a>] | [<a href="docs/README-ML.md">മലയാളം</a>] | [<a href="docs/README-JP.md">日本語</a>] | [<a href="docs/README-NL.md">Nederlands</a>] | [<a href="docs/README-IT.md">Italiano</a>] | [<a href="docs/README-RU.md">Русский</a>] | [<a href="docs/README-PTBR.md">Português (Brasil)</a>] | [<a href="docs/README-EO.md">Esperanto</a>] | [<a href="docs/README-KR.md">한국어</a>] | [<a href="docs/README-AR.md">العربي</a>] | [<a href="docs/README-VN.md">Tiếng Việt</a>] | [<a href="docs/README-DA.md">Dansk</a>] | [<a href="docs/README-GR.md">Ελληνικά</a>] | [<a href="docs/README-TR.md">Türkçe</a>]<br>
|
||||
<b>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">RustDesk Doc</a> to your native language</b>
|
||||
</p>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
version: 1
|
||||
script:
|
||||
- rm -rf ./AppDir || true
|
||||
- bsdtar -zxvf ../rustdesk-1.2.2.deb
|
||||
- bsdtar -zxvf ../rustdesk-1.2.3.deb
|
||||
- tar -xvf ./data.tar.xz
|
||||
- mkdir ./AppDir
|
||||
- mv ./usr ./AppDir/usr
|
||||
@ -18,7 +18,7 @@ AppDir:
|
||||
id: rustdesk
|
||||
name: rustdesk
|
||||
icon: rustdesk
|
||||
version: 1.2.2
|
||||
version: 1.2.3
|
||||
exec: usr/lib/rustdesk/rustdesk
|
||||
exec_args: $@
|
||||
apt:
|
||||
|
@ -2,7 +2,7 @@
|
||||
version: 1
|
||||
script:
|
||||
- rm -rf ./AppDir || true
|
||||
- bsdtar -zxvf ../rustdesk-1.2.2.deb
|
||||
- bsdtar -zxvf ../rustdesk-1.2.3.deb
|
||||
- tar -xvf ./data.tar.xz
|
||||
- mkdir ./AppDir
|
||||
- mv ./usr ./AppDir/usr
|
||||
@ -18,7 +18,7 @@ AppDir:
|
||||
id: rustdesk
|
||||
name: rustdesk
|
||||
icon: rustdesk
|
||||
version: 1.2.2
|
||||
version: 1.2.3
|
||||
exec: usr/lib/rustdesk/rustdesk
|
||||
exec_args: $@
|
||||
apt:
|
||||
|
7
build.py
7
build.py
@ -545,13 +545,6 @@ def main():
|
||||
'cp libsciter.dylib target/release/bundle/osx/RustDesk.app/Contents/MacOS/')
|
||||
# https://github.com/sindresorhus/create-dmg
|
||||
system2('/bin/rm -rf *.dmg')
|
||||
plist = "target/release/bundle/osx/RustDesk.app/Contents/Info.plist"
|
||||
txt = open(plist).read()
|
||||
with open(plist, "wt") as fh:
|
||||
fh.write(txt.replace("</dict>", """
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
</dict>"""))
|
||||
pa = os.environ.get('P')
|
||||
if pa:
|
||||
system2('''
|
||||
|
89
docs/CODE_OF_CONDUCT-TR.md
Normal file
89
docs/CODE_OF_CONDUCT-TR.md
Normal file
@ -0,0 +1,89 @@
|
||||
# Katkıda Bulunanların Davranış Kuralları
|
||||
|
||||
## Taahhüdümüz
|
||||
|
||||
Biz üyeler, katkıda bulunanlar ve liderler olarak, yaş, beden büyüklüğü, görünür veya görünmez engellilik, etnik köken, cinsiyet özellikleri, cinsiyet kimliği ve ifadesi, deneyim seviyesi, eğitim, sosyo-ekonomik durum, milliyet, kişisel görünüm, ırk, din veya cinsel kimlik ve yönelim ayrımı gözetmeksizin herkes için topluluğumuzdaki katılımı taciz içermeyen bir deneyim haline getirmeyi taahhüt ederiz.
|
||||
|
||||
Açık, hoşgörülü, çeşitli, kapsayıcı ve sağlıklı bir topluluğa katkıda bulunacak şekillerde hareket etmeyi ve etkileşimde bulunmayı taahhüt ederiz.
|
||||
|
||||
## Standartlarımız
|
||||
|
||||
Topluluğumuz için olumlu bir ortam yaratmaya katkıda bulunan davranış örnekleri şunlardır:
|
||||
|
||||
* Diğer insanlara empati ve nezaket göstermek
|
||||
* Farklı görüşlere, bakış açılarına ve deneyimlere saygılı olmak
|
||||
* Yapıcı eleştiriyi vermek ve zarifçe kabul etmek
|
||||
* Hatalarımızdan etkilenenlere sorumluluk kabul etmek, özür dilemek ve deneyimden öğrenmek
|
||||
* Sadece bireyler olarak değil, aynı zamanda genel topluluk için en iyisi üzerine odaklanmak
|
||||
|
||||
Kabul edilemez davranış örnekleri şunları içerir:
|
||||
|
||||
* Cinselleştirilmiş dil veya imgelerin kullanımı ve cinsel ilgi veya herhangi bir türdeki yaklaşımlar
|
||||
* Trollük, aşağılayıcı veya hakaret içeren yorumlar ve kişisel veya siyasi saldırılar
|
||||
* Kamuoyu veya özel taciz
|
||||
* Başkalarının fiziksel veya e-posta adresi gibi özel bilgilerini, açık izinleri olmadan yayınlamak
|
||||
* Profesyonel bir ortamda makul bir şekilde uygunsuz kabul edilebilecek diğer davranışlar
|
||||
|
||||
## Uygulama Sorumlulukları
|
||||
|
||||
Topluluk liderleri, kabul edilebilir davranış standartlarımızı açıklığa kavuşturmak ve uygulamakla sorumludur ve uygunsuz, tehditkar, saldırgan veya zarar verici herhangi bir davranışa yanıt olarak uygun ve adil düzeltici önlemler alacaklardır.
|
||||
|
||||
Topluluk liderleri, bu Davranış Kurallarına uyumlu olmayan yorumları, taahhütlerini veya kodu, wiki düzenlemelerini, sorunları ve diğer katkıları kaldırma, düzenleme veya reddetme hakkına sahiptir. Denetim kararlarının nedenlerini uygun olduğunda ileteceklerdir.
|
||||
|
||||
## Kapsam
|
||||
|
||||
Bu Davranış Kuralları, tüm topluluk alanlarında geçerlidir ve aynı zamanda birey resmi olarak topluluğu halka açık alanlarda temsil ettiğinde de geçerlidir. Topluluğumuzu temsil etme örnekleri, resmi bir e-posta adresi kullanmak, resmi bir sosyal medya hesabı üzerinden gönderi yapmak veya çevrimiçi veya çevrimdışı bir etkinlikte atanmış bir temsilci olarak hareket etmeyi içerir.
|
||||
|
||||
## Uygulama
|
||||
|
||||
Taciz edici, rahatsız edici veya başka türlü kabul edilemez davranış örnekleri, [info@rustdesk.com](mailto:info@rustdesk.com) adresindeki uygulama sorumlularına bildirilebilir. Tüm şikayetler hızlı ve adil bir şekilde incelenecek ve araştırılacaktır.
|
||||
|
||||
Tüm topluluk liderleri, olayın raporlayıcısının gizliliğine ve güvenliğine saygı gösterme yükümlülüğündedir.
|
||||
|
||||
## Uygulama Kılavuzları
|
||||
|
||||
Topluluk liderleri, bu Davranış Kurallarını ihlal olarak değerlendirdikleri herhangi bir eylem için bu Topluluk Etkisi Kılavuzlarını izleyeceklerdir:
|
||||
|
||||
### 1. Düzeltme
|
||||
|
||||
**Topluluk Etkisi**: Topluluk içinde profesyonel veya hoşgörülü olmayan uygun olmayan dil veya diğer davranışların kullanımı.
|
||||
|
||||
**Sonuç**: Topluluk liderlerinden özel ve yazılı bir uyarı almak, ihlalin niteliği ve davranışın nedeninin açıklığa kavuşturulması. Bir kamu özrü istenebilir.
|
||||
|
||||
### 2. Uyarı
|
||||
|
||||
**Topluluk Etkisi**: Tek bir olay veya dizi aracılığıyla bir ihlal.
|
||||
|
||||
**Sonuç**: Devam eden davranış için sonuçları olan bir uyarı. Topluluk liderleri de dahil olmak üzere ihlalle ilgili kişilerle etkileşim, belirli bir süre boyunca önerilmez. Bu, topluluk alanlarında ve sosyal medya gibi harici kanallarda etkileşimleri içerir. Bu koşulları ihlal etmek geçici veya kalıcı bir yasağa yol açabilir.
|
||||
|
||||
### 3. Geçici Yasak
|
||||
|
||||
**Topluluk Etkisi**: Sürekli uygunsuz davranış da dahil olmak üzere topluluk standartlarının ciddi bir ihlali.
|
||||
|
||||
**Sonuç**: Belirli bir süre için toplulukla herhangi bir türdeki etkileşim veya halka açık iletişimden geçici bir yasak. Bu dönem boyunca, toplul
|
||||
|
||||
ukla veya uygulama kurallarını uygulayanlarla her türlü kamuoyu veya özel etkileşim izin verilmez. Bu koşulları ihlal etmek geçici veya kalıcı bir yasağa yol açabilir.
|
||||
|
||||
### 4. Kalıcı Yasak
|
||||
|
||||
**Topluluk Etkisi**: Topluluk standartlarının ihlalinde sürekli bir desen sergilemek, bireye sürekli olarak uygun olmayan davranışlarda bulunmak, bir bireye tacizde bulunmak veya birey sınıflarına karşı saldırganlık veya aşağılama yapmak.
|
||||
|
||||
**Sonuç**: Topluluk içinde her türlü halka açık etkileşimden kalıcı bir yasak.
|
||||
|
||||
## Atıf
|
||||
|
||||
Bu Davranış Kuralları, [Contributor Covenant][anasayfa], 2.0 sürümünden uyarlanmıştır ve
|
||||
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0] adresinde bulunmaktadır.
|
||||
|
||||
Topluluk Etkisi Kılavuzları,
|
||||
[Mozilla'nın davranış kuralları uygulama merdiveni][Mozilla DK] tarafından ilham alınarak oluşturulmuştur.
|
||||
|
||||
Bu davranış kuralları hakkında yaygın soruların cevapları için, SSS'ye göz atın:
|
||||
[https://www.contributor-covenant.org/faq][SSS]. Çeviriler,
|
||||
[https://www.contributor-covenant.org/translations][çeviriler] adresinde bulunabilir.
|
||||
|
||||
[anasayfa]: https://www.contributor-covenant.org
|
||||
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
|
||||
[Mozilla DK]: https://github.com/mozilla/diversity
|
||||
[SSS]: https://www.contributor-covenant.org/faq
|
||||
[çeviriler]: https://www.contributor-covenant.org/translations
|
31
docs/CONTRIBUTING-ID.md
Normal file
31
docs/CONTRIBUTING-ID.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Berkontribusi dalam pengembangan RustDesk
|
||||
|
||||
RustDesk mengajak semua orang untuk ikut berkontribusi. Berikut ini adalah panduan jika kamu sedang mempertimbangkan untuk memberikan bantuan kepada kami:
|
||||
|
||||
## Kontirbusi
|
||||
|
||||
Untuk melakukan kontribusi pada RustDesk atau dependensinya, sebaiknya dilakukan dalam bentuk pull request di GitHub. Setiap permintaan pull request akan ditinjau oleh kontributor utama atau seseorang yang memiliki wewenang untuk menggabungkan perubahan kode, baik yang sudah dimasukkan ke dalam struktur utama ataupun memberikan umpan balik untuk perubahan yang akan diperlukan. Setiap kontribusi harus sesuai dengan format ini, juga termasuk yang berasal dari kontributor utama.
|
||||
|
||||
Apabila kamu ingin mengatasi sebuah masalah yang sudah ada di daftar issue, harap klaim terlebih dahulu dengan memberikan komentar pada GitHub issue yang ingin kamu kerjakan. Hal ini dilakukan untuk mencegah terjadinya duplikasi dari kontributor pada daftar issue yang sama.
|
||||
|
||||
## Pemeriksaan Pull Request
|
||||
|
||||
- Branch yang menjadi acuan adalah branch master dari repositori utama dan, jika diperlukan, lakukan rebase ke branch master yang terbaru sebelum kamu mengirim pull request. Apabila terdapat masalah kita melakukan proses merge ke branch master kemungkinan kamu akan diminta untuk melakukan rebase pada perubahan yang sudah dibuat.
|
||||
|
||||
- Sebaiknya buatlah commit seminimal mungkin, sambil memastikan bahwa setiap commit yang dibuat sudah benar (contohnya, setiap commit harus bisa di kompilasi dan berhasil melewati tahap test).
|
||||
|
||||
- Setiap commit harus disertai dengan tanda tangan Sertifikat Asal Pengembang (Developer Certificate of Origin) (<http://developercertificate.org>), yang mengindikasikan bahwa kamu (and your employer if applicable) bersedia untuk patuh terhadap persyaratan dari [lisensi projek](../LICENCE). Di git bash, ini adalah opsi parameter `-s` pada `git commit`
|
||||
|
||||
- Jika perubahan yang kamu buat tidak mendapat tinjauan atau kamu membutuhkan orang tertentu untuk meninjaunya, kamu bisa @-reply seorang reviewer meminta peninjauan dalam permintaan pull request atau komentar, atau kamu bisa meminta tinjauan melalui [email](mailto:info@rustdesk.com).
|
||||
|
||||
- Sertakan test yang relevan terhadap bug atau fitur baru yang sudah dikerjakan.
|
||||
|
||||
Untuk instruksi Git yang lebih lanjut, cek disini [GitHub workflow 101](https://github.com/servo/servo/wiki/GitHub-workflow).
|
||||
|
||||
## Tindakan
|
||||
|
||||
<https://github.com/rustdesk/rustdesk/blob/master/docs/CODE_OF_CONDUCT-ID.md>
|
||||
|
||||
## Komunikasi
|
||||
|
||||
Kontributor RustDesk sering berkunjung ke [Discord](https://discord.gg/nDceKgxnkV).
|
31
docs/CONTRIBUTING-TR.md
Normal file
31
docs/CONTRIBUTING-TR.md
Normal file
@ -0,0 +1,31 @@
|
||||
# RustDesk'a Katkı Sağlamak
|
||||
|
||||
RustDesk, herkesten katkıyı memnuniyetle karşılar. Eğer bize yardımcı olmayı düşünüyorsanız, işte rehberlik eden kurallar:
|
||||
|
||||
## Katkılar
|
||||
|
||||
RustDesk veya bağımlılıklarına yapılan katkılar, GitHub pull istekleri şeklinde yapılmalıdır. Her bir pull isteği, çekirdek katkıcı tarafından gözden geçirilecek (yamaları kabul etme izni olan biri) ve ana ağaca kabul edilecek veya gerekli değişiklikler için geri bildirim verilecektir. Tüm katkılar bu formata uymalıdır, çekirdek katkıcılardan gelenler bile.
|
||||
|
||||
Eğer bir konu üzerinde çalışmak isterseniz, önce üzerinde çalışmak istediğinizi belirten bir yorum yaparak konuyu talep ediniz. Bu, katkı sağlayanların aynı konuda çift çalışmasını engellemek içindir.
|
||||
|
||||
## Pull İstek Kontrol Listesi
|
||||
|
||||
- Master dalından dallandırın ve gerekiyorsa pull isteğinizi göndermeden önce mevcut master dalına rebase yapın. Eğer master ile temiz bir şekilde birleşmezse, değişikliklerinizi rebase yapmanız istenebilir.
|
||||
|
||||
- Her bir commit mümkün olduğunca küçük olmalıdır, ancak her commit'in bağımsız olarak doğru olduğundan emin olun (örneğin, her commit derlenebilir ve testleri geçmelidir).
|
||||
|
||||
- Commit'ler, bir Geliştirici Sertifikası ile desteklenmelidir (http://developercertificate.org). Bu, [proje lisansının](../LICENCE) koşullarına uymayı kabul ettiğinizi gösteren bir onaydır. Git'te bunu `git commit` seçeneği olarak `-s` seçeneği ile yapabilirsiniz.
|
||||
|
||||
- Yamalarınız gözden geçirilmiyorsa veya belirli bir kişinin gözden geçirmesine ihtiyacınız varsa, çekme isteği veya yorum içinde bir gözden geçirmeyi istemek için bir inceleyiciyi @etiketleyebilir veya inceleme için [e-posta](mailto:info@rustdesk.com) ile talep edebilirsiniz.
|
||||
|
||||
- Düzelttiğiniz hatanın veya eklediğiniz yeni özelliğin ilgili testlerini ekleyin.
|
||||
|
||||
Daha spesifik git talimatları için, [GitHub iş akışı 101](https://github.com/servo/servo/wiki/GitHub-workflow)'e bakınız.
|
||||
|
||||
## Davranış
|
||||
|
||||
https://github.com/rustdesk/rustdesk/blob/master/docs/CODE_OF_CONDUCT-TR.md
|
||||
|
||||
## İletişim
|
||||
|
||||
RustDesk katkı sağlayıcıları, [Discord](https://discord.gg/nDceKgxnkV) kanalını sık sık ziyaret ederler.
|
12
docs/DEVCONTAINER-TR.md
Normal file
12
docs/DEVCONTAINER-TR.md
Normal file
@ -0,0 +1,12 @@
|
||||
Docker konteynerinde devcontainer'ın başlatılmasından sonra, hata ayıklama modunda bir Linux ikili dosyası oluşturulur.
|
||||
|
||||
Şu anda devcontainer, hata ayıklama ve sürüm modunda hem Linux hem de Android derlemeleri sunmaktadır.
|
||||
|
||||
Aşağıda, belirli derlemeler oluşturmak için projenin kökünden çalıştırılması gereken komutlar yer almaktadır.
|
||||
|
||||
Komut | Derleme Türü | Mod
|
||||
-|-|-
|
||||
`.devcontainer/build.sh --debug linux` | Linux | hata ayıklama
|
||||
`.devcontainer/build.sh --release linux` | Linux | sürüm
|
||||
`.devcontainer/build.sh --debug android` | Android-arm64 | hata ayıklama
|
||||
`.devcontainer/build.sh --release android` | Android-arm64 | sürüm
|
@ -6,52 +6,64 @@
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<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>
|
||||
<b>Kami membutuhkan bantuanmu untuk menterjemahkan file README dan <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> ke Bahasa Indonesia</b>
|
||||
</p>
|
||||
|
||||
Birbincang bersama kami: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||
Mari mengobrol bersama kami: [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)
|
||||
|
||||
Perangkat lunak desktop jarak jauh lainnya, ditulis dengan Rust. Bekerja begitu saja, tidak memerlukan konfigurasi. Anda memiliki kendali penuh atas data Anda, tanpa khawatir tentang keamanan. Anda dapat menggunakan server rendezvous/relay kami, [konfigurasi server sendiri](https://rustdesk.com/server), or [tulis rendezvous/relay server anda sendiri](https://github.com/rustdesk/rustdesk-server-demo).
|
||||
Merupakan perangkat lunak Remote Desktop yang baru, dan dibangun dengan Rust. Bahkan kamu bisa langsung menggunakannya tanpa perlu melakukan konfigurasi tambahan. Serta memiliki kontrol penuh terhadap semua data, tanpa perlu merasa was-was tentang isu keamanan, dan yang lebih menarik adalah memiliki opsi untuk menggunakan server rendezvous/relay milik kami, [konfigurasi server sendiri](https://rustdesk.com/server), atau [tulis rendezvous/relay server anda sendiri](https://github.com/rustdesk/rustdesk-server-demo).
|
||||
|
||||
RustDesk menyambut baik kontribusi dari semua orang. Lihat [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) untuk membantu sebelum memulai.
|
||||
RustDesk mengajak semua orang untuk ikut berkontribusi. Lihat [`docs/CONTRIBUTING-ID.md`](CONTRIBUTING-ID.md) untuk melihat panduan.
|
||||
|
||||
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
|
||||
[**UNDUH BINARY**](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
## Publik Server Gratis
|
||||
## Server Publik Gratis
|
||||
|
||||
Di bawah ini adalah server yang bisa Anda gunakan secara gratis, dapat berubah seiring waktu. Jika Anda tidak dekat dengan salah satu dari ini, jaringan Anda mungkin lambat.
|
||||
| Lokasi | Vendor | Spesifikasi |
|
||||
Di bawah ini merupakan server gratis yang bisa kamu gunakan, seiring dengan waktu mungkin akan terjadi perubahan spesifikasi pada setiap server yang ada. Jika lokasi kamu berada jauh dengan salah satu server yang tersedia, kemungkinan koneksi akan terasa lambat ketika melakukan proses remote.
|
||||
| Lokasi | Penyedia | Spesifikasi |
|
||||
| --------- | ------------- | ------------------ |
|
||||
| Germany | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
| Jerman | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4GB RAM |
|
||||
| Ukraina (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Dependencies
|
||||
## Dev Container
|
||||
|
||||
Versi desktop menggunakan [sciter](https://sciter.com/) untuk GUI, silahkan download sendiri sciter dynamic library.
|
||||
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
|
||||
|
||||
Apabila PC kamu sudah terinstal VS Code dan Docker, kamu bisa mengklik badge yang ada diatas untuk memulainya. Dengan mengklik badge tersebut secara otomatis akan menginstal ekstensi pada VS Code, lakukan kloning (clone) source code kedalam container volume, dan aktifkan dev container untuk menggunakannya.
|
||||
|
||||
## Dependensi
|
||||
|
||||
Pada versi desktop, antarmuka pengguna (GUI) menggunakan [Sciter](https://sciter.com/) atau flutter
|
||||
|
||||
Kamu bisa mengunduh Sciter dynamic library disini.
|
||||
|
||||
[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)
|
||||
|
||||
## Langkah untuk RAW Build
|
||||
## Langkah awal untuk memulai
|
||||
|
||||
- Siapkan env pengembangan Rust dan C++ build env
|
||||
- Siapkan env development Rust dan env build C++
|
||||
|
||||
- Install [vcpkg](https://github.com/microsoft/vcpkg), dan arahkan `VCPKG_ROOT` env variable dengan benar
|
||||
- Install [vcpkg](https://github.com/microsoft/vcpkg), dan atur variabel env `VCPKG_ROOT` dengan benar
|
||||
|
||||
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
|
||||
- Linux/MacOS: vcpkg install libvpx libyuv opus aom
|
||||
|
||||
- jalankan `cargo run`
|
||||
|
||||
## Bagaimana Build di Linux
|
||||
## [Build](https://rustdesk.com/docs/en/dev/build/)
|
||||
|
||||
## Cara Build di 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
|
||||
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
|
||||
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
|
||||
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
|
||||
```
|
||||
|
||||
### Fedora 28 (CentOS 8)
|
||||
@ -78,7 +90,7 @@ export VCPKG_ROOT=$HOME/vcpkg
|
||||
vcpkg/vcpkg install libvpx libyuv opus aom
|
||||
```
|
||||
|
||||
### Perbaiki libvpx (Untuk Fedora)
|
||||
### Mengatasi masalah libvpx (Untuk Fedora)
|
||||
|
||||
```sh
|
||||
cd vcpkg/buildtrees/libvpx/src
|
||||
@ -104,13 +116,40 @@ mv libsciter-gtk.so target/debug
|
||||
VCPKG_ROOT=$HOME/vcpkg cargo run
|
||||
```
|
||||
|
||||
### Ubah Wayland menjadi X11 (Xorg)
|
||||
### Mengubah Wayland ke X11 (Xorg)
|
||||
|
||||
RustDesk tidak mendukung Wayland. Cek [ini](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) untuk mengonfigurasi Xorg sebagai sesi GNOME default.
|
||||
RustDesk tidak mendukung Wayland. Cek [ini](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) untuk mengonfigurasi Xorg sebagai sesi standar di GNOME.
|
||||
|
||||
## Bagaimana build dengan Docker
|
||||
## Kompatibilitas dengan Wayland
|
||||
|
||||
Mulailah dengan mengkloning repositori dan build dengan docker container:
|
||||
Sepertinya Wayland tidak memiliki API untuk mengirimkan ketukan tombol ke jendela lain. Maka dari itu, RustDesk menggunakan API dari level yang lebih rendah, lebih tepatnya perangkat `/dev/uinput` (linux kernel level)
|
||||
|
||||
Saat Wayland menjadi sisi yang dikendalikan atau sisi yang sedang diremote, kamu harus memulai dengan cara ini
|
||||
|
||||
```bash
|
||||
# Start uinput service
|
||||
$ sudo rustdesk --service
|
||||
$ rustdesk
|
||||
```
|
||||
|
||||
**Harap Diperhatikan**: Saat Perekaman layar menggunakan Wayland antarmuka (UI) yang ditampilkan akan berbeda. Untuk saat ini RustDesk hanya mendukung org.freedesktop.portal.ScreenCast.
|
||||
|
||||
```bash
|
||||
$ dbus-send --session --print-reply \
|
||||
--dest=org.freedesktop.portal.Desktop \
|
||||
/org/freedesktop/portal/desktop \
|
||||
org.freedesktop.DBus.Properties.Get \
|
||||
string:org.freedesktop.portal.ScreenCast string:version
|
||||
# Not support
|
||||
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
|
||||
# Support
|
||||
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
|
||||
variant uint32 4
|
||||
```
|
||||
|
||||
## Cara Build dengan Docker
|
||||
|
||||
Mulailah dengan melakukan kloning (clone) repositori dan build dengan docker container:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
@ -118,25 +157,25 @@ cd rustdesk
|
||||
docker build -t "rustdesk-builder" .
|
||||
```
|
||||
|
||||
Kemudian, setiap kali Anda perlu build aplikasi, jalankan perintah berikut:
|
||||
Selanjutnya, setiap kali ketika kamu akan melakukan build aplikasi, jalankan perintah berikut:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
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:
|
||||
Perlu diingat bahwa pada saat build pertama kali, mungkin memerlukan waktu lebih lama sebelum dependensi di-cache, build berikutnya akan lebih cepat. Selain itu, jika perlu menentukan argumen yang berbeda untuk perintah build, kamu dapat melakukannya di akhir perintah di posisi `<OPTIONAL-ARGS>`. Misalnya, jika ingin membangun versi rilis yang dioptimalkan, jalankan perintah di atas dan tambahkan `--release`. Hasil eksekusi perintah tersebut akan tersimpan pada target folder di sistem kamu, dan dapat dijalankan dengan:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
```
|
||||
|
||||
Atau, jika Anda menjalankan rilis yang dapat dieksekusi:
|
||||
Atau, jika kamu menjalankan rilis yang dapat dieksekusi:
|
||||
|
||||
```sh
|
||||
target/release/rustdesk
|
||||
```
|
||||
|
||||
Harap pastikan bahwa Anda menjalankan perintah ini dari root repositori RustDesk, jika tidak, aplikasi mungkin tidak dapat menemukan sumber daya yang diperlukan. Perhatikan juga perintah cargo seperti `install` atau `run` saat ini tidak didukung melalui metode ini karena mereka akan menginstal atau menjalankan program di dalam container bukan pada host.
|
||||
Harap pastikan bahwa kamu menjalankan perintah ini dari repositori root RustDesk, jika tidak demikian, aplikasi mungkin tidak dapat menemukan sumber yang diperlukan. Dan juga, perintah cargo seperti `install` atau `run` saat ini tidak didukung melalui metode ini karena, proses menginstal atau menjalankan program terjadi di dalam container bukan pada host.
|
||||
|
||||
## Struktur File
|
||||
|
||||
|
223
docs/README-TR.md
Normal file
223
docs/README-TR.md
Normal file
@ -0,0 +1,223 @@
|
||||
|
||||
<p align="center">
|
||||
<img src="../res/logo-header.svg" alt="RustDesk - Uzak masaüstü uygulamanız"><br>
|
||||
<a href="#free-public-servers">Sunucular</a> •
|
||||
<a href="#raw-steps-to-build">Derleme</a> •
|
||||
<a href="#how-to-build-with-docker">Docker ile Derleme</a> •
|
||||
<a href="#file-structure">Dosya Yapısı</a> •
|
||||
<a href="#snapshot">Ekran Görüntüleri</a><br>
|
||||
[<a href="docs/README-UA.md">Українська</a>] | [<a href="docs/README-CS.md">česky</a>] | [<a href="docs/README-ZH.md">中文</a>] | [<a href="docs/README-HU.md">Magyar</a>] | [<a href="docs/README-ES.md">Español</a>] | [<a href="docs/README-FA.md">فارسی</a>] | [<a href="docs/README-FR.md">Français</a>] | [<a href="docs/README-DE.md">Deutsch</a>] | [<a href="docs/README-PL.md">Polski</a>] | [<a href="docs/README-ID.md">Indonesian</a>] | [<a href="docs/README-FI.md">Suomi</a>] | [<a href="docs/README-ML.md">മലയാളം</a>] | [<a href="docs/README-JP.md">日本語</a>] | [<a href="docs/README-NL.md">Nederlands</a>] | [<a href="docs/README-IT.md">Italiano</a>] | [<a href="docs/README-RU.md">Русский</a>] | [<a href="docs/README-PTBR.md">Português (Brasil)</a>] | [<a href="docs/README-EO.md">Esperanto</a>] | [<a href="docs/README-KR.md">한국어</a>] | [<a href="docs/README-AR.md">العربي</a>] | [<a href="docs/README-VN.md">Tiếng Việt</a>] | [<a href="docs/README-DA.md">Dansk</a>] | [<a href="docs/README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> ve <a href="https://github.com/rustdesk/doc.rustdesk.com">RustDesk Belge</a>'sini ana dilinize çevirmemiz için yardımınıza ihtiyacımız var</b>
|
||||
</p>
|
||||
|
||||
Bizimle sohbet edin: [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)
|
||||
|
||||
Başka bir uzak masaüstü yazılımı daha, Rust dilinde yazılmış. Hemen kullanıma hazır, hiçbir yapılandırma gerektirmez. Verilerinizin tam kontrolünü elinizde tutarsınız ve güvenlikle ilgili endişeleriniz olmaz. Kendi buluş/iletme sunucumuzu kullanabilirsiniz, [kendi sunucunuzu kurabilirsiniz](https://rustdesk.com/server) veya [kendi buluş/iletme sunucunuzu yazabilirsiniz](https://github.com/rustdesk/rustdesk-server-demo).
|
||||
|
||||
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
|
||||
|
||||
RustDesk, herkesten katkıyı kabul eder. Başlamak için [CONTRIBUTING.md](docs/CONTRIBUTING-TR.md) belgesine göz atın.
|
||||
|
||||
[**SSS**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
|
||||
|
||||
[**BİNARİ İNDİR**](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
[**NİGHTLY DERLEME**](https://github.com/rustdesk/rustdesk/releases/tag/nightly)
|
||||
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="F-Droid'de Alın"
|
||||
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
|
||||
|
||||
## Ücretsiz Genel Sunucular
|
||||
|
||||
Aşağıda ücretsiz olarak kullandığınız sunucular listelenmiştir, zaman içinde değişebilirler. Eğer bunlardan birine yakın değilseniz, ağınız yavaş olabilir.
|
||||
| Konum | Sağlayıcı | Özellikler |
|
||||
| --------- | ------------- | ------------------ |
|
||||
| Almanya | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4 GB RAM |
|
||||
| Almanya | [Codext](https://codext.de) | 4 vCPU / 8 GB RAM |
|
||||
| Ukrayna (Kiev) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
|
||||
|
||||
## Geliştirici Konteyneri
|
||||
|
||||
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
|
||||
|
||||
Eğer zaten VS Code ve Docker kurulu ise yukarıdaki rozete tıklayarak başlayabilirsiniz. Tıklamak, VS Code'un gerektiğinde Dev Konteyner eklentisini otomatik olarak yüklemesine, kaynak kodunu bir konteyner hacmine klonlamasına ve kullanım için bir geliştirici konteyneri başlatmasına neden olur.
|
||||
|
||||
Daha fazla bilgi için [DEVCONTAINER.md](docs/DEVCONTAINER-TR.md) belgesine bakabilirsiniz.
|
||||
|
||||
## Bağımlılıklar
|
||||
|
||||
Masaüstü sürümleri GUI için
|
||||
|
||||
[Sciter](https://sciter.com/) veya Flutter kullanır, bu kılavuz sadece Sciter içindir.
|
||||
|
||||
Lütfen Sciter dinamik kütüphanesini kendiniz indirin.
|
||||
|
||||
[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)
|
||||
|
||||
## Temel Derleme Adımları
|
||||
|
||||
- Rust geliştirme ortamınızı ve C++ derleme ortamınızı hazırlayın.
|
||||
|
||||
- [vcpkg](https://github.com/microsoft/vcpkg) yükleyin ve `VCPKG_ROOT` çevresel değişkenini doğru bir şekilde ayarlayın.
|
||||
|
||||
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
|
||||
- Linux/macOS: vcpkg install libvpx libyuv opus aom
|
||||
|
||||
- `cargo run` komutunu çalıştırın.
|
||||
|
||||
## [Derleme](https://rustdesk.com/docs/en/dev/build/)
|
||||
|
||||
## Linux Üzerinde Derleme Nasıl Yapılır
|
||||
|
||||
### Ubuntu 18 (Debian 10)
|
||||
|
||||
```sh
|
||||
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
|
||||
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
|
||||
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
|
||||
```
|
||||
|
||||
### openSUSE Tumbleweed
|
||||
|
||||
```sh
|
||||
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
|
||||
```
|
||||
|
||||
### 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 pipewire
|
||||
```
|
||||
|
||||
### vcpkg'yi Yükleyin
|
||||
|
||||
```sh
|
||||
git clone https://github.com/microsoft/vcpkg
|
||||
cd vcpkg
|
||||
git checkout 2023.04.15
|
||||
cd ..
|
||||
vcpkg/bootstrap-vcpkg.sh
|
||||
export VCPKG_ROOT=$HOME/vcpkg
|
||||
vcpkg/vcpkg install libvpx libyuv opus aom
|
||||
```
|
||||
|
||||
### libvpx'i Düzeltin (Fedora için)
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Derleme
|
||||
|
||||
```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) Olarak Değiştirme
|
||||
|
||||
RustDesk, Wayland'ı desteklemez. Xorg'u GNOME oturumu olarak varsayılan olarak ayarlamak için [burayı](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) kontrol edin.
|
||||
|
||||
## Wayland Desteği
|
||||
|
||||
Wayland'ın diğer pencerelere tuş vuruşu göndermek için herhangi bir API sağlamadığı görünmektedir. Bu nedenle, RustDesk daha düşük bir seviyeden, yani Linux çekirdek seviyesindeki `/dev/uinput` cihazının API'sini kullanır.
|
||||
|
||||
Wayland tarafı kontrol edildiğinde, aşağıdaki şekilde başlatmanız gerekir:
|
||||
```bash
|
||||
# uinput servisini başlatın
|
||||
$ sudo rustdesk --service
|
||||
$ rustdesk
|
||||
```
|
||||
**Uyarı**: Wayland ekran kaydı farklı arayüzler kullanır. RustDesk şu anda yalnızca org.freedesktop.portal.ScreenCast'ı destekler.
|
||||
```bash
|
||||
$ dbus-send --session --print-reply \
|
||||
--dest=org.freedesktop.portal.Desktop \
|
||||
/org/freedesktop/portal/desktop \
|
||||
org.freedesktop.DBus.Properties.Get \
|
||||
string:org.freedesktop.portal.ScreenCast string:version
|
||||
# Desteklenmez
|
||||
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
|
||||
# Desteklenir
|
||||
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
|
||||
variant uint32 4
|
||||
```
|
||||
|
||||
## Docker ile Derleme Nasıl Yapılır
|
||||
|
||||
Öncelikle deposunu klonlayın ve Docker konteynerini oluşturun:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
cd rustdesk
|
||||
docker build -t "rustdesk-builder" .
|
||||
```
|
||||
|
||||
Ardından, uygulamayı derlemek için her seferinde aşağıdaki komutu çalıştırın:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
İlk derleme, bağımlılıklar önbelleğe alınmadan önce daha uzun sürebilir, sonraki derlemeler daha hızlı olacaktır. Ayrıca, derleme komutuna isteğe bağlı argümanlar belirtmeniz gerekiyorsa, bunu
|
||||
|
||||
komutun sonunda `<İSTEĞE BAĞLI-ARGÜMANLAR>` pozisyonunda yapabilirsiniz. Örneğin, optimize edilmiş bir sürümü derlemek isterseniz, yukarıdaki komutu çalıştırdıktan sonra `--release` ekleyebilirsiniz. Oluşan yürütülebilir dosya sisteminizdeki hedef klasöründe bulunacak ve şu komutla çalıştırılabilir:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
```
|
||||
|
||||
Veya, yayın yürütülebilir dosyası çalıştırılıyorsa:
|
||||
|
||||
```sh
|
||||
target/release/rustdesk
|
||||
```
|
||||
|
||||
Lütfen bu komutları RustDesk deposunun kökünden çalıştırdığınızdan emin olun, aksi takdirde uygulama gereken kaynakları bulamayabilir. Ayrıca, `install` veya `run` gibi diğer cargo altkomutları şu anda bu yöntem aracılığıyla desteklenmemektedir, çünkü bunlar programı konteyner içinde kurar veya çalıştırır ve ana makinede değil.
|
||||
|
||||
## Dosya Yapısı
|
||||
|
||||
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video kodlayıcı, yapılandırma, tcp/udp sarmalayıcı, protobuf, dosya transferi için fs işlevleri ve diğer bazı yardımcı işlevler
|
||||
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: ekran yakalama
|
||||
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: platforma özgü klavye/fare kontrolü
|
||||
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
|
||||
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: ses/pasta/klavye/video hizmetleri ve ağ bağlantıları
|
||||
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: bir eş bağlantısı başlatır
|
||||
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: [rustdesk-server](https://github.com/rustdesk/rustdesk-server) ile iletişim kurar, uzak doğrudan (TCP delik vurma) veya iletme bağlantısını bekler
|
||||
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: platforma özgü kod
|
||||
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: mobil için Flutter kodu
|
||||
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Flutter web istemcisi için JavaScript
|
||||
|
||||
## Ekran Görüntüleri
|
||||
|
||||
![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)
|
||||
```
|
9
docs/SECURITY-TR.md
Normal file
9
docs/SECURITY-TR.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Güvenlik Politikası
|
||||
|
||||
## Bir Güvenlik Açığı Bildirme
|
||||
|
||||
Projemiz için güvenliği çok önemsiyoruz. Kullanıcıların keşfettikleri herhangi bir güvenlik açığını bize bildirmelerini teşvik ediyoruz.
|
||||
Eğer RustDesk projesinde bir güvenlik açığı bulursanız, lütfen info@rustdesk.com adresine sorumlu bir şekilde bildirin.
|
||||
|
||||
Şu an için bir hata ödül programımız bulunmamaktadır. Büyük bir sorunu çözmeye çalışan küçük bir ekibiz. Herhangi bir güvenlik açığını sorumlu bir şekilde bildirmenizi rica ederiz,
|
||||
böylece tüm topluluk için güvenli bir uygulama oluşturmaya devam edebiliriz.
|
@ -12,7 +12,7 @@
|
||||
"name": "rustdesk",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [
|
||||
"bsdtar -zxvf rustdesk-1.2.2.deb",
|
||||
"bsdtar -zxvf rustdesk-1.2.3.deb",
|
||||
"tar -xvf ./data.tar.xz",
|
||||
"cp -r ./usr/* /app/",
|
||||
"mkdir -p /app/bin && ln -s /app/lib/rustdesk/rustdesk /app/bin/rustdesk",
|
||||
@ -26,7 +26,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "file",
|
||||
"path": "../rustdesk-1.2.2.deb"
|
||||
"path": "../rustdesk-1.2.3.deb"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
|
@ -26,6 +26,13 @@ const val WHEEL_BUTTON_UP = 34
|
||||
const val WHEEL_DOWN = 523331
|
||||
const val WHEEL_UP = 963
|
||||
|
||||
const val TOUCH_SCALE_START = 1
|
||||
const val TOUCH_SCALE = 2
|
||||
const val TOUCH_SCALE_END = 3
|
||||
const val TOUCH_PAN_START = 4
|
||||
const val TOUCH_PAN_UPDATE = 5
|
||||
const val TOUCH_PAN_END = 6
|
||||
|
||||
const val WHEEL_STEP = 120
|
||||
const val WHEEL_DURATION = 50L
|
||||
const val LONG_TAP_DELAY = 200L
|
||||
@ -167,6 +174,30 @@ class InputService : AccessibilityService() {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
fun onTouchInput(mask: Int, _x: Int, _y: Int) {
|
||||
when (mask) {
|
||||
TOUCH_PAN_UPDATE -> {
|
||||
mouseX -= _x * SCREEN_INFO.scale
|
||||
mouseY -= _y * SCREEN_INFO.scale
|
||||
mouseX = max(0, mouseX);
|
||||
mouseY = max(0, mouseY);
|
||||
continueGesture(mouseX, mouseY)
|
||||
}
|
||||
TOUCH_PAN_START -> {
|
||||
mouseX = max(0, _x) * SCREEN_INFO.scale
|
||||
mouseY = max(0, _y) * SCREEN_INFO.scale
|
||||
startGesture(mouseX, mouseY)
|
||||
}
|
||||
TOUCH_PAN_END -> {
|
||||
endGesture(mouseX, mouseY)
|
||||
mouseX = max(0, _x) * SCREEN_INFO.scale
|
||||
mouseY = max(0, _y) * SCREEN_INFO.scale
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
private fun consumeWheelActions() {
|
||||
if (isWheelActionsPolling) {
|
||||
|
@ -71,17 +71,26 @@ class MainService : Service() {
|
||||
|
||||
@Keep
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
fun rustMouseInput(mask: Int, x: Int, y: Int) {
|
||||
fun rustPointerInput(kind: String, mask: Int, x: Int, y: Int) {
|
||||
// turn on screen with LIFT_DOWN when screen off
|
||||
if (!powerManager.isInteractive && mask == LIFT_DOWN) {
|
||||
if (!powerManager.isInteractive && (kind == "touch" || mask == LIFT_DOWN)) {
|
||||
if (wakeLock.isHeld) {
|
||||
Log.d(logTag,"Turn on Screen, WakeLock release")
|
||||
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)
|
||||
when (kind) {
|
||||
"touch" -> {
|
||||
InputService.ctx?.onTouchInput(mask, x, y)
|
||||
}
|
||||
"mouse" -> {
|
||||
InputService.ctx?.onMouseInput(mask, x, y)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
1
flutter/assets/auth-gitlab.svg
Normal file
1
flutter/assets/auth-gitlab.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="236" preserveAspectRatio="xMidYMid"><path fill="#e24329" d="m128.075 236.075 47.104-144.97H80.97z"/><path fill="#fc6d26" d="M128.075 236.074 80.97 91.104H14.956z"/><path fill="#fca326" d="M14.956 91.104.642 135.16a9.752 9.752 0 0 0 3.542 10.903l123.891 90.012z"/><path fill="#e24329" d="M14.956 91.105H80.97L52.601 3.79c-1.46-4.493-7.816-4.492-9.275 0z"/><path fill="#fc6d26" d="m128.075 236.074 47.104-144.97h66.015z"/><path fill="#fca326" d="m241.194 91.104 14.314 44.056a9.752 9.752 0 0 1-3.543 10.903l-123.89 90.012z"/><path fill="#e24329" d="M241.194 91.105h-66.015l28.37-87.315c1.46-4.493 7.816-4.492 9.275 0z"/></svg>
|
After Width: | Height: | Size: 684 B |
@ -1077,7 +1077,7 @@ Color str2color(String str, [alpha = 0xFF]) {
|
||||
return Color((hash & 0xFF7FFF) | (alpha << 24));
|
||||
}
|
||||
|
||||
Color str2color2(String str, [alpha = 0xFF]) {
|
||||
Color str2color2(String str, {List<int> existing = const []}) {
|
||||
Map<String, Color> colorMap = {
|
||||
"red": Colors.red,
|
||||
"green": Colors.green,
|
||||
@ -1094,10 +1094,10 @@ Color str2color2(String str, [alpha = 0xFF]) {
|
||||
};
|
||||
final color = colorMap[str.toLowerCase()];
|
||||
if (color != null) {
|
||||
return color.withAlpha(alpha);
|
||||
return color.withAlpha(0xFF);
|
||||
}
|
||||
if (str.toLowerCase() == 'yellow') {
|
||||
return Colors.yellow.withAlpha(alpha);
|
||||
return Colors.yellow.withAlpha(0xFF);
|
||||
}
|
||||
var hash = 0;
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
@ -1105,7 +1105,15 @@ Color str2color2(String str, [alpha = 0xFF]) {
|
||||
}
|
||||
List<Color> colorList = colorMap.values.toList();
|
||||
hash = hash % colorList.length;
|
||||
return colorList[hash].withAlpha(alpha);
|
||||
var result = colorList[hash].withAlpha(0xFF);
|
||||
if (existing.contains(result.value)) {
|
||||
Color? notUsed =
|
||||
colorList.firstWhereOrNull((e) => !existing.contains(e.value));
|
||||
if (notUsed != null) {
|
||||
result = notUsed;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const K = 1024;
|
||||
|
@ -7,6 +7,7 @@ import 'package:flutter_hbb/models/ab_model.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flex_color_picker/flex_color_picker.dart';
|
||||
|
||||
import '../../common.dart';
|
||||
import 'dialog.dart';
|
||||
@ -513,7 +514,7 @@ class AddressBookTag extends StatelessWidget {
|
||||
child: Obx(() => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: tags.contains(name)
|
||||
? str2color2(name, 0xFF)
|
||||
? gFFI.abModel.getTagColor(name)
|
||||
: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(4)),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0),
|
||||
@ -528,7 +529,7 @@ class AddressBookTag extends StatelessWidget {
|
||||
shape: BoxShape.circle,
|
||||
color: tags.contains(name)
|
||||
? Colors.white
|
||||
: str2color2(name)),
|
||||
: gFFI.abModel.getTagColor(name)),
|
||||
).marginOnly(right: radius / 2),
|
||||
Expanded(
|
||||
child: Text(name,
|
||||
@ -568,6 +569,30 @@ class AddressBookTag extends StatelessWidget {
|
||||
Future.delayed(Duration.zero, () => Get.back());
|
||||
});
|
||||
}),
|
||||
getEntry(translate(translate('Change Color')), () async {
|
||||
final model = gFFI.abModel;
|
||||
Color oldColor = model.getTagColor(name);
|
||||
Color newColor = await showColorPickerDialog(
|
||||
context,
|
||||
oldColor,
|
||||
pickersEnabled: {
|
||||
ColorPickerType.accent: false,
|
||||
ColorPickerType.wheel: true,
|
||||
},
|
||||
pickerTypeLabels: {
|
||||
ColorPickerType.primary: translate("Primary Color"),
|
||||
ColorPickerType.wheel: translate("HSV Color"),
|
||||
},
|
||||
actionButtons: ColorPickerActionButtons(
|
||||
dialogOkButtonLabel: translate("OK"),
|
||||
dialogCancelButtonLabel: translate("Cancel")),
|
||||
showColorCode: true,
|
||||
);
|
||||
if (oldColor != newColor) {
|
||||
model.setTagColor(name, newColor);
|
||||
model.pushAb();
|
||||
}
|
||||
}),
|
||||
getEntry(translate("Delete"), () {
|
||||
gFFI.abModel.deleteTag(name);
|
||||
gFFI.abModel.pushAb();
|
||||
|
@ -14,6 +14,7 @@ import './dialog.dart';
|
||||
|
||||
const kOpSvgList = [
|
||||
'github',
|
||||
'gitlab',
|
||||
'google',
|
||||
'apple',
|
||||
'okta',
|
||||
@ -72,6 +73,11 @@ class ButtonOP extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final opLabel = {
|
||||
'github': 'GitHub',
|
||||
'gitlab': 'GitLab'
|
||||
}[op.toLowerCase()] ??
|
||||
toCapitalized(op);
|
||||
return Row(children: [
|
||||
Container(
|
||||
height: height,
|
||||
@ -97,8 +103,7 @@ class ButtonOP extends StatelessWidget {
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${translate("Continue with")} ${op.toLowerCase() == "github" ? "GitHub" : toCapitalized(op)}')),
|
||||
child: Text('${translate("Continue with")} $opLabel')),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -63,8 +63,6 @@ class _PeerCardState extends State<_PeerCard>
|
||||
|
||||
Widget _buildMobile() {
|
||||
final peer = super.widget.peer;
|
||||
final name =
|
||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||
final PeerTabModel peerTabModel = Provider.of(context);
|
||||
return Card(
|
||||
margin: EdgeInsets.symmetric(horizontal: 2),
|
||||
@ -86,35 +84,8 @@ class _PeerCardState extends State<_PeerCard>
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 12, top: 8, bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: str2color('${peer.id}${peer.platform}', 0x7f),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: getPlatformImage(peer.platform)),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(children: [
|
||||
getOnline(4, peer.online),
|
||||
Text(peer.alias.isEmpty
|
||||
? formatID(peer.id)
|
||||
: peer.alias)
|
||||
]),
|
||||
Text(name)
|
||||
],
|
||||
).paddingOnly(left: 8.0),
|
||||
),
|
||||
checkBoxOrActionMoreMobile(peer),
|
||||
],
|
||||
),
|
||||
)));
|
||||
child: _buildPeerTile(context, peer, null)),
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildDesktop() {
|
||||
@ -161,29 +132,30 @@ class _PeerCardState extends State<_PeerCard>
|
||||
}
|
||||
|
||||
Widget _buildPeerTile(
|
||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||
BuildContext context, Peer peer, Rx<BoxDecoration?>? deco) {
|
||||
final name =
|
||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||
final greyStyle = TextStyle(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6));
|
||||
final child = Obx(
|
||||
() => Container(
|
||||
foregroundDecoration: deco.value,
|
||||
child: Row(
|
||||
final child = Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: str2color('${peer.id}${peer.platform}', 0x7f),
|
||||
borderRadius: BorderRadius.only(
|
||||
borderRadius: isMobile
|
||||
? BorderRadius.circular(_tileRadius)
|
||||
: BorderRadius.only(
|
||||
topLeft: Radius.circular(_tileRadius),
|
||||
bottomLeft: Radius.circular(_tileRadius),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
width: 42,
|
||||
child: getPlatformImage(peer.platform, size: 30).paddingAll(6),
|
||||
width: isMobile ? 50 : 42,
|
||||
height: isMobile ? 50 : null,
|
||||
child: getPlatformImage(peer.platform, size: isMobile ? 38 : 30)
|
||||
.paddingAll(6),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
@ -200,21 +172,19 @@ class _PeerCardState extends State<_PeerCard>
|
||||
child: Column(
|
||||
children: [
|
||||
Row(children: [
|
||||
getOnline(8, peer.online),
|
||||
getOnline(isMobile ? 4 : 8, peer.online),
|
||||
Expanded(
|
||||
child: Text(
|
||||
peer.alias.isEmpty
|
||||
? formatID(peer.id)
|
||||
: peer.alias,
|
||||
peer.alias.isEmpty ? formatID(peer.id) : peer.alias,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
)),
|
||||
]).marginOnly(bottom: 0, top: 2),
|
||||
]).marginOnly(top: isMobile ? 0 : 2),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
name,
|
||||
style: greyStyle,
|
||||
style: isMobile ? null : greyStyle,
|
||||
textAlign: TextAlign.start,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@ -222,26 +192,36 @@ class _PeerCardState extends State<_PeerCard>
|
||||
],
|
||||
).marginOnly(top: 2),
|
||||
),
|
||||
checkBoxOrActionMoreDesktop(peer, isTile: true),
|
||||
isMobile
|
||||
? checkBoxOrActionMoreMobile(peer)
|
||||
: checkBoxOrActionMoreDesktop(peer, isTile: true),
|
||||
],
|
||||
).paddingOnly(left: 10.0, top: 3.0),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
final colors = _frontN(peer.tags, 25).map((e) => str2color2(e)).toList();
|
||||
final colors =
|
||||
_frontN(peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList();
|
||||
return Tooltip(
|
||||
message: peer.tags.isNotEmpty
|
||||
message: isMobile
|
||||
? ''
|
||||
: peer.tags.isNotEmpty
|
||||
? '${translate('Tags')}: ${peer.tags.join(', ')}'
|
||||
: '',
|
||||
child: Stack(children: [
|
||||
child,
|
||||
deco == null
|
||||
? child
|
||||
: Obx(
|
||||
() => Container(
|
||||
foregroundDecoration: deco.value,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
if (colors.isNotEmpty)
|
||||
Positioned(
|
||||
top: 2,
|
||||
right: 10,
|
||||
right: isMobile ? 20 : 10,
|
||||
child: CustomPaint(
|
||||
painter: TagPainter(radius: 3, colors: colors),
|
||||
),
|
||||
@ -332,7 +312,8 @@ class _PeerCardState extends State<_PeerCard>
|
||||
),
|
||||
);
|
||||
|
||||
final colors = _frontN(peer.tags, 25).map((e) => str2color2(e)).toList();
|
||||
final colors =
|
||||
_frontN(peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList();
|
||||
return Tooltip(
|
||||
message: peer.tags.isNotEmpty
|
||||
? '${translate('Tags')}: ${peer.tags.join(', ')}'
|
||||
@ -752,14 +733,14 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
style: style,
|
||||
),
|
||||
proc: () async {
|
||||
if (tab == PeerTabIndex.ab) {
|
||||
gFFI.abModel.unrememberPassword(id);
|
||||
bool result = gFFI.abModel.changePassword(id, '');
|
||||
await bind.mainForgetPassword(id: id);
|
||||
gFFI.abModel.pushAb();
|
||||
} else {
|
||||
bind.mainForgetPassword(id: id);
|
||||
showToast(translate('Successful'));
|
||||
bool toast = false;
|
||||
if (result) {
|
||||
toast = tab == PeerTabIndex.ab;
|
||||
gFFI.abModel.pushAb(toastIfFail: toast, toastIfSucc: toast);
|
||||
}
|
||||
if (!toast) showToast(translate('Successful'));
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
|
@ -426,7 +426,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
Widget selectionCount(int count) {
|
||||
return Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text('$count selected'),
|
||||
child: Text('$count ${translate('Selected')}'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -722,7 +722,6 @@ Widget _hoverAction(
|
||||
);
|
||||
return Obx(
|
||||
() => Container(
|
||||
padding: padding,
|
||||
margin: EdgeInsets.symmetric(horizontal: 1),
|
||||
decoration:
|
||||
(hover.value || hoverableWhenfalse?.value == false) ? deco : null,
|
||||
@ -730,6 +729,6 @@ Widget _hoverAction(
|
||||
onHover: (value) => hover.value = value,
|
||||
onTap: onTap,
|
||||
onTapDown: onTapDown,
|
||||
child: child)),
|
||||
child: Container(padding: padding, child: child))),
|
||||
);
|
||||
}
|
||||
|
@ -421,16 +421,13 @@ class AddressBookPeersView extends BasePeersView {
|
||||
if (selectedTags.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
if (idents.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
for (final tag in selectedTags) {
|
||||
if (!idents.contains(tag)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (idents.contains(tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class MyGroupPeerView extends BasePeersView {
|
||||
|
@ -6,6 +6,7 @@ import 'package:flutter/gestures.dart';
|
||||
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/models/model.dart';
|
||||
import 'package:flutter_hbb/models/input_model.dart';
|
||||
|
||||
@ -263,9 +264,9 @@ class _RawTouchGestureDetectorRegionState
|
||||
if (scale != 0) {
|
||||
bind.sessionSendPointer(
|
||||
sessionId: sessionId,
|
||||
msg: json.encode({
|
||||
'touch': {'scale': scale}
|
||||
}));
|
||||
msg: json.encode(
|
||||
PointerEventToRust(kPointerEventKindTouch, 'scale', scale)
|
||||
.toJson()));
|
||||
}
|
||||
} else {
|
||||
// mobile
|
||||
@ -283,9 +284,8 @@ class _RawTouchGestureDetectorRegionState
|
||||
if (isDesktop) {
|
||||
bind.sessionSendPointer(
|
||||
sessionId: sessionId,
|
||||
msg: json.encode({
|
||||
'touch': {'scale': 0}
|
||||
}));
|
||||
msg: json.encode(
|
||||
PointerEventToRust(kPointerEventKindTouch, 'scale', 0).toJson()));
|
||||
} else {
|
||||
// mobile
|
||||
_scale = 1;
|
||||
|
@ -5,6 +5,7 @@ import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
|
||||
const double kDesktopRemoteTabBarHeight = 28.0;
|
||||
const int kInvalidWindowId = -1;
|
||||
const int kMainWindowId = 0;
|
||||
|
||||
const String kPeerPlatformWindows = "Windows";
|
||||
@ -38,7 +39,7 @@ const String kWindowEventGetRemoteList = "get_remote_list";
|
||||
const String kWindowEventGetSessionIdList = "get_session_id_list";
|
||||
|
||||
const String kWindowEventMoveTabToNewWindow = "move_tab_to_new_window";
|
||||
const String kWindowEventCloseForSeparateWindow = "close_for_separate_window";
|
||||
const String kWindowEventGetCachedSessionData = "get_cached_session_data";
|
||||
|
||||
const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
|
||||
const String kOptionOpenInTabs = "allow-open-in-tabs";
|
||||
@ -54,6 +55,9 @@ const String kTabLabelSettingPage = "Settings";
|
||||
const String kWindowPrefix = "wm_";
|
||||
const int kWindowMainId = 0;
|
||||
|
||||
const String kPointerEventKindTouch = "touch";
|
||||
const String kPointerEventKindMouse = "mouse";
|
||||
|
||||
// the executable name of the portable version
|
||||
const String kEnvPortableExecutable = "RUSTDESK_APPNAME";
|
||||
|
||||
|
@ -17,7 +17,6 @@ import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import '../../common/widgets/dialog.dart';
|
||||
import '../../common/widgets/login.dart';
|
||||
|
@ -35,6 +35,7 @@ class RemotePage extends StatefulWidget {
|
||||
Key? key,
|
||||
required this.id,
|
||||
required this.sessionId,
|
||||
required this.tabWindowId,
|
||||
required this.password,
|
||||
required this.toolbarState,
|
||||
required this.tabController,
|
||||
@ -44,6 +45,7 @@ class RemotePage extends StatefulWidget {
|
||||
|
||||
final String id;
|
||||
final SessionID? sessionId;
|
||||
final int? tabWindowId;
|
||||
final String? password;
|
||||
final ToolbarState toolbarState;
|
||||
final String? switchUuid;
|
||||
@ -106,6 +108,7 @@ class _RemotePageState extends State<RemotePage>
|
||||
password: widget.password,
|
||||
switchUuid: widget.switchUuid,
|
||||
forceRelay: widget.forceRelay,
|
||||
tabWindowId: widget.tabWindowId,
|
||||
);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
||||
@ -225,15 +228,29 @@ class _RemotePageState extends State<RemotePage>
|
||||
removeSharedStates(widget.id);
|
||||
}
|
||||
|
||||
Widget buildBody(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
|
||||
Widget emptyOverlay() => BlockableOverlay(
|
||||
/// the Overlay key will be set with _blockableOverlayState in BlockableOverlay
|
||||
/// see override build() in [BlockableOverlay]
|
||||
body: BlockableOverlay(
|
||||
state: _blockableOverlayState,
|
||||
underlying: Container(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
);
|
||||
|
||||
Widget buildBody(BuildContext context) {
|
||||
remoteToolbar(BuildContext context) => RemoteToolbar(
|
||||
id: widget.id,
|
||||
ffi: _ffi,
|
||||
state: widget.toolbarState,
|
||||
onEnterOrLeaveImageSetter: (func) =>
|
||||
_onEnterOrLeaveImage4Toolbar = func,
|
||||
onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Toolbar = null,
|
||||
);
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
body: Stack(
|
||||
children: [
|
||||
Container(
|
||||
color: Colors.black,
|
||||
child: RawKeyFocusScope(
|
||||
focusNode: _rawKeyFocusNode,
|
||||
@ -257,17 +274,21 @@ class _RemotePageState extends State<RemotePage>
|
||||
},
|
||||
inputModel: _ffi.inputModel,
|
||||
child: getBodyForDesktop(context))),
|
||||
upperLayer: [
|
||||
OverlayEntry(
|
||||
builder: (context) => RemoteToolbar(
|
||||
id: widget.id,
|
||||
ffi: _ffi,
|
||||
state: widget.toolbarState,
|
||||
onEnterOrLeaveImageSetter: (func) =>
|
||||
_onEnterOrLeaveImage4Toolbar = func,
|
||||
onEnterOrLeaveImageCleaner: () =>
|
||||
_onEnterOrLeaveImage4Toolbar = null,
|
||||
))
|
||||
Obx(() => Stack(
|
||||
children: [
|
||||
_ffi.ffiModel.pi.isSet.isTrue &&
|
||||
_ffi.ffiModel.waitForFirstImage.isTrue
|
||||
? emptyOverlay()
|
||||
: Offstage(),
|
||||
// Use Overlay to enable rebuild every time on menu button click.
|
||||
_ffi.ffiModel.pi.isSet.isTrue
|
||||
? Overlay(initialEntries: [
|
||||
OverlayEntry(builder: remoteToolbar)
|
||||
])
|
||||
: remoteToolbar(context),
|
||||
_ffi.ffiModel.pi.isSet.isFalse ? emptyOverlay() : Offstage(),
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -55,6 +55,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
RemoteCountState.init();
|
||||
peerId = params['id'];
|
||||
final sessionId = params['session_id'];
|
||||
final tabWindowId = params['tab_window_id'];
|
||||
if (peerId != null) {
|
||||
ConnectionTypeState.init(peerId!);
|
||||
tabController.onSelected = (id) {
|
||||
@ -77,6 +78,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
key: ValueKey(peerId),
|
||||
id: peerId!,
|
||||
sessionId: sessionId == null ? null : SessionID(sessionId),
|
||||
tabWindowId: tabWindowId,
|
||||
password: params['password'],
|
||||
toolbarState: _toolbarState,
|
||||
tabController: tabController,
|
||||
@ -98,12 +100,14 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
print(
|
||||
"[Remote Page] call ${call.method} with args ${call.arguments} from window $fromWindowId");
|
||||
|
||||
dynamic returnValue;
|
||||
// for simplify, just replace connectionId
|
||||
if (call.method == kWindowEventNewRemoteDesktop) {
|
||||
final args = jsonDecode(call.arguments);
|
||||
final id = args['id'];
|
||||
final switchUuid = args['switch_uuid'];
|
||||
final sessionId = args['session_id'];
|
||||
final tabWindowId = args['tab_window_id'];
|
||||
windowOnTop(windowId());
|
||||
ConnectionTypeState.init(id);
|
||||
_toolbarState.setShow(
|
||||
@ -118,6 +122,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
key: ValueKey(id),
|
||||
id: id,
|
||||
sessionId: sessionId == null ? null : SessionID(sessionId),
|
||||
tabWindowId: tabWindowId,
|
||||
password: args['password'],
|
||||
toolbarState: _toolbarState,
|
||||
tabController: tabController,
|
||||
@ -147,12 +152,24 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
.map((e) => '${e.key},${(e.page as RemotePage).ffi.sessionId}')
|
||||
.toList()
|
||||
.join(';');
|
||||
} else if (call.method == kWindowEventCloseForSeparateWindow) {
|
||||
} else if (call.method == kWindowEventGetCachedSessionData) {
|
||||
// Ready to show new window and close old tab.
|
||||
final peerId = call.arguments;
|
||||
try {
|
||||
final remotePage = tabController.state.value.tabs
|
||||
.firstWhere((tab) => tab.key == peerId)
|
||||
.page as RemotePage;
|
||||
returnValue = remotePage.ffi.ffiModel.cachedPeerData.toString();
|
||||
} catch (e) {
|
||||
debugPrint('Failed to get cached session data: $e');
|
||||
}
|
||||
if (returnValue != null) {
|
||||
closeSessionOnDispose[peerId] = false;
|
||||
tabController.closeBy(peerId);
|
||||
}
|
||||
}
|
||||
_update_remote_count();
|
||||
return returnValue;
|
||||
});
|
||||
Future.delayed(Duration.zero, () {
|
||||
restoreWindowPosition(
|
||||
@ -249,7 +266,11 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
if (e.kind != ui.PointerDeviceKind.mouse) {
|
||||
return;
|
||||
}
|
||||
if (e.buttons == 2) {
|
||||
final remotePage = tabController.state.value.tabs
|
||||
.firstWhere((tab) => tab.key == key)
|
||||
.page as RemotePage;
|
||||
if (remotePage.ffi.ffiModel.pi.isSet.isTrue &&
|
||||
e.buttons == 2) {
|
||||
showRightMenu(
|
||||
(CancelFunc cancelFunc) {
|
||||
return _tabMenuBuilder(key, cancelFunc);
|
||||
@ -337,7 +358,15 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
));
|
||||
}
|
||||
|
||||
if (perms['keyboard'] != false && !ffi.ffiModel.viewOnly) {}
|
||||
if (perms['keyboard'] != false && !ffi.ffiModel.viewOnly) {
|
||||
menu.add(RemoteMenuEntry.insertLock(sessionId, padding,
|
||||
dismissFunc: cancelFunc));
|
||||
|
||||
if (pi.platform == kPeerPlatformLinux || pi.sasEnabled) {
|
||||
menu.add(RemoteMenuEntry.insertCtrlAltDel(sessionId, padding,
|
||||
dismissFunc: cancelFunc));
|
||||
}
|
||||
}
|
||||
|
||||
menu.addAll([
|
||||
MenuEntryDivider<String>(),
|
||||
|
@ -771,7 +771,7 @@ class ScreenAdjustor {
|
||||
updateScreen() async {
|
||||
final v = await rustDeskWinManager.call(
|
||||
WindowType.Main, kWindowGetWindowInfo, '');
|
||||
final String valueStr = v;
|
||||
final String valueStr = v.result;
|
||||
if (valueStr.isEmpty) {
|
||||
_screen = null;
|
||||
} else {
|
||||
|
@ -77,7 +77,7 @@ CancelFunc showRightMenu(ToastBuilder builder,
|
||||
targetContext: context,
|
||||
verticalOffset: 0,
|
||||
horizontalOffset: 0,
|
||||
duration: Duration(seconds: 4),
|
||||
duration: Duration(seconds: 300),
|
||||
animationDuration: Duration(milliseconds: 0),
|
||||
animationReverseDuration: Duration(milliseconds: 0),
|
||||
preferDirection: PreferDirection.rightTop,
|
||||
|
@ -268,11 +268,13 @@ hideCmWindow({bool isStartup = false}) async {
|
||||
await windowManager.hide();
|
||||
});
|
||||
} else {
|
||||
if (await windowManager.getOpacity() != 0) {
|
||||
await windowManager.setOpacity(0);
|
||||
bind.mainHideDocker();
|
||||
await windowManager.minimize();
|
||||
await windowManager.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _runApp(
|
||||
|
@ -28,6 +28,7 @@ class AbModel {
|
||||
final pullError = "".obs;
|
||||
final pushError = "".obs;
|
||||
final tags = [].obs;
|
||||
final RxMap<String, int> tagColors = Map<String, int>.fromEntries([]).obs;
|
||||
final peers = List<Peer>.empty(growable: true).obs;
|
||||
final sortTags = shouldSortTags().obs;
|
||||
final retrying = false.obs;
|
||||
@ -80,10 +81,11 @@ class AbModel {
|
||||
if (resp.body.toLowerCase() == "null") {
|
||||
// normal reply, emtpy ab return null
|
||||
tags.clear();
|
||||
tagColors.clear();
|
||||
peers.clear();
|
||||
} else if (resp.body.isNotEmpty) {
|
||||
Map<String, dynamic> json =
|
||||
_jsonDecode(utf8.decode(resp.bodyBytes), resp.statusCode);
|
||||
_jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode);
|
||||
if (json.containsKey('error')) {
|
||||
throw json['error'];
|
||||
} else if (json.containsKey('data')) {
|
||||
@ -93,26 +95,7 @@ class AbModel {
|
||||
} catch (e) {}
|
||||
final data = jsonDecode(json['data']);
|
||||
if (data != null) {
|
||||
final oldOnlineIDs =
|
||||
peers.where((e) => e.online).map((e) => e.id).toList();
|
||||
tags.clear();
|
||||
peers.clear();
|
||||
if (data['tags'] is List) {
|
||||
tags.value = data['tags'];
|
||||
}
|
||||
if (data['peers'] is List) {
|
||||
for (final peer in data['peers']) {
|
||||
peers.add(Peer.fromJson(peer));
|
||||
}
|
||||
}
|
||||
if (isFull(false)) {
|
||||
peers.removeRange(licensedDevices, peers.length);
|
||||
}
|
||||
// restore online
|
||||
peers
|
||||
.where((e) => oldOnlineIDs.contains(e.id))
|
||||
.map((e) => e.online = true)
|
||||
.toList();
|
||||
_deserialize(data);
|
||||
_saveCache(); // save on success
|
||||
}
|
||||
}
|
||||
@ -211,12 +194,15 @@ class AbModel {
|
||||
it.first.alias = alias;
|
||||
}
|
||||
|
||||
void unrememberPassword(String id) {
|
||||
bool changePassword(String id, String hash) {
|
||||
final it = peers.where((element) => element.id == id);
|
||||
if (it.isEmpty) {
|
||||
return;
|
||||
if (it.isNotEmpty) {
|
||||
if (it.first.hash != hash) {
|
||||
it.first.hash = hash;
|
||||
return true;
|
||||
}
|
||||
it.first.hash = '';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> pushAb(
|
||||
@ -225,6 +211,7 @@ class AbModel {
|
||||
bool isRetry = false}) async {
|
||||
debugPrint(
|
||||
"pushAb: toastIfFail:$toastIfFail, toastIfSucc:$toastIfSucc, isRetry:$isRetry");
|
||||
if (!gFFI.userModel.isLogin) return false;
|
||||
pushError.value = '';
|
||||
if (isRetry) retrying.value = true;
|
||||
DateTime startTime = DateTime.now();
|
||||
@ -238,10 +225,7 @@ class AbModel {
|
||||
final api = "${await bind.mainGetApiServer()}/api/ab";
|
||||
var authHeaders = getHttpHeaders();
|
||||
authHeaders['Content-Type'] = "application/json";
|
||||
final peersJsonData = peers.map((e) => e.toAbUploadJson()).toList();
|
||||
final body = jsonEncode({
|
||||
"data": jsonEncode({"tags": tags, "peers": peersJsonData})
|
||||
});
|
||||
final body = jsonEncode({"data": jsonEncode(_serialize())});
|
||||
http.Response resp;
|
||||
// support compression
|
||||
if (licensedDevices > 0 && body.length > 1024) {
|
||||
@ -257,7 +241,7 @@ class AbModel {
|
||||
ret = true;
|
||||
_saveCache();
|
||||
} else {
|
||||
Map<String, dynamic> json = _jsonDecode(resp.body, resp.statusCode);
|
||||
Map<String, dynamic> json = _jsonDecodeResp(resp.body, resp.statusCode);
|
||||
if (json.containsKey('error')) {
|
||||
throw json['error'];
|
||||
} else if (resp.statusCode == 200) {
|
||||
@ -314,6 +298,7 @@ class AbModel {
|
||||
void deleteTag(String tag) {
|
||||
gFFI.abModel.selectedTags.remove(tag);
|
||||
tags.removeWhere((element) => element == tag);
|
||||
tagColors.remove(tag);
|
||||
for (var peer in peers) {
|
||||
if (peer.tags.isEmpty) {
|
||||
continue;
|
||||
@ -349,6 +334,11 @@ class AbModel {
|
||||
}
|
||||
}).toList();
|
||||
}
|
||||
int? oldColor = tagColors[oldTag];
|
||||
if (oldColor != null) {
|
||||
tagColors.remove(oldTag);
|
||||
tagColors.addAll({newTag: oldColor});
|
||||
}
|
||||
}
|
||||
|
||||
void unsetSelectedTags() {
|
||||
@ -364,6 +354,20 @@ class AbModel {
|
||||
}
|
||||
}
|
||||
|
||||
Color getTagColor(String tag) {
|
||||
int? colorValue = tagColors[tag];
|
||||
if (colorValue != null) {
|
||||
return Color(colorValue);
|
||||
}
|
||||
return str2color2(tag, existing: tagColors.values.toList());
|
||||
}
|
||||
|
||||
setTagColor(String tag, Color color) {
|
||||
if (tags.contains(tag)) {
|
||||
tagColors[tag] = color.value;
|
||||
}
|
||||
}
|
||||
|
||||
void merge(Peer r, Peer p) {
|
||||
p.hash = r.hash.isEmpty ? p.hash : r.hash;
|
||||
p.username = r.username.isEmpty ? p.username : r.username;
|
||||
@ -463,12 +467,10 @@ class AbModel {
|
||||
|
||||
_saveCache() {
|
||||
try {
|
||||
final peersJsonData = peers.map((e) => e.toAbUploadJson()).toList();
|
||||
final m = <String, dynamic>{
|
||||
var m = _serialize();
|
||||
m.addAll(<String, dynamic>{
|
||||
"access_token": bind.mainGetLocalOption(key: 'access_token'),
|
||||
"peers": peersJsonData,
|
||||
"tags": tags.map((e) => e.toString()).toList(),
|
||||
};
|
||||
});
|
||||
bind.mainSaveAb(json: jsonEncode(m));
|
||||
} catch (e) {
|
||||
debugPrint('ab save:$e');
|
||||
@ -484,22 +486,13 @@ class AbModel {
|
||||
final cache = await bind.mainLoadAb();
|
||||
final data = jsonDecode(cache);
|
||||
if (data == null || data['access_token'] != access_token) return;
|
||||
tags.clear();
|
||||
peers.clear();
|
||||
if (data['tags'] is List) {
|
||||
tags.value = data['tags'];
|
||||
}
|
||||
if (data['peers'] is List) {
|
||||
for (final peer in data['peers']) {
|
||||
peers.add(Peer.fromJson(peer));
|
||||
}
|
||||
}
|
||||
_deserialize(data);
|
||||
} catch (e) {
|
||||
debugPrint("load ab cache: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _jsonDecode(String body, int statusCode) {
|
||||
Map<String, dynamic> _jsonDecodeResp(String body, int statusCode) {
|
||||
try {
|
||||
Map<String, dynamic> json = jsonDecode(body);
|
||||
return json;
|
||||
@ -512,6 +505,50 @@ class AbModel {
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _serialize() {
|
||||
final peersJsonData = peers.map((e) => e.toAbUploadJson()).toList();
|
||||
final tagColorJsonData = jsonEncode(tagColors);
|
||||
return {
|
||||
"tags": tags,
|
||||
"peers": peersJsonData,
|
||||
"tag_colors": tagColorJsonData
|
||||
};
|
||||
}
|
||||
|
||||
_deserialize(dynamic data) {
|
||||
if (data == null) return;
|
||||
final oldOnlineIDs = peers.where((e) => e.online).map((e) => e.id).toList();
|
||||
tags.clear();
|
||||
tagColors.clear();
|
||||
peers.clear();
|
||||
if (data['tags'] is List) {
|
||||
tags.value = data['tags'];
|
||||
}
|
||||
if (data['peers'] is List) {
|
||||
for (final peer in data['peers']) {
|
||||
peers.add(Peer.fromJson(peer));
|
||||
}
|
||||
}
|
||||
if (isFull(false)) {
|
||||
peers.removeRange(licensedDevices, peers.length);
|
||||
}
|
||||
// restore online
|
||||
peers
|
||||
.where((e) => oldOnlineIDs.contains(e.id))
|
||||
.map((e) => e.online = true)
|
||||
.toList();
|
||||
if (data['tag_colors'] is String) {
|
||||
Map<String, dynamic> map = jsonDecode(data['tag_colors']);
|
||||
tagColors.value = Map<String, int>.from(map);
|
||||
}
|
||||
// add color to tag
|
||||
final tagsWithoutColor =
|
||||
tags.toList().where((e) => !tagColors.containsKey(e)).toList();
|
||||
for (var t in tagsWithoutColor) {
|
||||
tagColors[t] = str2color2(t, existing: tagColors.values.toList()).value;
|
||||
}
|
||||
}
|
||||
|
||||
reSyncToast(Future<bool> future) {
|
||||
if (!shouldSyncAb()) return;
|
||||
Future.delayed(Duration.zero, () async {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:texture_rgba_renderer/texture_rgba_renderer.dart';
|
||||
|
||||
@ -21,7 +20,6 @@ class RenderTexture {
|
||||
_sessionId = sessionId;
|
||||
|
||||
textureRenderer.createTexture(_textureKey).then((id) async {
|
||||
debugPrint("id: $id, texture_key: $_textureKey");
|
||||
if (id != -1) {
|
||||
final ptr = await textureRenderer.getTexturePtr(_textureKey);
|
||||
platformFFI.registerTexture(sessionId, ptr);
|
||||
|
@ -35,6 +35,24 @@ extension ToString on MouseButtons {
|
||||
}
|
||||
}
|
||||
|
||||
class PointerEventToRust {
|
||||
final String kind;
|
||||
final String type;
|
||||
final dynamic value;
|
||||
|
||||
PointerEventToRust(this.kind, this.type, this.value);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'k': kind,
|
||||
'v': {
|
||||
't': type,
|
||||
'v': value,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class InputModel {
|
||||
final WeakReference<FFI> parent;
|
||||
String keyboardMode = "legacy";
|
||||
@ -62,11 +80,11 @@ class InputModel {
|
||||
int _lastButtons = 0;
|
||||
Offset lastMousePos = Offset.zero;
|
||||
|
||||
get id => parent.target?.id ?? "";
|
||||
|
||||
late final SessionID sessionId;
|
||||
|
||||
bool get keyboardPerm => parent.target!.ffiModel.keyboard;
|
||||
String get id => parent.target?.id ?? '';
|
||||
String? get peerPlatform => parent.target?.ffiModel.pi.platform;
|
||||
|
||||
InputModel(this.parent) {
|
||||
sessionId = parent.target!.sessionId;
|
||||
@ -223,14 +241,8 @@ class InputModel {
|
||||
command: command);
|
||||
}
|
||||
|
||||
Map<String, dynamic> getEvent(PointerEvent evt, String type) {
|
||||
Map<String, dynamic> _getMouseEvent(PointerEvent evt, String type) {
|
||||
final Map<String, dynamic> out = {};
|
||||
out['x'] = evt.position.dx;
|
||||
out['y'] = evt.position.dy;
|
||||
if (alt) out['alt'] = 'true';
|
||||
if (shift) out['shift'] = 'true';
|
||||
if (ctrl) out['ctrl'] = 'true';
|
||||
if (command) out['command'] = 'true';
|
||||
|
||||
// Check update event type and set buttons to be sent.
|
||||
int buttons = _lastButtons;
|
||||
@ -260,7 +272,6 @@ class InputModel {
|
||||
|
||||
out['buttons'] = buttons;
|
||||
out['type'] = type;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -292,7 +303,7 @@ class InputModel {
|
||||
}
|
||||
|
||||
/// Modify the given modifier map [evt] based on current modifier key status.
|
||||
Map<String, String> modify(Map<String, String> evt) {
|
||||
Map<String, dynamic> modify(Map<String, dynamic> evt) {
|
||||
if (ctrl) evt['ctrl'] = 'true';
|
||||
if (shift) evt['shift'] = 'true';
|
||||
if (alt) evt['alt'] = 'true';
|
||||
@ -334,35 +345,41 @@ class InputModel {
|
||||
isPhysicalMouse.value = true;
|
||||
}
|
||||
if (isPhysicalMouse.value) {
|
||||
handleMouse(getEvent(e, _kMouseEventMove));
|
||||
handleMouse(_getMouseEvent(e, _kMouseEventMove), e.position);
|
||||
}
|
||||
}
|
||||
|
||||
void onPointerPanZoomStart(PointerPanZoomStartEvent e) {
|
||||
_lastScale = 1.0;
|
||||
_stopFling = true;
|
||||
|
||||
if (peerPlatform == kPeerPlatformAndroid) {
|
||||
handlePointerEvent('touch', 'pan_start', e.position);
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.flutter.dev/release/breaking-changes/trackpad-gestures
|
||||
void onPointerPanZoomUpdate(PointerPanZoomUpdateEvent e) {
|
||||
if (peerPlatform != kPeerPlatformAndroid) {
|
||||
final scale = ((e.scale - _lastScale) * 1000).toInt();
|
||||
_lastScale = e.scale;
|
||||
|
||||
if (scale != 0) {
|
||||
bind.sessionSendPointer(
|
||||
sessionId: sessionId,
|
||||
msg: json.encode({
|
||||
'touch': {'scale': scale}
|
||||
}));
|
||||
msg: json.encode(
|
||||
PointerEventToRust(kPointerEventKindTouch, 'scale', scale)
|
||||
.toJson()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final delta = e.panDelta;
|
||||
_trackpadLastDelta = delta;
|
||||
|
||||
var x = delta.dx.toInt();
|
||||
var y = delta.dy.toInt();
|
||||
if (parent.target?.ffiModel.pi.platform == kPeerPlatformLinux) {
|
||||
if (peerPlatform == kPeerPlatformLinux) {
|
||||
_trackpadScrollUnsent += (delta * _trackpadSpeed);
|
||||
x = _trackpadScrollUnsent.dx.truncate();
|
||||
y = _trackpadScrollUnsent.dy.truncate();
|
||||
@ -378,11 +395,15 @@ class InputModel {
|
||||
}
|
||||
}
|
||||
if (x != 0 || y != 0) {
|
||||
if (peerPlatform == kPeerPlatformAndroid) {
|
||||
handlePointerEvent('touch', 'pan_update', Offset(x.toDouble(), y.toDouble()));
|
||||
} else {
|
||||
bind.sessionSendMouse(
|
||||
sessionId: sessionId,
|
||||
msg: '{"type": "trackpad", "x": "$x", "y": "$y"}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _scheduleFling(double x, double y, int delay) {
|
||||
if ((x == 0 && y == 0) || _stopFling) {
|
||||
@ -436,11 +457,15 @@ class InputModel {
|
||||
}
|
||||
|
||||
void onPointerPanZoomEnd(PointerPanZoomEndEvent e) {
|
||||
if (peerPlatform == kPeerPlatformAndroid) {
|
||||
handlePointerEvent('touch', 'pan_end', e.position);
|
||||
return;
|
||||
}
|
||||
|
||||
bind.sessionSendPointer(
|
||||
sessionId: sessionId,
|
||||
msg: json.encode({
|
||||
'touch': {'scale': 0}
|
||||
}));
|
||||
msg: json.encode(
|
||||
PointerEventToRust(kPointerEventKindTouch, 'scale', 0).toJson()));
|
||||
|
||||
waitLastFlingDone();
|
||||
_stopFling = false;
|
||||
@ -465,21 +490,21 @@ class InputModel {
|
||||
}
|
||||
}
|
||||
if (isPhysicalMouse.value) {
|
||||
handleMouse(getEvent(e, _kMouseEventDown));
|
||||
handleMouse(_getMouseEvent(e, _kMouseEventDown), e.position);
|
||||
}
|
||||
}
|
||||
|
||||
void onPointUpImage(PointerUpEvent e) {
|
||||
if (e.kind != ui.PointerDeviceKind.mouse) return;
|
||||
if (isPhysicalMouse.value) {
|
||||
handleMouse(getEvent(e, _kMouseEventUp));
|
||||
handleMouse(_getMouseEvent(e, _kMouseEventUp), e.position);
|
||||
}
|
||||
}
|
||||
|
||||
void onPointMoveImage(PointerMoveEvent e) {
|
||||
if (e.kind != ui.PointerDeviceKind.mouse) return;
|
||||
if (isPhysicalMouse.value) {
|
||||
handleMouse(getEvent(e, _kMouseEventMove));
|
||||
handleMouse(_getMouseEvent(e, _kMouseEventMove), e.position);
|
||||
}
|
||||
}
|
||||
|
||||
@ -504,19 +529,16 @@ class InputModel {
|
||||
}
|
||||
|
||||
void refreshMousePos() => handleMouse({
|
||||
'x': lastMousePos.dx,
|
||||
'y': lastMousePos.dy,
|
||||
'buttons': 0,
|
||||
'type': _kMouseEventMove,
|
||||
});
|
||||
}, lastMousePos);
|
||||
|
||||
void tryMoveEdgeOnExit(Offset pos) => handleMouse(
|
||||
{
|
||||
'x': pos.dx,
|
||||
'y': pos.dy,
|
||||
'buttons': 0,
|
||||
'type': _kMouseEventMove,
|
||||
},
|
||||
pos,
|
||||
onExit: true,
|
||||
);
|
||||
|
||||
@ -550,17 +572,49 @@ class InputModel {
|
||||
return Offset(x, y);
|
||||
}
|
||||
|
||||
void handleMouse(
|
||||
Map<String, dynamic> evt, {
|
||||
bool onExit = false,
|
||||
}) {
|
||||
double x = evt['x'];
|
||||
double y = max(0.0, evt['y']);
|
||||
final cursorModel = parent.target!.cursorModel;
|
||||
void handlePointerEvent(String kind, String type, Offset offset) {
|
||||
double x = offset.dx;
|
||||
double y = offset.dy;
|
||||
if (_checkPeerControlProtected(x, y)) {
|
||||
return;
|
||||
}
|
||||
// Only touch events are handled for now. So we can just ignore buttons.
|
||||
// to-do: handle mouse events
|
||||
|
||||
late final dynamic evtValue;
|
||||
if (type == 'pan_update') {
|
||||
evtValue = {
|
||||
'x': x.toInt(),
|
||||
'y': y.toInt(),
|
||||
};
|
||||
} else {
|
||||
final isMoveTypes = ['pan_start', 'pan_end'];
|
||||
final pos = handlePointerDevicePos(
|
||||
kPointerEventKindTouch,
|
||||
x,
|
||||
y,
|
||||
isMoveTypes.contains(type),
|
||||
type,
|
||||
);
|
||||
if (pos == null) {
|
||||
return;
|
||||
}
|
||||
evtValue = {
|
||||
'x': pos.x,
|
||||
'y': pos.y,
|
||||
};
|
||||
}
|
||||
|
||||
final evt = PointerEventToRust(kind, type, evtValue).toJson();
|
||||
bind.sessionSendPointer(
|
||||
sessionId: sessionId, msg: json.encode(modify(evt)));
|
||||
}
|
||||
|
||||
bool _checkPeerControlProtected(double x, double y) {
|
||||
final cursorModel = parent.target!.cursorModel;
|
||||
if (cursorModel.isPeerControlProtected) {
|
||||
lastMousePos = ui.Offset(x, y);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!cursorModel.gotMouseControl) {
|
||||
@ -571,10 +625,23 @@ class InputModel {
|
||||
cursorModel.gotMouseControl = true;
|
||||
} else {
|
||||
lastMousePos = ui.Offset(x, y);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
lastMousePos = ui.Offset(x, y);
|
||||
return false;
|
||||
}
|
||||
|
||||
void handleMouse(
|
||||
Map<String, dynamic> evt,
|
||||
Offset offset, {
|
||||
bool onExit = false,
|
||||
}) {
|
||||
double x = offset.dx;
|
||||
double y = max(0.0, offset.dy);
|
||||
if (_checkPeerControlProtected(x, y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var type = '';
|
||||
var isMove = false;
|
||||
@ -592,17 +659,58 @@ class InputModel {
|
||||
return;
|
||||
}
|
||||
evt['type'] = type;
|
||||
|
||||
final pos = handlePointerDevicePos(
|
||||
kPointerEventKindMouse,
|
||||
x,
|
||||
y,
|
||||
isMove,
|
||||
type,
|
||||
onExit: onExit,
|
||||
buttons: evt['buttons'],
|
||||
);
|
||||
if (pos == null) {
|
||||
return;
|
||||
}
|
||||
if (type != '') {
|
||||
evt['x'] = '0';
|
||||
evt['y'] = '0';
|
||||
} else {
|
||||
evt['x'] = '${pos.x}';
|
||||
evt['y'] = '${pos.y}';
|
||||
}
|
||||
|
||||
Map<int, String> mapButtons = {
|
||||
kPrimaryMouseButton: 'left',
|
||||
kSecondaryMouseButton: 'right',
|
||||
kMiddleMouseButton: 'wheel',
|
||||
kBackMouseButton: 'back',
|
||||
kForwardMouseButton: 'forward'
|
||||
};
|
||||
evt['buttons'] = mapButtons[evt['buttons']] ?? '';
|
||||
bind.sessionSendMouse(sessionId: sessionId, msg: json.encode(modify(evt)));
|
||||
}
|
||||
|
||||
Point? handlePointerDevicePos(
|
||||
String kind,
|
||||
double x,
|
||||
double y,
|
||||
bool isMove,
|
||||
String evtType, {
|
||||
bool onExit = false,
|
||||
int buttons = kPrimaryMouseButton,
|
||||
}) {
|
||||
y -= CanvasModel.topToEdge;
|
||||
x -= CanvasModel.leftToEdge;
|
||||
final canvasModel = parent.target!.canvasModel;
|
||||
final nearThr = 3;
|
||||
var nearRight = (canvasModel.size.width - x) < nearThr;
|
||||
var nearBottom = (canvasModel.size.height - y) < nearThr;
|
||||
|
||||
final ffiModel = parent.target!.ffiModel;
|
||||
if (isMove) {
|
||||
canvasModel.moveDesktopMouse(x, y);
|
||||
}
|
||||
|
||||
final nearThr = 3;
|
||||
var nearRight = (canvasModel.size.width - x) < nearThr;
|
||||
var nearBottom = (canvasModel.size.height - y) < nearThr;
|
||||
final d = ffiModel.display;
|
||||
final imageWidth = d.width * canvasModel.scale;
|
||||
final imageHeight = d.height * canvasModel.scale;
|
||||
@ -650,7 +758,7 @@ class InputModel {
|
||||
} catch (e) {
|
||||
debugPrintStack(
|
||||
label: 'canvasModel.scale value ${canvasModel.scale}, $e');
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
int minX = d.x.toInt();
|
||||
@ -659,40 +767,16 @@ class InputModel {
|
||||
int maxY = (d.y + d.height).toInt() - 1;
|
||||
evtX = trySetNearestRange(evtX, minX, maxX, 5);
|
||||
evtY = trySetNearestRange(evtY, minY, maxY, 5);
|
||||
if (kind == kPointerEventKindMouse) {
|
||||
if (evtX < minX || evtY < minY || evtX > maxX || evtY > maxY) {
|
||||
// If left mouse up, no early return.
|
||||
if (evt['buttons'] != kPrimaryMouseButton || type != 'up') {
|
||||
return;
|
||||
if (!(buttons == kPrimaryMouseButton && evtType == 'up')) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type != '') {
|
||||
evtX = 0;
|
||||
evtY = 0;
|
||||
}
|
||||
|
||||
evt['x'] = '$evtX';
|
||||
evt['y'] = '$evtY';
|
||||
var buttons = '';
|
||||
switch (evt['buttons']) {
|
||||
case kPrimaryMouseButton:
|
||||
buttons = 'left';
|
||||
break;
|
||||
case kSecondaryMouseButton:
|
||||
buttons = 'right';
|
||||
break;
|
||||
case kMiddleMouseButton:
|
||||
buttons = 'wheel';
|
||||
break;
|
||||
case kBackMouseButton:
|
||||
buttons = 'back';
|
||||
break;
|
||||
case kForwardMouseButton:
|
||||
buttons = 'forward';
|
||||
break;
|
||||
}
|
||||
evt['buttons'] = buttons;
|
||||
bind.sessionSendMouse(sessionId: sessionId, msg: json.encode(evt));
|
||||
return Point(evtX, evtY);
|
||||
}
|
||||
|
||||
/// Web only
|
||||
|
@ -4,6 +4,7 @@ import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
@ -37,11 +38,52 @@ import 'platform_model.dart';
|
||||
|
||||
typedef HandleMsgBox = Function(Map<String, dynamic> evt, String id);
|
||||
typedef ReconnectHandle = Function(OverlayDialogManager, SessionID, bool);
|
||||
final _waitForImageDialogShow = <UuidValue, bool>{};
|
||||
final _waitForFirstImage = <UuidValue, bool>{};
|
||||
final _constSessionId = Uuid().v4obj();
|
||||
|
||||
class CachedPeerData {
|
||||
Map<String, dynamic> updatePrivacyMode = {};
|
||||
Map<String, dynamic> peerInfo = {};
|
||||
List<Map<String, dynamic>> cursorDataList = [];
|
||||
Map<String, dynamic> lastCursorId = {};
|
||||
bool secure = false;
|
||||
bool direct = false;
|
||||
|
||||
CachedPeerData();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return jsonEncode({
|
||||
'updatePrivacyMode': updatePrivacyMode,
|
||||
'peerInfo': peerInfo,
|
||||
'cursorDataList': cursorDataList,
|
||||
'lastCursorId': lastCursorId,
|
||||
'secure': secure,
|
||||
'direct': direct,
|
||||
});
|
||||
}
|
||||
|
||||
static CachedPeerData? fromString(String s) {
|
||||
try {
|
||||
final map = jsonDecode(s);
|
||||
final data = CachedPeerData();
|
||||
data.updatePrivacyMode = map['updatePrivacyMode'];
|
||||
data.peerInfo = map['peerInfo'];
|
||||
for (final cursorData in map['cursorDataList']) {
|
||||
data.cursorDataList.add(cursorData);
|
||||
}
|
||||
data.lastCursorId = map['lastCursorId'];
|
||||
data.secure = map['secure'];
|
||||
data.direct = map['direct'];
|
||||
return data;
|
||||
} catch (e) {
|
||||
debugPrint('Failed to parse CachedPeerData: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FfiModel with ChangeNotifier {
|
||||
CachedPeerData cachedPeerData = CachedPeerData();
|
||||
PeerInfo _pi = PeerInfo();
|
||||
Display _display = Display();
|
||||
|
||||
@ -56,6 +98,10 @@ class FfiModel with ChangeNotifier {
|
||||
WeakReference<FFI> parent;
|
||||
late final SessionID sessionId;
|
||||
|
||||
RxBool waitForImageDialogShow = true.obs;
|
||||
Timer? waitForImageTimer;
|
||||
RxBool waitForFirstImage = true.obs;
|
||||
|
||||
Map<String, bool> get permissions => _permissions;
|
||||
|
||||
Display get display => _display;
|
||||
@ -114,9 +160,12 @@ class FfiModel with ChangeNotifier {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
clearPermissions();
|
||||
waitForImageTimer?.cancel();
|
||||
}
|
||||
|
||||
setConnectionType(String peerId, bool secure, bool direct) {
|
||||
cachedPeerData.secure = secure;
|
||||
cachedPeerData.direct = direct;
|
||||
_secure = secure;
|
||||
_direct = direct;
|
||||
try {
|
||||
@ -143,6 +192,22 @@ class FfiModel with ChangeNotifier {
|
||||
_permissions.clear();
|
||||
}
|
||||
|
||||
handleCachedPeerData(CachedPeerData data, String peerId) async {
|
||||
handleMsgBox({
|
||||
'type': 'success',
|
||||
'title': 'Successful',
|
||||
'text': 'Connected, waiting for image...',
|
||||
'link': '',
|
||||
}, sessionId, peerId);
|
||||
updatePrivacyMode(data.updatePrivacyMode, sessionId, peerId);
|
||||
setConnectionType(peerId, data.secure, data.direct);
|
||||
handlePeerInfo(data.peerInfo, peerId);
|
||||
for (var element in data.cursorDataList) {
|
||||
handleCursorData(element);
|
||||
}
|
||||
handleCursorId(data.lastCursorId);
|
||||
}
|
||||
|
||||
// todo: why called by two position
|
||||
StreamEventHandler startEventListener(SessionID sessionId, String peerId) {
|
||||
return (evt) async {
|
||||
@ -159,9 +224,9 @@ class FfiModel with ChangeNotifier {
|
||||
} else if (name == 'switch_display') {
|
||||
handleSwitchDisplay(evt, sessionId, peerId);
|
||||
} else if (name == 'cursor_data') {
|
||||
await parent.target?.cursorModel.updateCursorData(evt);
|
||||
await handleCursorData(evt);
|
||||
} else if (name == 'cursor_id') {
|
||||
await parent.target?.cursorModel.updateCursorId(evt);
|
||||
await handleCursorId(evt);
|
||||
} else if (name == 'cursor_position') {
|
||||
await parent.target?.cursorModel.updateCursorPosition(evt, peerId);
|
||||
} else if (name == 'clipboard') {
|
||||
@ -241,6 +306,17 @@ class FfiModel with ChangeNotifier {
|
||||
handleReloading(evt);
|
||||
} else if (name == 'plugin_option') {
|
||||
handleOption(evt);
|
||||
} else if (name == "sync_peer_password_to_ab") {
|
||||
if (desktopType == DesktopType.main) {
|
||||
final id = evt['id'];
|
||||
final password = evt['password'];
|
||||
if (id != null && password != null) {
|
||||
if (gFFI.abModel
|
||||
.changePassword(id.toString(), password.toString())) {
|
||||
gFFI.abModel.pushAb(toastIfFail: false, toastIfSucc: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debugPrint('Unknown event name: $name');
|
||||
}
|
||||
@ -425,7 +501,7 @@ class FfiModel with ChangeNotifier {
|
||||
closeConnection();
|
||||
}
|
||||
|
||||
if (_waitForFirstImage[sessionId] == false) return;
|
||||
if (waitForFirstImage.isFalse) return;
|
||||
dialogManager.show(
|
||||
(setState, close, context) => CustomAlertDialog(
|
||||
title: null,
|
||||
@ -436,7 +512,12 @@ class FfiModel with ChangeNotifier {
|
||||
onCancel: onClose),
|
||||
tag: '$sessionId-waiting-for-image',
|
||||
);
|
||||
_waitForImageDialogShow[sessionId] = true;
|
||||
waitForImageDialogShow.value = true;
|
||||
waitForImageTimer = Timer(Duration(milliseconds: 1500), () {
|
||||
if (waitForFirstImage.isTrue) {
|
||||
bind.sessionInputOsPassword(sessionId: sessionId, value: '');
|
||||
}
|
||||
});
|
||||
bind.sessionOnWaitingForImageDialogShow(sessionId: sessionId);
|
||||
}
|
||||
|
||||
@ -453,6 +534,8 @@ class FfiModel with ChangeNotifier {
|
||||
|
||||
/// Handle the peer info event based on [evt].
|
||||
handlePeerInfo(Map<String, dynamic> evt, String peerId) async {
|
||||
cachedPeerData.peerInfo = evt;
|
||||
|
||||
// recent peer updated by handle_peer_info(ui_session_interface.rs) --> handle_peer_info(client.rs) --> save_config(client.rs)
|
||||
bind.mainLoadRecentPeers();
|
||||
|
||||
@ -503,7 +586,7 @@ class FfiModel with ChangeNotifier {
|
||||
}
|
||||
if (displays.isNotEmpty) {
|
||||
_reconnects = 1;
|
||||
_waitForFirstImage[sessionId] = true;
|
||||
waitForFirstImage.value = true;
|
||||
}
|
||||
Map<String, dynamic> features = json.decode(evt['features']);
|
||||
_pi.features.privacyMode = features['privacy_mode'] == 1;
|
||||
@ -527,6 +610,7 @@ class FfiModel with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
_pi.isSet.value = true;
|
||||
stateGlobal.resetLastResolutionGroupValues(peerId);
|
||||
|
||||
notifyListeners();
|
||||
@ -568,9 +652,20 @@ class FfiModel with ChangeNotifier {
|
||||
return d;
|
||||
}
|
||||
|
||||
handleCursorId(Map<String, dynamic> evt) async {
|
||||
cachedPeerData.lastCursorId = evt;
|
||||
await parent.target?.cursorModel.updateCursorId(evt);
|
||||
}
|
||||
|
||||
handleCursorData(Map<String, dynamic> evt) async {
|
||||
cachedPeerData.cursorDataList.add(evt);
|
||||
await parent.target?.cursorModel.updateCursorData(evt);
|
||||
}
|
||||
|
||||
/// Handle the peer info synchronization event based on [evt].
|
||||
handleSyncPeerInfo(Map<String, dynamic> evt, SessionID sessionId) async {
|
||||
if (evt['displays'] != null) {
|
||||
cachedPeerData.peerInfo['displays'] = evt['displays'];
|
||||
List<dynamic> displays = json.decode(evt['displays']);
|
||||
List<Display> newDisplays = [];
|
||||
for (int i = 0; i < displays.length; ++i) {
|
||||
@ -1585,7 +1680,6 @@ class FFI {
|
||||
/// dialogManager use late to ensure init after main page binding [globalKey]
|
||||
late final dialogManager = OverlayDialogManager();
|
||||
|
||||
late final bool isSessionAdded;
|
||||
late final SessionID sessionId;
|
||||
late final ImageModel imageModel; // session
|
||||
late final FfiModel ffiModel; // session
|
||||
@ -1604,7 +1698,6 @@ class FFI {
|
||||
late final ElevationModel elevationModel; // session
|
||||
|
||||
FFI(SessionID? sId) {
|
||||
isSessionAdded = sId != null;
|
||||
sessionId = sId ?? (isDesktop ? Uuid().v4obj() : _constSessionId);
|
||||
imageModel = ImageModel(WeakReference(this));
|
||||
ffiModel = FfiModel(WeakReference(this));
|
||||
@ -1630,7 +1723,8 @@ class FFI {
|
||||
bool isRdp = false,
|
||||
String? switchUuid,
|
||||
String? password,
|
||||
bool? forceRelay}) {
|
||||
bool? forceRelay,
|
||||
int? tabWindowId}) {
|
||||
closed = false;
|
||||
auditNote = '';
|
||||
assert(!(isFileTransfer && isPortForward), 'more than one connect type');
|
||||
@ -1645,7 +1739,9 @@ class FFI {
|
||||
imageModel.id = id;
|
||||
cursorModel.id = id;
|
||||
}
|
||||
if (!isSessionAdded) {
|
||||
// If tabWindowId != null, this session is a "tab -> window" one.
|
||||
// Else this session is a new one.
|
||||
if (tabWindowId == null) {
|
||||
// ignore: unused_local_variable
|
||||
final addRes = bind.sessionAddSync(
|
||||
sessionId: sessionId,
|
||||
@ -1666,8 +1762,25 @@ class FFI {
|
||||
// Preserved for the rgba data.
|
||||
stream.listen((message) {
|
||||
if (closed) return;
|
||||
if (isSessionAdded && !isToNewWindowNotified.value) {
|
||||
bind.sessionReadyToNewWindow(sessionId: sessionId);
|
||||
if (tabWindowId != null && !isToNewWindowNotified.value) {
|
||||
// Session is read to be moved to a new window.
|
||||
// Get the cached data and handle the cached data.
|
||||
Future.delayed(Duration.zero, () async {
|
||||
final cachedData = await DesktopMultiWindow.invokeMethod(
|
||||
tabWindowId, kWindowEventGetCachedSessionData, id);
|
||||
if (cachedData == null) {
|
||||
// unreachable
|
||||
debugPrint('Unreachable, the cached data is empty.');
|
||||
return;
|
||||
}
|
||||
final data = CachedPeerData.fromString(cachedData);
|
||||
if (data == null) {
|
||||
debugPrint('Unreachable, the cached data cannot be decoded.');
|
||||
return;
|
||||
}
|
||||
ffiModel.handleCachedPeerData(data, id);
|
||||
await bind.sessionRefresh(sessionId: sessionId);
|
||||
});
|
||||
isToNewWindowNotified.value = true;
|
||||
}
|
||||
() async {
|
||||
@ -1710,12 +1823,13 @@ class FFI {
|
||||
}
|
||||
|
||||
void onEvent2UIRgba() async {
|
||||
if (_waitForImageDialogShow[sessionId] == true) {
|
||||
_waitForImageDialogShow[sessionId] = false;
|
||||
if (ffiModel.waitForImageDialogShow.isTrue) {
|
||||
ffiModel.waitForImageDialogShow.value = false;
|
||||
ffiModel.waitForImageTimer?.cancel();
|
||||
clearWaitingForImage(dialogManager, sessionId);
|
||||
}
|
||||
if (_waitForFirstImage[sessionId] == true) {
|
||||
_waitForFirstImage[sessionId] = false;
|
||||
if (ffiModel.waitForFirstImage.value == true) {
|
||||
ffiModel.waitForFirstImage.value = false;
|
||||
dialogManager.dismissAll();
|
||||
await canvasModel.updateViewStyle();
|
||||
await canvasModel.updateScrollStyle();
|
||||
@ -1830,7 +1944,7 @@ class Features {
|
||||
bool privacyMode = false;
|
||||
}
|
||||
|
||||
class PeerInfo {
|
||||
class PeerInfo with ChangeNotifier {
|
||||
String version = '';
|
||||
String username = '';
|
||||
String hostname = '';
|
||||
@ -1842,6 +1956,8 @@ class PeerInfo {
|
||||
List<Resolution> resolutions = [];
|
||||
Map<String, dynamic> platform_additions = {};
|
||||
|
||||
RxBool isSet = false.obs;
|
||||
|
||||
bool get is_wayland => platform_additions['is_wayland'] == true;
|
||||
bool get is_headless => platform_additions['headless'] == true;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ class ServerModel with ChangeNotifier {
|
||||
String _verificationMethod = "";
|
||||
String _temporaryPasswordLength = "";
|
||||
String _approveMode = "";
|
||||
int _zeroClientLengthCounter = 0;
|
||||
|
||||
late String _emptyIdShow;
|
||||
late final IDTextEditingController _serverId;
|
||||
@ -120,6 +121,17 @@ class ServerModel with ChangeNotifier {
|
||||
_emptyIdShow = translate("Generating ...");
|
||||
_serverId = IDTextEditingController(text: _emptyIdShow);
|
||||
|
||||
// initital _hideCm at startup
|
||||
final verificationMethod =
|
||||
bind.mainGetOptionSync(key: "verification-method");
|
||||
final approveMode = bind.mainGetOptionSync(key: 'approve-mode');
|
||||
_hideCm = option2bool(
|
||||
'allow-hide-cm', bind.mainGetOptionSync(key: 'allow-hide-cm'));
|
||||
if (!(approveMode == 'password' &&
|
||||
verificationMethod == kUsePermanentPassword)) {
|
||||
_hideCm = false;
|
||||
}
|
||||
|
||||
timerCallback() async {
|
||||
final connectionStatus =
|
||||
jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>;
|
||||
@ -134,6 +146,17 @@ class ServerModel with ChangeNotifier {
|
||||
if (res != null) {
|
||||
debugPrint("clients not match!");
|
||||
updateClientState(res);
|
||||
} else {
|
||||
if (_clients.isEmpty) {
|
||||
hideCmWindow();
|
||||
if (_zeroClientLengthCounter++ == 12) {
|
||||
// 6 second
|
||||
windowManager.close();
|
||||
}
|
||||
} else {
|
||||
_zeroClientLengthCounter = 0;
|
||||
if (!_hideCm) showCmWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,6 +445,7 @@ class ServerModel with ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
final oldClientLenght = _clients.length;
|
||||
_clients.clear();
|
||||
tabController.state.value.tabs.clear();
|
||||
|
||||
@ -434,6 +458,16 @@ class ServerModel with ChangeNotifier {
|
||||
debugPrint("Failed to decode clientJson '$clientJson', error $e");
|
||||
}
|
||||
}
|
||||
if (desktopType == DesktopType.cm) {
|
||||
if (_clients.isEmpty) {
|
||||
hideCmWindow();
|
||||
} else if (!_hideCm) {
|
||||
showCmWindow();
|
||||
}
|
||||
}
|
||||
if (_clients.length != oldClientLenght) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void addConnection(Map<String, dynamic> evt) {
|
||||
@ -461,6 +495,9 @@ class ServerModel with ChangeNotifier {
|
||||
_clients.removeAt(index_disconnected);
|
||||
tabController.remove(index_disconnected);
|
||||
}
|
||||
if (desktopType == DesktopType.cm && !_hideCm) {
|
||||
showCmWindow();
|
||||
}
|
||||
scrollToBottom();
|
||||
notifyListeners();
|
||||
if (isAndroid && !client.authorized) showLoginDialog(client);
|
||||
@ -581,6 +618,9 @@ class ServerModel with ChangeNotifier {
|
||||
parent.target?.dialogManager.dismissByTag(getLoginDialogTag(id));
|
||||
parent.target?.invokeMethod("cancel_notification", id);
|
||||
}
|
||||
if (desktopType == DesktopType.cm && _clients.isEmpty) {
|
||||
hideCmWindow();
|
||||
}
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint("onClientRemove failed,error:$e");
|
||||
|
@ -95,6 +95,7 @@ class UserModel {
|
||||
_parseAndUpdateUser(UserPayload user) {
|
||||
userName.value = user.name;
|
||||
isAdmin.value = user.isAdmin;
|
||||
bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(user));
|
||||
}
|
||||
|
||||
// update ab and group status
|
||||
|
@ -28,6 +28,13 @@ extension Index on int {
|
||||
}
|
||||
}
|
||||
|
||||
class MultiWindowCallResult {
|
||||
int windowId;
|
||||
dynamic result;
|
||||
|
||||
MultiWindowCallResult(this.windowId, this.result);
|
||||
}
|
||||
|
||||
/// Window Manager
|
||||
/// mainly use it in `Main Window`
|
||||
/// use it in sub window is not recommended
|
||||
@ -47,6 +54,7 @@ class RustDeskMultiWindowManager {
|
||||
var params = {
|
||||
'type': WindowType.RemoteDesktop.index,
|
||||
'id': peerId,
|
||||
'tab_window_id': windowId,
|
||||
'session_id': sessionId,
|
||||
};
|
||||
await _newSession(
|
||||
@ -57,17 +65,15 @@ class RustDeskMultiWindowManager {
|
||||
_remoteDesktopWindows,
|
||||
jsonEncode(params),
|
||||
);
|
||||
await DesktopMultiWindow.invokeMethod(
|
||||
windowId, kWindowEventCloseForSeparateWindow, peerId);
|
||||
}
|
||||
|
||||
newSessionWindow(
|
||||
Future<int> newSessionWindow(
|
||||
WindowType type, String remoteId, String msg, List<int> windows) async {
|
||||
final windowController = await DesktopMultiWindow.createWindow(msg);
|
||||
final windowId = windowController.windowId;
|
||||
windowController
|
||||
..setFrame(const Offset(0, 0) &
|
||||
Size(1280 + windowController.windowId * 20,
|
||||
720 + windowController.windowId * 20))
|
||||
..setFrame(
|
||||
const Offset(0, 0) & Size(1280 + windowId * 20, 720 + windowId * 20))
|
||||
..center()
|
||||
..setTitle(getWindowNameWithId(
|
||||
remoteId,
|
||||
@ -76,11 +82,12 @@ class RustDeskMultiWindowManager {
|
||||
if (Platform.isMacOS) {
|
||||
Future.microtask(() => windowController.show());
|
||||
}
|
||||
registerActiveWindow(windowController.windowId);
|
||||
windows.add(windowController.windowId);
|
||||
registerActiveWindow(windowId);
|
||||
windows.add(windowId);
|
||||
return windowId;
|
||||
}
|
||||
|
||||
_newSession(
|
||||
Future<MultiWindowCallResult> _newSession(
|
||||
bool openInTabs,
|
||||
WindowType type,
|
||||
String methodName,
|
||||
@ -90,9 +97,10 @@ class RustDeskMultiWindowManager {
|
||||
) async {
|
||||
if (openInTabs) {
|
||||
if (windows.isEmpty) {
|
||||
await newSessionWindow(type, remoteId, msg, windows);
|
||||
final windowId = await newSessionWindow(type, remoteId, msg, windows);
|
||||
return MultiWindowCallResult(windowId, null);
|
||||
} else {
|
||||
call(type, methodName, msg);
|
||||
return call(type, methodName, msg);
|
||||
}
|
||||
} else {
|
||||
if (_inactiveWindows.isNotEmpty) {
|
||||
@ -103,15 +111,16 @@ class RustDeskMultiWindowManager {
|
||||
await DesktopMultiWindow.invokeMethod(windowId, methodName, msg);
|
||||
WindowController.fromWindowId(windowId).show();
|
||||
registerActiveWindow(windowId);
|
||||
return;
|
||||
return MultiWindowCallResult(windowId, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
await newSessionWindow(type, remoteId, msg, windows);
|
||||
final windowId = await newSessionWindow(type, remoteId, msg, windows);
|
||||
return MultiWindowCallResult(windowId, null);
|
||||
}
|
||||
}
|
||||
|
||||
Future<dynamic> newSession(
|
||||
Future<MultiWindowCallResult> newSession(
|
||||
WindowType type,
|
||||
String methodName,
|
||||
String remoteId,
|
||||
@ -143,15 +152,15 @@ class RustDeskMultiWindowManager {
|
||||
for (final windowId in windows) {
|
||||
if (await DesktopMultiWindow.invokeMethod(
|
||||
windowId, kWindowEventActiveSession, remoteId)) {
|
||||
return;
|
||||
return MultiWindowCallResult(windowId, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await _newSession(openInTabs, type, methodName, remoteId, windows, msg);
|
||||
return _newSession(openInTabs, type, methodName, remoteId, windows, msg);
|
||||
}
|
||||
|
||||
Future<dynamic> newRemoteDesktop(
|
||||
Future<MultiWindowCallResult> newRemoteDesktop(
|
||||
String remoteId, {
|
||||
String? password,
|
||||
String? switchUuid,
|
||||
@ -168,7 +177,7 @@ class RustDeskMultiWindowManager {
|
||||
);
|
||||
}
|
||||
|
||||
Future<dynamic> newFileTransfer(String remoteId,
|
||||
Future<MultiWindowCallResult> newFileTransfer(String remoteId,
|
||||
{String? password, bool? forceRelay}) async {
|
||||
return await newSession(
|
||||
WindowType.FileTransfer,
|
||||
@ -180,7 +189,7 @@ class RustDeskMultiWindowManager {
|
||||
);
|
||||
}
|
||||
|
||||
Future<dynamic> newPortForward(String remoteId, bool isRDP,
|
||||
Future<MultiWindowCallResult> newPortForward(String remoteId, bool isRDP,
|
||||
{String? password, bool? forceRelay}) async {
|
||||
return await newSession(
|
||||
WindowType.PortForward,
|
||||
@ -193,18 +202,22 @@ class RustDeskMultiWindowManager {
|
||||
);
|
||||
}
|
||||
|
||||
Future<dynamic> call(WindowType type, String methodName, dynamic args) async {
|
||||
Future<MultiWindowCallResult> call(
|
||||
WindowType type, String methodName, dynamic args) async {
|
||||
final wnds = _findWindowsByType(type);
|
||||
if (wnds.isEmpty) {
|
||||
return;
|
||||
return MultiWindowCallResult(kInvalidWindowId, null);
|
||||
}
|
||||
for (final windowId in wnds) {
|
||||
if (_activeWindows.contains(windowId)) {
|
||||
return await DesktopMultiWindow.invokeMethod(
|
||||
windowId, methodName, args);
|
||||
final res =
|
||||
await DesktopMultiWindow.invokeMethod(windowId, methodName, args);
|
||||
return MultiWindowCallResult(windowId, res);
|
||||
}
|
||||
}
|
||||
return await DesktopMultiWindow.invokeMethod(wnds[0], methodName, args);
|
||||
final res =
|
||||
await DesktopMultiWindow.invokeMethod(wnds[0], methodName, args);
|
||||
return MultiWindowCallResult(wnds[0], res);
|
||||
}
|
||||
|
||||
List<int> _findWindowsByType(WindowType type) {
|
||||
|
@ -37,8 +37,6 @@
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>$(PRODUCT_COPYRIGHT)</string>
|
||||
<key>NSMainNibFile</key>
|
||||
|
@ -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.2.2
|
||||
version: 1.2.3
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0"
|
||||
@ -97,6 +97,7 @@ dependencies:
|
||||
dropdown_button2: ^2.0.0
|
||||
uuid: ^3.0.7
|
||||
auto_size_text_field: ^2.2.1
|
||||
flex_color_picker: ^3.3.0
|
||||
|
||||
dev_dependencies:
|
||||
icons_launcher: ^2.0.4
|
||||
|
@ -118,9 +118,29 @@ message TouchScaleUpdate {
|
||||
int32 scale = 1;
|
||||
}
|
||||
|
||||
message TouchPanStart {
|
||||
int32 x = 1;
|
||||
int32 y = 2;
|
||||
}
|
||||
|
||||
message TouchPanUpdate {
|
||||
// The delta x position relative to the previous position.
|
||||
int32 x = 1;
|
||||
// The delta y position relative to the previous position.
|
||||
int32 y = 2;
|
||||
}
|
||||
|
||||
message TouchPanEnd {
|
||||
int32 x = 1;
|
||||
int32 y = 2;
|
||||
}
|
||||
|
||||
message TouchEvent {
|
||||
oneof union {
|
||||
TouchScaleUpdate scale_update = 1;
|
||||
TouchPanStart pan_start = 2;
|
||||
TouchPanUpdate pan_update = 3;
|
||||
TouchPanEnd pan_end = 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1525,6 +1525,12 @@ pub struct Ab {
|
||||
pub peers: Vec<AbPeer>,
|
||||
#[serde(default, deserialize_with = "deserialize_vec_string")]
|
||||
pub tags: Vec<String>,
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "deserialize_string",
|
||||
skip_serializing_if = "String::is_empty"
|
||||
)]
|
||||
pub tag_colors: String,
|
||||
}
|
||||
|
||||
impl Ab {
|
||||
|
@ -154,17 +154,18 @@ pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_init(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_main_service_mouse_input(mask: i32, x: i32, y: i32) -> JniResult<()> {
|
||||
pub fn call_main_service_pointer_input(kind: &str, mask: i32, x: i32, y: i32) -> JniResult<()> {
|
||||
if let (Some(jvm), Some(ctx)) = (
|
||||
JVM.read().unwrap().as_ref(),
|
||||
MAIN_SERVICE_CTX.read().unwrap().as_ref(),
|
||||
) {
|
||||
let mut env = jvm.attach_current_thread_as_daemon()?;
|
||||
let kind = env.new_string(kind)?;
|
||||
env.call_method(
|
||||
ctx,
|
||||
"rustMouseInput",
|
||||
"(III)V",
|
||||
&[JValue::Int(mask), JValue::Int(x), JValue::Int(y)],
|
||||
"rustPointerInput",
|
||||
"(Ljava/lang/String;III)V",
|
||||
&[JValue::Object(&JObject::from(kind)), JValue::Int(mask), JValue::Int(x), JValue::Int(y)],
|
||||
)?;
|
||||
return Ok(());
|
||||
} else {
|
||||
|
@ -1,5 +1,5 @@
|
||||
pkgname=rustdesk
|
||||
pkgver=1.2.2
|
||||
pkgver=1.2.3
|
||||
pkgrel=0
|
||||
epoch=
|
||||
pkgdesc=""
|
||||
|
@ -1,5 +1,5 @@
|
||||
Name: rustdesk
|
||||
Version: 1.2.2
|
||||
Version: 1.2.3
|
||||
Release: 0
|
||||
Summary: RPM package
|
||||
License: GPL-3.0
|
||||
|
@ -1,5 +1,5 @@
|
||||
Name: rustdesk
|
||||
Version: 1.2.2
|
||||
Version: 1.2.3
|
||||
Release: 0
|
||||
Summary: RPM package
|
||||
License: GPL-3.0
|
||||
|
@ -1,5 +1,5 @@
|
||||
Name: rustdesk
|
||||
Version: 1.2.2
|
||||
Version: 1.2.3
|
||||
Release: 0
|
||||
Summary: RPM package
|
||||
License: GPL-3.0
|
||||
|
@ -57,7 +57,10 @@ use scrap::{
|
||||
ImageFormat, ImageRgb,
|
||||
};
|
||||
|
||||
use crate::is_keyboard_mode_supported;
|
||||
use crate::{
|
||||
common::input::{MOUSE_BUTTON_LEFT, MOUSE_TYPE_DOWN, MOUSE_TYPE_UP},
|
||||
is_keyboard_mode_supported,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "flutter"))]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
@ -1074,6 +1077,7 @@ pub struct LoginConfigHandler {
|
||||
pub direct: Option<bool>,
|
||||
pub received: bool,
|
||||
switch_uuid: Option<String>,
|
||||
pub save_ab_password_to_recent: bool, // true: connected with ab password
|
||||
}
|
||||
|
||||
impl Deref for LoginConfigHandler {
|
||||
@ -1647,11 +1651,26 @@ impl LoginConfigHandler {
|
||||
log::debug!("remember password of {}", self.id);
|
||||
}
|
||||
} else {
|
||||
if !password0.is_empty() {
|
||||
if self.save_ab_password_to_recent {
|
||||
config.password = password;
|
||||
log::debug!("save ab password of {} to recent", self.id);
|
||||
} else if !password0.is_empty() {
|
||||
config.password = Default::default();
|
||||
log::debug!("remove password of {}", self.id);
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "flutter")]
|
||||
{
|
||||
// sync ab password with PeerConfig password
|
||||
let password = base64::encode(config.password.clone(), base64::Variant::Original);
|
||||
let evt: HashMap<&str, String> = HashMap::from([
|
||||
("name", "sync_peer_password_to_ab".to_string()),
|
||||
("id", self.id.clone()),
|
||||
("password", password),
|
||||
]);
|
||||
let evt = serde_json::ser::to_string(&evt).unwrap_or("".to_owned());
|
||||
crate::flutter::push_global_event(crate::flutter::APP_TYPE_MAIN, evt);
|
||||
}
|
||||
if config.keyboard_mode.is_empty() {
|
||||
if is_keyboard_mode_supported(&KeyboardMode::Map, get_version_number(&pi.version)) {
|
||||
config.keyboard_mode = KeyboardMode::Map.to_string();
|
||||
@ -2041,13 +2060,20 @@ pub fn send_pointer_device_event(
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `interface` - The interface for sending data.
|
||||
fn activate_os(interface: &impl Interface) {
|
||||
/// * `send_click` - Whether to send a click event.
|
||||
fn activate_os(interface: &impl Interface, send_click: bool) {
|
||||
let left_down = MOUSE_BUTTON_LEFT << 3 | MOUSE_TYPE_DOWN;
|
||||
let left_up = MOUSE_BUTTON_LEFT << 3 | MOUSE_TYPE_UP;
|
||||
send_mouse(left_up, 0, 0, false, false, false, false, interface);
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
send_mouse(0, 0, 0, false, false, false, false, interface);
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
send_mouse(0, 3, 3, false, false, false, false, interface);
|
||||
if send_click {
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
send_mouse(1 | 1 << 3, 0, 0, false, false, false, false, interface);
|
||||
send_mouse(2 | 1 << 3, 0, 0, false, false, false, false, interface);
|
||||
send_mouse(left_down, 0, 0, false, false, false, false, interface);
|
||||
send_mouse(left_up, 0, 0, false, false, false, false, interface);
|
||||
}
|
||||
/*
|
||||
let mut key_event = KeyEvent::new();
|
||||
// do not use Esc, which has problem with Linux
|
||||
@ -2080,10 +2106,15 @@ pub fn input_os_password(p: String, activate: bool, interface: impl Interface) {
|
||||
/// * `activate` - Whether to activate OS.
|
||||
/// * `interface` - The interface for sending data.
|
||||
fn _input_os_password(p: String, activate: bool, interface: impl Interface) {
|
||||
let input_password = !p.is_empty();
|
||||
if activate {
|
||||
activate_os(&interface);
|
||||
// Click event is used to bring up the password input box.
|
||||
activate_os(&interface, input_password);
|
||||
std::thread::sleep(Duration::from_millis(1200));
|
||||
}
|
||||
if !input_password {
|
||||
return;
|
||||
}
|
||||
let mut key_event = KeyEvent::new();
|
||||
key_event.press = true;
|
||||
let mut msg_out = Message::new();
|
||||
@ -2173,6 +2204,7 @@ pub fn handle_login_error(
|
||||
err: &str,
|
||||
interface: &impl Interface,
|
||||
) -> bool {
|
||||
lc.write().unwrap().save_ab_password_to_recent = false;
|
||||
if err == LOGIN_MSG_PASSWORD_EMPTY {
|
||||
lc.write().unwrap().password = Default::default();
|
||||
interface.msgbox("input-password", "Password Required", "", "");
|
||||
@ -2252,11 +2284,15 @@ pub async fn handle_hash(
|
||||
.find_map(|p| if p.id == id { Some(p) } else { None })
|
||||
{
|
||||
if let Ok(hash) = base64::decode(p.hash.clone(), base64::Variant::Original) {
|
||||
if !hash.is_empty() {
|
||||
password = hash;
|
||||
lc.write().unwrap().save_ab_password_to_recent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lc.write().unwrap().password = password.clone();
|
||||
let password = if password.is_empty() {
|
||||
// login without password, the remote side can click accept
|
||||
interface.msgbox("input-password", "Password Required", "", "");
|
||||
@ -2328,9 +2364,9 @@ pub async fn handle_login_from_ui(
|
||||
hasher.update(&lc.read().unwrap().hash.salt);
|
||||
let res = hasher.finalize();
|
||||
lc.write().unwrap().remember = remember;
|
||||
lc.write().unwrap().password = res[..].into();
|
||||
res[..].into()
|
||||
};
|
||||
lc.write().unwrap().password = hash_password.clone();
|
||||
let mut hasher2 = Sha256::new();
|
||||
hasher2.update(&hash_password[..]);
|
||||
hasher2.update(&lc.read().unwrap().hash.challenge);
|
||||
@ -2366,7 +2402,7 @@ pub trait Interface: Send + Clone + 'static + Sized {
|
||||
fn send(&self, data: Data);
|
||||
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str);
|
||||
fn handle_login_error(&mut self, err: &str) -> bool;
|
||||
fn handle_peer_info(&mut self, pi: PeerInfo, is_cached_pi: bool);
|
||||
fn handle_peer_info(&mut self, pi: PeerInfo);
|
||||
fn on_error(&self, err: &str) {
|
||||
self.msgbox("error", "Error", err, "");
|
||||
}
|
||||
|
@ -125,18 +125,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
.await
|
||||
{
|
||||
Ok((mut peer, direct, pk)) => {
|
||||
let is_secured = peer.is_secured();
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
self.handler
|
||||
.cache_flutter
|
||||
.write()
|
||||
.unwrap()
|
||||
.is_secured_direct
|
||||
.replace((is_secured, direct));
|
||||
}
|
||||
self.handler.set_connection_type(is_secured, direct); // flutter -> connection_ready
|
||||
self.handler.set_connection_type(peer.is_secured(), direct); // flutter -> connection_ready
|
||||
self.handler.update_direct(Some(direct));
|
||||
if conn_type == ConnType::DEFAULT_CONN {
|
||||
self.handler
|
||||
@ -1021,12 +1010,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
}
|
||||
Some(login_response::Union::PeerInfo(pi)) => {
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
self.handler.cache_flutter.write().unwrap().pi = pi.clone();
|
||||
}
|
||||
self.handler.handle_peer_info(pi, false);
|
||||
self.handler.handle_peer_info(pi);
|
||||
#[cfg(not(feature = "flutter"))]
|
||||
self.check_clipboard_file_context();
|
||||
if !(self.handler.is_file_transfer() || self.handler.is_port_forward()) {
|
||||
@ -1073,22 +1057,9 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
_ => {}
|
||||
},
|
||||
Some(message::Union::CursorData(cd)) => {
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
let mut lock = self.handler.cache_flutter.write().unwrap();
|
||||
if !lock.cursor_data.contains_key(&cd.id) {
|
||||
lock.cursor_data.insert(cd.id, cd.clone());
|
||||
}
|
||||
}
|
||||
self.handler.set_cursor_data(cd);
|
||||
}
|
||||
Some(message::Union::CursorId(id)) => {
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
self.handler.cache_flutter.write().unwrap().cursor_id = id;
|
||||
}
|
||||
self.handler.set_cursor_id(id.to_string());
|
||||
}
|
||||
Some(message::Union::CursorPosition(cp)) => {
|
||||
@ -1305,16 +1276,6 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
}
|
||||
Some(misc::Union::SwitchDisplay(s)) => {
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
self.handler
|
||||
.cache_flutter
|
||||
.write()
|
||||
.unwrap()
|
||||
.sp
|
||||
.replace(s.clone());
|
||||
}
|
||||
self.handler.handle_peer_switch_display(&s);
|
||||
self.video_sender.send(MediaData::Reset).ok();
|
||||
if s.width > 0 && s.height > 0 {
|
||||
@ -1506,12 +1467,6 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
}
|
||||
Some(message::Union::PeerInfo(pi)) => {
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
self.handler.cache_flutter.write().unwrap().pi.displays =
|
||||
pi.displays.clone();
|
||||
}
|
||||
self.handler.set_displays(&pi.displays);
|
||||
}
|
||||
_ => {}
|
||||
|
@ -25,7 +25,7 @@ use hbb_common::{
|
||||
protobuf::Enum,
|
||||
protobuf::Message as _,
|
||||
rendezvous_proto::*,
|
||||
sleep, socket_client,
|
||||
socket_client,
|
||||
tcp::FramedStream,
|
||||
tokio, ResultType,
|
||||
};
|
||||
@ -831,30 +831,19 @@ pub fn check_software_update() {
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn check_software_update_() -> hbb_common::ResultType<()> {
|
||||
sleep(3.).await;
|
||||
let url = "https://github.com/rustdesk/rustdesk/releases/latest";
|
||||
let latest_release_response = reqwest::get(url).await?;
|
||||
let latest_release_version = latest_release_response
|
||||
.url()
|
||||
.path()
|
||||
.rsplit('/')
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
let rendezvous_server = format!("rs-sg.rustdesk.com:{}", config::RENDEZVOUS_PORT);
|
||||
let (mut socket, rendezvous_server) =
|
||||
socket_client::new_udp_for(&rendezvous_server, CONNECT_TIMEOUT).await?;
|
||||
let response_url = latest_release_response.url().to_string();
|
||||
|
||||
let mut msg_out = RendezvousMessage::new();
|
||||
msg_out.set_software_update(SoftwareUpdate {
|
||||
url: crate::VERSION.to_owned(),
|
||||
..Default::default()
|
||||
});
|
||||
socket.send(&msg_out, rendezvous_server).await?;
|
||||
use hbb_common::protobuf::Message;
|
||||
for _ in 0..2 {
|
||||
if let Some(Ok((bytes, _))) = socket.next_timeout(READ_TIMEOUT).await {
|
||||
if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) {
|
||||
if let Some(rendezvous_message::Union::SoftwareUpdate(su)) = msg_in.union {
|
||||
let version = hbb_common::get_version_from_url(&su.url);
|
||||
if get_version_number(&version) > get_version_number(crate::VERSION) {
|
||||
*SOFTWARE_UPDATE_URL.lock().unwrap() = su.url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if get_version_number(&latest_release_version) > get_version_number(crate::VERSION) {
|
||||
*SOFTWARE_UPDATE_URL.lock().unwrap() = response_url;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -36,9 +36,11 @@ pub(crate) const APP_TYPE_CM: &str = "cm";
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
pub(crate) const APP_TYPE_CM: &str = "main";
|
||||
|
||||
pub(crate) const APP_TYPE_DESKTOP_REMOTE: &str = "remote";
|
||||
pub(crate) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer";
|
||||
pub(crate) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward";
|
||||
// Do not remove the following constants.
|
||||
// Uncomment them when they are used.
|
||||
// pub(crate) const APP_TYPE_DESKTOP_REMOTE: &str = "remote";
|
||||
// pub(crate) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer";
|
||||
// pub(crate) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward";
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub(crate) static ref CUR_SESSION_ID: RwLock<SessionID> = Default::default();
|
||||
@ -1130,6 +1132,85 @@ pub fn stop_global_event_stream(app_type: String) {
|
||||
let _ = GLOBAL_EVENT_STREAM.write().unwrap().remove(&app_type);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn session_send_touch_scale(
|
||||
session_id: SessionID,
|
||||
v: &serde_json::Value,
|
||||
alt: bool,
|
||||
ctrl: bool,
|
||||
shift: bool,
|
||||
command: bool,
|
||||
) {
|
||||
match v.get("v").and_then(|s| s.as_i64()) {
|
||||
Some(scale) => {
|
||||
if let Some(session) = SESSIONS.read().unwrap().get(&session_id) {
|
||||
session.send_touch_scale(scale as _, alt, ctrl, shift, command);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn session_send_touch_pan(
|
||||
session_id: SessionID,
|
||||
v: &serde_json::Value,
|
||||
pan_event: &str,
|
||||
alt: bool,
|
||||
ctrl: bool,
|
||||
shift: bool,
|
||||
command: bool,
|
||||
) {
|
||||
match v.get("v") {
|
||||
Some(v) => match (
|
||||
v.get("x").and_then(|x| x.as_i64()),
|
||||
v.get("y").and_then(|y| y.as_i64()),
|
||||
) {
|
||||
(Some(x), Some(y)) => {
|
||||
if let Some(session) = SESSIONS.read().unwrap().get(&session_id) {
|
||||
session
|
||||
.send_touch_pan_event(pan_event, x as _, y as _, alt, ctrl, shift, command);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn session_send_touch_event(
|
||||
session_id: SessionID,
|
||||
v: &serde_json::Value,
|
||||
alt: bool,
|
||||
ctrl: bool,
|
||||
shift: bool,
|
||||
command: bool,
|
||||
) {
|
||||
match v.get("t").and_then(|t| t.as_str()) {
|
||||
Some("scale") => session_send_touch_scale(session_id, v, alt, ctrl, shift, command),
|
||||
Some(pan_event) => {
|
||||
session_send_touch_pan(session_id, v, pan_event, alt, ctrl, shift, command)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_send_pointer(session_id: SessionID, msg: String) {
|
||||
if let Ok(m) = serde_json::from_str::<HashMap<String, serde_json::Value>>(&msg) {
|
||||
let alt = m.get("alt").is_some();
|
||||
let ctrl = m.get("ctrl").is_some();
|
||||
let shift = m.get("shift").is_some();
|
||||
let command = m.get("command").is_some();
|
||||
match (m.get("k"), m.get("v")) {
|
||||
(Some(k), Some(v)) => match k.as_str() {
|
||||
Some("touch") => session_send_touch_event(session_id, v, alt, ctrl, shift, command),
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn get_rgba() {}
|
||||
|
||||
|
@ -597,14 +597,6 @@ pub fn session_change_resolution(session_id: SessionID, display: i32, width: i32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_ready_to_new_window(session_id: SessionID) {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if let Some(session) = SESSIONS.write().unwrap().get_mut(&session_id) {
|
||||
session.restore_flutter_cache();
|
||||
session.refresh_video();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_set_size(_session_id: SessionID, _width: usize, _height: usize) {
|
||||
#[cfg(feature = "flutter_texture_render")]
|
||||
if let Some(session) = SESSIONS.write().unwrap().get_mut(&_session_id) {
|
||||
@ -895,7 +887,7 @@ pub fn main_load_recent_peers_for_ab(filter: String) -> String {
|
||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers(id_filters)
|
||||
.drain(..)
|
||||
.map(|(id, _, p)| peer_to_map_ab(id, p))
|
||||
.map(|(id, _, p)| peer_to_map(id, p))
|
||||
.collect();
|
||||
return serde_json::ser::to_string(&peers).unwrap_or("".to_owned());
|
||||
}
|
||||
@ -1179,21 +1171,7 @@ pub fn main_load_ab() -> String {
|
||||
}
|
||||
|
||||
pub fn session_send_pointer(session_id: SessionID, msg: String) {
|
||||
if let Ok(m) = serde_json::from_str::<HashMap<String, serde_json::Value>>(&msg) {
|
||||
let alt = m.get("alt").is_some();
|
||||
let ctrl = m.get("ctrl").is_some();
|
||||
let shift = m.get("shift").is_some();
|
||||
let command = m.get("command").is_some();
|
||||
if let Some(touch_event) = m.get("touch") {
|
||||
if let Some(scale) = touch_event.get("scale") {
|
||||
if let Some(session) = SESSIONS.read().unwrap().get(&session_id) {
|
||||
if let Some(scale) = scale.as_i64() {
|
||||
session.send_touch_scale(scale as _, alt, ctrl, shift, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super::flutter::session_send_pointer(session_id, msg);
|
||||
}
|
||||
|
||||
pub fn session_send_mouse(session_id: SessionID, msg: String) {
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", "未成功获取地址簿"),
|
||||
("push_ab_failed_tip", "未成功上传地址簿"),
|
||||
("synced_peer_readded_tip", "最近会话中存在的设备将会被重新同步到地址簿。"),
|
||||
("Change Color", "更改颜色"),
|
||||
("Primary Color", "基本色"),
|
||||
("HSV Color", "HSV 色"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", "Nepodařilo se obnovit adresář"),
|
||||
("push_ab_failed_tip", "Nepodařilo se synchronizovat adresář se serverem"),
|
||||
("synced_peer_readded_tip", "Zařízení, která byla přítomna v posledních relacích, budou synchronizována zpět do adresáře."),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", "Aktualisierung des Adressbuchs fehlgeschlagen"),
|
||||
("push_ab_failed_tip", "Synchronisierung des Adressbuchs mit dem Server fehlgeschlagen"),
|
||||
("synced_peer_readded_tip", "Die Geräte, die in den letzten Sitzungen vorhanden waren, werden erneut zum Adressbuch hinzugefügt."),
|
||||
("Change Color", "Farbe ändern"),
|
||||
("Primary Color", "Primärfarbe"),
|
||||
("HSV Color", "HSV-Farbe"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", "No se ha podido refrescar el directorio"),
|
||||
("push_ab_failed_tip", "No se ha podido sincronizar el directorio con el servidor"),
|
||||
("synced_peer_readded_tip", "Los dispositivos presentes en sesiones recientes se sincronizarán con el directorio."),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
131
src/lang/fr.rs
131
src/lang/fr.rs
@ -75,7 +75,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Do you want to enter again?", "Voulez-vous participer à nouveau ?"),
|
||||
("Connection Error", "Erreur de connexion"),
|
||||
("Error", "Erreur"),
|
||||
("Reset by the peer", "La connexion a été fermée par la machine distante"),
|
||||
("Reset by the peer", "La connexion a été fermée par l'appareil distant"),
|
||||
("Connecting...", "Connexion..."),
|
||||
("Connection in progress. Please wait.", "Connexion en cours. Veuillez patienter."),
|
||||
("Please try 1 minute later", "Réessayez dans une minute"),
|
||||
@ -92,8 +92,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Refresh File", "Rafraîchir le contenu"),
|
||||
("Local", "Local"),
|
||||
("Remote", "Distant"),
|
||||
("Remote Computer", "Ordinateur distant"),
|
||||
("Local Computer", "Ordinateur local"),
|
||||
("Remote Computer", "Appareil distant"),
|
||||
("Local Computer", "Appareil local"),
|
||||
("Confirm Delete", "Confirmer la suppression"),
|
||||
("Delete", "Supprimer"),
|
||||
("Properties", "Propriétés"),
|
||||
@ -129,9 +129,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Show remote cursor", "Afficher le curseur distant"),
|
||||
("Show quality monitor", "Afficher le moniteur de qualité"),
|
||||
("Disable clipboard", "Désactiver le presse-papier"),
|
||||
("Lock after session end", "Verrouiller l'ordinateur distant après la déconnexion"),
|
||||
("Lock after session end", "Verrouiller l'appareil distant après la déconnexion"),
|
||||
("Insert", "Envoyer"),
|
||||
("Insert Lock", "Verrouiller l'ordinateur distant"),
|
||||
("Insert Lock", "Verrouiller l'appareil distant"),
|
||||
("Refresh", "Rafraîchir l'écran"),
|
||||
("ID does not exist", "L'ID n'existe pas"),
|
||||
("Failed to connect to rendezvous server", "Échec de la connexion au serveur rendezvous"),
|
||||
@ -188,7 +188,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Relayed and encrypted connection", "Connexion relais chiffrée"),
|
||||
("Direct and unencrypted connection", "Connexion directe non chiffrée"),
|
||||
("Relayed and unencrypted connection", "Connexion relais non chiffrée"),
|
||||
("Enter Remote ID", "Entrer l'ID de l'appareil à distance"),
|
||||
("Enter Remote ID", "Entrer l'ID de l'appareil distant"),
|
||||
("Enter your password", "Entrer votre mot de passe"),
|
||||
("Logging in...", "En cours de connexion ..."),
|
||||
("Enable RDP session sharing", "Activer le partage de session RDP"),
|
||||
@ -210,7 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Settings", "Paramètres"),
|
||||
("Username", " Nom d'utilisateur"),
|
||||
("Invalid port", "Port invalide"),
|
||||
("Closed manually by the peer", "Fermé manuellement par la machine distante"),
|
||||
("Closed manually by the peer", "Fermé manuellement par l'appareil distant"),
|
||||
("Enable remote configuration modification", "Autoriser la modification de la configuration à distance"),
|
||||
("Run without install", "Exécuter sans installer"),
|
||||
("Connect via relay", "Connexion via relais"),
|
||||
@ -223,18 +223,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Verification code", "Code de vérification"),
|
||||
("verification_tip", "Un nouvel appareil a été détecté et un code de vérification a été envoyé à l'adresse e-mail enregistrée, entrez le code de vérification pour continuer la connexion."),
|
||||
("Logout", "Déconnexion"),
|
||||
("Tags", "Étiqueter"),
|
||||
("Tags", "Étiquettes"),
|
||||
("Search ID", "Rechercher un ID"),
|
||||
("whitelist_sep", "Vous pouvez utiliser une virgule, un point-virgule, un espace ou une nouvelle ligne comme séparateur"),
|
||||
("Add ID", "Ajouter un ID"),
|
||||
("Add Tag", "Ajouter une balise"),
|
||||
("Unselect all tags", "Désélectionner toutes les balises"),
|
||||
("Add Tag", "Ajout étiquette(s)"),
|
||||
("Unselect all tags", "Désélectionner toutes les étiquettes"),
|
||||
("Network error", "Erreur réseau"),
|
||||
("Username missed", "Nom d'utilisateur manquant"),
|
||||
("Password missed", "Mot de passe manquant"),
|
||||
("Wrong credentials", "Identifiant ou mot de passe erroné"),
|
||||
("The verification code is incorrect or has expired", ""),
|
||||
("Edit Tag", "Modifier la balise"),
|
||||
("The verification code is incorrect or has expired", "Le code de vérification est incorrect ou a expiré"),
|
||||
("Edit Tag", "Gestion étiquettes"),
|
||||
("Unremember Password", "Oublier le Mot de passe"),
|
||||
("Favorites", "Favoris"),
|
||||
("Add to Favorites", "Ajouter aux Favoris"),
|
||||
@ -245,7 +245,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Hostname", "Nom d'hôte"),
|
||||
("Discovered", "Découvert"),
|
||||
("install_daemon_tip", "Pour une exécution au démarrage du système, vous devez installer le service système."),
|
||||
("Remote ID", "ID de l'appareil à distance"),
|
||||
("Remote ID", "ID de l'appareil distant"),
|
||||
("Paste", "Coller"),
|
||||
("Paste here?", "Coller ici?"),
|
||||
("Are you sure to close the connection?", "Êtes-vous sûr de fermer la connexion?"),
|
||||
@ -274,7 +274,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Chat", "Discuter"),
|
||||
("Total", "Total"),
|
||||
("items", "éléments"),
|
||||
("Selected", "Sélectionné"),
|
||||
("Selected", "Sélectionné(s)"),
|
||||
("Screen Capture", "Capture d'écran"),
|
||||
("Input Control", "Contrôle de saisie"),
|
||||
("Audio Capture", "Capture audio"),
|
||||
@ -301,9 +301,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Succeeded", "Succès"),
|
||||
("Someone turns on privacy mode, exit", "Quelqu'un active le mode de confidentialité, quittez"),
|
||||
("Unsupported", "Non pris en charge"),
|
||||
("Peer denied", "Machine distante refusée"),
|
||||
("Peer denied", "Appareil distant refusé"),
|
||||
("Please install plugins", "Veuillez installer les plugins"),
|
||||
("Peer exit", ""),
|
||||
("Peer exit", "Appareil distant déconnecté"),
|
||||
("Failed to turn off", "Échec de la désactivation"),
|
||||
("Turned off", "Désactivé"),
|
||||
("In privacy mode", "en mode privé"),
|
||||
@ -337,8 +337,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Ratio", "Rapport"),
|
||||
("Image Quality", "Qualité d'image"),
|
||||
("Scroll Style", "Style de défilement"),
|
||||
("Show Toolbar", ""),
|
||||
("Hide Toolbar", ""),
|
||||
("Show Toolbar", "Afficher la barre d'outils"),
|
||||
("Hide Toolbar", "Masquer la barre d'outils"),
|
||||
("Direct Connection", "Connexion directe"),
|
||||
("Relay Connection", "Connexion relais"),
|
||||
("Secure Connection", "Connexion sécurisée"),
|
||||
@ -367,8 +367,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Use IP Whitelisting", "Utiliser une liste blanche d'IP"),
|
||||
("Network", "Réseau"),
|
||||
("Enable RDP", "Activer connection RDP"),
|
||||
("Pin Toolbar", ""),
|
||||
("Unpin Toolbar", ""),
|
||||
("Pin Toolbar", "Épingler la barre d'outil"),
|
||||
("Unpin Toolbar", "Détacher la barre d'outil"),
|
||||
("Recording", "Enregistrement"),
|
||||
("Directory", "Répertoire"),
|
||||
("Automatically record incoming sessions", "Enregistrement automatique des sessions entrantes"),
|
||||
@ -382,7 +382,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Write a message", "Ecrire un message"),
|
||||
("Prompt", ""),
|
||||
("Please wait for confirmation of UAC...", "Veuillez attendre la confirmation de l'UAC..."),
|
||||
("elevated_foreground_window_tip", "La fenêtre actuelle que la machine distante nécessite des privilèges plus élevés pour fonctionner, elle ne peut donc pas être atteinte par la souris et le clavier. Vous pouvez demander à l'utilisateur distant de réduire la fenêtre actuelle ou de cliquer sur le bouton d'élévation dans la fenêtre de gestion des connexions. Pour éviter ce problème, il est recommandé d'installer le logiciel sur l'appareil distant."),
|
||||
("elevated_foreground_window_tip", "La fenêtre actuelle de l'appareil distant nécessite des privilèges plus élevés pour fonctionner, elle ne peut donc pas être atteinte par la souris et le clavier. Vous pouvez demander à l'utilisateur distant de réduire la fenêtre actuelle ou de cliquer sur le bouton d'élévation dans la fenêtre de gestion des connexions. Pour éviter ce problème, il est recommandé d'installer le logiciel sur l'appareil distant."),
|
||||
("Disconnected", "Déconnecté"),
|
||||
("Other", "Divers"),
|
||||
("Confirm before closing multiple tabs", "Confirmer avant de fermer plusieurs onglets"),
|
||||
@ -392,7 +392,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Wayland requires Ubuntu 21.04 or higher version.", "Wayland nécessite Ubuntu 21.04 ou une version supérieure."),
|
||||
("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland nécessite une version supérieure de la distribution Linux. Veuillez essayer le bureau X11 ou changer votre système d'exploitation."),
|
||||
("JumpLink", "Afficher"),
|
||||
("Please Select the screen to be shared(Operate on the peer side).", "Veuillez sélectionner l'écran à partager (côté machine distante)."),
|
||||
("Please Select the screen to be shared(Operate on the peer side).", "Veuillez sélectionner l'écran à partager (côté appareil distant)."),
|
||||
("Show RustDesk", "Afficher RustDesk"),
|
||||
("This PC", "Ce PC"),
|
||||
("or", "ou"),
|
||||
@ -452,16 +452,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Auto", "Auto"),
|
||||
("Other Default Options", "Autres options par défaut"),
|
||||
("Voice call", "Appel voix"),
|
||||
("Text chat", "Conversation textuelfle"),
|
||||
("Text chat", "Conversation textuelle"),
|
||||
("Stop voice call", "Stopper l'appel voix"),
|
||||
("relay_hint_tip", "Il se peut qu'il ne doit pas possible de se connecter directement, vous pouvez essayer de vous connecter via un relais. \nEn outre, si vous souhaitez utiliser directement le relais, vous pouvez ajouter le suffixe \"/r\" à l'ID ou sélectionner l'option \"Toujours se connecter via le relais\" dans la fiche pair."),
|
||||
("relay_hint_tip", "Il se peut qu'il ne doit pas possible de se connecter directement, vous pouvez essayer de vous connecter via un relais. \nEn outre, si vous souhaitez utiliser directement le relais, vous pouvez ajouter le suffixe \"/r\" à l'ID ou sélectionner l'option \"Toujours se connecter via le relais\" dans la fiche appareils distants."),
|
||||
("Reconnect", "Se reconnecter"),
|
||||
("Codec", "Codec"),
|
||||
("Resolution", "Résolution"),
|
||||
("No transfers in progress", "Pas de transfert en cours"),
|
||||
("Set one-time password length", "Définir la longueur du mot de passe à usage unique"),
|
||||
("install_cert_tip", ""),
|
||||
("confirm_install_cert_tip", ""),
|
||||
("install_cert_tip", "Installer le certificat RustDesk"),
|
||||
("confirm_install_cert_tip", "Il s'agit d'un certificat RustDesk, auquel on peut faire confiance. Le certificat sera utilisé pour approuver et installer les pilotes RustDesk si nécessaire."),
|
||||
("RDP Settings", "Configuration RDP"),
|
||||
("Sort by", "Trier par"),
|
||||
("New Connection", "Nouvelle connexion"),
|
||||
@ -470,14 +470,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Maximize", "Maximiser"),
|
||||
("Your Device", "Votre appareil"),
|
||||
("empty_recent_tip", "Oups, pas de sessions récentes!\nIl est temps d'en prévoir une nouvelle."),
|
||||
("empty_favorite_tip", "Vous n'avez pas encore de pairs favoris?\nTrouvons quelqu'un avec qui vous connecter et ajoutez-le à vos favoris!"),
|
||||
("empty_lan_tip", "Oh non, il semble que nous n'ayons pas encore de pairs découverts."),
|
||||
("empty_address_book_tip", "Ouh là là! il semble qu'il n'y ait actuellement aucun pair répertorié dans votre carnet d'adresses."),
|
||||
("empty_favorite_tip", "Vous n'avez pas encore d'appareils distants favorits?\nTrouvons quelqu'un avec qui vous connecter et ajoutez-les à vos favoris!"),
|
||||
("empty_lan_tip", "Oh non, il semble que nous n'ayons pas encore d'appareils réseau local découverts."),
|
||||
("empty_address_book_tip", "Ouh là là! il semble qu'il n'y ait actuellement aucun appareil distant répertorié dans votre carnet d'adresses."),
|
||||
("eg: admin", "ex: admin"),
|
||||
("Empty Username", "Nom d'utilisation non spécifié"),
|
||||
("Empty Password", "Mot de passe non spécifié"),
|
||||
("Me", "Moi"),
|
||||
("identical_file_tip", "Ce fichier est identique à celui du pair."),
|
||||
("identical_file_tip", "Ce fichier est identique à celui de l'appareil distant."),
|
||||
("show_monitors_tip", "Afficher les moniteurs dans la barre d'outils"),
|
||||
("View Mode", "Mode vue"),
|
||||
("login_linux_tip", "Se connecter au compte Linux distant"),
|
||||
@ -498,45 +498,48 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Fingerprint", "Empreinte digitale"),
|
||||
("Copy Fingerprint", "Copier empreinte digitale"),
|
||||
("no fingerprints", "Pas d'empreintes digitales"),
|
||||
("Select a peer", "Sélectionnez la machine distante"),
|
||||
("Select peers", "Sélectionnez des machines distantes"),
|
||||
("Select a peer", "Sélectionnez l'appareil distant"),
|
||||
("Select peers", "Sélectionnez des appareils distants"),
|
||||
("Plugins", "Plugins"),
|
||||
("Uninstall", "Désinstaller"),
|
||||
("Update", "Mise à jour"),
|
||||
("Enable", "Activé"),
|
||||
("Disable", "Desactivé"),
|
||||
("Options", "Options"),
|
||||
("resolution_original_tip", ""),
|
||||
("resolution_fit_local_tip", ""),
|
||||
("resolution_custom_tip", ""),
|
||||
("Collapse toolbar", ""),
|
||||
("Accept and Elevate", ""),
|
||||
("accept_and_elevate_btn_tooltip", ""),
|
||||
("clipboard_wait_response_timeout_tip", ""),
|
||||
("Incoming connection", ""),
|
||||
("Outgoing connection", ""),
|
||||
("Exit", ""),
|
||||
("Open", ""),
|
||||
("logout_tip", ""),
|
||||
("Service", ""),
|
||||
("Start", ""),
|
||||
("Stop", ""),
|
||||
("exceed_max_devices", ""),
|
||||
("Sync with recent sessions", ""),
|
||||
("Sort tags", ""),
|
||||
("Open connection in new tab", ""),
|
||||
("Move tab to new window", ""),
|
||||
("Can not be empty", ""),
|
||||
("Already exists", ""),
|
||||
("Change Password", ""),
|
||||
("Refresh Password", ""),
|
||||
("ID", ""),
|
||||
("Grid View", ""),
|
||||
("List View", ""),
|
||||
("Select", ""),
|
||||
("Toggle Tags", ""),
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("resolution_original_tip", "Résolution d'origine"),
|
||||
("resolution_fit_local_tip", "Adapter la résolution local"),
|
||||
("resolution_custom_tip", "Résolution personnalisée"),
|
||||
("Collapse toolbar", "Réduire la barre d'outils"),
|
||||
("Accept and Elevate", "Accepter et autoriser l'augmentation des privilèges"),
|
||||
("accept_and_elevate_btn_tooltip", "Accepter la connexion l'augmentation des privilèges UAC."),
|
||||
("clipboard_wait_response_timeout_tip", "Expiration du délai d'attente presse-papiers."),
|
||||
("Incoming connection", "Connexion entrante"),
|
||||
("Outgoing connection", "Connexion sortante"),
|
||||
("Exit", "Quitter"),
|
||||
("Open", "Ouvrir"),
|
||||
("logout_tip", "Êtes-vous sûr de vouloir vous déconnecter?"),
|
||||
("Service", "Service"),
|
||||
("Start", "Lancer"),
|
||||
("Stop", "Stopper"),
|
||||
("exceed_max_devices", "Vous avez atteint le nombre maximal d'appareils gérés."),
|
||||
("Sync with recent sessions", "Synchroniser avec les sessions récentes"),
|
||||
("Sort tags", "Trier les étiquettes"),
|
||||
("Open connection in new tab", "Ouvrir la connexion dans un nouvel onglet"),
|
||||
("Move tab to new window", "Déplacer l'onglet vers une nouvelle fenêtre"),
|
||||
("Can not be empty", "Ne peux pas être vide"),
|
||||
("Already exists", "Existe déjà"),
|
||||
("Change Password", "Changer le mot de passe"),
|
||||
("Refresh Password", "Actualiser le mot de passe"),
|
||||
("ID", "ID"),
|
||||
("Grid View", "Vue Grille"),
|
||||
("List View", "Vue Liste"),
|
||||
("Select", "Sélectionner"),
|
||||
("Toggle Tags", "Basculer vers les étiquettes"),
|
||||
("pull_ab_failed_tip", "Impossible d'actualiser le carnet d'adresses"),
|
||||
("push_ab_failed_tip", "Échec de la synchronisation du carnet d'adresses"),
|
||||
("synced_peer_readded_tip", "Les appareils qui étaient présents dans les sessions récentes seront synchronisés avec le carnet d'adresses."),
|
||||
("Change Color", "Modifier la couleur"),
|
||||
("Primary Color", "Couleur primaire"),
|
||||
("HSV Color", "Couleur TSL"),
|
||||
].iter().cloned().collect();
|
||||
}
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
491
src/lang/id.rs
491
src/lang/id.rs
@ -4,7 +4,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Status", "Status"),
|
||||
("Your Desktop", "Desktop Anda"),
|
||||
("desk_tip", "Desktop Anda dapat diakses dengan ID dan kata sandi ini."),
|
||||
("Password", "Password"),
|
||||
("Password", "Kata sandi"),
|
||||
("Ready", "Siap"),
|
||||
("Established", "Didirikan"),
|
||||
("connecting_status", "Menghubungkan ke jaringan RustDesk..."),
|
||||
@ -15,48 +15,48 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("not_ready_status", "Belum siap. Silakan periksa koneksi Anda"),
|
||||
("Control Remote Desktop", "Kontrol Remote Desktop"),
|
||||
("Transfer File", "File Transfer"),
|
||||
("Connect", "Terhubung"),
|
||||
("Connect", "Hubungkan"),
|
||||
("Recent Sessions", "Sesi Terkini"),
|
||||
("Address Book", "Buku Alamat"),
|
||||
("Confirmation", "Konfirmasi"),
|
||||
("TCP Tunneling", "TCP Tunneling"),
|
||||
("Remove", "Hapus"),
|
||||
("Refresh random password", "Segarkan kata sandi acak"),
|
||||
("Set your own password", "Tetapkan kata sandi Anda sendiri"),
|
||||
("Refresh random password", "Perbarui kata sandi acak"),
|
||||
("Set your own password", "Tetapkan kata sandi Anda"),
|
||||
("Enable Keyboard/Mouse", "Aktifkan Keyboard/Mouse"),
|
||||
("Enable Clipboard", "Aktifkan Papan Klip"),
|
||||
("Enable File Transfer", "Aktifkan Transfer File"),
|
||||
("Enable TCP Tunneling", "Aktifkan TCP Tunneling"),
|
||||
("IP Whitelisting", "Daftar Putih IP"),
|
||||
("IP Whitelisting", "Daftar IP yang diizinkan"),
|
||||
("ID/Relay Server", "ID/Relay Server"),
|
||||
("Import Server Config", "Impor Konfigurasi Server"),
|
||||
("Export Server Config", "Ekspor Konfigutasi Server"),
|
||||
("Export Server Config", "Ekspor Konfigurasi Server"),
|
||||
("Import server configuration successfully", "Impor konfigurasi server berhasil"),
|
||||
("Export server configuration successfully", "Ekspor konfigurasi server berhasil"),
|
||||
("Invalid server configuration", "Konfigurasi server tidak valid"),
|
||||
("Clipboard is empty", "Papan klip kosong"),
|
||||
("Stop service", "Hentikan Layanan"),
|
||||
("Change ID", "Ubah ID"),
|
||||
("Your new ID", ""),
|
||||
("length %min% to %max%", ""),
|
||||
("starts with a letter", ""),
|
||||
("allowed characters", ""),
|
||||
("Your new ID", "ID baru anda"),
|
||||
("length %min% to %max%", "panjang %min% s/d %max%"),
|
||||
("starts with a letter", "Dimulai dengan huruf"),
|
||||
("allowed characters", "Karakter yang dapat digunakan"),
|
||||
("id_change_tip", "Hanya karakter a-z, A-Z, 0-9 dan _ (underscore) yang diperbolehkan. Huruf pertama harus a-z, A-Z. Panjang antara 6 dan 16."),
|
||||
("Website", "Website"),
|
||||
("Website", "Situs Web"),
|
||||
("About", "Tentang"),
|
||||
("Slogan_tip", ""),
|
||||
("Slogan_tip", "Dibuat dengan penuh kasih sayang dalam dunia yang penuh kekacauan ini"),
|
||||
("Privacy Statement", "Pernyataan Privasi"),
|
||||
("Mute", "Bisukan"),
|
||||
("Build Date", ""),
|
||||
("Version", ""),
|
||||
("Build Date", "Tanggal Build"),
|
||||
("Version", "Versi"),
|
||||
("Home", ""),
|
||||
("Audio Input", "Masukkan Audio"),
|
||||
("Audio Input", "Input Audio"),
|
||||
("Enhancements", "Peningkatan"),
|
||||
("Hardware Codec", "Codec Perangkat Keras"),
|
||||
("Adaptive bitrate", "Kecepatan Bitrate Adaptif"),
|
||||
("ID Server", "Server ID"),
|
||||
("Relay Server", "Server Relay"),
|
||||
("API Server", "API Server"),
|
||||
("API Server", "Server API"),
|
||||
("invalid_http", "harus dimulai dengan http:// atau https://"),
|
||||
("Invalid IP", "IP tidak valid"),
|
||||
("Invalid format", "Format tidak valid"),
|
||||
@ -66,16 +66,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Cancel", "Batal"),
|
||||
("Skip", "Lanjutkan"),
|
||||
("Close", "Tutup"),
|
||||
("Retry", "Ulangi"),
|
||||
("Retry", "Coba lagi"),
|
||||
("OK", "Oke"),
|
||||
("Password Required", "Kata sandi dibutuhkan"),
|
||||
("Please enter your password", "Silahkan masukkan kata sandi anda"),
|
||||
("Remember password", "Ingat Password"),
|
||||
("Remember password", "Ingat kata sandi"),
|
||||
("Wrong Password", "Kata sandi Salah"),
|
||||
("Do you want to enter again?", "Apakah anda ingin masuk lagi?"),
|
||||
("Connection Error", "Kesalahan koneksi"),
|
||||
("Error", "Kesalahan"),
|
||||
("Reset by the peer", "Setel ulang oleh rekan"),
|
||||
("Reset by the peer", "Direset oleh rekan"),
|
||||
("Connecting...", "Menghubungkan..."),
|
||||
("Connection in progress. Please wait.", "Koneksi sedang berlangsung. Mohon tunggu."),
|
||||
("Please try 1 minute later", "Silahkan coba 1 menit lagi"),
|
||||
@ -114,19 +114,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Speed", "Kecepatan"),
|
||||
("Custom Image Quality", "Sesuaikan Kualitas Gambar"),
|
||||
("Privacy mode", "Mode Privasi"),
|
||||
("Block user input", "Blokir masukan pengguna"),
|
||||
("Unblock user input", "Jangan blokir masukan pengguna"),
|
||||
("Block user input", "Blokir input pengguna"),
|
||||
("Unblock user input", "Jangan blokir input pengguna"),
|
||||
("Adjust Window", "Sesuaikan Jendela"),
|
||||
("Original", "Asli"),
|
||||
("Shrink", "Susutkan"),
|
||||
("Stretch", "Regangkan"),
|
||||
("Scrollbar", "Scroll bar"),
|
||||
("ScrollAuto", "Gulir Otomatis"),
|
||||
("Scrollbar", "Scrollbar"),
|
||||
("ScrollAuto", "Scroll Otomatis"),
|
||||
("Good image quality", "Kualitas Gambar Baik"),
|
||||
("Balanced", "Seimbang"),
|
||||
("Optimize reaction time", "Optimalkan waktu reaksi"),
|
||||
("Custom", "Kustom"),
|
||||
("Show remote cursor", "Tampilkan remote kursor"),
|
||||
("Show remote cursor", "Tampilkan kursor remote"),
|
||||
("Show quality monitor", "Tampilkan kualitas monitor"),
|
||||
("Disable clipboard", "Matikan papan klip"),
|
||||
("Lock after session end", "Kunci setelah sesi berakhir"),
|
||||
@ -143,36 +143,36 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Failed to connect via rendezvous server", "Gagal terkoneksi via rendezvous server"),
|
||||
("Failed to connect via relay server", "Gagal terkoneksi via relay server"),
|
||||
("Failed to make direct connection to remote desktop", "Gagal membuat koneksi langsung ke desktop jarak jauh"),
|
||||
("Set Password", "Tetapkan Password"),
|
||||
("Set Password", "Tetapkan kata sandi"),
|
||||
("OS Password", "Kata Sandi OS"),
|
||||
("install_tip", "Karena UAC, RustDesk tidak dapat bekerja dengan baik sebagai sisi remote dalam beberapa kasus. Untuk menghindari UAC, silakan klik tombol di bawah ini untuk menginstal RustDesk ke sistem."),
|
||||
("Click to upgrade", "Klik untuk upgrade"),
|
||||
("Click to download", "Kli untuk download"),
|
||||
("Click to update", "Klik untuk update"),
|
||||
("Click to download", "Klik untuk unduh"),
|
||||
("Click to update", "Klik untuk memperbarui"),
|
||||
("Configure", "Konfigurasi"),
|
||||
("config_acc", "Untuk mengontrol Desktop Anda dari jarak jauh, Anda perlu memberikan izin \"Aksesibilitas\" RustDesk."),
|
||||
("config_screen", "Untuk mengakses Desktop Anda dari jarak jauh, Anda perlu memberikan izin \"Perekaman Layar\" RustDesk."),
|
||||
("Installing ...", "Menginstall"),
|
||||
("Install", "Instal"),
|
||||
("Installation", "Instalasi"),
|
||||
("Installation Path", "Jalur Instalasi"),
|
||||
("Installation Path", "Direktori Instalasi"),
|
||||
("Create start menu shortcuts", "Buat pintasan start menu"),
|
||||
("Create desktop icon", "Buat icon desktop"),
|
||||
("agreement_tip", "Dengan memulai instalasi, Anda menerima perjanjian lisensi."),
|
||||
("Accept and Install", "Terima dan Install"),
|
||||
("End-user license agreement", "Perjanjian lisensi pengguna akhir"),
|
||||
("Generating ...", "Menghasilkan..."),
|
||||
("End-user license agreement", "Perjanjian lisensi pengguna"),
|
||||
("Generating ...", "Memproses..."),
|
||||
("Your installation is lower version.", "Instalasi Anda adalah versi yang lebih rendah."),
|
||||
("not_close_tcp_tip", "Jangan tutup jendela ini saat menggunakan tunnel"),
|
||||
("Listening ...", "Mendengarkan..."),
|
||||
("Remote Host", "Remote Host"),
|
||||
("Remote Port", "Remote Port"),
|
||||
("Listening ...", "Menghubungkan..."),
|
||||
("Remote Host", "Host Remote"),
|
||||
("Remote Port", "Port Remote"),
|
||||
("Action", "Aksi"),
|
||||
("Add", "Tambah"),
|
||||
("Local Port", "Port Lokal"),
|
||||
("Local Address", "Alamat lokal"),
|
||||
("Change Local Port", "Ubah Port Lokal"),
|
||||
("setup_server_tip", "Untuk koneksi yang lebih cepat, silakan atur server Anda sendiri"),
|
||||
("setup_server_tip", "Sudah siap, Untuk mendapatkan koneksi yang lebih baik, disarankan untuk menginstal di server anda sendiri"),
|
||||
("Too short, at least 6 characters.", "Terlalu pendek, setidaknya 6 karekter."),
|
||||
("The confirmation is not identical.", "Konfirmasi tidak identik."),
|
||||
("Permissions", "Izin"),
|
||||
@ -182,46 +182,46 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Allow using keyboard and mouse", "Izinkan menggunakan keyboard dan mouse"),
|
||||
("Allow using clipboard", "Izinkan menggunakan papan klip"),
|
||||
("Allow hearing sound", "Izinkan mendengarkan suara"),
|
||||
("Allow file copy and paste", "Izinkan penyalinan dan tempel file"),
|
||||
("Connected", "Terkoneksi"),
|
||||
("Allow file copy and paste", "Izinkan salin dan tempel file"),
|
||||
("Connected", "Terhubung"),
|
||||
("Direct and encrypted connection", "Koneksi langsung dan terenkripsi"),
|
||||
("Relayed and encrypted connection", "Koneksi relai dan terenkripsi"),
|
||||
("Direct and unencrypted connection", "Koneksi langsung dan tidak terenkripsi"),
|
||||
("Relayed and unencrypted connection", "Koneksi relai dan tidak terenkripsi"),
|
||||
("Enter Remote ID", "Masukkan Remote ID"),
|
||||
("Enter your password", "Masukkan password anda"),
|
||||
("Relayed and encrypted connection", "Koneksi relay dan terenkripsi"),
|
||||
("Direct and unencrypted connection", "Koneksi langsung dan tanpa enkripsi"),
|
||||
("Relayed and unencrypted connection", "Koneksi relay dan tanpa enkripsi"),
|
||||
("Enter Remote ID", "Masukkan ID Remote"),
|
||||
("Enter your password", "Masukkan kata sandi anda"),
|
||||
("Logging in...", "Masuk..."),
|
||||
("Enable RDP session sharing", "Aktifkan berbagi sesi RDP"),
|
||||
("Auto Login", "Auto Login (Hanya valid jika Anda menyetel \"Kunci setelah sesi berakhir\")"),
|
||||
("Auto Login", "Login Otomatis (Hanya berlaku jika Anda mengatur \"Kunci setelah sesi berakhir\")"),
|
||||
("Enable Direct IP Access", "Aktifkan Akses IP Langsung"),
|
||||
("Rename", "Ubah nama"),
|
||||
("Space", "Spasi"),
|
||||
("Create Desktop Shortcut", "Buat Pintasan Desktop"),
|
||||
("Change Path", "Ubah Jalur"),
|
||||
("Change Path", "Ubah Direktori"),
|
||||
("Create Folder", "Buat Folder"),
|
||||
("Please enter the folder name", "Silahkan masukkan nama folder"),
|
||||
("Fix it", "Memperbaiki"),
|
||||
("Fix it", "Perbaiki"),
|
||||
("Warning", "Peringatan"),
|
||||
("Login screen using Wayland is not supported", "Layar masuk menggunakan Wayland tidak didukung"),
|
||||
("Reboot required", "Diperlukan boot ulang"),
|
||||
("Unsupported display server", "Server tampilan tidak didukung "),
|
||||
("x11 expected", "x11 diharapkan"),
|
||||
("x11 expected", "Diperlukan x11"),
|
||||
("Port", "Port"),
|
||||
("Settings", "Pengaturan"),
|
||||
("Username", "Username"),
|
||||
("Username", "Nama pengguna"),
|
||||
("Invalid port", "Kesalahan port"),
|
||||
("Closed manually by the peer", "Ditutup secara manual oleh peer"),
|
||||
("Enable remote configuration modification", "Aktifkan modifikasi konfigurasi jarak jauh"),
|
||||
("Closed manually by the peer", "Ditutup secara manual oleh rekan"),
|
||||
("Enable remote configuration modification", "Aktifkan modifikasi konfigurasi remotE"),
|
||||
("Run without install", "Jalankan tanpa menginstal"),
|
||||
("Connect via relay", ""),
|
||||
("Always connect via relay", "Selalu terhubung melalui relai"),
|
||||
("whitelist_tip", "Hanya whitelisted IP yang dapat mengakses saya"),
|
||||
("Connect via relay", "Sambungkan via relay"),
|
||||
("Always connect via relay", "Selalu terhubung melalui relay"),
|
||||
("whitelist_tip", "Hanya IP yang diizikan dapat mengakses"),
|
||||
("Login", "Masuk"),
|
||||
("Verify", ""),
|
||||
("Remember me", ""),
|
||||
("Trust this device", ""),
|
||||
("Verification code", ""),
|
||||
("verification_tip", ""),
|
||||
("Verify", "Verifikasi"),
|
||||
("Remember me", "Ingatkan saya"),
|
||||
("Trust this device", "Izinkan perangkat ini"),
|
||||
("Verification code", "Kode verifikasi"),
|
||||
("verification_tip", "Kode verifikasi sudah dikirim ke email yang terdaftar, masukkan kode verifikasi untuk melanjutkan."),
|
||||
("Logout", "Keluar"),
|
||||
("Tags", "Tag"),
|
||||
("Search ID", "Cari ID"),
|
||||
@ -230,31 +230,31 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Add Tag", "Tambah Tag"),
|
||||
("Unselect all tags", "Batalkan pilihan semua tag"),
|
||||
("Network error", "Kesalahan Jaringan"),
|
||||
("Username missed", "Username tidak sesuai"),
|
||||
("Username missed", "Nama pengguna tidak sesuai"),
|
||||
("Password missed", "Kata sandi tidak sesuai"),
|
||||
("Wrong credentials", "Username atau password salah"),
|
||||
("The verification code is incorrect or has expired", ""),
|
||||
("Wrong credentials", "Nama pengguna atau kata sandi salah"),
|
||||
("The verification code is incorrect or has expired", "Kode verifikasi salah atau sudah kadaluarsa"),
|
||||
("Edit Tag", "Ubah Tag"),
|
||||
("Unremember Password", "Lupa Kata Sandi"),
|
||||
("Unremember Password", "Lupakan Kata Sandi"),
|
||||
("Favorites", "Favorit"),
|
||||
("Add to Favorites", "Tambah ke Favorit"),
|
||||
("Remove from Favorites", "Hapus dari favorit"),
|
||||
("Empty", "Kosong"),
|
||||
("Invalid folder name", "Nama folder tidak valid"),
|
||||
("Socks5 Proxy", "Socks5 Proxy"),
|
||||
("Socks5 Proxy", "Proxy Socks5"),
|
||||
("Hostname", "Hostname"),
|
||||
("Discovered", "Telah ditemukan"),
|
||||
("install_daemon_tip", "Untuk memulai saat boot, Anda perlu menginstal system service."),
|
||||
("Remote ID", "Remote ID"),
|
||||
("Remote ID", "ID Remote"),
|
||||
("Paste", "Tempel"),
|
||||
("Paste here?", "Tempel disini?"),
|
||||
("Are you sure to close the connection?", "Apakah anda yakin akan menutup koneksi?"),
|
||||
("Download new version", "Untuk versi baru"),
|
||||
("Touch mode", "Mode Sentuh"),
|
||||
("Download new version", "Unduh versi baru"),
|
||||
("Touch mode", "Mode Layar Sentuh"),
|
||||
("Mouse mode", "Mode Mouse"),
|
||||
("One-Finger Tap", "Ketuk Satu Jari"),
|
||||
("Left Mouse", "Mouse Kiri"),
|
||||
("One-Long Tap", "Ketuk Satu Panjang"),
|
||||
("One-Long Tap", "Ketuk Tahan"),
|
||||
("Two-Finger Tap", "Ketuk Dua Jari"),
|
||||
("Right Mouse", "Mouse Kanan"),
|
||||
("One-Finger Move", "Gerakan Satu Jari"),
|
||||
@ -275,25 +275,25 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Total", "Total"),
|
||||
("items", "item"),
|
||||
("Selected", "Dipilih"),
|
||||
("Screen Capture", "Rekam Layar"),
|
||||
("Input Control", "kontrol input"),
|
||||
("Screen Capture", "Tangkapan Layar"),
|
||||
("Input Control", "Kontrol input"),
|
||||
("Audio Capture", "Rekam Suara"),
|
||||
("File Connection", "Koneksi File"),
|
||||
("Screen Connection", "koneksi layar"),
|
||||
("Do you accept?", "Apakah diperbolehkan?"),
|
||||
("Screen Connection", "Koneksi layar"),
|
||||
("Do you accept?", "Apakah anda setuju?"),
|
||||
("Open System Setting", "Buka Pengaturan Sistem"),
|
||||
("How to get Android input permission?", ""),
|
||||
("How to get Android input permission?", "Bagaimana cara mendapatkan izin input dari Android?"),
|
||||
("android_input_permission_tip1", "Agar perangkat jarak jauh dapat mengontrol perangkat Android Anda melalui mouse atau sentuhan, Anda harus mengizinkan RustDesk untuk menggunakan layanan \"Aksesibilitas\"."),
|
||||
("android_input_permission_tip2", "Silakan buka halaman pengaturan sistem berikutnya, temukan dan masuk ke [Layanan Terinstal], aktifkan layanan [Input RustDesk]."),
|
||||
("android_new_connection_tip", "Permintaan kontrol baru telah diterima, yang ingin mengontrol perangkat Anda saat ini."),
|
||||
("android_service_will_start_tip", "Mengaktifkan \"Tangkapan Layar\" akan memulai layanan secara otomatis, memungkinkan perangkat lain untuk meminta sambungan ke perangkat Anda."),
|
||||
("android_stop_service_tip", "Menutup layanan akan secara otomatis menutup semua koneksi yang dibuat."),
|
||||
("android_new_connection_tip", "Permintaan akses remote telah diterima"),
|
||||
("android_service_will_start_tip", "Mengaktifkan \"Tangkapan Layar\" akan memulai secara otomatis, memungkinkan perangkat lain untuk meminta koneksi ke perangkat Anda."),
|
||||
("android_stop_service_tip", "Menutup layanan secara otomatis akan menutup semua koneksi yang dibuat."),
|
||||
("android_version_audio_tip", "Versi Android saat ini tidak mendukung pengambilan audio, harap tingkatkan ke Android 10 atau lebih tinggi."),
|
||||
("android_start_service_tip", ""),
|
||||
("android_permission_may_not_change_tip", ""),
|
||||
("android_start_service_tip", "Tap [Mulai Layanan] atau aktifkan izin [Tangkapan Layar] untuk memulai berbagi layar."),
|
||||
("android_permission_may_not_change_tip", "Izin untuk koneksi yang sudah terhubung mungkin tidak dapat diubah secara instan hingga terhubung kembali"),
|
||||
("Account", "Akun"),
|
||||
("Overwrite", "Timpa"),
|
||||
("This file exists, skip or overwrite this file?", "File ini sudah ada, lewati atau timpa file ini?"),
|
||||
("Overwrite", "Ganti"),
|
||||
("This file exists, skip or overwrite this file?", "File ini sudah ada, lewati atau ganti file ini?"),
|
||||
("Quit", "Keluar"),
|
||||
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
|
||||
("Help", "Bantuan"),
|
||||
@ -301,32 +301,32 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Succeeded", "Berhasil"),
|
||||
("Someone turns on privacy mode, exit", "Seseorang mengaktifkan mode privasi, keluar"),
|
||||
("Unsupported", "Tidak didukung"),
|
||||
("Peer denied", "Rekan ditolak"),
|
||||
("Peer denied", "Rekan menolak"),
|
||||
("Please install plugins", "Silakan instal plugin"),
|
||||
("Peer exit", "keluar rekan"),
|
||||
("Peer exit", "Rekan keluar"),
|
||||
("Failed to turn off", "Gagal mematikan"),
|
||||
("Turned off", "Matikan"),
|
||||
("Turned off", "Dimatikan"),
|
||||
("In privacy mode", "Dalam mode privasi"),
|
||||
("Out privacy mode", "Keluar dari mode privasi"),
|
||||
("Language", "Bahasa"),
|
||||
("Keep RustDesk background service", "Pertahankan RustDesk berjalan pada background service"),
|
||||
("Keep RustDesk background service", "Pertahankan RustDesk berjalan pada service background"),
|
||||
("Ignore Battery Optimizations", "Abaikan Pengoptimalan Baterai"),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Start on Boot", ""),
|
||||
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||
("Connection not allowed", "Koneksi tidak dijinkan"),
|
||||
("Legacy mode", "Mode lama"),
|
||||
("android_open_battery_optimizations_tip", "Jika anda ingin menonaktifkan fitur ini, buka halam pengaturan, cari dan pilih [Baterai], Uncheck [Tidak dibatasi]"),
|
||||
("Start on Boot", "Mulai saat dihidupkan"),
|
||||
("Start the screen sharing service on boot, requires special permissions", "Mulai layanan berbagi layar saat sistem dinyalakan, memerlukan izin khusus."),
|
||||
("Connection not allowed", "Koneksi tidak dizinkan"),
|
||||
("Legacy mode", "Mode lawas"),
|
||||
("Map mode", "Mode peta"),
|
||||
("Translate mode", "Mode terjemahan"),
|
||||
("Use permanent password", "Gunakan kata sandi permanaen"),
|
||||
("Use both passwords", "Gunakan kedua kata sandi "),
|
||||
("Use both passwords", "Gunakan kedua kata sandi"),
|
||||
("Set permanent password", "Setel kata sandi permanen"),
|
||||
("Enable Remote Restart", "Aktifkan Restart Jarak Jauh"),
|
||||
("Allow remote restart", "Ijinkan Restart Jarak Jauh"),
|
||||
("Restart Remote Device", "Restart Perangkat Jarak Jauh"),
|
||||
("Are you sure you want to restart", "Apakah Anda yakin untuk memulai ulang"),
|
||||
("Restarting Remote Device", "Memulai Ulang Perangkat Jarak Jauh"),
|
||||
("remote_restarting_tip", ""),
|
||||
("Enable Remote Restart", "Aktifkan Restart Secara Remote"),
|
||||
("Allow remote restart", "Ijinkan Restart Secara Remote"),
|
||||
("Restart Remote Device", "Restart Perangkat Secara Remote"),
|
||||
("Are you sure you want to restart", "Apakah Anda yakin ingin merestart"),
|
||||
("Restarting Remote Device", "Merestart Perangkat Remote"),
|
||||
("remote_restarting_tip", "Perangkat remote sedang merestart, harap tutup pesan ini dan sambungkan kembali dengan kata sandi permanen setelah beberapa saat."),
|
||||
("Copied", "Disalin"),
|
||||
("Exit Fullscreen", "Keluar dari Layar Penuh"),
|
||||
("Fullscreen", "Layar penuh"),
|
||||
@ -334,11 +334,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Select Monitor", "Pilih Monitor"),
|
||||
("Control Actions", "Tindakan Kontrol"),
|
||||
("Display Settings", "Pengaturan tampilan"),
|
||||
("Ratio", "Perbandingan"),
|
||||
("Ratio", "Rasio"),
|
||||
("Image Quality", "Kualitas gambar"),
|
||||
("Scroll Style", "Gaya Gulir"),
|
||||
("Show Toolbar", ""),
|
||||
("Hide Toolbar", ""),
|
||||
("Scroll Style", "Gaya Scroll"),
|
||||
("Show Toolbar", "Tampilkan Toolbar"),
|
||||
("Hide Toolbar", "Sembunyikan Toolbar"),
|
||||
("Direct Connection", "Koneksi langsung"),
|
||||
("Relay Connection", "Koneksi Relay"),
|
||||
("Secure Connection", "Koneksi aman"),
|
||||
@ -348,31 +348,31 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("General", "Umum"),
|
||||
("Security", "Keamanan"),
|
||||
("Theme", "Tema"),
|
||||
("Dark Theme", "Tema gelap"),
|
||||
("Light Theme", ""),
|
||||
("Dark Theme", "Tema Gelap"),
|
||||
("Light Theme", "Tema Terang"),
|
||||
("Dark", "Gelap"),
|
||||
("Light", "Terang"),
|
||||
("Follow System", "Ikuti sistem"),
|
||||
("Follow System", "Ikuti Sistem"),
|
||||
("Enable hardware codec", "Aktifkan codec perangkat keras"),
|
||||
("Unlock Security Settings", "Buka Kunci Pengaturan Keamanan"),
|
||||
("Unlock Security Settings", "Buka Keamanan Pengaturan"),
|
||||
("Enable Audio", "Aktifkan Audio"),
|
||||
("Unlock Network Settings", "Buka Kunci Pengaturan Jaringan"),
|
||||
("Unlock Network Settings", "Buka Keamanan Pengaturan Jaringan"),
|
||||
("Server", "Server"),
|
||||
("Direct IP Access", "Direct IP Access"),
|
||||
("Direct IP Access", "Akses IP Langsung"),
|
||||
("Proxy", "Proxy"),
|
||||
("Apply", "Terapkan"),
|
||||
("Disconnect all devices?", "Putuskan sambungan semua perangkat?"),
|
||||
("Clear", ""),
|
||||
("Audio Input Device", ""),
|
||||
("Use IP Whitelisting", "Gunakan Daftar Putih IP"),
|
||||
("Clear", "Bersihkan"),
|
||||
("Audio Input Device", "Input Perangkat Audio"),
|
||||
("Use IP Whitelisting", "Gunakan daftar IP yang diizinkan"),
|
||||
("Network", "Jaringan"),
|
||||
("Enable RDP", "Aktifkan RDP"),
|
||||
("Pin Toolbar", ""),
|
||||
("Unpin Toolbar", ""),
|
||||
("Recording", "Rekaman"),
|
||||
("Pin Toolbar", "Sematkan Toolbar"),
|
||||
("Unpin Toolbar", "Batal sematkan Toolbar"),
|
||||
("Recording", "Sedang Merekam"),
|
||||
("Directory", "Direktori"),
|
||||
("Automatically record incoming sessions", "Secara otomatis merekam sesi masuk"),
|
||||
("Change", "Mengubah"),
|
||||
("Change", "Ubah"),
|
||||
("Start session recording", "Mulai sesi perekaman"),
|
||||
("Stop session recording", "Hentikan sesi perekaman"),
|
||||
("Enable Recording Session", "Aktifkan Sesi Perekaman"),
|
||||
@ -381,8 +381,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Deny LAN Discovery", "Tolak Penemuan LAN"),
|
||||
("Write a message", "Menulis pesan"),
|
||||
("Prompt", ""),
|
||||
("Please wait for confirmation of UAC...", ""),
|
||||
("elevated_foreground_window_tip", ""),
|
||||
("Please wait for confirmation of UAC...", "Harap tunggu konfirmasi UAC"),
|
||||
("elevated_foreground_window_tip", "Jendela remote desktop ini memerlukan hak akses khusus, jadi anda tidak bisa menggunakan mouse dan keyboard untuk sementara. Anda bisa meminta pihak pengguna yang diremote untuk menyembunyikan jendela ini atau klik tombol elevasi di jendela pengaturan koneksi. Untuk menghindari masalah ini, direkomendasikan untuk menginstall aplikasi secara permanen"),
|
||||
("Disconnected", "Terputus"),
|
||||
("Other", "Lainnya"),
|
||||
("Confirm before closing multiple tabs", "Konfirmasi sebelum menutup banyak tab"),
|
||||
@ -391,152 +391,155 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Share", "Berbagi Layar"),
|
||||
("Wayland requires Ubuntu 21.04 or higher version.", "Wayland membutuhkan Ubuntu 21.04 atau versi yang lebih tinggi."),
|
||||
("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland membutuhkan versi distro linux yang lebih tinggi. Silakan coba desktop X11 atau ubah OS Anda."),
|
||||
("JumpLink", "View"),
|
||||
("Please Select the screen to be shared(Operate on the peer side).", "Silakan Pilih layar yang akan dibagikan (Operasi di sisi rekan)."),
|
||||
("JumpLink", "Tautan Cepat"),
|
||||
("Please Select the screen to be shared(Operate on the peer side).", "Silakan Pilih layar yang akan dibagikan kepada rekan anda."),
|
||||
("Show RustDesk", "Tampilkan RustDesk"),
|
||||
("This PC", "PC ini"),
|
||||
("or", "atau"),
|
||||
("Continue with", "Lanjutkan dengan"),
|
||||
("Elevate", ""),
|
||||
("Zoom cursor", ""),
|
||||
("Elevate", "Elevasi"),
|
||||
("Zoom cursor", "Perbersar Kursor"),
|
||||
("Accept sessions via password", "Izinkan sesi dengan kata sandi"),
|
||||
("Accept sessions via click", "Izinkan sesi dengan klik"),
|
||||
("Accept sessions via both", "Izinkan sesi dengan keduanya"),
|
||||
("Please wait for the remote side to accept your session request...", "Harap tunggu sisi jarak jauh untuk menerima permintaan sesi Anda..."),
|
||||
("Please wait for the remote side to accept your session request...", "Harap tunggu pihak pengguna remote untuk menerima permintaan sesi..."),
|
||||
("One-time Password", "Kata sandi satu kali"),
|
||||
("Use one-time password", "Gunakan kata sandi satu kali"),
|
||||
("One-time password length", ""),
|
||||
("Request access to your device", ""),
|
||||
("Hide connection management window", ""),
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Skipped", ""),
|
||||
("Add to Address Book", ""),
|
||||
("Group", ""),
|
||||
("One-time password length", "Panjang kata sandi satu kali pakai"),
|
||||
("Request access to your device", "Permintaan akses ke perangkat ini"),
|
||||
("Hide connection management window", "Sembunyikan jendela pengaturan koneksi"),
|
||||
("hide_cm_tip", "Izinkan untuk menyembunyikan hanya jika menerima sesi melalui kata sandi dan menggunakan kata sandi permanen"),
|
||||
("wayland_experiment_tip", "Dukungan Wayland masih dalam tahap percobaan, harap gunakan X11 jika Anda memerlukan akses tanpa pengawasan"),
|
||||
("Right click to select tabs", "Klik kanan untuk memilih tab"),
|
||||
("Skipped", "Dilewati"),
|
||||
("Add to Address Book", "Tambahkan ke Buku Alamat"),
|
||||
("Group", "Grup"),
|
||||
("Search", "Pencarian"),
|
||||
("Closed manually by web console", ""),
|
||||
("Local keyboard type", ""),
|
||||
("Select local keyboard type", ""),
|
||||
("software_render_tip", ""),
|
||||
("Always use software rendering", ""),
|
||||
("config_input", ""),
|
||||
("config_microphone", ""),
|
||||
("request_elevation_tip", ""),
|
||||
("Wait", ""),
|
||||
("Elevation Error", ""),
|
||||
("Ask the remote user for authentication", ""),
|
||||
("Choose this if the remote account is administrator", ""),
|
||||
("Transmit the username and password of administrator", ""),
|
||||
("still_click_uac_tip", ""),
|
||||
("Request Elevation", ""),
|
||||
("wait_accept_uac_tip", ""),
|
||||
("Elevate successfully", ""),
|
||||
("uppercase", ""),
|
||||
("lowercase", ""),
|
||||
("digit", ""),
|
||||
("special character", ""),
|
||||
("length>=8", ""),
|
||||
("Weak", ""),
|
||||
("Medium", ""),
|
||||
("Strong", ""),
|
||||
("Switch Sides", ""),
|
||||
("Please confirm if you want to share your desktop?", ""),
|
||||
("Display", ""),
|
||||
("Default View Style", ""),
|
||||
("Default Scroll Style", ""),
|
||||
("Default Image Quality", ""),
|
||||
("Default Codec", ""),
|
||||
("Bitrate", ""),
|
||||
("FPS", ""),
|
||||
("Auto", ""),
|
||||
("Other Default Options", ""),
|
||||
("Voice call", ""),
|
||||
("Text chat", ""),
|
||||
("Stop voice call", ""),
|
||||
("relay_hint_tip", ""),
|
||||
("Reconnect", ""),
|
||||
("Codec", ""),
|
||||
("Resolution", ""),
|
||||
("No transfers in progress", ""),
|
||||
("Set one-time password length", ""),
|
||||
("install_cert_tip", ""),
|
||||
("confirm_install_cert_tip", ""),
|
||||
("RDP Settings", ""),
|
||||
("Sort by", ""),
|
||||
("New Connection", ""),
|
||||
("Restore", ""),
|
||||
("Minimize", ""),
|
||||
("Maximize", ""),
|
||||
("Your Device", ""),
|
||||
("empty_recent_tip", ""),
|
||||
("empty_favorite_tip", ""),
|
||||
("empty_lan_tip", ""),
|
||||
("empty_address_book_tip", ""),
|
||||
("Closed manually by web console", "Ditutup secara manual dari konsol web."),
|
||||
("Local keyboard type", "Tipe papan ketik"),
|
||||
("Select local keyboard type", "Pilih tipe papan ketik"),
|
||||
("software_render_tip", "Jika anda menggunakan kartu grafis Nvidia pada sistem linux dan jendela windows ditutup secara instan setelah terhung, silahkan ubah ke driver open-source Nouveau, dibutukan untuk merestart aplikasi"),
|
||||
("Always use software rendering", "Selalu gunakan software rendering"),
|
||||
("config_input", "Untuk menggunakan input keyboard remote, anda perlu memberikan izin \"Pemantauan Input\" pada RustDesk"),
|
||||
("config_microphone", "Untuk berbicara secara remote, anda perlu memberikan izin \"Rekam Audio\" pada RustDesk"),
|
||||
("request_elevation_tip", "Anda juga bisa meminta izin elevasi jika ada pihak pengguna remote"),
|
||||
("Wait", "Tunggu"),
|
||||
("Elevation Error", "Kesalahan Elevasi"),
|
||||
("Ask the remote user for authentication", "Minta pihak pengguna remote untuk otentikasi"),
|
||||
("Choose this if the remote account is administrator", "Pilih ini jika akun adalah \"administrator\""),
|
||||
("Transmit the username and password of administrator", "Transmisikan nama pengguna dan kata sandi administrator"),
|
||||
("still_click_uac_tip", "Masih memerlukan persetujuan pihak pengguna remote untuk mengklik OK pada jendela UAC RustDesk yang sedang berjalan"),
|
||||
("Request Elevation", "Permintaan Elevasi"),
|
||||
("wait_accept_uac_tip", "Harap tunggu pihak pengguna remote menerima jendela UAC."),
|
||||
("Elevate successfully", "Elevasi berhasil"),
|
||||
("uppercase", "Huruf besar"),
|
||||
("lowercase", "Huruf kecil"),
|
||||
("digit", "angka"),
|
||||
("special character", "Karakter spesial"),
|
||||
("length>=8", "panjang>=8"),
|
||||
("Weak", "Lemah"),
|
||||
("Medium", "Sedang"),
|
||||
("Strong", "Kuat"),
|
||||
("Switch Sides", "Ganti Posisi"),
|
||||
("Please confirm if you want to share your desktop?", "Harap konfirmasi apakah Anda ingin berbagi layar?"),
|
||||
("Display", "Tampilan"),
|
||||
("Default View Style", "Gaya Tampilan Default"),
|
||||
("Default Scroll Style", "Gaya Scroll Default"),
|
||||
("Default Image Quality", "Kualitas Gambar Default"),
|
||||
("Default Codec", "Codec default"),
|
||||
("Bitrate", "Bitrate"),
|
||||
("FPS", "FPS"),
|
||||
("Auto", "Otomatis"),
|
||||
("Other Default Options", "Opsi Default Lainnya"),
|
||||
("Voice call", "Panggilan suara"),
|
||||
("Text chat", "Obrolan Teks"),
|
||||
("Stop voice call", "Hentikan panggilan suara"),
|
||||
("relay_hint_tip", "Tidak memungkinkan untuk terhubung secara langsung; anda bisa mencoba terhubung via relay. Selain itu, jika ingin menggunakan relay pada percobaan pertama, silahkan tambah akhiran \"/r\" pada ID atau pilih \"Selalu terhubung via relay\" di pilihan sesi terbaru."),
|
||||
("Reconnect", "Menyambungkan ulang"),
|
||||
("Codec", "Codec"),
|
||||
("Resolution", "Resolusi"),
|
||||
("No transfers in progress", "Tidak ada transfer data yang sedang berlangsung"),
|
||||
("Set one-time password length", "Atur panjang kata sandi satu kali pakai"),
|
||||
("install_cert_tip", "Install sertifikat RustDesk"),
|
||||
("confirm_install_cert_tip", "Ini adalah sertifikat pengujian RustDesk, yang dapat dipercaya. Sertifikat ini akan digunakan untuk menginstal driver RustDesk saat diperlukan"),
|
||||
("RDP Settings", "Pengaturan RDP"),
|
||||
("Sort by", "Urutkan berdasarkan"),
|
||||
("New Connection", "Koneksi baru"),
|
||||
("Restore", "Mengembalikan"),
|
||||
("Minimize", "Meminimalkan"),
|
||||
("Maximize", "Memaksimalkan"),
|
||||
("Your Device", "Perangkat anda"),
|
||||
("empty_recent_tip", "Tidak ada sesi terbaru!"),
|
||||
("empty_favorite_tip", "Belum ada rekan favorit?\nTemukan seseorang untuk terhubung dan tambahkan ke favorit!"),
|
||||
("empty_lan_tip", "Sepertinya kami belum menemukan rekan"),
|
||||
("empty_address_book_tip", "Tampaknya saat ini tidak ada rekan yang terdaftar dalam buku alamat Anda"),
|
||||
("eg: admin", ""),
|
||||
("Empty Username", ""),
|
||||
("Empty Password", ""),
|
||||
("Me", ""),
|
||||
("identical_file_tip", ""),
|
||||
("show_monitors_tip", ""),
|
||||
("View Mode", ""),
|
||||
("login_linux_tip", ""),
|
||||
("verify_rustdesk_password_tip", ""),
|
||||
("remember_account_tip", ""),
|
||||
("os_account_desk_tip", ""),
|
||||
("OS Account", ""),
|
||||
("another_user_login_title_tip", ""),
|
||||
("another_user_login_text_tip", ""),
|
||||
("xorg_not_found_title_tip", ""),
|
||||
("xorg_not_found_text_tip", ""),
|
||||
("no_desktop_title_tip", ""),
|
||||
("no_desktop_text_tip", ""),
|
||||
("No need to elevate", ""),
|
||||
("System Sound", ""),
|
||||
("Default", ""),
|
||||
("New RDP", ""),
|
||||
("Empty Username", "Nama pengguna kosong"),
|
||||
("Empty Password", "Kata sandi kosong"),
|
||||
("Me", "Saya"),
|
||||
("identical_file_tip", "Data ini identik dengan milik rekan"),
|
||||
("show_monitors_tip", "Tampilkan monitor di toolbar"),
|
||||
("View Mode", "Mode Tampilan"),
|
||||
("login_linux_tip", "Anda harus masuk ke akun remote linux untuk mengaktifkan sesi X desktop"),
|
||||
("verify_rustdesk_password_tip", "Verifikasi Kata Sandi RustDesk"),
|
||||
("remember_account_tip", "Ingat akun ini"),
|
||||
("os_account_desk_tip", "Akun ini digunakan untuk masuk ke sistem operasi remote dan mengaktifkan sesi desktop dalam mode tanpa tampilan (headless)"),
|
||||
("OS Account", "Akun OS"),
|
||||
("another_user_login_title_tip", "Akun ini sedang digunakan"),
|
||||
("another_user_login_text_tip", "Putuskan koneksi diperangkat lain"),
|
||||
("xorg_not_found_title_tip", "Xorg tidak ditemukan"),
|
||||
("xorg_not_found_text_tip", "Silahkan install Xorg"),
|
||||
("no_desktop_title_tip", "Desktop tidak tersedia"),
|
||||
("no_desktop_text_tip", "Silahkan install GNOME Desktop"),
|
||||
("No need to elevate", "Tidak perlu elevasi"),
|
||||
("System Sound", "Suara Sistem"),
|
||||
("Default", "Default"),
|
||||
("New RDP", "RDP Baru"),
|
||||
("Fingerprint", ""),
|
||||
("Copy Fingerprint", ""),
|
||||
("no fingerprints", ""),
|
||||
("Select a peer", ""),
|
||||
("Select peers", ""),
|
||||
("Plugins", ""),
|
||||
("Uninstall", ""),
|
||||
("Update", ""),
|
||||
("Enable", ""),
|
||||
("Disable", ""),
|
||||
("Options", ""),
|
||||
("resolution_original_tip", ""),
|
||||
("resolution_fit_local_tip", ""),
|
||||
("resolution_custom_tip", ""),
|
||||
("Select a peer", "Pilih rekan"),
|
||||
("Select peers", "Pilih rekan-rekan"),
|
||||
("Plugins", "Plugin"),
|
||||
("Uninstall", "Hapus instalasi"),
|
||||
("Update", "Perbarui"),
|
||||
("Enable", "Aktifkan"),
|
||||
("Disable", "Nonaktifkan"),
|
||||
("Options", "Opsi"),
|
||||
("resolution_original_tip", "Resolusi original"),
|
||||
("resolution_fit_local_tip", "Sesuaikan resolusi lokal"),
|
||||
("resolution_custom_tip", "Resolusi kustom"),
|
||||
("Collapse toolbar", ""),
|
||||
("Accept and Elevate", ""),
|
||||
("accept_and_elevate_btn_tooltip", ""),
|
||||
("clipboard_wait_response_timeout_tip", ""),
|
||||
("Incoming connection", ""),
|
||||
("Outgoing connection", ""),
|
||||
("Exit", ""),
|
||||
("Open", ""),
|
||||
("logout_tip", ""),
|
||||
("Service", ""),
|
||||
("Start", ""),
|
||||
("Stop", ""),
|
||||
("exceed_max_devices", ""),
|
||||
("Sync with recent sessions", ""),
|
||||
("Sort tags", ""),
|
||||
("Open connection in new tab", ""),
|
||||
("Move tab to new window", ""),
|
||||
("Can not be empty", ""),
|
||||
("Already exists", ""),
|
||||
("Change Password", ""),
|
||||
("Refresh Password", ""),
|
||||
("ID", ""),
|
||||
("Grid View", ""),
|
||||
("List View", ""),
|
||||
("Select", ""),
|
||||
("Toggle Tags", ""),
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Accept and Elevate", "Terima dan Elevasi"),
|
||||
("accept_and_elevate_btn_tooltip", "Terima koneksi dan elevasi izin UAC"),
|
||||
("clipboard_wait_response_timeout_tip", "Batas waktu habis saat menunggu respons salinan"),
|
||||
("Incoming connection", "Koneksi akan masuk"),
|
||||
("Outgoing connection", "Koneksi akan keluar"),
|
||||
("Exit", "Keluar"),
|
||||
("Open", "Buka"),
|
||||
("logout_tip", "Apakah Anda yakin ingin keluar?"),
|
||||
("Service", "Service"),
|
||||
("Start", "Mulai"),
|
||||
("Stop", "Berhenti"),
|
||||
("exceed_max_devices", "Anda telah mencapai jumlah maksimal perangkat yang dikelola"),
|
||||
("Sync with recent sessions", "Sinkronkan dengan sesi terbaru"),
|
||||
("Sort tags", "Urutkan tag"),
|
||||
("Open connection in new tab", "Buka koneksi di tab baru"),
|
||||
("Move tab to new window", "Pindahkan tab ke jendela baru"),
|
||||
("Can not be empty", "Tidak boleh kosong"),
|
||||
("Already exists", "Sudah ada"),
|
||||
("Change Password", "Ganti kata sandi"),
|
||||
("Refresh Password", "Perbarui Kata Sandi"),
|
||||
("ID", "ID"),
|
||||
("Grid View", "Tampilan Kotak"),
|
||||
("List View", "Tampilan Daftar"),
|
||||
("Select", "Pilih"),
|
||||
("Toggle Tags", "Toggle Tag"),
|
||||
("pull_ab_failed_tip", "Gagal memuat ulang buku alamat"),
|
||||
("push_ab_failed_tip", "Gagal menyinkronkan buku alamat ke server"),
|
||||
("synced_peer_readded_tip", "Perangkat yang terdaftar dalam sesi-sesi terbaru akan di-sinkronkan kembali ke buku alamat."),
|
||||
("Change Color", "Ganti warna"),
|
||||
("Primary Color", "Warna utama"),
|
||||
("HSV Color", "Warna HSV"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -537,6 +537,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Toggle Tags", "Attiva/disattiva tag"),
|
||||
("pull_ab_failed_tip", "Impossibile aggiornare la rubrica"),
|
||||
("push_ab_failed_tip", "Impossibile sincronizzare la rubrica con il server"),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("synced_peer_readded_tip", "I dispositivi presenti nelle sessioni recenti saranno sincronizzati di nuovo nella rubrica."),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", "Невозможно обновить адресную книгу"),
|
||||
("push_ab_failed_tip", "Невозможно синхронизировать адресную книгу с сервером"),
|
||||
("synced_peer_readded_tip", "Устройства, присутствовавшие в последних сеансах, будут синхронизированы с адресной книгой."),
|
||||
("Change Color", "Изменить цвет"),
|
||||
("Primary Color", "Основной цвет"),
|
||||
("HSV Color", "HSV цвет"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
263
src/lang/tr.rs
263
src/lang/tr.rs
@ -408,135 +408,138 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("One-time password length", "Tek seferlik şifre uzunluğu"),
|
||||
("Request access to your device", "Cihazınıza erişim talep edin"),
|
||||
("Hide connection management window", "Bağlantı yönetimi penceresini gizle"),
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Skipped", ""),
|
||||
("Add to Address Book", ""),
|
||||
("Group", ""),
|
||||
("Search", ""),
|
||||
("Closed manually by web console", ""),
|
||||
("Local keyboard type", ""),
|
||||
("Select local keyboard type", ""),
|
||||
("software_render_tip", ""),
|
||||
("Always use software rendering", ""),
|
||||
("config_input", ""),
|
||||
("config_microphone", ""),
|
||||
("request_elevation_tip", ""),
|
||||
("Wait", ""),
|
||||
("Elevation Error", ""),
|
||||
("Ask the remote user for authentication", ""),
|
||||
("Choose this if the remote account is administrator", ""),
|
||||
("Transmit the username and password of administrator", ""),
|
||||
("still_click_uac_tip", ""),
|
||||
("Request Elevation", ""),
|
||||
("wait_accept_uac_tip", ""),
|
||||
("Elevate successfully", ""),
|
||||
("uppercase", ""),
|
||||
("lowercase", ""),
|
||||
("digit", ""),
|
||||
("special character", ""),
|
||||
("length>=8", ""),
|
||||
("Weak", ""),
|
||||
("Medium", ""),
|
||||
("Strong", ""),
|
||||
("Switch Sides", ""),
|
||||
("Please confirm if you want to share your desktop?", ""),
|
||||
("Display", ""),
|
||||
("Default View Style", ""),
|
||||
("Default Scroll Style", ""),
|
||||
("Default Image Quality", ""),
|
||||
("Default Codec", ""),
|
||||
("Bitrate", ""),
|
||||
("FPS", ""),
|
||||
("Auto", ""),
|
||||
("Other Default Options", ""),
|
||||
("Voice call", ""),
|
||||
("Text chat", ""),
|
||||
("Stop voice call", ""),
|
||||
("relay_hint_tip", ""),
|
||||
("Reconnect", ""),
|
||||
("Codec", ""),
|
||||
("Resolution", ""),
|
||||
("No transfers in progress", ""),
|
||||
("Set one-time password length", ""),
|
||||
("install_cert_tip", ""),
|
||||
("confirm_install_cert_tip", ""),
|
||||
("RDP Settings", ""),
|
||||
("Sort by", ""),
|
||||
("New Connection", ""),
|
||||
("Restore", ""),
|
||||
("Minimize", ""),
|
||||
("Maximize", ""),
|
||||
("Your Device", ""),
|
||||
("empty_recent_tip", ""),
|
||||
("empty_favorite_tip", ""),
|
||||
("empty_lan_tip", ""),
|
||||
("empty_address_book_tip", ""),
|
||||
("eg: admin", ""),
|
||||
("Empty Username", ""),
|
||||
("Empty Password", ""),
|
||||
("Me", ""),
|
||||
("identical_file_tip", ""),
|
||||
("show_monitors_tip", ""),
|
||||
("View Mode", ""),
|
||||
("login_linux_tip", ""),
|
||||
("verify_rustdesk_password_tip", ""),
|
||||
("remember_account_tip", ""),
|
||||
("os_account_desk_tip", ""),
|
||||
("OS Account", ""),
|
||||
("another_user_login_title_tip", ""),
|
||||
("another_user_login_text_tip", ""),
|
||||
("xorg_not_found_title_tip", ""),
|
||||
("xorg_not_found_text_tip", ""),
|
||||
("no_desktop_title_tip", ""),
|
||||
("no_desktop_text_tip", ""),
|
||||
("No need to elevate", ""),
|
||||
("System Sound", ""),
|
||||
("Default", ""),
|
||||
("New RDP", ""),
|
||||
("Fingerprint", ""),
|
||||
("Copy Fingerprint", ""),
|
||||
("no fingerprints", ""),
|
||||
("Select a peer", ""),
|
||||
("Select peers", ""),
|
||||
("Plugins", ""),
|
||||
("Uninstall", ""),
|
||||
("Update", ""),
|
||||
("Enable", ""),
|
||||
("Disable", ""),
|
||||
("Options", ""),
|
||||
("resolution_original_tip", ""),
|
||||
("resolution_fit_local_tip", ""),
|
||||
("resolution_custom_tip", ""),
|
||||
("Collapse toolbar", ""),
|
||||
("Accept and Elevate", ""),
|
||||
("accept_and_elevate_btn_tooltip", ""),
|
||||
("clipboard_wait_response_timeout_tip", ""),
|
||||
("Incoming connection", ""),
|
||||
("Outgoing connection", ""),
|
||||
("Exit", ""),
|
||||
("Open", ""),
|
||||
("logout_tip", ""),
|
||||
("Service", ""),
|
||||
("Start", ""),
|
||||
("Stop", ""),
|
||||
("exceed_max_devices", ""),
|
||||
("Sync with recent sessions", ""),
|
||||
("Sort tags", ""),
|
||||
("Open connection in new tab", ""),
|
||||
("Move tab to new window", ""),
|
||||
("Can not be empty", ""),
|
||||
("Already exists", ""),
|
||||
("Change Password", ""),
|
||||
("Refresh Password", ""),
|
||||
("ID", ""),
|
||||
("Grid View", ""),
|
||||
("List View", ""),
|
||||
("Select", ""),
|
||||
("Toggle Tags", ""),
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("hide_cm_tip", "Oturumları yalnızca parola ile kabul edebilir ve kalıcı parola kullanıyorsanız gizlemeye izin verin"),
|
||||
("wayland_experiment_tip", "Wayland desteği deneysel aşamada olduğundan, gerektiğinde X11'i kullanmanız önerilir"),
|
||||
("Right click to select tabs", "Sekmeleri seçmek için sağ tıklayın"),
|
||||
("Skipped", "Atlandı"),
|
||||
("Add to Address Book", "Adres Defterine Ekle"),
|
||||
("Group", "Grup"),
|
||||
("Search", "Ara"),
|
||||
("Closed manually by web console", "Web konsoluyla manuel olarak kapatıldı"),
|
||||
("Local keyboard type", "Yerel klavye türü"),
|
||||
("Select local keyboard type", "Yerel klavye türünü seçin"),
|
||||
("software_render_tip", "Linux altında Nvidia grafik kartı kullanıyorsanız ve uzak pencere bağlandıktan hemen sonra kapanıyorsa, açık kaynaklı Nouveau sürücüsüne geçmeyi ve yazılım renderleme seçeneğini seçmeyi deneyin. Yazılımı yeniden başlatmanız gerekebilir."),
|
||||
("Always use software rendering", "Her zaman yazılım renderleme kullan"),
|
||||
("config_input", "Uzaktaki masaüstünü klavye ile kontrol etmek için RustDesk'e \"Giriş İzleme\" izinleri vermelisiniz."),
|
||||
("config_microphone", "Uzaktan konuşmak için RustDesk'e \"Ses Kaydı\" izinleri vermelisiniz."),
|
||||
("request_elevation_tip", "Ayrıca, uzak tarafta biri varsa yükseltme isteğinde bulunabilirsiniz."),
|
||||
("Wait", "Bekle"),
|
||||
("Elevation Error", "Yükseltme Hatası"),
|
||||
("Ask the remote user for authentication", "Uzaktaki kullanıcıdan kimlik doğrulamasını isteyin"),
|
||||
("Choose this if the remote account is administrator", "Uzak hesap yönetici ise bunu seçin"),
|
||||
("Transmit the username and password of administrator", "Yönetici kullanıcı adı ve parolasını iletim yapın"),
|
||||
("still_click_uac_tip", "Uzaktaki kullanıcının çalışan RustDesk'in UAC penceresinde hala Tamam'ı tıklaması gerekmektedir."),
|
||||
("Request Elevation", "Yükseltme İsteği"),
|
||||
("wait_accept_uac_tip", "Lütfen uzaktaki kullanıcının UAC iletişim kutusunu kabul etmesini bekleyin."),
|
||||
("Elevate successfully", "Başarıyla yükseltildi"),
|
||||
("uppercase", "büyük harf"),
|
||||
("lowercase", "küçük harf"),
|
||||
("digit", "rakam"),
|
||||
("special character", "özel karakter"),
|
||||
("length>=8", "uzunluk>=8"),
|
||||
("Weak", "Zayıf"),
|
||||
("Medium", "Orta"),
|
||||
("Strong", "Güçlü"),
|
||||
("Switch Sides", "Tarafları Değiştir"),
|
||||
("Please confirm if you want to share your desktop?", "Masaüstünüzü paylaşmak isteyip istemediğinizi onaylayın?"),
|
||||
("Display", "Görüntüle"),
|
||||
("Default View Style", "Varsayılan Görünüm Stili"),
|
||||
("Default Scroll Style", "Varsayılan Kaydırma Stili"),
|
||||
("Default Image Quality", "Varsayılan Görüntü Kalitesi"),
|
||||
("Default Codec", "Varsayılan Kodlayıcı"),
|
||||
("Bitrate", "Bit Hızı"),
|
||||
("FPS", "FPS"),
|
||||
("Auto", "Otomatik"),
|
||||
("Other Default Options", "Diğer Varsayılan Seçenekler"),
|
||||
("Voice call", "Sesli görüşme"),
|
||||
("Text chat", "Metin sohbeti"),
|
||||
("Stop voice call", "Sesli görüşmeyi durdur"),
|
||||
("relay_hint_tip", "Doğrudan bağlanmak mümkün olmayabilir; röle aracılığıyla bağlanmayı deneyebilirsiniz. Ayrıca, ilk denemenizde bir röle kullanmak istiyorsanız, ID'nin sonuna \"/r\" ekleyebilir veya son oturum kartındaki \"Her Zaman Röle Üzerinden Bağlan\" seçeneğini seçebilirsiniz."),
|
||||
("Reconnect", "Yeniden Bağlan"),
|
||||
("Codec", "Kodlayıcı"),
|
||||
("Resolution", "Çözünürlük"),
|
||||
("No transfers in progress", "Devam eden aktarımlar yok"),
|
||||
("Set one-time password length", "Bir seferlik parola uzunluğunu ayarla"),
|
||||
("install_cert_tip", "RustDesk sertifikasını yükleyin"),
|
||||
("confirm_install_cert_tip", "Bu, güvenilir olabilecek bir RustDesk test sertifikasıdır. Sertifika, gerekli olduğunda RustDesk sürücülerini güvenilir ve yüklemek üzere kullanacaktır."),
|
||||
("RDP Settings", "RDP Ayarları"),
|
||||
("Sort by", "Sırala"),
|
||||
("New Connection", "Yeni Bağlantı"),
|
||||
("Restore", "Geri Yükle"),
|
||||
("Minimize", "Simge Durumuna Küçült"),
|
||||
("Maximize", "Büyüt"),
|
||||
("Your Device", "Cihazınız"),
|
||||
("empty_recent_tip", "Üzgünüz, henüz son oturum yok!\nYeni bir plan yapma zamanı."),
|
||||
("empty_favorite_tip", "Henüz favori cihazınız yok mu?\nBağlanacak ve favorilere eklemek için birini bulalım!"),
|
||||
("empty_lan_tip", "Hayır, henüz hiçbir cihaz bulamadık gibi görünüyor."),
|
||||
("empty_address_book_tip", "Üzgünüm, şu anda adres defterinizde kayıtlı cihaz yok gibi görünüyor."),
|
||||
("eg: admin", "örn: admin"),
|
||||
("Empty Username", "Boş Kullanıcı Adı"),
|
||||
("Empty Password", "Boş Parola"),
|
||||
("Me", "Ben"),
|
||||
("identical_file_tip", "Bu dosya, cihazın dosyası ile aynıdır."),
|
||||
("show_monitors_tip", "Monitörleri araç çubuğunda göster"),
|
||||
("View Mode", "Görünüm Modu"),
|
||||
("login_linux_tip", "X masaüstü oturumu başlatmak için uzaktaki Linux hesabına giriş yapmanız gerekiyor"),
|
||||
("verify_rustdesk_password_tip", "RustDesk parolasını doğrulayın"),
|
||||
("remember_account_tip", "Bu hesabı hatırla"),
|
||||
("os_account_desk_tip", "Bu hesap, uzaktaki işletim sistemine giriş yapmak ve başsız masaüstü oturumunu etkinleştirmek için kullanılır."),
|
||||
("OS Account", "İşletim Sistemi Hesabı"),
|
||||
("another_user_login_title_tip", "Başka bir kullanıcı zaten oturum açtı"),
|
||||
("another_user_login_text_tip", "Bağlantıyı Kapat"),
|
||||
("xorg_not_found_title_tip", "Xorg bulunamadı"),
|
||||
("xorg_not_found_text_tip", "Lütfen Xorg'u yükleyin"),
|
||||
("no_desktop_title_tip", "Masaüstü mevcut değil"),
|
||||
("no_desktop_text_tip", "Lütfen GNOME masaüstünü yükleyin"),
|
||||
("No need to elevate", "Yükseltmeye gerek yok"),
|
||||
("System Sound", "Sistem Ses"),
|
||||
("Default", "Varsayılan"),
|
||||
("New RDP", "Yeni RDP"),
|
||||
("Fingerprint", "Parmak İzi"),
|
||||
("Copy Fingerprint", "Parmak İzini Kopyala"),
|
||||
("no fingerprints", "parmak izi yok"),
|
||||
("Select a peer", "Bir cihaz seçin"),
|
||||
("Select peers", "Cihazları seçin"),
|
||||
("Plugins", "Eklentiler"),
|
||||
("Uninstall", "Kaldır"),
|
||||
("Update", "Güncelle"),
|
||||
("Enable", "Etkinleştir"),
|
||||
("Disable", "Devre Dışı Bırak"),
|
||||
("Options", "Seçenekler"),
|
||||
("resolution_original_tip", "Orijinal çözünürlük"),
|
||||
("resolution_fit_local_tip", "Yerel çözünürlüğe sığdır"),
|
||||
("resolution_custom_tip", "Özel çözünürlük"),
|
||||
("Collapse toolbar", "Araç çubuğunu daralt"),
|
||||
("Accept and Elevate", "Kabul et ve yükselt"),
|
||||
("accept_and_elevate_btn_tooltip", "Bağlantıyı kabul et ve UAC izinlerini yükselt."),
|
||||
("clipboard_wait_response_timeout_tip", "Kopyalama yanıtı için zaman aşımına uğradı."),
|
||||
("Incoming connection", "Gelen bağlantı"),
|
||||
("Outgoing connection", "Giden bağlantı"),
|
||||
("Exit", "Çıkış"),
|
||||
("Open", "Aç"),
|
||||
("logout_tip", "Çıkış yapmak istediğinizden emin misiniz?"),
|
||||
("Service", "Hizmet"),
|
||||
("Start", "Başlat"),
|
||||
("Stop", "Durdur"),
|
||||
("exceed_max_devices", "Yönetilen cihazların maksimum sayısına ulaştınız."),
|
||||
("Sync with recent sessions", "Son oturumlarla senkronize et"),
|
||||
("Sort tags", "Etiketleri sırala"),
|
||||
("Open connection in new tab", "Bağlantıyı yeni sekmede aç"),
|
||||
("Move tab to new window", "Sekmeyi yeni pencereye taşı"),
|
||||
("Can not be empty", "Boş olamaz"),
|
||||
("Already exists", "Zaten var"),
|
||||
("Change Password", "Parolayı Değiştir"),
|
||||
("Refresh Password", "Parolayı Yenile"),
|
||||
("ID", "Kimlik"),
|
||||
("Grid View", "Izgara Görünümü"),
|
||||
("List View", "Liste Görünümü"),
|
||||
("Select", "Seç"),
|
||||
("Toggle Tags", "Etiketleri Değiştir"),
|
||||
("pull_ab_failed_tip", "Adres defterini yenileyemedi"),
|
||||
("push_ab_failed_tip", "Adres defterini sunucuya senkronize edemedi"),
|
||||
("synced_peer_readded_tip", "Son oturumlar listesinde bulunan cihazlar adres defterine geri senkronize edilecektir."),
|
||||
("Change Color", "Rengi Değiştir"),
|
||||
("Primary Color", "Birincil Renk"),
|
||||
("HSV Color", "HSV Rengi"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -528,15 +528,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Move tab to new window", "移動標籤到新視窗"),
|
||||
("Can not be empty", "不能為空"),
|
||||
("Already exists", "已經存在"),
|
||||
("Change Password", ""),
|
||||
("Refresh Password", ""),
|
||||
("ID", ""),
|
||||
("Grid View", ""),
|
||||
("List View", ""),
|
||||
("Select", ""),
|
||||
("Toggle Tags", ""),
|
||||
("pull_ab_failed_tip", "未成功獲取地址簿"),
|
||||
("push_ab_failed_tip", "未成功上傳地址簿"),
|
||||
("synced_peer_readded_tip", "最近會話中存在的設備將會被重新同步到地址簿。"),
|
||||
("Change Password", "更改密碼"),
|
||||
("Refresh Password", "重新整理密碼"),
|
||||
("ID", "ID"),
|
||||
("Grid View", "網格檢視"),
|
||||
("List View", "清單檢視"),
|
||||
("Select", "選擇"),
|
||||
("Toggle Tags", "切換標籤"),
|
||||
("pull_ab_failed_tip", "通訊錄更新失敗"),
|
||||
("push_ab_failed_tip", "成功同步通訊錄至伺服器"),
|
||||
("synced_peer_readded_tip", "最近會話中存在的設備將會被重新同步到通訊錄。"),
|
||||
("Change Color", "更改顏色"),
|
||||
("Primary Color", "基本色"),
|
||||
("HSV Color", "HSV 色"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -538,5 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ async fn connect_and_login(
|
||||
return Ok(None);
|
||||
}
|
||||
Some(login_response::Union::PeerInfo(pi)) => {
|
||||
interface.handle_peer_info(pi, false);
|
||||
interface.handle_peer_info(pi);
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
|
@ -39,7 +39,7 @@ use hbb_common::{
|
||||
tokio_util::codec::{BytesCodec, Framed},
|
||||
};
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
use scrap::android::call_main_service_mouse_input;
|
||||
use scrap::android::call_main_service_pointer_input;
|
||||
use serde_json::{json, value::Value};
|
||||
use sha2::{Digest, Sha256};
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
@ -135,6 +135,14 @@ struct Session {
|
||||
random_password: String,
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
struct StartCmIpcPara {
|
||||
rx_to_cm: mpsc::UnboundedReceiver<ipc::Data>,
|
||||
tx_from_cm: mpsc::UnboundedSender<ipc::Data>,
|
||||
rx_desktop_ready: mpsc::Receiver<()>,
|
||||
tx_cm_stream_ready: mpsc::Sender<()>,
|
||||
}
|
||||
|
||||
pub struct Connection {
|
||||
inner: ConnInner,
|
||||
stream: super::Stream,
|
||||
@ -193,6 +201,8 @@ pub struct Connection {
|
||||
linux_headless_handle: LinuxHeadlessHandle,
|
||||
closed: bool,
|
||||
delay_response_instant: Instant,
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
start_cm_ipc_para: Option<StartCmIpcPara>,
|
||||
}
|
||||
|
||||
impl ConnInner {
|
||||
@ -324,6 +334,13 @@ impl Connection {
|
||||
linux_headless_handle,
|
||||
closed: false,
|
||||
delay_response_instant: Instant::now(),
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
start_cm_ipc_para: Some(StartCmIpcPara {
|
||||
rx_to_cm,
|
||||
tx_from_cm,
|
||||
rx_desktop_ready,
|
||||
tx_cm_stream_ready,
|
||||
}),
|
||||
};
|
||||
let addr = hbb_common::try_into_v4(addr);
|
||||
if !conn.on_open(addr).await {
|
||||
@ -332,14 +349,6 @@ impl Connection {
|
||||
sleep(1.).await;
|
||||
return;
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) =
|
||||
start_ipc(rx_to_cm, tx_from_cm, rx_desktop_ready, tx_cm_stream_ready).await
|
||||
{
|
||||
log::error!("ipc to connection manager exit: {}", err);
|
||||
}
|
||||
});
|
||||
#[cfg(target_os = "android")]
|
||||
start_channel(rx_to_cm, tx_from_cm);
|
||||
if !conn.keyboard {
|
||||
@ -1316,6 +1325,24 @@ impl Connection {
|
||||
self.video_ack_required = lr.video_ack_required;
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
fn try_start_cm_ipc(&mut self) {
|
||||
if let Some(p) = self.start_cm_ipc_para.take() {
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = start_ipc(
|
||||
p.rx_to_cm,
|
||||
p.tx_from_cm,
|
||||
p.rx_desktop_ready,
|
||||
p.tx_cm_stream_ready,
|
||||
)
|
||||
.await
|
||||
{
|
||||
log::error!("ipc to connection manager exit: {}", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_message(&mut self, msg: Message) -> bool {
|
||||
if let Some(message::Union::LoginRequest(lr)) = msg.union {
|
||||
self.handle_login_request_without_validation(&lr).await;
|
||||
@ -1379,6 +1406,9 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
self.try_start_cm_ipc();
|
||||
|
||||
#[cfg(any(
|
||||
feature = "flatpak",
|
||||
feature = "appimage",
|
||||
@ -1455,17 +1485,21 @@ impl Connection {
|
||||
self.send_login_error("Too many wrong password attempts")
|
||||
.await;
|
||||
Self::post_alarm_audit(
|
||||
AlarmAuditType::ManyWrongPassword,
|
||||
AlarmAuditType::ExceedThirtyAttempts,
|
||||
json!({
|
||||
"ip":self.ip,
|
||||
"id":lr.my_id.clone(),
|
||||
"name": lr.my_name.clone(),
|
||||
}),
|
||||
);
|
||||
} else if time == failure.0 && failure.1 > 6 {
|
||||
self.send_login_error("Please try 1 minute later").await;
|
||||
Self::post_alarm_audit(
|
||||
AlarmAuditType::FrequentAttempt,
|
||||
AlarmAuditType::SixAttemptsWithinOneMinute,
|
||||
json!({
|
||||
"ip":self.ip,
|
||||
"id":lr.my_id.clone(),
|
||||
"name": lr.my_name.clone(),
|
||||
}),
|
||||
);
|
||||
} else if !self.validate_password() {
|
||||
@ -1539,6 +1573,8 @@ impl Connection {
|
||||
self.from_switch = true;
|
||||
self.try_start_cm(lr.my_id.clone(), lr.my_name.clone(), true);
|
||||
self.send_logon_response().await;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
self.try_start_cm_ipc();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1547,8 +1583,8 @@ impl Connection {
|
||||
match msg.union {
|
||||
Some(message::Union::MouseEvent(me)) => {
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
if let Err(e) = call_main_service_mouse_input(me.mask, me.x, me.y) {
|
||||
log::debug!("call_main_service_mouse_input fail:{}", e);
|
||||
if let Err(e) = call_main_service_pointer_input("mouse", me.mask, me.x, me.y) {
|
||||
log::debug!("call_main_service_pointer_input fail:{}", e);
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if self.peer_keyboard_enabled() {
|
||||
@ -1560,8 +1596,35 @@ impl Connection {
|
||||
self.input_mouse(me, self.inner.id());
|
||||
}
|
||||
}
|
||||
Some(message::Union::PointerDeviceEvent(pde)) =>
|
||||
{
|
||||
Some(message::Union::PointerDeviceEvent(pde)) => {
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
if let Err(e) = match pde.union {
|
||||
Some(pointer_device_event::Union::TouchEvent(touch)) => match touch.union {
|
||||
Some(touch_event::Union::PanStart(pan_start)) => {
|
||||
call_main_service_pointer_input(
|
||||
"touch",
|
||||
4,
|
||||
pan_start.x,
|
||||
pan_start.y,
|
||||
)
|
||||
}
|
||||
Some(touch_event::Union::PanUpdate(pan_update)) => {
|
||||
call_main_service_pointer_input(
|
||||
"touch",
|
||||
5,
|
||||
pan_update.x,
|
||||
pan_update.y,
|
||||
)
|
||||
}
|
||||
Some(touch_event::Union::PanEnd(pan_end)) => {
|
||||
call_main_service_pointer_input("touch", 6, pan_end.x, pan_end.y)
|
||||
}
|
||||
_ => Ok(()),
|
||||
},
|
||||
_ => Ok(()),
|
||||
} {
|
||||
log::debug!("call_main_service_pointer_input fail:{}", e);
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if self.peer_keyboard_enabled() {
|
||||
MOUSE_MOVE_TIME.store(get_time(), Ordering::SeqCst);
|
||||
@ -2504,8 +2567,8 @@ mod privacy_mode {
|
||||
|
||||
pub enum AlarmAuditType {
|
||||
IpWhitelist = 0,
|
||||
ManyWrongPassword = 1,
|
||||
FrequentAttempt = 2,
|
||||
ExceedThirtyAttempts = 1,
|
||||
SixAttemptsWithinOneMinute = 2,
|
||||
}
|
||||
|
||||
pub enum FileAuditType {
|
||||
|
@ -325,6 +325,7 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
|
||||
// for tmp use, without real conn id
|
||||
let mut write_jobs: Vec<fs::TransferJob> = Vec::new();
|
||||
#[cfg(windows)]
|
||||
let is_authorized = self.cm.is_authorized(self.conn_id);
|
||||
|
||||
#[cfg(windows)]
|
||||
|
@ -594,7 +594,7 @@ pub fn current_is_wayland() -> bool {
|
||||
|
||||
#[inline]
|
||||
pub fn get_new_version() -> String {
|
||||
hbb_common::get_version_from_url(&*SOFTWARE_UPDATE_URL.lock().unwrap())
|
||||
(*SOFTWARE_UPDATE_URL.lock().unwrap().rsplit('/').next().unwrap_or("")).to_string()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -625,6 +625,7 @@ pub fn discover() {
|
||||
|
||||
#[cfg(feature = "flutter")]
|
||||
pub fn peer_to_map(id: String, p: PeerConfig) -> HashMap<&'static str, String> {
|
||||
use hbb_common::sodiumoxide::base64;
|
||||
HashMap::<&str, String>::from_iter([
|
||||
("id", id),
|
||||
("username", p.info.username.clone()),
|
||||
@ -634,18 +635,11 @@ pub fn peer_to_map(id: String, p: PeerConfig) -> HashMap<&'static str, String> {
|
||||
"alias",
|
||||
p.options.get("alias").unwrap_or(&"".to_owned()).to_owned(),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
#[cfg(feature = "flutter")]
|
||||
pub fn peer_to_map_ab(id: String, p: PeerConfig) -> HashMap<&'static str, String> {
|
||||
use hbb_common::sodiumoxide::base64;
|
||||
let mut m = peer_to_map(id, p.clone());
|
||||
m.insert(
|
||||
(
|
||||
"hash",
|
||||
base64::encode(p.password, base64::Variant::Original),
|
||||
);
|
||||
m
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
#[cfg(feature = "flutter")]
|
||||
|
@ -48,17 +48,6 @@ pub static IS_IN: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
const CHANGE_RESOLUTION_VALID_TIMEOUT_SECS: u64 = 15;
|
||||
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
#[derive(Default)]
|
||||
pub struct CacheFlutter {
|
||||
pub pi: PeerInfo,
|
||||
pub sp: Option<SwitchDisplay>,
|
||||
pub cursor_data: HashMap<u64, CursorData>,
|
||||
pub cursor_id: u64,
|
||||
pub is_secured_direct: Option<(bool, bool)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Session<T: InvokeUiSession> {
|
||||
pub session_id: SessionID, // different from the one in LoginConfigHandler, used for flutter UI message pass
|
||||
@ -73,9 +62,6 @@ pub struct Session<T: InvokeUiSession> {
|
||||
pub server_file_transfer_enabled: Arc<RwLock<bool>>,
|
||||
pub server_clipboard_enabled: Arc<RwLock<bool>>,
|
||||
pub last_change_display: Arc<Mutex<ChangeDisplayRecord>>,
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub cache_flutter: Arc<RwLock<CacheFlutter>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -724,6 +710,49 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
send_pointer_device_event(evt, alt, ctrl, shift, command, self);
|
||||
}
|
||||
|
||||
pub fn send_touch_pan_event(
|
||||
&self,
|
||||
event: &str,
|
||||
x: i32,
|
||||
y: i32,
|
||||
alt: bool,
|
||||
ctrl: bool,
|
||||
shift: bool,
|
||||
command: bool,
|
||||
) {
|
||||
let mut touch_evt = TouchEvent::new();
|
||||
match event {
|
||||
"pan_start" => {
|
||||
touch_evt.set_pan_start(TouchPanStart {
|
||||
x,
|
||||
y,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
"pan_update" => {
|
||||
touch_evt.set_pan_update(TouchPanUpdate {
|
||||
x,
|
||||
y,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
"pan_end" => {
|
||||
touch_evt.set_pan_end(TouchPanEnd {
|
||||
x,
|
||||
y,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
log::warn!("unknown touch pan event: {}", event);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut evt = PointerDeviceEvent::new();
|
||||
evt.set_touch_event(touch_evt);
|
||||
send_pointer_device_event(evt, alt, ctrl, shift, command, self);
|
||||
}
|
||||
|
||||
pub fn send_mouse(
|
||||
&self,
|
||||
mask: i32,
|
||||
@ -1095,7 +1124,7 @@ impl<T: InvokeUiSession> Interface for Session<T> {
|
||||
handle_login_error(self.lc.clone(), err, self)
|
||||
}
|
||||
|
||||
fn handle_peer_info(&mut self, mut pi: PeerInfo, is_cached_pi: bool) {
|
||||
fn handle_peer_info(&mut self, mut pi: PeerInfo) {
|
||||
log::debug!("handle_peer_info :{:?}", pi);
|
||||
pi.username = self.lc.read().unwrap().get_username(&pi);
|
||||
if pi.current_display as usize >= pi.displays.len() {
|
||||
@ -1116,13 +1145,11 @@ impl<T: InvokeUiSession> Interface for Session<T> {
|
||||
self.msgbox("error", "Remote Error", "No Display", "");
|
||||
return;
|
||||
}
|
||||
if !is_cached_pi {
|
||||
self.try_change_init_resolution(pi.current_display);
|
||||
let p = self.lc.read().unwrap().should_auto_login();
|
||||
if !p.is_empty() {
|
||||
input_os_password(p, true, self.clone());
|
||||
}
|
||||
}
|
||||
let current = &pi.displays[pi.current_display as usize];
|
||||
self.set_display(
|
||||
current.x,
|
||||
@ -1222,23 +1249,6 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
pub fn ctrl_alt_del(&self) {
|
||||
self.send_key_event(&crate::keyboard::client::event_ctrl_alt_del());
|
||||
}
|
||||
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub fn restore_flutter_cache(&mut self) {
|
||||
if let Some((is_secured, direct)) = self.cache_flutter.read().unwrap().is_secured_direct {
|
||||
self.set_connection_type(is_secured, direct);
|
||||
}
|
||||
let pi = self.cache_flutter.read().unwrap().pi.clone();
|
||||
self.handle_peer_info(pi, true);
|
||||
if let Some(sp) = self.cache_flutter.read().unwrap().sp.as_ref() {
|
||||
self.handle_peer_switch_display(sp);
|
||||
}
|
||||
for (_, cd) in self.cache_flutter.read().unwrap().cursor_data.iter() {
|
||||
self.set_cursor_data(cd.clone());
|
||||
}
|
||||
self.set_cursor_id(self.cache_flutter.read().unwrap().cursor_id.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
|
Loading…
Reference in New Issue
Block a user