Compare commits

..

114 Commits
1.2.0 ... 1.2.1

Author SHA1 Message Date
RustDesk
a9f0223ea3 Merge pull request #4967 from 21pages/fix
fix port-forward/rdp empty show
2023-07-12 15:09:17 +08:00
RustDesk
9577439a58 Merge pull request #4965 from FastAct/patch-16
Update nl.rs
2023-07-12 14:59:22 +08:00
21pages
d5f615f8d6 fix port-forward/rdp empty show
Signed-off-by: 21pages <pages21@163.com>
2023-07-12 14:58:16 +08:00
FastAct
99801258a2 Update nl.rs 2023-07-12 08:47:40 +02:00
rustdesk
018f551d13 add gstreamer1.0-pipewire dep to ubuntu, do not add it to fedora,
because it occurs since fedora 33, and since fedora 34, it was installed
with the system by default
2023-07-12 13:18:18 +08:00
rustdesk
63cb39f365 failed to use if: ${{ matrix.job.arch == 'armv7' || inputs.upload-artifact == 'true' }} 2023-07-11 23:09:32 +08:00
RustDesk
6bb6535d3e Merge pull request #4959 from Mr-Update/patch-14
Update de.rs
2023-07-11 22:56:05 +08:00
rustdesk
25011331ed fix ci 2023-07-11 22:54:57 +08:00
Mr-Update
b3f799a7dd Update de.rs 2023-07-11 14:46:32 +02:00
Mr-Update
9a110c703e Update de.rs 2023-07-11 14:40:14 +02:00
rustdesk
c076d4a17c try to disable arm ci again 2023-07-11 16:34:06 +08:00
RustDesk
de36ec5b2c Merge pull request #4948 from mehdi-song/patch-33
Update fa.rs
2023-07-11 13:05:40 +08:00
RustDesk
5ce9037310 Merge pull request #4949 from mehdi-song/patch-34
Update fa.rs
2023-07-11 13:05:05 +08:00
RustDesk
e7ac67effc Merge pull request #4947 from dignow/fix/win_elevate_prompt_on_start
fix, win, elevate prompt on start
2023-07-11 13:04:07 +08:00
mehdi-song
17dbd27cfd Update fa.rs 2023-07-11 04:54:46 +00:00
mehdi-song
dcc0a1425e Update fa.rs 2023-07-11 04:33:09 +00:00
dignow
97359d6eae fix, win, elevate prompt on start
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-11 12:19:28 +08:00
rustdesk
cfcfd127b2 remove tags for the other ci 2023-07-11 10:35:52 +08:00
RustDesk
21a172478c Merge pull request #4946 from 21pages/fix
revert cm show
2023-07-11 10:17:45 +08:00
21pages
3db0f90af9 revert cm fixed width
Signed-off-by: 21pages <pages21@163.com>
2023-07-11 10:05:31 +08:00
21pages
d7ef3df2b3 Revert "try fix linux cm abnormal show"
This reverts commit 193426a3c1.
2023-07-11 10:01:56 +08:00
RustDesk
e927d49aa2 Merge pull request #4944 from bovirus/master
Update italian language
2023-07-10 23:42:05 +08:00
bovirus
4b8017292e Update italian language 2023-07-10 17:40:04 +02:00
rustdesk
6b8d2a33c0 fix lang 2023-07-10 23:02:26 +08:00
RustDesk
3c5e27b0ca Merge pull request #4943 from 21pages/fix_chat
opt mobile chat
2023-07-10 22:46:37 +08:00
21pages
fcf6b5466a mobile show chat icon overlay on main page
Signed-off-by: 21pages <pages21@163.com>
2023-07-10 22:21:03 +08:00
21pages
745f33a8c1 opt mobile unread and autoSizeTextField
Signed-off-by: 21pages <pages21@163.com>
2023-07-10 22:21:01 +08:00
21pages
193426a3c1 try fix linux cm abnormal show
Signed-off-by: 21pages <pages21@163.com>
2023-07-10 22:20:38 +08:00
RustDesk
e4f0a7b1d5 Merge pull request #4941 from Mr-Update/patch-13
Update de.rs
2023-07-10 17:32:41 +08:00
RustDesk
48a11cd8ca Merge pull request #4940 from bovirus/master
Update italian language
2023-07-10 17:32:28 +08:00
rustdesk
a96eb236c1 try to fix issue #3261 with new api, but not sure if it works 2023-07-10 17:31:04 +08:00
Mr-Update
2acb5d58cf Update de.rs 2023-07-10 11:14:40 +02:00
bovirus
c890cbf611 Update italian language 2023-07-10 10:29:44 +02:00
RustDesk
4e19777ba0 Merge pull request #4939 from 21pages/fix_chat
opt chat model
2023-07-10 16:10:23 +08:00
21pages
22994ee8f2 opt chatModel, linux same startup height as dart
Signed-off-by: 21pages <pages21@163.com>
2023-07-10 16:02:47 +08:00
rustdesk
142813a7e7 fix bump.sh 2023-07-10 15:48:15 +08:00
rustdesk
21c1c06a20 change to 1.2.1 2023-07-10 14:38:13 +08:00
RustDesk
8028b23086 Merge pull request #4934 from 21pages/fix_chat
opt mobile chat page
2023-07-10 13:27:09 +08:00
21pages
6c6f6c081e mobile unread chat
Signed-off-by: 21pages <pages21@163.com>
2023-07-10 12:57:38 +08:00
rustdesk
e1bd925877 fix lang 2023-07-10 09:30:41 +08:00
rustdesk
8a2d702348 fix lang 2023-07-10 09:28:54 +08:00
RustDesk
13abacf6e0 Merge pull request #4930 from dignow/fix/wrong_verification_code
wrong verification code
2023-07-10 09:27:25 +08:00
dignow
2bc41bfd90 wrong verification code
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-10 00:24:35 +08:00
21pages
65529b10b6 opt mobile chat message store
Signed-off-by: 21pages <pages21@163.com>
2023-07-09 21:45:35 +08:00
rustdesk
f5d0cece43 add res/osx-dist.sh 2023-07-09 17:59:01 +08:00
rustdesk
99d25002f7 add libappindicator3-1 to deb package 2023-07-09 16:30:27 +08:00
RustDesk
be4cc32e2f Merge pull request #4926 from dignow/refact/remote_toolbar_menu
Refact/remote toolbar menu
2023-07-09 15:03:31 +08:00
dignow
02c9d3fe2c refact, remote toolbar menu, sub menu
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-09 14:40:16 +08:00
dignow
317bc21a1b refact, remote toolbar menu, macos
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-09 14:40:16 +08:00
dignow
3148ab214a refact, remote toolbar menu, windows
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-09 14:40:16 +08:00
dignow
e21502e6a5 refact, remote toolbar, menu
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-09 14:40:16 +08:00
rustdesk
147391eaa1 tab theme minor adjustment 2023-07-09 14:26:46 +08:00
RustDesk
081ac7b4d1 Merge pull request #4925 from mehdi-song/patch-32
Update fa.rs
2023-07-09 13:41:13 +08:00
mehdi-song
f2a02c154c Update fa.rs 2023-07-09 04:32:01 +00:00
RustDesk
6762ef220f Merge pull request #4921 from dignow/fix/win_privacy_broker
Fix/win privacy broker
2023-07-09 11:10:06 +08:00
21pages
89b3e68788 add chatbox border
Signed-off-by: 21pages <pages21@163.com>
2023-07-09 10:35:10 +08:00
dignow
e80da4af0d debug, win, privacy mode, broker
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-09 02:06:24 +08:00
dignow
32c349dd8b tmp commit
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-09 02:06:24 +08:00
dignow
8bb851704a kill broker on exit service
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-09 02:06:24 +08:00
dignow
1648895156 fix, win privacy mode, broker process
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-09 02:06:24 +08:00
RustDesk
84d7115e36 Merge pull request #4920 from 21pages/fix_cm
fixed cm control page width
2023-07-08 22:34:25 +08:00
21pages
9f1d4e8ba6 fixed cm control page width and not resizable
Signed-off-by: 21pages <pages21@163.com>
2023-07-08 19:30:50 +08:00
rustdesk
8c186edd18 new cm tab style 2023-07-08 17:55:55 +08:00
RustDesk
94763e9fe0 Merge pull request #4918 from dtemme/fixPrivacyUrl
Fix privacy URL
2023-07-08 16:47:30 +08:00
RustDesk
91c53b1db4 Merge pull request #4917 from 21pages/fix_chat
Fix chat
2023-07-08 16:40:31 +08:00
Daniel Temme
ebe0681748 Fix privacy URL 2023-07-08 10:37:56 +02:00
21pages
76987105c1 fix android show chat overly out of remote page
Signed-off-by: 21pages <pages21@163.com>
2023-07-08 16:08:23 +08:00
21pages
b0f22d8693 fix cm unread and add vertical divider
Signed-off-by: 21pages <pages21@163.com>
2023-07-08 14:52:12 +08:00
rustdesk
778f46bbb2 do not use is_root, use check_super_user_permission instead, because on
windows, not easy to run as system
2023-07-08 12:43:55 +08:00
rustdesk
afd77181ff println in winmain 2023-07-08 12:34:40 +08:00
rustdesk
685d960b1e revert back to old window_on_top, it works fine in my test 2023-07-07 22:18:14 +08:00
rustdesk
53fdddb1a9 new on_top not work on macos 2023-07-07 21:57:52 +08:00
RustDesk
54d03b2ecf Merge pull request #4902 from 21pages/urilink
add missing uri pattern
2023-07-07 21:08:52 +08:00
21pages
beb14f90b9 add missing uri pattern: rustdesk://<id>
Signed-off-by: 21pages <pages21@163.com>
2023-07-07 19:18:48 +08:00
RustDesk
daa59af5be Merge pull request #4901 from 21pages/urilink
flutter: file transfer/port forward/rdp command line support
2023-07-07 18:12:32 +08:00
21pages
1f137b3542 flutter: file-transfer/port forward/rdp support
Signed-off-by: 21pages <pages21@163.com>
2023-07-07 17:03:18 +08:00
rustdesk
12149bf3e3 let's maintain ny and sg only 2023-07-07 15:59:03 +08:00
rustdesk
b85cb81d9e maintain one id server only 2023-07-07 15:55:51 +08:00
rustdesk
1954f905af fix #4896 2023-07-07 13:33:08 +08:00
RustDesk
e5dad3467b Merge pull request #4892 from flusheDData/master
Update es.rs
2023-07-06 22:40:57 +08:00
Miguel F. G
5a9d50db46 Update es.rs
Removal of a space before text
2023-07-06 16:37:30 +02:00
RustDesk
6c8c3b4a7a Merge pull request #4885 from NT-VPS/master
+free public server
2023-07-06 14:39:07 +08:00
RustDesk
d6709e069c Merge pull request #4889 from 21pages/fix_remote_chat
opt remote chat page
2023-07-06 14:37:35 +08:00
21pages
a5675c06e7 when remote page receive chat message: if minimized, top page and switch tab, otherwise add unread message count
Signed-off-by: 21pages <pages21@163.com>
2023-07-06 14:14:19 +08:00
Konstantin
099a5edef3 +free public server 2023-07-05 20:03:13 +03:00
RustDesk
3dfe8b27e1 Merge pull request #4878 from 21pages/fix_chat
Fix chat
2023-07-05 22:26:48 +08:00
rustdesk
3bb9417a9d fix #4880 2023-07-05 22:12:54 +08:00
RustDesk
b225494137 Merge pull request #4882 from jacotec/master
Add missing definition for "upload-tag"
2023-07-05 22:01:22 +08:00
Marco Jakobs
4bde36e186 Update flutter-build.yml 2023-07-05 15:24:44 +02:00
21pages
87e06e974e desktop block remote click chat page
Signed-off-by: 21pages <pages21@163.com>
2023-07-05 20:20:51 +08:00
21pages
7c4c69aa75 cm unread message count
Signed-off-by: 21pages <pages21@163.com>
2023-07-05 20:20:51 +08:00
21pages
769e46d3e6 fix cm window_on_top when behind other window
Signed-off-by: 21pages <pages21@163.com>
2023-07-05 20:20:49 +08:00
rustdesk
dce35ff881 flutter-tag.yml 2023-07-05 17:47:10 +08:00
RustDesk
16ecbf117c Merge pull request #4875 from bovirus/master
Update italian language
2023-07-05 16:31:26 +08:00
RustDesk
dc45952558 Merge pull request #4876 from dignow/fix/revert_desktop_login_
revert desktop login dialog
2023-07-05 16:31:13 +08:00
dignow
4707e0ed2e revert desktop login dialog
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-05 16:15:39 +08:00
bovirus
1ea009ff61 Update italian language 2023-07-05 10:12:52 +02:00
RustDesk
bf4854a444 Merge pull request #4874 from dignow/refact/mobile_dismiss_login_before_verification
dismiss login dialog before verification
2023-07-05 16:05:01 +08:00
dignow
a3024fdd14 trivial change
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-05 15:48:30 +08:00
dignow
92a4d88532 dismiss login dialog before verification
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-05 14:53:29 +08:00
RustDesk
5cef21fd40 Merge pull request #4873 from 21pages/fix
fix config load
2023-07-05 11:02:32 +08:00
RustDesk
b52661083c Merge pull request #4872 from rustdesk/revert-4869-fix
Revert "remove cm use peer info as title"
2023-07-05 10:57:08 +08:00
RustDesk
b41c6be341 Revert "remove cm use peer info as title" 2023-07-05 10:56:54 +08:00
21pages
07c21ced70 update confy, not create if not exist when load
Signed-off-by: 21pages <pages21@163.com>
2023-07-05 10:56:36 +08:00
21pages
533fc082bd fix relay_hint_tip
Signed-off-by: 21pages <pages21@163.com>
2023-07-05 10:38:33 +08:00
21pages
9ad9cb8ff2 Revert "remove peers platform filter for failed connections"
This reverts commit b5cf959206.
2023-07-05 09:58:00 +08:00
RustDesk
f47a5600b1 Merge pull request #4869 from 21pages/fix
remove cm use peer info as title
2023-07-05 09:24:24 +08:00
21pages
31a7d817da remove cm use peer info as title
Signed-off-by: 21pages <pages21@163.com>
2023-07-05 08:15:09 +08:00
RustDesk
65924cb134 Merge pull request #4859 from dignow/fix/toolbar_os_password
Fix/toolbar os password
2023-07-04 23:16:43 +08:00
dignow
437b5f2ca3 debug
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-04 20:26:43 +08:00
dignow
b75edd9710 fix os password
Signed-off-by: dignow <linlong1265@gmail.com>
2023-07-04 19:43:39 +08:00
RustDesk
06af880b12 Merge pull request #4846 from xalt7x/master
Update ua.rs
2023-07-04 12:46:22 +08:00
rustdesk
bb79abffe4 remove lightsail 2023-07-04 12:45:37 +08:00
Yevhen
89933bb2bd Update ua.rs 2023-07-03 22:57:08 +03:00
121 changed files with 1263 additions and 461 deletions

View File

@@ -13,8 +13,6 @@ on:
push:
branches:
- master
tags:
- '*'
paths-ignore:
- ".github/**"
- "docs/**"

View File

@@ -6,6 +6,9 @@ on:
upload-artifact:
type: boolean
default: true
upload-tag:
type: string
default: "nightly"
env:
CARGO_NDK_VERSION: "3.1.2"
@@ -15,11 +18,11 @@ env:
# for arm64 linux
FLUTTER_ELINUX_VERSION: "3.10.5"
FLUTTER_ELINUX_COMMIT_ID: "410b3ca42f2cd0c485edf517a1666652bab442d4"
TAG_NAME: "nightly"
TAG_NAME: "${{ inputs.upload-tag }}"
# vcpkg version: 2023.04.15
# for multiarch gcc compatibility
VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1"
VERSION: "1.2.0"
VERSION: "1.2.1"
NDK_VERSION: "r25c"
#signing keys env variable checks
ANDROID_SIGNING_KEY: '${{ secrets.ANDROID_SIGNING_KEY }}'
@@ -374,6 +377,7 @@ jobs:
uses: ./.github/workflows/bridge.yml
build-rustdesk-ios:
if: ${{ inputs.upload-artifact == 'true' }}
needs: [generate-bridge-linux]
name: build rustdesk ios ipa ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ${{ matrix.job.os }}
@@ -779,6 +783,7 @@ jobs:
path: target/release/liblibrustdesk.so
build-rustdesk-lib-linux-arm:
if: ${{ inputs.upload-artifact == 'true' }}
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: ${{ matrix.job.os }}
@@ -941,6 +946,7 @@ jobs:
path: target/release/liblibrustdesk.so
build-rustdesk-sciter-arm:
if: ${{ inputs.upload-artifact == 'true' }}
needs: [build-vcpkg-deps-linux]
name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ${{ matrix.job.os }}
@@ -1099,6 +1105,7 @@ jobs:
path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb
build-rustdesk-linux-arm:
if: ${{ inputs.upload-artifact == 'true' }}
needs: [build-rustdesk-lib-linux-arm]
name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ubuntu-20.04 # 20.04 has more performance on arm build

View File

@@ -9,8 +9,6 @@ on:
push:
branches:
- master
tags:
- '*'
paths-ignore:
- ".github/**"
- "docs/**"
@@ -21,4 +19,4 @@ jobs:
uses: ./.github/workflows/flutter-build.yml
with:
upload-artifact: false

View File

@@ -12,3 +12,4 @@ jobs:
secrets: inherit
with:
upload-artifact: true
upload-tag: "nightly"

18
.github/workflows/flutter-tag.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Flutter Tag Build
on:
workflow_dispatch:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- '[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-[0-9]+'
- '[0-9]+.[0-9]+.[0-9]+-[0-9]+'
jobs:
run-flutter-tag-build:
uses: ./.github/workflows/flutter-build.yml
secrets: inherit
with:
upload-artifact: true
upload-tag: "1.2.1"

View File

@@ -10,7 +10,7 @@ env:
# vcpkg version: 2022.05.10
# for multiarch gcc compatibility
VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44"
VERSION: "1.2.0"
VERSION: "1.2.1"
jobs:
build-for-windows-2022-12-05:

4
Cargo.lock generated
View File

@@ -1054,7 +1054,7 @@ dependencies = [
[[package]]
name = "confy"
version = "0.4.0-2"
source = "git+https://github.com/open-trade/confy#9f231b2039cf8a8f8cdf6b829c5ac0016e146077"
source = "git+https://github.com/open-trade/confy#7855cd3c32b1a60b44e5076ee8f6b4131da10350"
dependencies = [
"directories-next",
"serde 1.0.163",
@@ -5146,7 +5146,7 @@ dependencies = [
[[package]]
name = "rustdesk"
version = "1.2.0"
version = "1.2.1"
dependencies = [
"android_logger",
"arboard",

View File

@@ -1,6 +1,6 @@
[package]
name = "rustdesk"
version = "1.2.0"
version = "1.2.1"
authors = ["rustdesk <info@rustdesk.com>"]
edition = "2021"
build= "build.rs"

View File

@@ -34,7 +34,6 @@ RustDesk welcomes contribution from everyone. See [CONTRIBUTING.md](docs/CONTRIB
Below are the servers you are using for free, they may change over time. If you are not close to one of these, your network may be slow.
| Location | Vendor | Specification |
| --------- | ------------- | ------------------ |
| South Korea (Seoul) | [AWS lightsail](https://aws.amazon.com) | 1 vCPU / 0.5 GB RAM |
| Germany | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4 GB RAM |
| Germany | [Codext](https://codext.de) | 4 vCPU / 8 GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |

View File

@@ -2,7 +2,7 @@
version: 1
script:
- rm -rf ./AppDir || true
- bsdtar -zxvf ../rustdesk-1.2.0.deb
- bsdtar -zxvf ../rustdesk-1.2.1.deb
- tar -xvf ./data.tar.xz
- mkdir ./AppDir
- mv ./usr ./AppDir/usr
@@ -17,7 +17,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.2.0
version: 1.2.1
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:
@@ -50,6 +50,7 @@ AppDir:
- libva-x11-2
- libvdpau1
- libgstreamer-plugins-base1.0-0
- gstreamer1.0-pipewire
- libwayland-cursor0
- libwayland-egl1
- libpulse0

View File

@@ -2,7 +2,7 @@
version: 1
script:
- rm -rf ./AppDir || true
- bsdtar -zxvf ../rustdesk-1.2.0.deb
- bsdtar -zxvf ../rustdesk-1.2.1.deb
- tar -xvf ./data.tar.xz
- mkdir ./AppDir
- mv ./usr ./AppDir/usr
@@ -17,7 +17,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.2.0
version: 1.2.1
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:
@@ -52,6 +52,7 @@ AppDir:
- libva-x11-2
- libvdpau1
- libgstreamer-plugins-base1.0-0
- gstreamer1.0-pipewire
- libwayland-cursor0
- libwayland-egl1
- libpulse0

View File

@@ -285,7 +285,7 @@ Version: %s
Architecture: %s
Maintainer: rustdesk <info@rustdesk.com>
Homepage: https://rustdesk.com
Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0, libpam0g
Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0, libpam0g, libappindicator3-1, gstreamer1.0-pipewire
Description: A remote control software.
""" % (version, get_arch())

View File

@@ -32,9 +32,9 @@
فيما يلي الخوادم التي تستخدمها مجانًا، وقد تتغير طوال الوقت. إذا لم تكن قريبًا من أحد هؤلاء، فقد تكون شبكتك بطيئة.
| الموقع | المورد | المواصفات |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## التبعيات

View File

@@ -27,9 +27,9 @@ Projekt RustDesk vítá přiložení ruky k dílu od každého. Jak začít se d
Níže jsou uvedeny servery zdarma k vašemu použití (údaje se mohou v čase měnit). Pokud se nenacházíte v oblastech světa poblíž nich, spojení může být pomalé.
| umístění | dodavatel | parametry |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## Softwarové součásti, na kterých závisí

View File

@@ -25,9 +25,9 @@ Nedenfor er de servere, du bruger gratis, det kan ændre sig med tiden. Hvis du
| Beliggenhed | Udbyder | Specifikation |
| ---------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
## Afhængigheder

View File

@@ -34,7 +34,6 @@ RustDesk heißt jegliche Mitarbeit willkommen. Schauen Sie sich [CONTRIBUTING-DE
Nachfolgend sind die Server gelistet, die Sie kostenlos nutzen können. Es kann sein, dass sich diese Liste immer mal wieder ändert. Falls Sie nicht in der Nähe einer dieser Server sind, kann es sein, dass Ihre Verbindung langsam sein wird.
| Standort | Anbieter | Spezifikation |
| --------- | ------------- | ------------------ |
| Südkorea (Seoul) | [AWS lightsail](https://aws.amazon.com/de/) | 1 vCPU / 0,5 GB RAM |
| Deutschland | [Hetzner](https://www.hetzner.com/de/) | 2 vCPU / 4 GB RAM |
| Deutschland | [Codext](https://codext.de/) | 4 vCPU / 8 GB RAM |
| Ukraine (Kiew) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |

View File

@@ -24,7 +24,6 @@ RustDesk bonvenigas kontribuon de ĉiuj. Vidu [`docs/CONTRIBUTING.md`](CONTRIBUT
Malsupre estas la serviloj, kiuj vi uzas senpage, ĝi povas ŝanĝi laŭlonge de la tempo. Se vi ne estas proksima de unu de tiuj, via reto povas esti malrapida.
| Situo | Vendanto | Detaloj |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |

View File

@@ -31,7 +31,6 @@ A continuación se muestran los servidores gratuitos, pueden cambiar a medida qu
| Ubicación | Compañía | Especificación |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |

View File

@@ -30,7 +30,6 @@
شما مي‌توانید از سرورهای زیر به رایگان استفاده کنید. این لیست ممکن است به مرور زمان تغییر می‌کند. اگر به این سرورها نزدیک نیستید، ممکن است اتصال شما کند باشد.
| موقعیت | سرویس دهنده | مشخصات |
| --------- | ------------- | ------------------ |
| کره‌ی جنوبی، سئول | AWS lightsail | 1 vCPU / 0.5GB RAM |
| آلمان | Hetzner | 2 vCPU / 4GB RAM |
| آلمان | Codext | 4 vCPU / 8GB RAM |

View File

@@ -24,7 +24,6 @@ RustDesk toivottaa avustukset tervetulleiksi kaikilta. Katso lisätietoja [`docs
Alla on palvelimia, joita voit käyttää ilmaiseksi, ne saattavat muuttua ajan mittaan. Jos et ole lähellä yhtä näistä, verkkosi voi olla hidas.
| Sijainti | Myyjä | Määrittely |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |

View File

@@ -25,7 +25,6 @@ Ci-dessous se trouvent les serveurs que vous utilisez gratuitement, cela peut ch
| Location | Vendor | Specification |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |

View File

@@ -34,7 +34,6 @@
Παρακάτω είναι οι διακομιστές που χρησιμοποιούνται δωρεάν, ενδέχεται να αλλάξουν με την πάροδο του χρόνου. Εάν δεν είστε κοντά σε ένα από αυτούς, το δίκτυό σας ίσως να είναι αργό.
| Περιοχή | Πάροχος | Προδιαγραφές |
| --------- | ------------- | ------------------ |
| Σεούλ | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Γερμανία | Hetzner | 2 vCPU / 4GB RAM |
| Γερμανία | Codext | 4 vCPU / 8GB RAM |
| Ουκρανία (Κίεβο) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |

View File

@@ -32,7 +32,6 @@ A RustDesk szívesen fogad minden contributiont, támogatást mindenkitől. Lás
Ezalatt az üzenet alatt találhatóak azok a publikus szerverek, amelyeket ingyen használhatsz. Ezek a szerverek változhatnak a jövőben, illetve a hálózatuk lehet hogy lassú lehet.
| Hely | Host | Specifikáció |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |

View File

@@ -24,7 +24,6 @@ RustDesk menyambut baik kontribusi dari semua orang. Lihat [`docs/CONTRIBUTING.m
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 |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |

View File

@@ -24,7 +24,6 @@ RustDesk accoglie il contributo di tutti. Per ulteriori informazioni su come ini
Qui sotto trovate i server che possono essere usati gratuitamente, la lista potrebbe cambiare nel tempo. Se non si è vicini a uno di questi server, la vostra connessione potrebbe essere lenta.
| Posizione | Vendor | Specifiche |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |

View File

@@ -29,7 +29,6 @@ RustDeskは誰からの貢献も歓迎します。 貢献するには [`docs/CON
下記のサーバーは、無料で使用できますが、後々変更されることがあります。これらのサーバーから遠い場合、接続が遅い可能性があります。
| Location | Vendor | Specification |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |

View File

@@ -29,7 +29,6 @@ RustDesk는 모든 기여를 환영합니다. 기여하고자 한다면 [`docs/C
표에 있는 서버는 무료로 사용할 수 있지만 추후 변경될 수도 있습니다. 이 서버에서 멀다면, 네트워크가 느려질 가능성도 있습니다.
| Location | Vendor | Specification |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |

View File

@@ -24,7 +24,6 @@
നിങ്ങൾ സൗജന്യമായി ഉപയോഗിക്കുന്ന സെർവറുകൾ ചുവടെയുണ്ട്, അത് സമയത്തിനനുസരിച്ച് മാറിയേക്കാം. നിങ്ങൾ ഇവയിലൊന്നിനോട് അടുത്തല്ലെങ്കിൽ, നിങ്ങളുടെ നെറ്റ്‌വർക്ക് സ്ലോ ആയേക്കാം.
| സ്ഥാനം | കച്ചവടക്കാരൻ | വിവരണം |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |

View File

@@ -32,7 +32,6 @@ RustDesk verwelkomt bijdragen van iedereen. Zie [`docs/CONTRIBUTING.md`](CONTRIB
Hieronder staan de servers die u gratis gebruikt, ze kunnen in de loop van de tijd veranderen. Als u niet in de buurt van een van deze servers bevindt, kan uw vervinding langzamer zijn.
| Locatie | Aanbieder | Specificaties |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Duitsland | Hetzner | 2 vCPU / 4GB RAM |
| Duitsland | Codext | 4 vCPU / 8GB RAM |
| Oekraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |

View File

@@ -34,7 +34,6 @@ RustDesk zaprasza do współpracy każdego. Zobacz [`docs/CONTRIBUTING-PL.md`](C
Poniżej znajdują się serwery, z których można korzystać za darmo, może się to zmienić z upływem czasu. Jeśli nie znajdujesz się w pobliżu jednego z nich, Twoja prędkość połączenia może być niska.
| Lokalizacja | Dostawca | Specyfikacja |
| --------- | ------------- | ------------------ |
| Korea Płd. (Seul) | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Niemcy | Hetzner | 2 vCPU / 4GB RAM |
| Niemcy | Codext | 4 vCPU / 8GB RAM |
| Ukraina (Kijów) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |

View File

@@ -25,7 +25,6 @@ Abaixo estão os servidores que você está utilizando de graça, ele pode mudar
| Localização | Fornecedor | Especificações |
| ----------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |

View File

@@ -33,11 +33,9 @@ RustDesk приветствует вклад каждого. Ознакомьт
Ниже приведены бесплатные публичные сервера, используемые по умолчанию. Имейте ввиду, они могут меняться со временем. Также стоит отметить, что скорость работы сети зависит от вашего местоположения и расстояния до серверов. Подключение происходит к ближайшему доступному.
| Расположение | Поставщик | Технические характеристики |
| --------- | ------------- | ------------------ |
| Сеул | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Сингапур | Vultr | 1 vCPU / 1GB RAM |
| Даллас | Vultr | 1 vCPU / 1GB RAM |
| Германия | Hetzner | 2 vCPU / 4GB RAM |
| Германия | Codext | 4 vCPU / 8GB RAM |
| Россия (Москва) | [nt-vps](https://nt-vps.ru) | 8 vCPU / 8GB RAM |
## Зависимости

View File

@@ -34,9 +34,6 @@ RustDesk вітає внесок кожного. Дивіться [`docs/CONTRIB
Нижче наведені сервери, для безкоштовного використання, вони можуть змінюватися з часом. Якщо ви не перебуваєте поруч з одним із них, ваша мережа може працювати повільно.
| Місцезнаходження | Постачальник | Технічні характеристики |
| --------- | ------------- | ------------------ |
| Південна Корея (Сеул) | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Сінгапур | Vultr | 1 vCPU / 1GB RAM |
| США (Даллас) | Vultr | 1 vCPU / 1GB RAM
| Німеччина | Hetzner | 2 VCPU / 4GB RAM |
| Німеччина | Codext | 4 vCPU / 8GB RAM |
| Україна (Київ) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |

View File

@@ -33,7 +33,6 @@ Dưới đây là những máy chủ mà bạn có thể sử dụng mà không
| Địa điểm | Nhà cung cấp | Cấu hình |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |

View File

@@ -26,7 +26,6 @@ Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https:
| Location | Vendor | Specification |
| --------- | ------------- | ------------------ |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |

View File

@@ -12,7 +12,7 @@
"name": "rustdesk",
"buildsystem": "simple",
"build-commands": [
"bsdtar -zxvf rustdesk-1.2.0.deb",
"bsdtar -zxvf rustdesk-1.2.1.deb",
"tar -xvf ./data.tar.xz",
"cp -r ./usr/* /app/",
"mkdir -p /app/bin && ln -s /app/lib/rustdesk/rustdesk /app/bin/rustdesk",
@@ -25,7 +25,7 @@
"sources": [
{
"type": "file",
"path": "../rustdesk-1.2.0.deb"
"path": "../rustdesk-1.2.1.deb"
},
{
"type": "file",

View File

@@ -176,6 +176,10 @@ class MyTheme {
static const Color dark = Colors.black87;
static const Color button = Color(0xFF2C8CFF);
static const Color hoverBorder = Color(0xFF999999);
static const Color bordDark = Colors.white24;
static const Color bordLight = Colors.black26;
static const Color dividerDark = Colors.white38;
static const Color dividerLight = Colors.black38;
// ListTile
static const ListTileThemeData listTileTheme = ListTileThemeData(
@@ -545,7 +549,7 @@ closeConnection({String? id}) {
}
}
void window_on_top(int? id) {
void window_on_top(int? id) async {
if (!isDesktop) {
return;
}
@@ -1538,7 +1542,7 @@ Future<bool> initUniLinks() async {
if (initialLink == null) {
return false;
}
return parseRustdeskUri(initialLink);
return handleUriLink(uriString: initialLink);
} catch (err) {
debugPrintStack(label: "$err");
return false;
@@ -1559,7 +1563,7 @@ StreamSubscription? listenUniLinks({handleByFlutter = true}) {
debugPrint("A uri was received: $uri.");
if (uri != null) {
if (handleByFlutter) {
callUniLinksUriHandler(uri);
handleUriLink(uri: uri);
} else {
bind.sendUrlScheme(url: uri.toString());
}
@@ -1572,90 +1576,147 @@ StreamSubscription? listenUniLinks({handleByFlutter = true}) {
return sub;
}
/// Handle command line arguments
///
/// * Returns true if we successfully handle the startup arguments.
bool checkArguments() {
if (kBootArgs.isNotEmpty) {
final ret = parseRustdeskUri(kBootArgs.first);
if (ret) {
return true;
enum UriLinkType {
remoteDesktop,
fileTransfer,
portForward,
rdp,
}
// uri link handler
bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
List<String>? args;
if (cmdArgs != null) {
args = cmdArgs;
// rustdesk <uri link>
if (args.isNotEmpty && args[0].startsWith(kUniLinksPrefix)) {
final uri = Uri.tryParse(args[0]);
if (uri != null) {
args = urlLinkToCmdArgs(uri);
}
}
} else if (uri != null) {
args = urlLinkToCmdArgs(uri);
} else if (uriString != null) {
final uri = Uri.tryParse(uriString);
if (uri != null) {
args = urlLinkToCmdArgs(uri);
}
}
// bootArgs:[--connect, 362587269, --switch_uuid, e3d531cc-5dce-41e0-bd06-5d4a2b1eec05]
// check connect args
var connectIndex = kBootArgs.indexOf("--connect");
if (connectIndex == -1) {
return false;
}
String? id =
kBootArgs.length <= connectIndex + 1 ? null : kBootArgs[connectIndex + 1];
String? password =
kBootArgs.length <= connectIndex + 2 ? null : kBootArgs[connectIndex + 2];
if (password != null && password.startsWith("--")) {
password = null;
}
final switchUuidIndex = kBootArgs.indexOf("--switch_uuid");
String? switchUuid = kBootArgs.length <= switchUuidIndex + 1
? null
: kBootArgs[switchUuidIndex + 1];
if (id != null) {
if (id.startsWith(kUniLinksPrefix)) {
return parseRustdeskUri(id);
} else {
// remove "--connect xxx" in the `bootArgs` array
kBootArgs.removeAt(connectIndex);
kBootArgs.removeAt(connectIndex);
// fallback to peer id
Future.delayed(Duration.zero, () {
rustDeskWinManager.newRemoteDesktop(id,
password: password, switch_uuid: switchUuid);
});
return true;
if (args == null) return false;
UriLinkType? type;
String? id;
String? password;
String? switchUuid;
bool? forceRelay;
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case '--connect':
case '--play':
type = UriLinkType.remoteDesktop;
id = args[i + 1];
i++;
break;
case '--file-transfer':
type = UriLinkType.fileTransfer;
id = args[i + 1];
i++;
break;
case '--port-forward':
type = UriLinkType.portForward;
id = args[i + 1];
i++;
break;
case '--rdp':
type = UriLinkType.rdp;
id = args[i + 1];
i++;
break;
case '--password':
password = args[i + 1];
i++;
break;
case '--switch_uuid':
switchUuid = args[i + 1];
i++;
break;
case '--relay':
forceRelay = true;
break;
default:
break;
}
}
if (type != null && id != null) {
switch (type) {
case UriLinkType.remoteDesktop:
Future.delayed(Duration.zero, () {
rustDeskWinManager.newRemoteDesktop(id!,
password: password,
switch_uuid: switchUuid,
forceRelay: forceRelay);
});
break;
case UriLinkType.fileTransfer:
Future.delayed(Duration.zero, () {
rustDeskWinManager.newFileTransfer(id!,
password: password, forceRelay: forceRelay);
});
break;
case UriLinkType.portForward:
Future.delayed(Duration.zero, () {
rustDeskWinManager.newPortForward(id!, false,
password: password, forceRelay: forceRelay);
});
break;
case UriLinkType.rdp:
Future.delayed(Duration.zero, () {
rustDeskWinManager.newPortForward(id!, true,
password: password, forceRelay: forceRelay);
});
break;
}
return true;
}
return false;
}
/// Parse `rustdesk://` unilinks
///
/// Returns true if we successfully handle the uri provided.
/// [Functions]
/// 1. New Connection: rustdesk://connection/new/your_peer_id
bool parseRustdeskUri(String uriPath) {
final uri = Uri.tryParse(uriPath);
if (uri == null) {
debugPrint("uri is not valid: $uriPath");
return false;
}
return callUniLinksUriHandler(uri);
}
/// uri handler
///
/// Returns true if we successfully handle the uri provided.
bool callUniLinksUriHandler(Uri uri) {
debugPrint("uni links called: $uri");
// new connection
String peerId;
List<String>? urlLinkToCmdArgs(Uri uri) {
String? command;
String? id;
if (uri.authority == "connection" && uri.path.startsWith("/new/")) {
peerId = uri.path.substring("/new/".length);
} else if (uri.authority == "connect") {
peerId = uri.path.substring(1);
// For compatibility
command = '--connect';
id = uri.path.substring("/new/".length);
} else if (['connect', "play", 'file-transfer', 'port-forward', 'rdp']
.contains(uri.authority)) {
command = '--${uri.authority}';
if (uri.path.length > 1) {
id = uri.path.substring(1);
}
} else if (uri.authority.length > 2 && uri.path.length <= 1) {
// "/" or ""
peerId = uri.authority;
} else {
return false;
// rustdesk://<connect-id>
command = '--connect';
id = uri.authority;
}
var param = uri.queryParameters;
String? switch_uuid = param["switch_uuid"];
String? password = param["password"];
Future.delayed(Duration.zero, () {
rustDeskWinManager.newRemoteDesktop(peerId,
password: password, switch_uuid: switch_uuid);
});
return true;
List<String> args = List.empty(growable: true);
if (command != null && id != null) {
args.add(command);
args.add(id);
var param = uri.queryParameters;
String? password = param["password"];
if (password != null) args.addAll(['--password', password]);
String? switch_uuid = param["switch_uuid"];
if (switch_uuid != null) args.addAll(['--switch_uuid', switch_uuid]);
if (param["relay"] != null) args.add("--relay");
return args;
}
return null;
}
connectMainDesktop(String id,
@@ -2101,3 +2162,64 @@ Future<void> start_service(bool is_start) async {
bind.mainSetOption(key: "stop-service", value: is_start ? "" : "Y");
}
}
typedef Future<bool> WhetherUseRemoteBlock();
Widget buildRemoteBlock({required Widget child, WhetherUseRemoteBlock? use}) {
var block = false.obs;
return Obx(() => MouseRegion(
onEnter: (_) async {
if (use != null && !await use()) {
block.value = false;
return;
}
var time0 = DateTime.now().millisecondsSinceEpoch;
await bind.mainCheckMouseTime();
Timer(const Duration(milliseconds: 120), () async {
var d = time0 - await bind.mainGetMouseTime();
if (d < 120) {
block.value = true;
}
});
},
onExit: (event) => block.value = false,
child: Stack(children: [
child,
Offstage(
offstage: !block.value,
child: Container(
color: Colors.black.withOpacity(0.5),
)),
]),
));
}
Widget unreadMessageCountBuilder(RxInt? count,
{double? size, double? fontSize}) {
return Obx(() => Offstage(
offstage: !((count?.value ?? 0) > 0),
child: Container(
width: size ?? 16,
height: size ?? 16,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Center(
child: Text("${count?.value ?? 0}",
maxLines: 1,
style: TextStyle(color: Colors.white, fontSize: fontSize ?? 10)),
),
)));
}
Widget unreadTopRightBuilder(RxInt? count, {Widget? icon}) {
return Stack(
children: [
icon ?? Icon(Icons.chat),
Positioned(
top: 0,
right: 0,
child: unreadMessageCountBuilder(count, size: 12, fontSize: 8))
],
);
}

View File

@@ -285,6 +285,29 @@ class PeerStringOption {
Get.find<RxString>(tag: tag(id, opt));
}
class UnreadChatCountState {
static String tag(id) => 'unread_chat_count_$id';
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
final RxInt state = RxInt(0);
Get.put(state, tag: key);
} else {
Get.find<RxInt>(tag: key).value = 0;
}
}
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
}
}
static RxInt find(String id) => Get.find<RxInt>(tag: tag(id));
}
initSharedStates(String id) {
PrivacyModeState.init(id);
BlockInputState.init(id);
@@ -294,6 +317,7 @@ initSharedStates(String id) {
RemoteCursorMovedState.init(id);
FingerprintState.init(id);
PeerBoolOption.init(id, 'zoom-cursor', () => false);
UnreadChatCountState.init(id);
}
removeSharedStates(String id) {
@@ -305,4 +329,5 @@ removeSharedStates(String id) {
RemoteCursorMovedState.delete(id);
FingerprintState.delete(id);
PeerBoolOption.delete(id, 'zoom-cursor');
UnreadChatCountState.delete(id);
}

View File

@@ -7,10 +7,16 @@ import 'package:provider/provider.dart';
import '../../mobile/pages/home_page.dart';
enum ChatPageType {
mobileMain,
desktopCM,
}
class ChatPage extends StatelessWidget implements PageShape {
late final ChatModel chatModel;
final ChatPageType? type;
ChatPage({ChatModel? chatModel}) {
ChatPage({ChatModel? chatModel, this.type}) {
this.chatModel = chatModel ?? gFFI.chatModel;
}
@@ -18,27 +24,53 @@ class ChatPage extends StatelessWidget implements PageShape {
final title = translate("Chat");
@override
final icon = Icon(Icons.chat);
final icon = unreadTopRightBuilder(gFFI.chatModel.mobileUnreadSum);
@override
final appBarActions = [
PopupMenuButton<int>(
PopupMenuButton<MessageKey>(
tooltip: "",
icon: Icon(Icons.group),
icon: unreadTopRightBuilder(gFFI.chatModel.mobileUnreadSum,
icon: Icon(Icons.group)),
itemBuilder: (context) {
// only mobile need [appBarActions], just bind gFFI.chatModel
final chatModel = gFFI.chatModel;
return chatModel.messages.entries.map((entry) {
final id = entry.key;
final key = entry.key;
final user = entry.value.chatUser;
return PopupMenuItem<int>(
child: Text("${user.firstName} ${user.id}"),
value: id,
final client = gFFI.serverModel.clients
.firstWhereOrNull((e) => e.id == key.connId);
final connected =
gFFI.serverModel.clients.any((e) => e.id == key.connId);
return PopupMenuItem<MessageKey>(
child: Row(
children: [
Icon(
key.isOut
? Icons.call_made_rounded
: Icons.call_received_rounded,
color: MyTheme.accent)
.marginOnly(right: 6),
Text("${user.firstName} ${user.id}"),
if (connected)
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color.fromARGB(255, 46, 205, 139)),
).marginSymmetric(horizontal: 2),
if (client != null)
unreadMessageCountBuilder(client.unreadChatMessageCount)
.marginOnly(left: 4)
],
),
value: key,
);
}).toList();
},
onSelected: (id) {
gFFI.chatModel.changeCurrentID(id);
onSelected: (key) {
gFFI.chatModel.changeCurrentKey(key);
})
];
@@ -50,16 +82,27 @@ class ChatPage extends StatelessWidget implements PageShape {
color: Theme.of(context).scaffoldBackgroundColor,
child: Consumer<ChatModel>(
builder: (context, chatModel, child) {
final currentUser = chatModel.currentUser;
final readOnly = type == ChatPageType.mobileMain &&
(chatModel.currentKey.connId == ChatModel.clientModeID ||
gFFI.serverModel.clients.every((e) =>
e.id != chatModel.currentKey.connId ||
chatModel.currentUser == null)) ||
type == ChatPageType.desktopCM &&
gFFI.serverModel.clients
.firstWhereOrNull(
(e) => e.id == chatModel.currentKey.connId)
?.disconnected ==
true;
return Stack(
children: [
LayoutBuilder(builder: (context, constraints) {
final chat = DashChat(
onSend: chatModel.send,
currentUser: chatModel.me,
messages:
chatModel.messages[chatModel.currentID]?.chatMessages ??
[],
messages: chatModel
.messages[chatModel.currentKey]?.chatMessages ??
[],
readOnly: readOnly,
inputOptions: InputOptions(
focusNode: chatModel.inputNode,
textController: chatModel.textController,
@@ -127,22 +170,6 @@ class ChatPage extends StatelessWidget implements PageShape {
);
return SelectionArea(child: chat);
}),
desktopType == DesktopType.cm ||
chatModel.currentID == ChatModel.clientModeID
? SizedBox.shrink()
: Padding(
padding: EdgeInsets.all(12),
child: Row(
children: [
Icon(Icons.account_circle, color: MyTheme.accent80),
SizedBox(width: 5),
Text(
"${currentUser.firstName} ${currentUser.id}",
style: TextStyle(color: MyTheme.accent),
),
],
),
),
],
).paddingOnly(bottom: 8);
},

View File

@@ -943,16 +943,20 @@ showSetOSPassword(
SessionID sessionId,
bool login,
OverlayDialogManager dialogManager,
String? osPassword,
Function()? closeCallback,
) async {
final controller = TextEditingController();
var password =
await bind.sessionGetOption(sessionId: sessionId, arg: 'os-password') ??
'';
osPassword ??= await bind.sessionGetOption(sessionId: sessionId, arg: 'os-password') ?? '';
var autoLogin =
await bind.sessionGetOption(sessionId: sessionId, arg: 'auto-login') !=
'';
controller.text = password;
controller.text = osPassword;
dialogManager.show((setState, close, context) {
closeWithCallback([dynamic]) {
close();
if (closeCallback != null) closeCallback();
}
submit() {
var text = controller.text.trim();
bind.sessionPeerOption(
@@ -964,7 +968,7 @@ showSetOSPassword(
if (text != '' && login) {
bind.sessionInputOsPassword(sessionId: sessionId, value: text);
}
close();
closeWithCallback();
}
return CustomAlertDialog(
@@ -998,7 +1002,7 @@ showSetOSPassword(
dialogButton(
"Cancel",
icon: Icon(Icons.close_rounded),
onPressed: close,
onPressed: closeWithCallback,
isOutline: true,
),
dialogButton(
@@ -1008,7 +1012,7 @@ showSetOSPassword(
),
],
onSubmit: submit,
onCancel: close,
onCancel: closeWithCallback,
);
});
}

View File

@@ -434,11 +434,16 @@ Future<bool?> loginDialog() async {
}
break;
case HttpType.kAuthResTypeEmailCheck:
setState(() => isInProgress = false);
final res = await verificationCodeDialog(resp.user);
if (res == true) {
if (isMobile) {
close(true);
return;
verificationCodeDialog(resp.user);
} else {
setState(() => isInProgress = false);
final res = await verificationCodeDialog(resp.user);
if (res == true) {
close(true);
return;
}
}
break;
default:
@@ -512,7 +517,11 @@ Future<bool?> loginDialog() async {
size: 25,
// No need to handle the branch of null.
// Because we can ensure the color is not null when debug.
color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.55),
color: Theme.of(context)
.textTheme
.titleLarge
?.color
?.withOpacity(0.55),
),
onTap: onDialogCancel,
hoverColor: Colors.red,

View File

@@ -32,7 +32,7 @@ class DraggableChatWindow extends StatelessWidget {
width: width,
height: height,
builder: (context, onPanUpdate) {
return isIOS
final child = isIOS
? ChatPage(chatModel: chatModel)
: Scaffold(
resizeToAvoidBottomInset: false,
@@ -44,6 +44,10 @@ class DraggableChatWindow extends StatelessWidget {
),
body: ChatPage(chatModel: chatModel),
);
return Container(
decoration:
BoxDecoration(border: Border.all(color: MyTheme.border)),
child: child);
});
}

View File

@@ -11,6 +11,8 @@ import 'package:flutter_hbb/models/model.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart';
bool isEditOsPassword = false;
class TTextMenu {
final Widget child;
final VoidCallback onPressed;
@@ -44,6 +46,28 @@ class TToggleMenu {
{required this.child, required this.value, required this.onChanged});
}
handleOsPasswordEditIcon(
SessionID sessionId, OverlayDialogManager dialogManager) {
isEditOsPassword = true;
showSetOSPassword(sessionId, false, dialogManager, null, () => isEditOsPassword = false);
}
handleOsPasswordAction(
SessionID sessionId, OverlayDialogManager dialogManager) async {
if (isEditOsPassword) {
isEditOsPassword = false;
return;
}
final password =
await bind.sessionGetOption(sessionId: sessionId, arg: 'os-password') ??
'';
if (password.isEmpty) {
showSetOSPassword(sessionId, true, dialogManager, password, () => isEditOsPassword = false);
} else {
bind.sessionInputOsPassword(sessionId: sessionId, value: password);
}
}
List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
final ffiModel = ffi.ffiModel;
final pi = ffiModel.pi;
@@ -63,17 +87,26 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
// osAccount / osPassword
v.add(
TTextMenu(
child: Row(children: [
Text(translate(pi.is_headless ? 'OS Account' : 'OS Password')),
Offstage(
offstage: isDesktop,
child:
Icon(Icons.edit, color: MyTheme.accent).marginOnly(left: 12))
]),
trailingIcon: Transform.scale(scale: 0.8, child: Icon(Icons.edit)),
onPressed: () => pi.is_headless
? showSetOSAccount(sessionId, ffi.dialogManager)
: showSetOSPassword(sessionId, false, ffi.dialogManager)),
child: Row(children: [
Text(translate(pi.is_headless ? 'OS Account' : 'OS Password')),
Offstage(
offstage: isDesktop,
child: Icon(Icons.edit, color: MyTheme.accent).marginOnly(left: 12),
)
]),
trailingIcon: Transform.scale(
scale: 0.8,
child: InkWell(
onTap: () => pi.is_headless
? showSetOSAccount(sessionId, ffi.dialogManager)
: handleOsPasswordEditIcon(sessionId, ffi.dialogManager),
child: Icon(Icons.edit),
),
),
onPressed: () => pi.is_headless
? showSetOSAccount(sessionId, ffi.dialogManager)
: handleOsPasswordAction(sessionId, ffi.dialogManager),
),
);
// paste
if (isMobile && perms['keyboard'] != false && perms['clipboard'] != false) {

View File

@@ -9,7 +9,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/models/user_model.dart';
import 'package:get/get.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:window_manager/window_manager.dart';

View File

@@ -1559,7 +1559,7 @@ class _AboutState extends State<_About> {
.marginSymmetric(vertical: 4.0)),
InkWell(
onTap: () {
launchUrlString('https://rustdesk.com/privacy');
launchUrlString('https://rustdesk.com/privacy.html');
},
child: Text(
translate('Privacy Statement'),

View File

@@ -52,10 +52,12 @@ class FileManagerPage extends StatefulWidget {
const FileManagerPage(
{Key? key,
required this.id,
required this.password,
required this.tabController,
this.forceRelay})
: super(key: key);
final String id;
final String? password;
final bool? forceRelay;
final DesktopTabController tabController;
@@ -79,7 +81,10 @@ class _FileManagerPageState extends State<FileManagerPage>
void initState() {
super.initState();
_ffi = FFI();
_ffi.start(widget.id, isFileTransfer: true, forceRelay: widget.forceRelay);
_ffi.start(widget.id,
isFileTransfer: true,
password: widget.password,
forceRelay: widget.forceRelay);
WidgetsBinding.instance.addPostFrameCallback((_) {
_ffi.dialogManager
.showLoading(translate('Connecting...'), onCancel: closeConnection);

View File

@@ -44,6 +44,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
page: FileManagerPage(
key: ValueKey(params['id']),
id: params['id'],
password: params['password'],
tabController: tabController,
forceRelay: params['forceRelay'],
)));
@@ -72,6 +73,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
page: FileManagerPage(
key: ValueKey(id),
id: id,
password: args['password'],
tabController: tabController,
forceRelay: args['forceRelay'],
)));

View File

@@ -183,9 +183,9 @@ class _InstallPageBodyState extends State<_InstallPageBody>
InkWell(
hoverColor: Colors.transparent,
onTap: () =>
launchUrlString('https://rustdesk.com/privacy'),
launchUrlString('https://rustdesk.com/privacy.html'),
child: Tooltip(
message: 'https://rustdesk.com/privacy',
message: 'https://rustdesk.com/privacy.html',
child: Row(children: [
Icon(Icons.launch_outlined, size: 16)
.marginOnly(right: 5),

View File

@@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -8,7 +7,6 @@ import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/models/model.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart';
import 'package:wakelock/wakelock.dart';
const double _kColumn1Width = 30;
const double _kColumn4Width = 100;
@@ -30,11 +28,13 @@ class PortForwardPage extends StatefulWidget {
const PortForwardPage(
{Key? key,
required this.id,
required this.password,
required this.tabController,
required this.isRDP,
this.forceRelay})
: super(key: key);
final String id;
final String? password;
final DesktopTabController tabController;
final bool isRDP;
final bool? forceRelay;
@@ -57,12 +57,10 @@ class _PortForwardPageState extends State<PortForwardPage>
_ffi = FFI();
_ffi.start(widget.id,
isPortForward: true,
password: widget.password,
forceRelay: widget.forceRelay,
isRdp: widget.isRDP);
Get.put(_ffi, tag: 'pf_${widget.id}');
if (!Platform.isLinux) {
Wakelock.enable();
}
debugPrint("Port forward page init success with id ${widget.id}");
widget.tabController.onSelected?.call(widget.id);
}
@@ -71,9 +69,6 @@ class _PortForwardPageState extends State<PortForwardPage>
void dispose() {
_ffi.close();
_ffi.dialogManager.dismissAll();
if (!Platform.isLinux) {
Wakelock.disable();
}
Get.delete<FFI>(tag: 'pf_${widget.id}');
super.dispose();
}

View File

@@ -43,6 +43,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
page: PortForwardPage(
key: ValueKey(params['id']),
id: params['id'],
password: params['password'],
tabController: tabController,
isRDP: isRDP,
forceRelay: params['forceRelay'],
@@ -77,6 +78,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
page: PortForwardPage(
key: ValueKey(args['id']),
id: id,
password: args['password'],
isRDP: isRDP,
tabController: tabController,
forceRelay: args['forceRelay'],

View File

@@ -56,15 +56,14 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
if (peerId != null) {
ConnectionTypeState.init(peerId);
tabController.onSelected = (id) {
final remotePage = tabController.state.value.tabs
.firstWhereOrNull((tab) => tab.key == id)
?.page;
final remotePage = tabController.widget(id);
if (remotePage is RemotePage) {
final ffi = remotePage.ffi;
bind.setCurSessionId(sessionId: ffi.sessionId);
}
WindowController.fromWindowId(windowId())
.setTitle(getWindowNameWithId(id));
UnreadChatCountState.find(id).value = 0;
};
tabController.add(TabInfo(
key: peerId,
@@ -206,6 +205,8 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
).paddingOnly(right: 5),
),
label,
unreadMessageCountBuilder(UnreadChatCountState.find(key))
.marginOnly(left: 4),
],
);

View File

@@ -100,10 +100,16 @@ class ConnectionManagerState extends State<ConnectionManager> {
gFFI.serverModel.tabController.onSelected = (client_id_str) {
final client_id = int.tryParse(client_id_str);
if (client_id != null) {
gFFI.chatModel.changeCurrentID(client_id);
final client =
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == client_id);
if (client != null) {
gFFI.chatModel.changeCurrentKey(MessageKey(client.peerId, client.id));
if (client.unreadChatMessageCount.value > 0) {
Future.delayed(Duration.zero, () {
client.unreadChatMessageCount.value = 0;
gFFI.chatModel.showChatPage(MessageKey(client.peerId, client.id));
});
}
windowManager.setTitle(getWindowNameWithId(client.peerId));
}
}
@@ -144,10 +150,11 @@ class ConnectionManagerState extends State<ConnectionManager> {
showClose: true,
onWindowCloseButton: handleWindowCloseButton,
controller: serverModel.tabController,
selectedBorderColor: MyTheme.accent,
maxLabelWidth: 100,
tail: buildScrollJumper(),
selectedTabBackgroundColor:
Theme.of(context).hintColor.withOpacity(0.2),
Theme.of(context).hintColor.withOpacity(0),
tabBuilder: (key, icon, label, themeConf) {
final client = serverModel.clients
.firstWhereOrNull((client) => client.id.toString() == key);
@@ -158,10 +165,8 @@ class ConnectionManagerState extends State<ConnectionManager> {
message: key,
waitDuration: Duration(seconds: 1),
child: label),
Obx(() => Offstage(
offstage:
!(client?.hasUnreadChatMessage.value ?? false),
child: Icon(Icons.circle, color: Colors.red, size: 10)))
unreadMessageCountBuilder(client?.unreadChatMessageCount)
.marginOnly(left: 4),
],
);
},
@@ -170,7 +175,16 @@ class ConnectionManagerState extends State<ConnectionManager> {
Consumer<ChatModel>(
builder: (_, model, child) => model.isShowCMChatPage
? Expanded(
child: ChatPage(),
child: buildRemoteBlock(
child: Container(
decoration: BoxDecoration(
border: Border(
right: BorderSide(
color: Theme.of(context)
.dividerColor))),
child:
ChatPage(type: ChatPageType.desktopCM)),
),
flex: (kConnectionManagerWindowSizeOpenChat.width -
kConnectionManagerWindowSizeClosedChat
.width)
@@ -437,7 +451,8 @@ class _CmHeaderState extends State<_CmHeader>
child: IconButton(
onPressed: () => checkClickTime(
client.id,
() => gFFI.chatModel.toggleCMChatPage(client.id),
() => gFFI.chatModel
.toggleCMChatPage(MessageKey(client.peerId, client.id)),
),
icon: SvgPicture.asset('assets/chat2.svg'),
splashRadius: kDesktopIconButtonSplashRadius,

View File

@@ -1,5 +1,7 @@
import 'dart:convert';
import 'dart:ui' as ui;
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -111,6 +113,36 @@ class _ToolbarTheme {
static const double buttonVMargin = 6;
static const double iconRadius = 8;
static const double elevation = 3;
static const Color bordDark = MyTheme.bordDark;
static const Color bordLight = MyTheme.bordLight;
static const Color dividerDark = MyTheme.dividerDark;
static const Color dividerLight = MyTheme.dividerLight;
static double dividerSpaceToAction = Platform.isWindows ? 8 : 14;
static double menuBorderRadius = Platform.isWindows ? 5.0 : 7.0;
static EdgeInsets menuPadding = Platform.isWindows
? EdgeInsets.fromLTRB(4, 12, 4, 12)
: EdgeInsets.fromLTRB(6, 14, 6, 14);
static const double menuButtonBorderRadius = 3.0;
static final defaultMenuStyle = MenuStyle(
side: MaterialStateProperty.all(BorderSide(
width: 1,
color: MyTheme.currentThemeMode() == ThemeMode.light
? _ToolbarTheme.bordLight
: _ToolbarTheme.bordDark,
)),
shape: MaterialStatePropertyAll(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(_ToolbarTheme.menuBorderRadius))),
padding: MaterialStateProperty.all(_ToolbarTheme.menuPadding),
);
static final defaultMenuButtonStyle = ButtonStyle(
backgroundColor: MaterialStatePropertyAll(Colors.transparent),
padding: MaterialStatePropertyAll(EdgeInsets.zero),
overlayColor: MaterialStatePropertyAll(Colors.transparent),
);
}
typedef DismissFunc = void Function();
@@ -475,9 +507,17 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
textStyle: MaterialStatePropertyAll(
TextStyle(fontWeight: FontWeight.normal),
),
shape: MaterialStatePropertyAll(RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(_ToolbarTheme.menuButtonBorderRadius))),
),
),
dividerTheme: DividerThemeData(space: 4),
dividerTheme: DividerThemeData(
space: _ToolbarTheme.dividerSpaceToAction,
color: MyTheme.currentThemeMode() == ThemeMode.light
? _ToolbarTheme.dividerLight
: _ToolbarTheme.dividerDark,
),
menuBarTheme: MenuBarThemeData(
style: MenuStyle(
padding: MaterialStatePropertyAll(EdgeInsets.zero),
@@ -1413,7 +1453,8 @@ class _ChatMenuState extends State<_ChatMenu> {
initPos = Offset(pos.dx, pos.dy + _ToolbarTheme.dividerHeight);
}
widget.ffi.chatModel.changeCurrentID(ChatModel.clientModeID);
widget.ffi.chatModel.changeCurrentKey(
MessageKey(widget.ffi.id, ChatModel.clientModeID));
widget.ffi.chatModel.toggleChatOverlay(chatInitPos: initPos);
});
}
@@ -1635,11 +1676,8 @@ class _IconSubmenuButtonState extends State<_IconSubmenuButton> {
width: _ToolbarTheme.buttonSize,
height: _ToolbarTheme.buttonSize,
child: SubmenuButton(
menuStyle: widget.menuStyle,
style: ButtonStyle(
backgroundColor: MaterialStatePropertyAll(Colors.transparent),
padding: MaterialStatePropertyAll(EdgeInsets.zero),
overlayColor: MaterialStatePropertyAll(Colors.transparent)),
menuStyle: widget.menuStyle ?? _ToolbarTheme.defaultMenuStyle,
style: _ToolbarTheme.defaultMenuButtonStyle,
onHover: (value) => setState(() {
hover = value;
}),
@@ -1681,6 +1719,7 @@ class _SubmenuButton extends StatelessWidget {
child: child,
menuChildren:
menuChildren.map((e) => _buildPointerTrackWidget(e, ffi)).toList(),
menuStyle: _ToolbarTheme.defaultMenuStyle,
);
}
}

View File

@@ -187,6 +187,10 @@ class DesktopTabController {
state.value.tabs.clear();
state.refresh();
}
Widget? widget(String key) {
return state.value.tabs.firstWhereOrNull((tab) => tab.key == key)?.page;
}
}
class TabThemeConf {
@@ -221,6 +225,7 @@ class DesktopTab extends StatelessWidget {
final double? maxLabelWidth;
final Color? selectedTabBackgroundColor;
final Color? unSelectedTabBackgroundColor;
final Color? selectedBorderColor;
final DesktopTabController controller;
@@ -248,6 +253,7 @@ class DesktopTab extends StatelessWidget {
this.maxLabelWidth,
this.selectedTabBackgroundColor,
this.unSelectedTabBackgroundColor,
this.selectedBorderColor,
}) : super(key: key) {
tabType = controller.tabType;
isMainWindow = tabType == DesktopTabType.main ||
@@ -295,37 +301,16 @@ class DesktopTab extends StatelessWidget {
if (tabType != DesktopTabType.main) {
return child;
}
var block = false.obs;
return Obx(() => MouseRegion(
onEnter: (_) async {
var access_mode = await bind.mainGetOption(key: 'access-mode');
var option = option2bool(
'allow-remote-config-modification',
await bind.mainGetOption(
key: 'allow-remote-config-modification'));
if (access_mode == 'view' || (access_mode.isEmpty && !option)) {
var time0 = DateTime.now().millisecondsSinceEpoch;
await bind.mainCheckMouseTime();
Timer(const Duration(milliseconds: 120), () async {
var d = time0 - await bind.mainGetMouseTime();
if (d < 120) {
block.value = true;
}
});
}
},
onExit: (_) => block.value = false,
child: Stack(
children: [
child,
Offstage(
offstage: !block.value,
child: Container(
color: Colors.black.withOpacity(0.5),
)),
],
),
));
return buildRemoteBlock(
child: child,
use: () async {
var access_mode = await bind.mainGetOption(key: 'access-mode');
var option = option2bool(
'allow-remote-config-modification',
await bind.mainGetOption(
key: 'allow-remote-config-modification'));
return access_mode == 'view' || (access_mode.isEmpty && !option);
});
}
List<Widget> _tabWidgets = [];
@@ -430,15 +415,17 @@ class DesktopTab extends StatelessWidget {
}
},
child: _ListView(
controller: controller,
tabBuilder: tabBuilder,
tabMenuBuilder: tabMenuBuilder,
labelGetter: labelGetter,
maxLabelWidth: maxLabelWidth,
selectedTabBackgroundColor:
selectedTabBackgroundColor,
unSelectedTabBackgroundColor:
unSelectedTabBackgroundColor))),
controller: controller,
tabBuilder: tabBuilder,
tabMenuBuilder: tabMenuBuilder,
labelGetter: labelGetter,
maxLabelWidth: maxLabelWidth,
selectedTabBackgroundColor:
selectedTabBackgroundColor,
unSelectedTabBackgroundColor:
unSelectedTabBackgroundColor,
selectedBorderColor: selectedBorderColor,
))),
],
))),
// hide simulated action buttons when we in compatible ui mode, because of reusing system title bar.
@@ -741,6 +728,7 @@ class _ListView extends StatelessWidget {
final LabelGetter? labelGetter;
final double? maxLabelWidth;
final Color? selectedTabBackgroundColor;
final Color? selectedBorderColor;
final Color? unSelectedTabBackgroundColor;
Rx<DesktopTabState> get state => controller.state;
@@ -753,6 +741,7 @@ class _ListView extends StatelessWidget {
this.maxLabelWidth,
this.selectedTabBackgroundColor,
this.unSelectedTabBackgroundColor,
this.selectedBorderColor,
});
/// Check whether to show ListView
@@ -805,6 +794,7 @@ class _ListView extends StatelessWidget {
selectedTabBackgroundColor: selectedTabBackgroundColor ??
MyTheme.tabbar(context).selectedTabBackgroundColor,
unSelectedTabBackgroundColor: unSelectedTabBackgroundColor,
selectedBorderColor: selectedBorderColor,
);
}).toList()));
}
@@ -825,6 +815,7 @@ class _Tab extends StatefulWidget {
final double? maxLabelWidth;
final Color? selectedTabBackgroundColor;
final Color? unSelectedTabBackgroundColor;
final Color? selectedBorderColor;
const _Tab({
Key? key,
@@ -842,6 +833,7 @@ class _Tab extends StatefulWidget {
this.maxLabelWidth,
this.selectedTabBackgroundColor,
this.unSelectedTabBackgroundColor,
this.selectedBorderColor,
}) : super(key: key);
@override
@@ -932,35 +924,46 @@ class _TabState extends State<_Tab> with RestorationMixin {
},
onTap: () => widget.onTap(),
child: Container(
color: isSelected
? widget.selectedTabBackgroundColor
: widget.unSelectedTabBackgroundColor,
child: Row(
children: [
SizedBox(
height: _kTabBarHeight,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildTabContent(),
Obx((() => _CloseButton(
visible: hover.value && widget.closable,
tabSelected: isSelected,
onClose: () => widget.onClose(),
)))
])).paddingOnly(left: 10, right: 5),
Offstage(
offstage: !showDivider,
child: VerticalDivider(
width: 1,
indent: _kDividerIndent,
endIndent: _kDividerIndent,
color: MyTheme.tabbar(context).dividerColor,
),
)
],
),
),
decoration: isSelected && widget.selectedBorderColor != null
? BoxDecoration(
border: Border(
bottom: BorderSide(
color: widget.selectedBorderColor!,
width: 1,
),
),
)
: null,
child: Container(
color: isSelected
? widget.selectedTabBackgroundColor
: widget.unSelectedTabBackgroundColor,
child: Row(
children: [
SizedBox(
height: _kTabBarHeight,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildTabContent(),
Obx((() => _CloseButton(
visible: hover.value && widget.closable,
tabSelected: isSelected,
onClose: () => widget.onClose(),
)))
])).paddingOnly(left: 10, right: 5),
Offstage(
offstage: !showDivider,
child: VerticalDivider(
width: 1,
indent: _kDividerIndent,
endIndent: _kDividerIndent,
color: MyTheme.tabbar(context).dividerColor,
),
)
],
),
)),
),
);
}
@@ -1116,14 +1119,14 @@ class TabbarTheme extends ThemeExtension<TabbarTheme> {
selectedIconColor: Color.fromARGB(255, 26, 26, 26),
unSelectedIconColor: Color.fromARGB(255, 96, 96, 96),
dividerColor: Color.fromARGB(255, 238, 238, 238),
hoverColor: Color.fromARGB(51, 158, 158, 158),
closeHoverColor: Color.fromARGB(255, 224, 224, 224),
selectedTabBackgroundColor: Color.fromARGB(255, 240, 240, 240));
hoverColor: Colors.white54,
closeHoverColor: Colors.white,
selectedTabBackgroundColor: Colors.white54);
static const dark = TabbarTheme(
selectedTabIconColor: MyTheme.accent,
unSelectedTabIconColor: Color.fromARGB(255, 30, 65, 98),
selectedTextColor: Color.fromARGB(255, 255, 255, 255),
selectedTextColor: Colors.white,
unSelectedTextColor: Color.fromARGB(255, 192, 192, 192),
selectedIconColor: Color.fromARGB(255, 192, 192, 192),
unSelectedIconColor: Color.fromARGB(255, 255, 255, 255),

View File

@@ -134,7 +134,7 @@ void runMainApp(bool startService) async {
// Check the startup argument, if we successfully handle the argument, we keep the main window hidden.
final handledByUniLinks = await initUniLinks();
debugPrint("handled by uni links: $handledByUniLinks");
if (handledByUniLinks || checkArguments()) {
if (handledByUniLinks || handleUriLink(cmdArgs: kBootArgs)) {
windowManager.hide();
} else {
windowManager.show();
@@ -225,6 +225,7 @@ void runConnectionManagerScreen(bool hide) async {
} else {
await showCmWindow(isStartup: true);
}
windowManager.setResizable(false);
// Start the uni links handler and redirect links to Native, not for Flutter.
listenUniLinks(handleByFlutter: false);
}

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:auto_size_text_field/auto_size_text_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import 'package:get/get.dart';
@@ -37,6 +38,7 @@ class ConnectionPage extends StatefulWidget implements PageShape {
class _ConnectionPageState extends State<ConnectionPage> {
/// Controller for the id input bar.
final _idController = IDTextEditingController();
final RxBool _idEmpty = true.obs;
/// Update url. If it's not null, means an update is available.
var _updateUrl = '';
@@ -60,6 +62,10 @@ class _ConnectionPageState extends State<ConnectionPage> {
if (_updateUrl.isNotEmpty) setState(() {});
});
}
_idController.addListener(() {
_idEmpty.value = _idController.text.isEmpty;
});
}
@override
@@ -126,7 +132,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
Expanded(
child: Container(
padding: const EdgeInsets.only(left: 16, right: 16),
child: TextField(
child: AutoSizeTextField(
minFontSize: 18,
autocorrect: false,
enableSuggestions: false,
keyboardType: TextInputType.visiblePassword,
@@ -158,6 +165,14 @@ class _ConnectionPageState extends State<ConnectionPage> {
),
),
),
Obx(() => Offstage(
offstage: _idEmpty.value,
child: IconButton(
onPressed: () {
_idController.clear();
},
icon: Icon(Icons.clear, color: MyTheme.darkGray)),
)),
SizedBox(
width: 60,
height: 60,

View File

@@ -1,13 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/overlay.dart';
import 'package:flutter_hbb/mobile/pages/server_page.dart';
import 'package:flutter_hbb/mobile/pages/settings_page.dart';
import 'package:get/get.dart';
import '../../common.dart';
import '../../common/widgets/chat_page.dart';
import 'connection_page.dart';
abstract class PageShape extends Widget {
final String title = "";
final Icon icon = Icon(null);
final Widget icon = Icon(null);
final List<Widget> appBarActions = [];
}
@@ -22,7 +24,9 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> {
var _selectedIndex = 0;
int get selectedIndex => _selectedIndex;
final List<PageShape> _pages = [];
final _blockableOverlayState = BlockableOverlayState();
void refreshPages() {
setState(() {
@@ -34,13 +38,14 @@ class _HomePageState extends State<HomePage> {
void initState() {
super.initState();
initPages();
_blockableOverlayState.applyFfi(gFFI);
}
void initPages() {
_pages.clear();
_pages.add(ConnectionPage());
if (isAndroid) {
_pages.addAll([ChatPage(), ServerPage()]);
_pages.addAll([ChatPage(type: ChatPageType.mobileMain), ServerPage()]);
}
_pages.add(SettingsPage());
}
@@ -62,7 +67,7 @@ class _HomePageState extends State<HomePage> {
// backgroundColor: MyTheme.grayBg,
appBar: AppBar(
centerTitle: true,
title: Text("RustDesk"),
title: appTitle(),
actions: _pages.elementAt(_selectedIndex).appBarActions,
),
bottomNavigationBar: BottomNavigationBar(
@@ -80,6 +85,8 @@ class _HomePageState extends State<HomePage> {
if (index == 1 && _selectedIndex != index) {
gFFI.chatModel.hideChatIconOverlay();
gFFI.chatModel.hideChatWindowOverlay();
gFFI.chatModel
.mobileClearClientUnread(gFFI.chatModel.currentKey.connId);
}
_selectedIndex = index;
}),
@@ -87,6 +94,53 @@ class _HomePageState extends State<HomePage> {
body: _pages.elementAt(_selectedIndex),
));
}
Widget appTitle() {
final currentUser = gFFI.chatModel.currentUser;
final currentKey = gFFI.chatModel.currentKey;
if (_selectedIndex == 1 &&
currentUser != null &&
currentKey.peerId.isNotEmpty) {
final connected =
gFFI.serverModel.clients.any((e) => e.id == currentKey.connId);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Tooltip(
message: currentKey.isOut
? translate('Outgoing connection')
: translate('Incoming connection'),
child: Icon(
currentKey.isOut
? Icons.call_made_rounded
: Icons.call_received_rounded,
),
),
Expanded(
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${currentUser.firstName} ${currentUser.id}",
),
if (connected)
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color.fromARGB(255, 133, 246, 199)),
).marginSymmetric(horizontal: 2),
],
),
),
),
],
);
}
return Text("RustDesk");
}
}
class WebHomePage extends StatelessWidget {

View File

@@ -43,8 +43,6 @@ class _RemotePageState extends State<RemotePage> {
double _mouseScrollIntegral = 0; // mouse scroll speed controller
Orientation? _currentOrientation;
final _blockableOverlayState = BlockableOverlayState();
final keyboardVisibilityController = KeyboardVisibilityController();
late final StreamSubscription<bool> keyboardSubscription;
final FocusNode _mobileFocusNode = FocusNode();
@@ -70,8 +68,9 @@ class _RemotePageState extends State<RemotePage> {
gFFI.qualityMonitorModel.checkShowQualityMonitor(sessionId);
keyboardSubscription =
keyboardVisibilityController.onChange.listen(onSoftKeyboardChanged);
_blockableOverlayState.applyFfi(gFFI);
initSharedStates(widget.id);
gFFI.chatModel
.changeCurrentKey(MessageKey(widget.id, ChatModel.clientModeID));
}
@override
@@ -351,8 +350,8 @@ class _RemotePageState extends State<RemotePage> {
color: Colors.white,
icon: Icon(Icons.message),
onPressed: () {
gFFI.chatModel
.changeCurrentID(ChatModel.clientModeID);
gFFI.chatModel.changeCurrentKey(MessageKey(
widget.id, ChatModel.clientModeID));
gFFI.chatModel.toggleChatOverlay();
},
)

View File

@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
import 'package:flutter_hbb/models/chat_model.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
@@ -419,14 +420,16 @@ class ConnectionManager extends StatelessWidget {
? const SizedBox.shrink()
: IconButton(
onPressed: () {
gFFI.chatModel.changeCurrentID(client.id);
gFFI.chatModel.changeCurrentKey(
MessageKey(client.peerId, client.id));
final bar = navigationBarKey.currentWidget;
if (bar != null) {
bar as BottomNavigationBar;
bar.onTap!(1);
}
},
icon: const Icon(Icons.chat)))
icon: unreadTopRightBuilder(
client.unreadChatMessageCount)))
],
),
client.authorized

View File

@@ -1,12 +1,18 @@
import 'dart:async';
import 'package:dash_chat_2/dash_chat_2.dart';
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:draggable_float_widget/draggable_float_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common/shared_state.dart';
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/mobile/pages/home_page.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:get/get.dart';
import 'package:uuid/uuid.dart';
import 'package:window_manager/window_manager.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -16,6 +22,24 @@ import '../common/widgets/overlay.dart';
import '../main.dart';
import 'model.dart';
class MessageKey {
final String peerId;
final int connId;
bool get isOut => connId == ChatModel.clientModeID;
MessageKey(this.peerId, this.connId);
@override
bool operator ==(other) {
return other is MessageKey &&
other.peerId == peerId &&
other.isOut == isOut;
}
@override
int get hashCode => peerId.hashCode ^ isOut.hashCode;
}
class MessageBody {
ChatUser chatUser;
List<ChatMessage> chatMessages;
@@ -45,6 +69,8 @@ class ChatModel with ChangeNotifier {
Rx<VoiceCallStatus> get voiceCallStatus => _voiceCallStatus;
TextEditingController textController = TextEditingController();
RxInt mobileUnreadSum = 0.obs;
MessageKey? latestReceivedKey;
@override
void dispose() {
@@ -53,19 +79,18 @@ class ChatModel with ChangeNotifier {
}
final ChatUser me = ChatUser(
id: "",
id: Uuid().v4().toString(),
firstName: translate("Me"),
);
late final Map<int, MessageBody> _messages = {}..[clientModeID] =
MessageBody(me, []);
late final Map<MessageKey, MessageBody> _messages = {};
var _currentID = clientModeID;
MessageKey _currentKey = MessageKey('', -2); // -2 is invalid value
late bool _isShowCMChatPage = false;
Map<int, MessageBody> get messages => _messages;
Map<MessageKey, MessageBody> get messages => _messages;
int get currentID => _currentID;
MessageKey get currentKey => _currentKey;
bool get isShowCMChatPage => _isShowCMChatPage;
@@ -115,15 +140,7 @@ class ChatModel with ChangeNotifier {
);
}
ChatUser get currentUser {
final user = messages[currentID]?.chatUser;
if (user == null) {
_currentID = clientModeID;
return me;
} else {
return user;
}
}
ChatUser? get currentUser => _messages[_currentKey]?.chatUser;
showChatIconOverlay({Offset offset = const Offset(200, 50)}) {
if (chatIconOverlayEntry != null) {
@@ -178,6 +195,12 @@ class ChatModel with ChangeNotifier {
final overlayState = _blockableOverlayState?.state;
if (overlayState == null) return;
if (isMobile &&
!gFFI.chatModel.currentKey.isOut && // not in remote page
gFFI.chatModel.latestReceivedKey != null) {
gFFI.chatModel.changeCurrentKey(gFFI.chatModel.latestReceivedKey!);
gFFI.chatModel.mobileClearClientUnread(gFFI.chatModel.currentKey.connId);
}
final overlay = OverlayEntry(builder: (context) {
return Listener(
onPointerDown: (_) {
@@ -229,21 +252,29 @@ class ChatModel with ChangeNotifier {
}
}
showChatPage(int id) async {
if (isConnManager) {
if (!_isShowCMChatPage) {
await toggleCMChatPage(id);
showChatPage(MessageKey key) async {
if (isDesktop) {
if (isConnManager) {
if (!_isShowCMChatPage) {
await toggleCMChatPage(key);
}
} else {
if (_isChatOverlayHide()) {
await toggleChatOverlay();
}
}
} else {
if (_isChatOverlayHide()) {
await toggleChatOverlay();
if (key.connId == clientModeID) {
if (_isChatOverlayHide()) {
await toggleChatOverlay();
}
}
}
}
toggleCMChatPage(int id) async {
if (gFFI.chatModel.currentID != id) {
gFFI.chatModel.changeCurrentID(id);
toggleCMChatPage(MessageKey key) async {
if (gFFI.chatModel.currentKey != key) {
gFFI.chatModel.changeCurrentKey(key);
}
if (_isShowCMChatPage) {
_isShowCMChatPage = !_isShowCMChatPage;
@@ -261,25 +292,30 @@ class ChatModel with ChangeNotifier {
}
}
changeCurrentID(int id) {
if (_messages.containsKey(id)) {
_currentID = id;
notifyListeners();
changeCurrentKey(MessageKey key) {
updateConnIdOfKey(key);
String? peerName;
if (key.connId == clientModeID) {
peerName = parent.target?.ffiModel.pi.username;
} else {
final client = parent.target?.serverModel.clients
.firstWhere((client) => client.id == id);
if (client == null) {
return debugPrint(
"Failed to changeCurrentID,remote user doesn't exist");
}
final chatUser = ChatUser(
id: client.peerId,
firstName: client.name,
);
_messages[id] = MessageBody(chatUser, []);
_currentID = id;
notifyListeners();
peerName = parent.target?.serverModel.clients
.firstWhereOrNull((client) => client.peerId == key.peerId)
?.name;
}
if (!_messages.containsKey(key)) {
final chatUser = ChatUser(
id: key.peerId,
firstName: peerName,
);
_messages[key] = MessageBody(chatUser, []);
} else {
if (peerName != null && peerName.isNotEmpty) {
_messages[key]?.chatUser.firstName = peerName;
}
}
_currentKey = key;
notifyListeners();
mobileClearClientUnread(key.connId);
}
receive(int id, String text) async {
@@ -292,49 +328,90 @@ class ChatModel with ChangeNotifier {
if (desktopType == DesktopType.cm) {
await showCmWindow();
}
String? peerId;
if (id == clientModeID) {
peerId = session.id;
} else {
peerId = session.serverModel.clients
.firstWhereOrNull((e) => e.id == id)
?.peerId;
}
if (peerId == null) {
debugPrint("Failed to receive msg, peerId is null");
return;
}
final messagekey = MessageKey(peerId, id);
// mobile: first message show overlay icon
if (!isDesktop && chatIconOverlayEntry == null) {
showChatIconOverlay();
}
// show chat page
await showChatPage(id);
int toId = currentID;
await showChatPage(messagekey);
late final ChatUser chatUser;
if (id == clientModeID) {
chatUser = ChatUser(
firstName: session.ffiModel.pi.username,
id: session.id,
id: peerId,
);
toId = id;
if (isDesktop) {
if (Get.isRegistered<DesktopTabController>()) {
DesktopTabController tabController = Get.find<DesktopTabController>();
var index = tabController.state.value.tabs
.indexWhere((e) => e.key == session.id);
final notSelected =
index >= 0 && tabController.state.value.selected != index;
// minisized: top and switch tab
// not minisized: add count
if (await WindowController.fromWindowId(stateGlobal.windowId)
.isMinimized()) {
window_on_top(stateGlobal.windowId);
if (notSelected) {
tabController.jumpTo(index);
}
} else {
if (notSelected) {
UnreadChatCountState.find(peerId).value += 1;
}
}
}
}
} else {
final client =
session.serverModel.clients.firstWhere((client) => client.id == id);
final client = session.serverModel.clients
.firstWhereOrNull((client) => client.id == id);
if (client == null) {
debugPrint("Failed to receive msg, client is null");
return;
}
if (isDesktop) {
window_on_top(null);
// disable auto jumpTo other tab when hasFocus, and mark unread message
final currentSelectedTab =
session.serverModel.tabController.state.value.selectedTabInfo;
if (currentSelectedTab.key != id.toString() && inputNode.hasFocus) {
client.hasUnreadChatMessage.value = true;
client.unreadChatMessageCount.value += 1;
} else {
parent.target?.serverModel.jumpTo(id);
toId = id;
}
} else {
toId = id;
if (HomePage.homeKey.currentState?.selectedIndex != 1 ||
_currentKey != messagekey) {
client.unreadChatMessageCount.value += 1;
mobileUpdateUnreadSum();
}
}
chatUser = ChatUser(id: client.peerId, firstName: client.name);
}
if (!_messages.containsKey(id)) {
_messages[id] = MessageBody(chatUser, []);
}
_messages[id]!.insert(
insertMessage(messagekey,
ChatMessage(text: text, user: chatUser, createdAt: DateTime.now()));
_currentID = toId;
if (id == clientModeID || _currentKey.peerId.isEmpty) {
// client or invalid
_currentKey = messagekey;
mobileClearClientUnread(messagekey.connId);
}
latestReceivedKey = messagekey;
notifyListeners();
}
@@ -344,17 +421,63 @@ class ChatModel with ChangeNotifier {
return;
}
message.text = trimmedText;
_messages[_currentID]?.insert(message);
if (_currentID == clientModeID && parent.target != null) {
insertMessage(_currentKey, message);
if (_currentKey.connId == clientModeID && parent.target != null) {
bind.sessionSendChat(sessionId: sessionId, text: message.text);
} else {
bind.cmSendChat(connId: _currentID, msg: message.text);
bind.cmSendChat(connId: _currentKey.connId, msg: message.text);
}
notifyListeners();
inputNode.requestFocus();
}
insertMessage(MessageKey key, ChatMessage message) {
updateConnIdOfKey(key);
if (!_messages.containsKey(key)) {
_messages[key] = MessageBody(message.user, []);
}
_messages[key]?.insert(message);
}
updateConnIdOfKey(MessageKey key) {
if (_messages.keys
.toList()
.firstWhereOrNull((e) => e == key && e.connId != key.connId) !=
null) {
final value = _messages.remove(key);
if (value != null) {
_messages[key] = value;
}
}
if (_currentKey == key || _currentKey.peerId.isEmpty) {
_currentKey = key; // hash != assign
}
}
void mobileUpdateUnreadSum() {
if (!isMobile) return;
var sum = 0;
parent.target?.serverModel.clients
.map((e) => sum += e.unreadChatMessageCount.value)
.toList();
Future.delayed(Duration.zero, () {
mobileUnreadSum.value = sum;
});
}
void mobileClearClientUnread(int id) {
if (!isMobile) return;
final client = parent.target?.serverModel.clients
.firstWhereOrNull((client) => client.id == id);
if (client != null) {
Future.delayed(Duration.zero, () {
client.unreadChatMessageCount.value = 0;
mobileUpdateUnreadSum();
});
}
}
close() {
hideChatIconOverlay();
hideChatWindowOverlay();

View File

@@ -248,7 +248,7 @@ class FfiModel with ChangeNotifier {
onUrlSchemeReceived(Map<String, dynamic> evt) {
final url = evt['url'].toString().trim();
if (url.startsWith(kUniLinksPrefix) && parseRustdeskUri(url)) {
if (url.startsWith(kUniLinksPrefix) && handleUriLink(uriString: url)) {
return;
}
switch (url) {

View File

@@ -233,7 +233,7 @@ class PlatformFFI {
'_appType:$_appType,info1-id:$id,info2-name:$name,dir:$_dir');
}
if (desktopType == DesktopType.cm) {
await _ffiBind.cmStartListenIpcThread();
await _ffiBind.cmInit();
}
await _ffiBind.mainDeviceId(id: id);
await _ffiBind.mainDeviceName(name: name);

View File

@@ -5,6 +5,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/main.dart';
import 'package:flutter_hbb/models/chat_model.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart';
import 'package:wakelock/wakelock.dart';
@@ -462,13 +463,7 @@ class ServerModel with ChangeNotifier {
key: client.id.toString(),
label: client.name,
closable: false,
onTap: () {
if (client.hasUnreadChatMessage.value) {
client.hasUnreadChatMessage.value = false;
final chatModel = parent.target!.chatModel;
chatModel.showChatPage(client.id);
}
},
onTap: () {},
page: desktop.buildConnectionCard(client)));
Future.delayed(Duration.zero, () async {
if (!hideCm) window_on_top(null);
@@ -480,6 +475,8 @@ class ServerModel with ChangeNotifier {
cmHiddenTimer = null;
});
}
parent.target?.chatModel
.updateConnIdOfKey(MessageKey(client.peerId, client.id));
}
void showLoginDialog(Client client) {
@@ -643,7 +640,7 @@ class Client {
bool inVoiceCall = false;
bool incomingVoiceCall = false;
RxBool hasUnreadChatMessage = false.obs;
RxInt unreadChatMessageCount = 0.obs;
Client(this.id, this.authorized, this.isFileTransfer, this.name, this.peerId,
this.keyboard, this.clipboard, this.audio);

View File

@@ -84,10 +84,12 @@ class RustDeskMultiWindowManager {
}
}
Future<dynamic> newFileTransfer(String remoteId, {bool? forceRelay}) async {
Future<dynamic> newFileTransfer(String remoteId,
{String? password, bool? forceRelay}) async {
var msg = jsonEncode({
"type": WindowType.FileTransfer.index,
"id": remoteId,
"password": password,
"forceRelay": forceRelay,
});
@@ -117,11 +119,12 @@ class RustDeskMultiWindowManager {
}
Future<dynamic> newPortForward(String remoteId, bool isRDP,
{bool? forceRelay}) async {
{String? password, bool? forceRelay}) async {
final msg = jsonEncode({
"type": WindowType.PortForward.index,
"id": remoteId,
"isRDP": isRDP,
"password": password,
"forceRelay": forceRelay,
});

View File

@@ -64,7 +64,7 @@ static void my_application_activate(GApplication* application) {
int width = 800, height = 600;
if (gIsConnectionManager) {
width = 300;
height = 400;
height = 490;
}
gtk_window_set_default_size(window, width, height); // <-- comment this line
gtk_widget_show(GTK_WIDGET(window));

View File

@@ -65,6 +65,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.0"
auto_size_text_field:
dependency: "direct main"
description:
name: auto_size_text_field
sha256: "8967129167193fefbb7a8707ade1bb71f9e52b9a5cf6da0132b7f6b7946c5a3f"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
back_button_interceptor:
dependency: "direct main"
description:
@@ -319,7 +327,7 @@ packages:
description:
path: "."
ref: HEAD
resolved-ref: "30518303e28702bf6b8110465293c05d21bc4cd2"
resolved-ref: aee670819f5fe7e8b0f05e0239dafb5c62f7a84b
url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window"
source: git
version: "0.1.0"

View File

@@ -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.0
version: 1.2.1
environment:
sdk: ">=2.17.0"
@@ -96,6 +96,7 @@ dependencies:
percent_indicator: ^4.2.2
dropdown_button2: ^2.0.0
uuid: ^3.0.7
auto_size_text_field: ^2.2.1
dev_dependencies:
icons_launcher: ^2.0.4

View File

@@ -84,7 +84,6 @@ const CHARS: &[char] = &[
pub const RENDEZVOUS_SERVERS: &[&str] = &[
"rs-ny.rustdesk.com",
"rs-sg.rustdesk.com",
"rs-cn.rustdesk.com",
];
pub const RS_PUB_KEY: &str = match option_env!("RS_PUB_KEY") {
@@ -1031,8 +1030,12 @@ impl PeerConfig {
};
let c = PeerConfig::load(&id_decoded_string);
if c.info.platform.is_empty() {
fs::remove_file(p).ok();
}
(id_decoded_string, t, c)
})
.filter(|p| !p.2.info.platform.is_empty())
.collect();
peers.sort_unstable_by(|a, b| b.1.cmp(&a.1));
return peers;

View File

@@ -1,5 +1,5 @@
pkgname=rustdesk
pkgver=1.2.0
pkgver=1.2.1
pkgrel=0
epoch=
pkgdesc=""

2
res/bump.sh Normal file
View File

@@ -0,0 +1,2 @@
#! /usr/bin/env bash
sed -i "s/$1/$2/g" res/*spec res/PKGBUILD flutter/pubspec.yaml Cargo.toml .github/workflows/*yml flatpak/*json appimage/*yml

14
res/osx-dist.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
echo $MACOS_CODESIGN_IDENTITY
cargo install flutter_rust_bridge_codegen --version 1.75.3 --features uuid
cd flutter; flutter pub get; cd -
~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart --c-output ./flutter/macos/Runner/bridge_generated.h
./build.py --flutter
rm rustdesk-$VERSION.dmg
# security find-identity -v
codesign --force --options runtime -s $MACOS_CODESIGN_IDENTITY --deep --strict ./flutter/build/macos/Build/Products/Release/RustDesk.app -vvv
create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-$VERSION.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app
codesign --force --options runtime -s $MACOS_CODESIGN_IDENTITY --deep --strict rustdesk-$VERSION.dmg -vvv
# notarize the rustdesk-${{ env.VERSION }}.dmg
rcodesign notary-submit --api-key-path ~/.p12/api-key.json --staple rustdesk-$VERSION.dmg

View File

@@ -1,5 +1,5 @@
Name: rustdesk
Version: 1.2.0
Version: 1.2.1
Release: 0
Summary: RPM package
License: GPL-3.0

View File

@@ -1,5 +1,5 @@
Name: rustdesk
Version: 1.2.0
Version: 1.2.1
Release: 0
Summary: RPM package
License: GPL-3.0

View File

@@ -1,5 +1,5 @@
Name: rustdesk
Version: 1.2.0
Version: 1.2.1
Release: 0
Summary: RPM package
License: GPL-3.0

View File

@@ -6,6 +6,18 @@ use hbb_common::log;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use hbb_common::platform::register_breakdown_handler;
#[macro_export]
macro_rules! my_println{
($($arg:tt)*) => {
#[cfg(not(windows))]
println!("{}", format_args!($($arg)*));
#[cfg(windows)]
crate::platform::message_box(
&format!("{}", format_args!($($arg)*))
);
};
}
/// shared by flutter and sciter main function
///
/// [Note]
@@ -19,15 +31,23 @@ pub fn core_main() -> Option<Vec<String>> {
let mut _is_elevate = false;
let mut _is_run_as_system = false;
let mut _is_quick_support = false;
let mut _is_flutter_connect = false;
let mut _is_flutter_invoke_new_connection = false;
let mut arg_exe = Default::default();
for arg in std::env::args() {
if i == 0 {
arg_exe = arg;
} else if i > 0 {
#[cfg(feature = "flutter")]
if arg == "--connect" || arg == "--play" {
_is_flutter_connect = true;
if [
"--connect",
"--play",
"--file-transfer",
"--port-forward",
"--rdp",
]
.contains(&arg.as_str())
{
_is_flutter_invoke_new_connection = true;
}
if arg == "--elevate" {
_is_elevate = true;
@@ -43,9 +63,9 @@ pub fn core_main() -> Option<Vec<String>> {
}
#[cfg(any(target_os = "linux", target_os = "windows"))]
if args.is_empty() {
#[cfg(target_os = "linux")]
hbb_common::allow_err!(crate::platform::check_autostart_config());
if crate::check_process("--server", false) && !crate::check_process("--tray", true) {
#[cfg(target_os = "linux")]
hbb_common::allow_err!(crate::platform::check_autostart_config());
hbb_common::allow_err!(crate::run_me(vec!["--tray"]));
}
}
@@ -63,7 +83,7 @@ pub fn core_main() -> Option<Vec<String>> {
}
}
#[cfg(feature = "flutter")]
if _is_flutter_connect {
if _is_flutter_invoke_new_connection {
return core_main_invoke_new_connection(std::env::args());
}
let click_setup = cfg!(windows) && args.is_empty() && crate::common::is_setup(&arg_exe);
@@ -75,7 +95,7 @@ pub fn core_main() -> Option<Vec<String>> {
args.clear();
}
if args.len() > 0 && args[0] == "--version" {
println!("{}", crate::VERSION);
my_println!("{}", crate::VERSION);
return None;
}
#[cfg(windows)]
@@ -215,18 +235,23 @@ pub fn core_main() -> Option<Vec<String>> {
return None;
} else if args[0] == "--password" {
if args.len() == 2 {
if crate::platform::is_root() {
if crate::platform::is_installed()
&& crate::platform::check_super_user_permission().unwrap_or_default()
{
crate::ipc::set_permanent_password(args[1].to_owned()).unwrap();
my_println!("Done!");
} else {
println!("Administrative privileges required!");
my_println!("Installation and administrative privileges required!");
}
}
return None;
} else if args[0] == "--get-id" {
if crate::platform::is_root() {
println!("{}", crate::ipc::get_id());
if crate::platform::is_installed()
&& crate::platform::check_super_user_permission().unwrap_or_default()
{
my_println!("{}", crate::ipc::get_id());
} else {
println!("Permission denied!");
my_println!("Installation and administrative privileges required!");
}
return None;
} else if args[0] == "--check-hwcodec-config" {
@@ -318,38 +343,48 @@ fn import_config(path: &str) {
/// If it returns [`Some`], then the process will continue, and flutter gui will be started.
#[cfg(feature = "flutter")]
fn core_main_invoke_new_connection(mut args: std::env::Args) -> Option<Vec<String>> {
args.position(|element| {
return element == "--connect" || element == "--play";
})?;
let mut peer_id = args.next().unwrap_or("".to_string());
if peer_id.is_empty() {
eprintln!("please provide a valid peer id");
return None;
}
let app_name = crate::get_app_name();
let ext = format!(".{}", app_name.to_lowercase());
if peer_id.ends_with(&ext) {
peer_id = peer_id.replace(&ext, "");
}
let mut switch_uuid = None;
while let Some(item) = args.next() {
if item == "--switch_uuid" {
switch_uuid = args.next();
let mut authority = None;
let mut id = None;
let mut param_array = vec![];
while let Some(arg) = args.next() {
match arg.as_str() {
"--connect" | "--play" | "--file-transfer" | "--port-forward" | "--rdp" => {
authority = Some((&arg.to_string()[2..]).to_owned());
id = args.next();
}
"--password" => {
if let Some(password) = args.next() {
param_array.push(format!("password={password}"));
}
}
"--relay" => {
param_array.push(format!("relay=true"));
}
// inner
"--switch_uuid" => {
if let Some(switch_uuid) = args.next() {
param_array.push(format!("switch_uuid={switch_uuid}"));
}
}
_ => {}
}
}
let mut param_array = vec![];
if switch_uuid.is_some() {
let switch_uuid = switch_uuid.map_or("".to_string(), |p| format!("switch_uuid={}", p));
param_array.push(switch_uuid);
let mut uni_links = Default::default();
if let Some(authority) = authority {
if let Some(mut id) = id {
let app_name = crate::get_app_name();
let ext = format!(".{}", app_name.to_lowercase());
if id.ends_with(&ext) {
id = id.replace(&ext, "");
}
let params = param_array.join("&");
let params_flag = if params.is_empty() { "" } else { "?" };
uni_links = format!("rustdesk://{}/{}{}{}", authority, id, params_flag, params);
}
}
if uni_links.is_empty() {
return None;
}
let params = param_array.join("&");
let params_flag = if params.is_empty() { "" } else { "?" };
#[allow(unused)]
let uni_links = format!(
"rustdesk://connection/new/{}{}{}",
peer_id, params_flag, params
);
#[cfg(target_os = "linux")]
return try_send_by_dbus(uni_links);

View File

@@ -910,7 +910,7 @@ pub mod connection_manager {
#[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn start_listen_ipc_thread() {
fn start_listen_ipc_thread() {
start_listen_ipc(true);
}
@@ -931,6 +931,12 @@ pub mod connection_manager {
}
}
#[inline]
pub fn cm_init() {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
start_listen_ipc_thread();
}
#[cfg(target_os = "android")]
use hbb_common::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};

View File

@@ -1448,9 +1448,9 @@ pub fn main_use_texture_render() -> SyncReturn<bool> {
}
}
pub fn cm_start_listen_ipc_thread() {
pub fn cm_init() {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
crate::flutter::connection_manager::start_listen_ipc_thread();
crate::flutter::connection_manager::cm_init();
}
/// Start an ipc server for receiving the url scheme.

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Nom d'usuari oblidat"),
("Password missed", "Contrasenya oblidada"),
("Wrong credentials", "Credencials incorrectes"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Editar tag"),
("Unremember Password", "Contrasenya oblidada"),
("Favorites", "Preferits"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "用户名没有填写"),
("Password missed", "密码没有填写"),
("Wrong credentials", "提供的登录信息错误"),
("The verification code is incorrect or has expired", "验证码错误或已超时"),
("Edit Tag", "修改标签"),
("Unremember Password", "忘记密码"),
("Favorites", "收藏"),
@@ -453,7 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Voice call", "语音通话"),
("Text chat", "文字聊天"),
("Stop voice call", "停止语音通话"),
("relay_hint_tip", "可能无法直连,可以尝试中继连接。\n另外,如果想直接使用中继连接,可以在 ID 后面添加/r或者在卡片选项里选择强制走中继连接。"),
("relay_hint_tip", "可能无法直连,可以尝试中继连接。\n另外,如果想直接使用中继连接,可以在 ID 后面添加/r如果最近访问里存在该卡片,也可以在卡片选项里选择强制走中继连接。"),
("Reconnect", "重连"),
("Codec", "编解码"),
("Resolution", "分辨率"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", "接受并提权"),
("accept_and_elevate_btn_tooltip", "接受连接并提升 UAC 权限"),
("clipboard_wait_response_timeout_tip", "等待拷贝响应超时"),
("Incoming connection", "收到的连接"),
("Outgoing connection", "发起的连接"),
("Exit", "退出"),
("Open", "打开"),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Chybí uživatelské jméno"),
("Password missed", "Chybí heslo"),
("Wrong credentials", "Nesprávné přihlašovací údaje"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Upravit štítek"),
("Unremember Password", "Přestat si heslo pamatovat"),
("Favorites", "Oblíbené"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Glemt brugernavn"),
("Password missed", "Glemt kodeord"),
("Wrong credentials", "Forkerte registreringsdata"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Rediger nøgleord"),
("Unremember Password", "Glem adgangskoden"),
("Favorites", "Favoritter"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Benutzername fehlt"),
("Password missed", "Passwort fehlt"),
("Wrong credentials", "Falsche Anmeldedaten"),
("The verification code is incorrect or has expired", "Der Verifizierungscode ist falsch oder abgelaufen"),
("Edit Tag", "Schlagwort bearbeiten"),
("Unremember Password", "Gespeichertes Passwort löschen"),
("Favorites", "Favoriten"),
@@ -453,7 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Voice call", "Sprachanruf"),
("Text chat", "Text-Chat"),
("Stop voice call", "Sprachanruf beenden"),
("relay_hint_tip", "Wenn eine direkte Verbindung nicht möglich ist, können Sie versuchen, eine Verbindung über einen Relay-Server herzustellen. \nWenn Sie eine Relay-Verbindung beim ersten Versuch herstellen möchten, können Sie das Suffix \"/r\" an die ID anhängen oder die Option \"Immer über Relay-Server verbinden\" auf der Gegenstelle auswählen."),
("relay_hint_tip", "Wenn eine direkte Verbindung nicht möglich ist, können Sie versuchen, eine Verbindung über einen Relay-Server herzustellen. \nWenn Sie eine Relay-Verbindung beim ersten Versuch herstellen möchten, können Sie das Suffix \"/r\" an die ID anhängen oder die Option \"Immer über Relay-Server verbinden\" in der Liste der letzten Sitzungen auswählen, sofern diese vorhanden ist."),
("Reconnect", "Erneut verbinden"),
("Codec", "Codec"),
("Resolution", "Auflösung"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", "Akzeptieren und Rechte erhöhen"),
("accept_and_elevate_btn_tooltip", "Akzeptieren Sie die Verbindung und erhöhen Sie die UAC-Berechtigungen."),
("clipboard_wait_response_timeout_tip", "Zeitüberschreitung beim Warten auf die Antwort der Kopie."),
("Incoming connection", "Eingehende Verbindung"),
("Outgoing connection", "Ausgehende Verbindung"),
("Exit", "Beenden"),
("Open", "Öffnen"),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Δεν συμπληρώσατε το όνομα χρήστη"),
("Password missed", "Δεν συμπληρώσατε τον κωδικό πρόσβασης"),
("Wrong credentials", "Λάθος διαπιστευτήρια"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Επεξεργασία ετικέτας"),
("Unremember Password", "Διαγραφή απομνημονευμένου κωδικού"),
("Favorites", "Αγαπημένα"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -44,7 +44,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("wait_accept_uac_tip", "Please wait for the remote user to accept the UAC dialog."),
("still_click_uac_tip", "Still requires the remote user to click OK on the UAC window of running RustDesk."),
("config_microphone", "In order to speak remotely, you need to grant RustDesk \"Record Audio\" permissions."),
("relay_hint_tip", "It may not be possible to connect directly; you can try connecting via relay. Additionally, if you want to use a relay on your first attempt, you can add the \"/r\" suffix to the ID or select the option \"Always connect via relay\" in the card of recent sessions."),
("relay_hint_tip", "It may not be possible to connect directly; you can try connecting via relay. Additionally, if you want to use a relay on your first attempt, you can add the \"/r\" suffix to the ID or select the option \"Always connect via relay\" in the card of recent sessions if it exists."),
("No transfers in progress", ""),
("idd_driver_tip", "Install virtual display driver which is used when you have no physical displays."),
("confirm_idd_driver_tip", "The option to install the virtual display driver is checked. Note that a test certificate will be installed to trust the virtual display driver. This test certificate will only be used to trust Rustdesk drivers."),

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Uzantnomo forgesita"),
("Password missed", "Pasvorto forgesita"),
("Wrong credentials", "Identigilo aŭ pasvorto erara"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Redakti etikedo"),
("Unremember Password", "Forgesi pasvorton"),
("Favorites", "Favorataj"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -208,7 +208,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("x11 expected", "x11 necesario"),
("Port", "Puerto"),
("Settings", "Ajustes"),
("Username", " Nombre de usuario"),
("Username", "Nombre de usuario"),
("Invalid port", "Puerto incorrecto"),
("Closed manually by the peer", "Cerrado manualmente por el par"),
("Enable remote configuration modification", "Habilitar modificación remota de configuración"),
@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Olvidó su nombre de usuario"),
("Password missed", "Olvidó su contraseña"),
("Wrong credentials", "Credenciales incorrectas"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Editar tag"),
("Unremember Password", "Olvidar contraseña"),
("Favorites", "Favoritos"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", "Aceptar y Elevar"),
("accept_and_elevate_btn_tooltip", "Aceptar la conexión y elevar permisos UAC."),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -80,7 +80,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Connection in progress. Please wait.", "در حال اتصال. لطفا متظر بمانید"),
("Please try 1 minute later", "لطفا بعد از 1 دقیقه مجددا تلاش کنید"),
("Login Error", "ورود ناموفق بود"),
("Successful", "ورود با موفقیت انجام شد"),
("Successful", "با موفقیت انجام شد"),
("Connected, waiting for image...", "...ارتباط برقرار شد. انتظار برای دریافت تصاویر"),
("Name", "نام"),
("Type", "نوع فایل"),
@@ -144,7 +144,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Failed to connect via relay server", "انجام نشد Relay اتصال از طریق سرور"),
("Failed to make direct connection to remote desktop", "اتصال مستقیم به دسکتاپ راه دور انجام نشد"),
("Set Password", "تنظیم رمزعبور"),
("OS Password", "رمز عیور سیستم عامل"),
("OS Password", "رمز عبور سیستم عامل"),
("install_tip", "لطفا برنامه را نصب کنید UAC و جلوگیری از خطای RustDesk برای راحتی در استفاده از نرم افزار"),
("Click to upgrade", "برای ارتقا کلیک کنید"),
("Click to download", "برای دانلود کلیک کنید"),
@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "نام کاربری وجود ندارد"),
("Password missed", "رمزعبور وجود ندارد"),
("Wrong credentials", "اعتبارنامه نادرست است"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "ویرایش برچسب"),
("Unremember Password", "رمز عبور ذخیره نشود"),
("Favorites", "اتصالات دلخواه"),
@@ -511,6 +512,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Collapse toolbar", "جمع کردن نوار ابزار"),
("Accept and Elevate", "بپذیرید و افزایش دهید"),
("accept_and_elevate_btn_tooltip", "را افزایش دهید UAC اتصال را بپذیرید و مجوزهای."),
("clipboard_wait_response_timeout_tip", ""),
("clipboard_wait_response_timeout_tip", "زمان انتظار برای مشخص شدن وضعیت کپی تمام شد."),
("Incoming connection", "اتصال ورودی"),
("Outgoing connection", "اتصال خروجی"),
("Exit", "خروج"),
("Open", "باز کردن"),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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"),
("Unremember Password", "Oublier le Mot de passe"),
("Favorites", "Favoris"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Üres felhasználónév"),
("Password missed", "Üres jelszó"),
("Wrong credentials", "Hibás felhasználónév vagy jelszó"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Címke szerkesztése"),
("Unremember Password", "A jelszó megjegyzésének törlése"),
("Favorites", "Kedvencek"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Username tidak sesuai"),
("Password missed", "Kata sandi tidak sesuai"),
("Wrong credentials", "Username atau password salah"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Ubah Tag"),
("Unremember Password", "Lupa Kata Sandi"),
("Favorites", "Favorit"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Nome utente mancante"),
("Password missed", "Password mancante"),
("Wrong credentials", "Credenziali errate"),
("The verification code is incorrect or has expired", "Il codice di verifica non è corretto o è scaduto"),
("Edit Tag", "Modifica tag"),
("Unremember Password", "Dimentica password"),
("Favorites", "Preferiti"),
@@ -453,7 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Voice call", "Chiamata vocale"),
("Text chat", "Chat testuale"),
("Stop voice call", "Interrompi chiamata vocale"),
("relay_hint_tip", "Se non è possibile connettersi direttamente, puoi provare a farlo tramite relay.\nInoltre, se si vuoi usare il relay al primo tentativo, è possibile aggiungere all'ID il suffisso '/r\' o selezionare nella scheda peer l'opzione 'Collegati sempre tramite relay'."),
("relay_hint_tip", "Se non è possibile connettersi direttamente, puoi provare a farlo tramite relay.\nInoltre, se si vuoi usare il relay al primo tentativo, è possibile aggiungere all'ID il suffisso '/r\' o selezionare nella scheda se esiste l'opzione 'Collegati sempre tramite relay'."),
("Reconnect", "Riconnetti"),
("Codec", "Codec"),
("Resolution", "Risoluzione"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", "Accetta ed eleva"),
("accept_and_elevate_btn_tooltip", "Accetta la connessione ed eleva le autorizzazioni UAC."),
("clipboard_wait_response_timeout_tip", "Timeout attesa risposta della copia."),
].iter().cloned().collect();
("Incoming connection", "Connessioni in entrata"),
("Outgoing connection", "Conenssioni in uscita"),
("Exit", "Esci da RustDesk"),
("Open", "Apri RustDesk"),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "ユーザー名がありません"),
("Password missed", "パスワードがありません"),
("Wrong credentials", "資格情報が間違っています"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "タグを編集"),
("Unremember Password", "パスワードの記憶を解除"),
("Favorites", "お気に入り"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "사용자명 누락"),
("Password missed", "비밀번호 누락"),
("Wrong credentials", "틀린 인증 정보"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "태그 수정"),
("Unremember Password", "패스워드 기억하지 않기"),
("Favorites", "즐겨찾기"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Қолданушы аты бос"),
("Password missed", "Құпия сөз бос"),
("Wrong credentials", "Бұрыс тіркелгі деректер"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Тақты Өндеу"),
("Unremember Password", "Құпия сөзді Ұмыту"),
("Favorites", "Таңдаулылар"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Prarastas vartotojo vardas"),
("Password missed", "Slaptažodis praleistas"),
("Wrong credentials", "Klaidingi kredencialai"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Redaguoti žymą"),
("Unremember Password", "Nebeprisiminti slaptažodžio"),
("Favorites", "Parankiniai"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Gebruikersnaam gemist"),
("Password missed", "Wachtwoord vergeten"),
("Wrong credentials", "Verkeerde inloggegevens"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Label Bewerken"),
("Unremember Password", "Wachtwoord vergeten"),
("Favorites", "Favorieten"),
@@ -511,6 +512,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Collapse toolbar", "Werkbalk samenvouwen"),
("Accept and Elevate", "Accepteren en Verheffen"),
("accept_and_elevate_btn_tooltip", "Accepteer de verbinding en verhoog de UAC-machtigingen."),
("clipboard_wait_response_timeout_tip", ""),
("clipboard_wait_response_timeout_tip", "Time-out in afwachting van kopieer-antwoord."),
("Incoming connection", "Inkomende verbinding"),
("Outgoing connection", "Uitgaande verbinding"),
("Exit", "Verlaten"),
("Open", "Open"),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Nieprawidłowe nazwa użytkownika"),
("Password missed", "Nieprawidłowe hasło"),
("Wrong credentials", "Błędne dane uwierzytelniające"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Edytuj tag"),
("Unremember Password", "Zapomnij hasło"),
("Favorites", "Ulubione"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", "Akceptuj i Podnieś uprawnienia"),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Nome de utilizador em falta"),
("Password missed", "Palavra-chave em falta"),
("Wrong credentials", "Nome de utilizador ou palavra-chave incorrectos"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Editar Tag"),
("Unremember Password", "Esquecer Palavra-chave"),
("Favorites", "Favoritos"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Nome de usuário requerido"),
("Password missed", "Senha requerida"),
("Wrong credentials", "Nome de usuário ou senha incorretos"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Editar Tag"),
("Unremember Password", "Esquecer Senha"),
("Favorites", "Favoritos"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

View File

@@ -233,6 +233,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Username missed", "Lipsește numele de utilizator"),
("Password missed", "Lipsește parola"),
("Wrong credentials", "Nume sau parolă greșită"),
("The verification code is incorrect or has expired", ""),
("Edit Tag", "Modifică etichetă"),
("Unremember Password", "Uită parola"),
("Favorites", "Favorite"),
@@ -512,5 +513,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept and Elevate", ""),
("accept_and_elevate_btn_tooltip", ""),
("clipboard_wait_response_timeout_tip", ""),
("Incoming connection", ""),
("Outgoing connection", ""),
("Exit", ""),
("Open", ""),
].iter().cloned().collect();
}

Some files were not shown because too many files have changed in this diff Show More