Compare commits

...

1321 Commits

Author SHA1 Message Date
fufesou
f0450db203 refact: mobile reset canvas (#9766)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-29 09:49:04 +08:00
21pages
3a75947553 add some missing web bridge (#9763)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-28 09:40:16 +08:00
fufesou
c565849062 fix: Function "LockScreen" on macOS since "ignore_flags" in enigo is introduced. (#9757)
1. LockScreen after connection is established.
2. LockScreen after "Map mode" or "Translate mode" keys are sent.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-27 20:36:21 +08:00
21pages
40e8f0d307 revert missing retry and opt keep session (#9755)
* Revert "fix missing retry (#8750)"

If `hasRetry` is true: there is a retry timeout;
If `hasRetry` is false: there is no retry button;

In
https://github.com/rustdesk/rustdesk/discussions/8748#discussioncomment-10081038,when
doesn't want inactive to retry,
cf0e3ec303/flutter/lib/models/model.dart (L444),
1.2.3 always show retry no matter what `hasRetry` is.

This reverts commit c3c99ba107.

* not keep session if there is no remote connection left.

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-26 22:05:54 +08:00
bovirus
129f6c869b Update Italian language (#9752) 2024-10-26 16:06:29 +08:00
Tobias Degen
924aa515c6 fix german translation (#9742) 2024-10-25 08:21:42 +08:00
solokot
c51771c854 Update ru.rs (#9741) 2024-10-25 08:08:02 +08:00
21pages
c8b9031996 refactor session insert, update if already exists (#9739)
* All share the same last_receive_time
* Not second port forward

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-24 23:14:43 +08:00
rustdesk
4da584055d fix ci 2024-10-24 22:52:06 +08:00
rustdesk
bd22b01370 fix "insert ctrl+alt+del" 2024-10-24 22:27:51 +08:00
Tobias Degen
b35b48086a Add translation string for better translationj capability (#9736)
* Add new translation für sending Ctrl+Alt+Del

* Add new translation string for sending Ctrl+Alt+Del
2024-10-24 21:52:39 +08:00
21pages
445e9ac285 no password required for file transfer action in remote control menu (#9731)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-24 17:20:48 +08:00
fufesou
7a3e1fe648 fix: ->macos, mouse events, key flags (#9733)
* fix: win->macos, mouse events,  key flags

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-24 14:33:37 +08:00
Alex Rijckaert
dfa9519d58 Update nl.rs (#9726) 2024-10-23 18:13:05 +08:00
fufesou
cc6f919080 feat: mobile map mode (#9717)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-23 09:28:39 +08:00
bovirus
2cdaca0fa3 Update Italian language (#9718) 2024-10-22 16:20:35 +08:00
21pages
6159449eba move option video-save-directory and allow-auto-record-outgoing to local (#9715)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-22 08:24:52 +08:00
Andrzej Rudnik
6088920f8d Update pl.rs (#9713)
* Update pl.rs

* Update pl.rs

Updated quota chars.
2024-10-21 19:55:34 +08:00
21pages
e8187588c1 auto record outgoing (#9711)
* Add option auto record outgoing session
* In the same connection, all displays and all windows share the same
  recording state.

todo:

Android check external storage permission

Known issue:

* Sciter old issue, stop the process directly without stop record, the record file can't play.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-21 14:34:06 +08:00
solokot
289076aa70 Update ru.rs (#9712) 2024-10-21 14:12:58 +08:00
fufesou
547da31095 fix: windows, exit fullscreen, remove redraw (#9700)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-19 19:41:55 +08:00
Mr-Update
1bf4ef1f46 Update de.rs (#9699) 2024-10-19 19:41:14 +08:00
21pages
1212d9fa2d web uni link (#9697)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-19 15:32:17 +08:00
fufesou
8c8a643cce fix: workaround physical display rotation (#9696)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-19 10:57:42 +08:00
Kleofass
675ffe0381 Update lv.rs (#9694) 2024-10-18 22:51:17 +08:00
bovirus
844caf8c15 Update Italian language (#9692) 2024-10-18 16:07:49 +08:00
flusheDData
0f6d28def7 Update es.rs (#9687)
New terms added
2024-10-18 08:55:39 +08:00
fufesou
0d3243e6dd fix: android, Korean input (#9667)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-18 08:55:18 +08:00
21pages
53d11e99d7 web only decode the latest image (#9689)
1. web only decode the latest image
2. web/ios remove relay server config when import

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-18 08:45:16 +08:00
21pages
defb3e6c73 fix gtk-sudo in non-English linux (#9680)
change LC_ALL from C.UTF-8 to C

Signed-off-by: 21pages <pages21@163.com>
2024-10-17 20:05:13 +08:00
fufesou
ae8dfe84a0 feat, web toast (#9686)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-17 17:23:09 +08:00
rustdesk
5e920f0fd0 fix ci 2024-10-16 22:37:40 +08:00
XLion
1a0814b201 Update tw.rs (#9672) 2024-10-16 19:33:27 +08:00
shleyZ
ace98d98ad fix: TextFormField onChanged event triggered multiple times when Korean input (#9644)
* fix: TextFormField onChanged event triggered multiple times when Korean input

* logic fix for iOS

* add comments

* move 'onChanged' logic to handleSoftKeyboardInput
2024-10-16 19:25:27 +08:00
Mr-Update
09083b3afa Update de.rs (#9668) 2024-10-16 11:23:12 +08:00
bovirus
36e11c61a9 Update Italian language (#9650) 2024-10-14 21:26:55 +08:00
21pages
55187e9243 fix theme radio update (#9647)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-14 17:46:38 +08:00
21pages
ae1c1a56e6 add missing code of last commit (#9646)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-14 15:51:51 +08:00
fufesou
cdd58e77eb fix: flickers child screen when resizing window (#9645)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-14 15:48:56 +08:00
21pages
ce924cc0d3 combine upload files/folder button (#9643)
* combine upload files/folder button

Signed-off-by: 21pages <sunboeasy@gmail.com>

* web compress cache

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-14 15:46:21 +08:00
fufesou
498b8ba3d6 fix: wayland CapsLock (#9641)
* fix: wayland CapsLock

Signed-off-by: fufesou <linlong1266@gmail.com>

* unformat for less changes

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: input, LockModes, do not check evt.down

Remove `evt.down`(revert the change) to avoid potential bugs.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-14 11:46:28 +08:00
21pages
af610b2408 web (#9640)
resolution, image quality, tab name, last remote id

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-14 11:15:52 +08:00
Yevhen Popok
6cdbcfc082 Update README-UA.md (#9639) 2024-10-14 11:02:57 +08:00
Yevhen Popok
9c7f51bc76 Update uk.rs (#9638) 2024-10-14 11:02:44 +08:00
fufesou
65683cc3e6 refact: remove redundant escape (#9634)
* refact: remove redundant escape

Signed-off-by: fufesou <linlong1266@gmail.com>

* flutter, early assert

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-13 01:07:47 +08:00
21pages
eb1ef0969c web file transfer (#9587)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-12 09:03:13 +08:00
21pages
29b01e9cef remove shift & tap enable multiselect (#9625)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-11 16:47:08 +08:00
21pages
cde7620eda fix web peer card tap (#9622)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-11 16:35:15 +08:00
Ibnul Mutaki
844b853074 update ID trans (#9621) 2024-10-11 10:43:22 +08:00
21pages
97f02ed25e Web password source (#9618)
* ensure window init finish

Signed-off-by: 21pages <sunboeasy@gmail.com>

* web password source

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-11 09:52:09 +08:00
Ibnul Mutaki
22c84bbbd1 Update ID translation (#9609)
* update id trans

* update id trans

* update inv

* update ind trans

* update ind trans

* update ind trans
2024-10-10 10:12:24 +08:00
Jernej Simončič
227f154ee7 Add missing Slovenian translations (#9606) 2024-10-10 07:35:46 +08:00
Ibnul Mutaki
59d7bf1e86 Update indonesian translate (#9601)
* update id trans

* update id trans

* update inv

* update ind trans
2024-10-09 10:44:20 +08:00
rustdesk
38fcf4e039 typo 2024-10-09 10:19:24 +08:00
James
4b3b31147e Update eo.rs (#9600) 2024-10-09 10:01:54 +08:00
fufesou
e6d4067f48 refact: remote toolbar style (#9597)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-08 21:16:07 +08:00
fufesou
507de628c9 fix: web, 2fa, trust (#9594)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-08 18:35:25 +08:00
fufesou
2591d4f044 fix: web chat (#9588)
* fix: web chat

Signed-off-by: fufesou <linlong1266@gmail.com>

* add missing svg

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-08 15:23:00 +08:00
XLion
9bcd0d1b03 Add Priority: to Debian control (#9586) 2024-10-07 22:08:17 +08:00
RustDesk
5555ba6b2f Revert "manifest updates from the Flathub's PR (#9581)" (#9585)
This reverts commit e06f456bbd.
2024-10-07 21:50:01 +08:00
XLion
28b6bc186f Add Section to Debian control (#9584) 2024-10-07 21:46:25 +08:00
fufesou
00d38260e1 fix: web send audit note (#9582)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-07 21:37:43 +08:00
Dmitry Beskov
e06f456bbd manifest updates from the Flathub's PR (#9581)
Signed-off-by: dmitry <43372966+besdar@users.noreply.github.com>
2024-10-07 18:55:25 +08:00
rustdesk
cc860b2906 https://github.com/flathub/flathub/pull/5670#issuecomment-2395337173 2024-10-07 15:26:41 +08:00
fufesou
839e8180e0 refact: web, no minimize btn on fullscreen (#9578)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-07 12:00:56 +08:00
fufesou
83aba804d0 fix: web fullscreen (#9577)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-07 00:01:14 +08:00
21pages
560c1effe8 remove mobile web setting, remove web/ios relay server setting (#9575)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-10-06 16:20:37 +08:00
rustdesk
e7353be0cd fix https://github.com/flathub/flathub/pull/5670#issuecomment-2395067890 2024-10-06 09:25:40 +08:00
fufesou
ba832362a7 fix: installed, copy&paste, special format (#9570)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-06 08:32:04 +08:00
Ibnul Mutaki
9ea09c1515 Update id (Indonesia) translation on some part (#9566)
* update id trans

* update id trans
2024-10-05 10:54:17 +08:00
fufesou
3a97b63e95 fix: windows, multi-window, move between monitors (#9562)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-04 22:05:38 +08:00
fufesou
dec3cde9b3 fix: deb, build, prerm (#9552)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-03 17:56:21 +08:00
rustdesk
c4d0b02478 fix ci 2024-10-03 15:38:55 +08:00
rustdesk
306dd77b81 fix https://github.com/rustdesk/rustdesk/issues/9537 2024-10-03 15:34:40 +08:00
BennyBeat
fd62751cb8 Improved Catalan translation (#9546)
* Improved Catalan translation

Improved Catalan translation

* Update ca.rs

Improved Catalan translation
2024-10-03 15:02:24 +08:00
hms5232
b0edfb8f70 fix wrong terms in tw lang (#9541)
"port" is "通訊埠", "連接埠" or just "埠" in Taiwan, "端口" is used in China.
2024-10-03 15:01:53 +08:00
fufesou
334526026c fix: web/mobile, skip querying onlines, if not in main page (#9535)
* fix: web, skip querying onlines, if not in main page

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: web/mobile, skip querying onlines

Signed-off-by: fufesou <linlong1266@gmail.com>

* Set isInMainPage to false after router is changed.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-01 15:25:59 +08:00
rustdesk
b5414ec002 fix https://github.com/rustdesk/rustdesk/issues/9527 2024-10-01 07:09:57 +08:00
fufesou
4eca8b9447 fix: web, forget password (#9524)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-10-01 07:04:43 +08:00
rustdesk
1ebc726acd fix ci 2024-09-30 10:54:05 +08:00
hashiguchi
d563372a91 Change the value of kMinQuality to 5 (#9508)
* Change the minimum value of the bitrate slider to 5

Signed-off-by: hashiguchi <hashiguchi@mtrx.jp>

* Change the value of kMinQuality to 5

Signed-off-by: hashiguchi <hashiguchi@mtrx.jp>

---------

Signed-off-by: hashiguchi <hashiguchi@mtrx.jp>
2024-09-28 21:52:05 +08:00
Alex Rijckaert
4a745d82f6 Update nl.rs (#9507) 2024-09-28 20:07:55 +08:00
fufesou
2f5f701dc7 fix: windows, subwindow, scale (#9506)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-28 19:43:28 +08:00
Iraccib
60a0099ba0 Update it.rs (#9503)
Changed "Scehrmo" to "Schermo"
2024-09-28 18:04:31 +08:00
rustdesk
30a7847100 fix ci 2024-09-28 15:53:39 +08:00
RustDesk
1e822fa135 Update nl.rs (#9501) 2024-09-28 15:49:11 +08:00
Alex Rijckaert
f6261883e8 Update nl.rs (#9500) 2024-09-28 15:33:08 +08:00
bovirus
3365844def Update Italian language (#9493) 2024-09-28 09:26:05 +08:00
solokot
769bbf1e1c Update ru.rs (#9495) 2024-09-28 09:25:52 +08:00
Mr-Update
81b999cfbe Update de.rs (#9496) 2024-09-28 09:25:35 +08:00
fufesou
9959217cc3 chore (#9491)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-27 23:10:51 +08:00
fufesou
3e6938bec6 refact: web desktop, web_id_input_tip (#9490)
* refact: web desktop, web_id_input_tip

Signed-off-by: fufesou <linlong1266@gmail.com>

* Update en.rs

* Update cn.rs

* Update en.rs

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-09-27 21:56:10 +08:00
fufesou
4459406578 fix: windows, window, restore from minimized state (#9482)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-27 14:19:25 +08:00
hashiguchi
beb1084e87 Change the minimum value of the bitrate slider to 5 (#9480)
Signed-off-by: hashiguchi <hashiguchi@mtrx.jp>
2024-09-27 10:14:46 +08:00
rustdesk
d4184fd865 bump to 1.3.2 2024-09-26 23:07:53 +08:00
21pages
ffc73f86a0 fix ab peers view, all peer tab use global peers model (#9475)
Use ChangeNotifierProvider<Peers>.value, and each peer tab has a global unique `Peers` model, then `load peers` and `build
peers` will always be the same one.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-26 22:08:32 +08:00
RustDesk
c74bdcdfdb Revert "fix peers view ChangeNotifierProvider update (#9459)" (#9471)
This reverts commit 7c55e3266b.
2024-09-26 14:20:37 +08:00
Kássio Oliveira
6d8b5b289f Refactor ScanPage for better performance and memory management (#9464)
- Added null checks in `reassemble` method to avoid potential null pointer exceptions when pausing/resuming the camera.
- Refactored image picking and QR code decoding process to use async/await, avoiding UI blocking with synchronous file reads.
- Improved exception handling by making it more specific to QR code reading errors.
- Introduced `StreamSubscription` for the QR scan listener and ensured proper cancellation in `dispose` method to prevent memory leaks.
- Separated button building logic (`_buildImagePickerButton`, `_buildFlashToggleButton`, `_buildCameraSwitchButton`) to enhance code readability and maintainability.
2024-09-26 10:34:12 +08:00
flusheDData
1d6873f622 New translation terms (#9463)
* Update es.rs

New terms added

* Update es.rs

New terms added
2024-09-26 09:29:49 +08:00
21pages
7c55e3266b fix peers view ChangeNotifierProvider update (#9459)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-25 15:11:11 +08:00
Lumiphare
ce5151032e Update README-ZH.md (#9457)
* Update CONTRIBUTING.md links to point to the Chinese version

* translated with AI assistance and manual refinement

* Adapted from the official Chinese translation of the Contributor Covenant

* Improve README-ZH.md

---------

Co-authored-by: sea <api@sea.com>
Co-authored-by: Lumiphare <lumiphare@foxmail.com>
2024-09-25 08:48:45 +08:00
bovirus
ba88bc9e8b Update Italian language (#9452) 2024-09-24 20:33:28 +08:00
fufesou
e0095aebda refact: web elevation (#9445)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-24 13:53:17 +08:00
rustdesk
664a3e186e clean SOFTWARE_UPDATE_URL 2024-09-24 12:00:37 +08:00
21pages
e4f7e126e5 fix check update (#9444)
check_software_update runs in a new thread, won't return directly

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-24 11:37:30 +08:00
Kleofass
49989e34e4 Update lv.rs (#9437) 2024-09-24 09:51:58 +08:00
fufesou
75a14fea23 fix: keyboard, change mode, await, on input source changed (#9434)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-23 17:31:37 +08:00
fufesou
f535406962 refact: web, keyboard, translate mode (#9432)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-23 16:58:21 +08:00
Vasyl Gello
f3f3bb538f Fix F-Droid build and bump Android NDK (#9428)
* Fix F-Droid build

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* Bump Android NDK to r27b

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

---------

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-09-23 16:43:59 +08:00
21pages
8fefd34c15 update web (#9427)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-23 09:18:22 +08:00
XLion
d98f947824 Update translation (#9426)
add translation for tw.rs and fix typo on cn.rs
2024-09-23 09:17:13 +08:00
solokot
5f52ce2c1b Update ru.rs (#9421) 2024-09-22 19:06:45 +08:00
Alex Rijckaert
1d799483d7 Update nl.rs (#9422) 2024-09-22 19:06:33 +08:00
rustdesk
3db55a718c build 48 2024-09-21 11:22:33 +08:00
Dmytro
a516f01feb Update uk.rs (#9416) 2024-09-20 21:38:16 +08:00
rustdesk
2e314bf032 disable init clipboard sync by default 2024-09-20 17:38:29 +08:00
jkh0kr
b93d4ce3fc Update ko.rs (#9411)
Add Korean language
2024-09-20 15:16:20 +08:00
fufesou
21bcfd173d fix: wayland, rdp input, mouse, scale (#9402)
* fix: wayland, rdp input, mouse, scale

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: rdp input, mouse, scale, check 0

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-20 11:15:19 +08:00
21pages
3d5262c36f opt gtk sudo ui, fix edit button show (#9399)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-20 11:06:56 +08:00
Mr-Update
cfd801c5d6 Update de.rs (#9401) 2024-09-20 10:54:50 +08:00
bovirus
216a72592d Update Italian language (#9403) 2024-09-20 10:54:37 +08:00
fufesou
ddd3401bd7 fix: keyboard, translate mode (#9406)
hotkey, linux -> win

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-20 10:52:19 +08:00
solokot
47139edd81 Update Russian translation (#9391)
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-09-19 18:48:54 +08:00
fufesou
c6e3f60a6b refact: flutter, ChangeNotifier, reduce rebuild (#9392)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-19 18:48:01 +08:00
21pages
88a99211f3 replace pkexec with gtk sudo (#9383)
* Fix https://github.com/rustdesk/rustdesk/issues/9286, replace pkexec
  with gtk sudo. Tested on gnome (ubuntu 22.04, debian 13), xfce (manjaro, suse), kde (kubuntu 23), lxqt (lubuntu 22), Cinnamon (mint 21.3), Mate (mint 21.2)
* Fix incorrect config of the main window opened by the tray, replace
  xdg-open with run_me, replace with dbus + run_me
* Fix `check_if_stop_service`, it causes the problem fixed in
  https://github.com/rustdesk/rustdesk/pull/8414, now revert that fix and fix itself.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-19 18:47:37 +08:00
fufesou
d08c335fdf fix: file transfer, show error, msgbox (#9389)
* fix: file transfer, show error, msgbox

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: translation

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-18 19:29:35 +08:00
fufesou
e5ec6957fe fix: option OPTION_ONE_WAY_FILE_TRANSFER (#9387)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-18 18:22:12 +08:00
rustdesk
e20f5dd001 fix ci 2024-09-18 13:00:15 +08:00
rustdesk
e1a6ccc100 fix ci 2024-09-18 12:37:26 +08:00
rustdesk
cc288272d3 OPTION_ONE_WAY_CLIPBOARD_REDIRECTION,
OPTION_ENABLE_CLIPBOARD_INIT_SYNC,
        OPTION_ALLOW_LOGON_SCREEN_PASSWORD,
        OPTION_ONE_WAY_FILE_TRANSFER,
2024-09-18 12:18:26 +08:00
Lumiphare
49ce4edb8a Chinese versions of CONTRIBUTING.md and CODE_OF_CONDUCT-ZH.md (#9386)
* Update CONTRIBUTING.md links to point to the Chinese version

* translated with AI assistance and manual refinement

* Adapted from the official Chinese translation of the Contributor Covenant

---------

Co-authored-by: sea <api@sea.com>
2024-09-18 10:39:26 +08:00
お餅のCreeeper
29c3b29bda Fix ja.rs typo (#9378) 2024-09-17 13:26:49 +08:00
お餅のCreeeper
8a8f708c3e update ja.rs (#9376) 2024-09-17 12:32:32 +08:00
fufesou
c5038b1a78 Fix/virtual display do not plug out if not plugged in (#9372)
* fix: win VD, do not plug out if not plugged in

Signed-off-by: fufesou <linlong1266@gmail.com>

* Forcibly virtual display on clicking button "-"

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-16 15:57:40 +08:00
21pages
f4c038ea93 update appindicator and recommends install it (#9364)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-15 14:33:59 +08:00
21pages
d9ea717056 fix last commit (#9355)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-14 10:03:50 +08:00
21pages
40af9dc78b not run window focus service on wayland (#9354)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-14 09:59:14 +08:00
Alex Rijckaert
81fc22a156 Update nl.rs (#9344)
* Update nl.rs

* Update nl.rs

file updated after adjusting (as test) @FastAct to RijckAlex (same person, new account)
2024-09-13 21:04:04 +08:00
rustdesk
2e7bd26e4c fix leak fix 2024-09-13 15:42:51 +08:00
rustdesk
179b562472 another leak 2024-09-13 15:41:42 +08:00
21pages
ab246fdcbf password lowercase check like uppercase (#9343)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-13 09:29:00 +08:00
rustdesk
d65d3b7326 fix ci 2024-09-13 09:21:50 +08:00
rustdesk
9f9a22ec63 uppercase for all 2024-09-13 08:46:21 +08:00
m-hume
a8f1a66043 Trim whitespace from Import server config (#9341) 2024-09-13 08:06:40 +08:00
21pages
0b3e7bf33e update hwcodec, fix linux ci (#9335)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-12 17:34:54 +08:00
fufesou
c358399eca refact: reduce try_get_displays() on login (#9333)
* refact: reduce try_get_displays() on login

Signed-off-by: fufesou <linlong1266@gmail.com>

* Function rename

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-12 14:44:40 +08:00
rustdesk
cacca7295c fix memory leak on mac because of wrong use of objc, by wrapping autoreleasepool 2024-09-12 14:26:29 +08:00
fufesou
d2e98cc620 fix: reduce rebuild for online state (#9331)
* fix: reduce rebuild for online state

Signed-off-by: fufesou <linlong1266@gmail.com>

* Query online, update  on focus changed

Signed-off-by: fufesou <linlong1266@gmail.com>

* Use  to ensure  is right

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact, window events on peer view

Signed-off-by: fufesou <linlong1266@gmail.com>

* chore

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove unused code

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-12 11:26:01 +08:00
fufesou
2e81bcb447 fix: Query online, remove loop retry (#9326)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-11 17:17:32 +08:00
fufesou
cbca0eb340 fix: keyboard, move tab to new window (#9322)
Do not disable keyboard when moving tab to new window on dispose.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-11 10:01:03 +08:00
fufesou
9380f33d7c Refact/options (#9318)
* refact options

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove unused msg

Signed-off-by: fufesou <linlong1266@gmail.com>

* web, toggle virtual display

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-10 23:54:59 +08:00
Gheorghi
519539ed0a Update bg.rs with more translations (#9317) 2024-09-10 23:22:14 +08:00
rustdesk
1f2a75fbd8 revert back pub lock because it does not fix leak 2024-09-10 21:28:07 +08:00
fufesou
51055a7e5b fix: tokio, call future in context of runtime (#9310)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-10 17:39:22 +08:00
fufesou
13effe7f14 fix: web, switch display (#9307)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-10 11:29:20 +08:00
21pages
943f96ef8c downgrade url_launcher/uni_links for linux ci (#9306)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-10 10:57:01 +08:00
rustdesk
260a82ee5c upgrade pub for flutter memory leak 2024-09-09 17:25:35 +08:00
fufesou
a2792d1527 comments (#9297)
* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-08 23:07:42 +08:00
fufesou
2922ebe22a Fix/clipboard retry if is occupied (#9293)
* fix: clipboard, retry if is occupied

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: clipboard service, hold runtime to cm ipc

Signed-off-by: fufesou <linlong1266@gmail.com>

* update arboard

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: log

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: get formats, return only not

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-08 21:13:05 +08:00
rustdesk
1e6944b380 apply --cm-no-ui to non-windows 2024-09-08 12:54:27 +08:00
21pages
993862c103 quit cm process if ipc connection to ipc server closed (#9292)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-08 12:37:41 +08:00
Andrzej Rudnik
c8cd564e69 Update pl.rs (#9285) 2024-09-07 17:58:07 +08:00
21pages
a4cd64f0d5 fix qsv memory leak by updating ffmpeg (#9266)
* fix qsv memory leak by updating ffmpeg

* Memory leaks occur when destroying FFmpeg QSV VRAM encoders. This issue is resolved with FFmpeg version 7.
* FFmpeg requires ffnvcodec version 12.1.14.0 or higher, and an NVIDIA driver version greater than 530. For more details, https://github.com/FFmpeg/nv-codec-headers/tree/n12.1.14.0.
* The code of NVIDIA VRAM encoder is not changed, still use Video Codec SDK version 11, which is unaffected by FFmpeg. Drivers newer than 470 can support this, but we may consider an update later, as the support check by sdk code may not be accurate for FFmpeg RAM encoders.
* The issue is related to FFmpeg, not libmfx. FFmpeg version 7 recommends using libvpl, but vcpkg currently lacks ports for libvpl. We can add these in the future.
* D3D11 Texture Rendering: The "Shared GPU Memory" in the task manager continue increasing when using D3D11 texture render, which can exceed the GPU memory limit (e.g., reaching up to 100GB). I don't know what it is and will try to find it out.
* Roughly tests on Windows, Linux, macOS, and Android for quick fix. Further testing will be performed, and I will share the results in this pr.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* update flutter_gpu_texture_render, fix shared gpu memory leak while
rendering

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-07 10:20:52 +08:00
21pages
f0ca4b9fee --no-server parameter to avoid server ipc occupied by ui (#9272)
Signed-off-by: 21pages <pages21@163.com>
2024-09-06 14:43:38 +08:00
Xp96
aa3402b44a Update ptbr.rs (#9271) 2024-09-06 01:15:07 +08:00
fufesou
26ebd0deb9 fix: clipboard, cmd ipc (#9270)
1. Send raw contents if `content_len` > 1024*3.
2. Send raw contents if it is not empty.
3. Try read clipboard again if no data from cm.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-05 23:39:07 +08:00
21pages
4150036589 remove first frame fallback if repeat (#9267)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-05 22:48:20 +08:00
fufesou
7a1157f1b0 refact: quality status event (#9268)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-05 22:37:14 +08:00
21pages
3bd34bf0b9 increase interval for restart linux ui, try fix loop start (#9264)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-05 18:34:48 +08:00
fufesou
5f29016861 fix: build web (#9259)
1. Web, build.
2. Web and mobile, `onSubmitted` for ID text field.
3. Web, remove unused key 'toggle_option'.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-04 22:27:52 +08:00
fufesou
e40243b55d Fix/wf cliprdr c bugs (#9253)
* fix: ResetEvent() after WaitForSingleObject()

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: check and free mem

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-04 17:04:48 +08:00
fufesou
dbbbd08934 fix: clipboard, support excel xml spreadsheet (#9252)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-04 16:44:36 +08:00
21pages
29e12b84a9 password max length prompt (#9248)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-04 11:31:13 +08:00
fufesou
04c0f66ca9 fix: set to OK if recv flag is TRUE (#9244)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-03 21:15:35 +08:00
fufesou
ec28567362 fix: win, file clipboard (#9243)
1. Return the result of `wait_response_event()` in
   `cliprdr_send_format_list()`
2. Add recv flags to avoid waiting a long time.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-03 20:55:45 +08:00
fufesou
d4377a13c5 refact: peer card, orientation (#9235)
* refact: peer card, orientation

Signed-off-by: fufesou <linlong1266@gmail.com>

* Do not change landscape/portrait on Desktop

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-03 19:06:11 +08:00
21pages
39e713838f Use fallback codec if first frame fails (#9242)
* Both encoding and decoding use fallback if first frame fails
* More aggresive max fail counter
* Update hwcodec, add judgement when length of the encoded data is zero, https://github.com/rustdesk/rustdesk-server-pro/discussions/382#discussioncomment-10525997
* Fix serde hwcodec config toml fails when the non-first vec![] is empty, https://github.com/toml-rs/toml-rs/issues/384, the config file is used for cache, when check process is not finished, the cache is used.
* Allow cm not start for pro user

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-09-03 18:48:17 +08:00
SimonHanel
75a4671bda Update da.rs (#9238)
* Update da.rs

Filled out every empty string and adjusted some for better translation.

Some translations might be janky due to my lack of context for what the string is used for, but it's good enough for now.

* Update da.rs

Minor tweaks
2024-09-03 10:09:25 +08:00
fufesou
827efabbc0 refact: remove fingerprint for web (#9226)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-02 02:00:59 +08:00
fufesou
532fe6aefb refact: web ui, login (#9225)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-01 23:14:57 +08:00
fufesou
ae339f039d refact: web ui (#9217)
* refact: web ui

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: remove AppBar shadow

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-09-01 00:30:07 +08:00
fufesou
bf390611ab fix: keyboard, sciter (#9216)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-31 19:02:50 +08:00
fufesou
e3f6829d02 refact: android ios, lan discovery (#9207)
* refact: android ios, lan discovery

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: build and runtime error

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-30 00:37:38 +08:00
fufesou
832002a10f refact: web, remote toolbar, pin (#9206)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-29 23:38:08 +08:00
RustDesk
d335cdbb0c Update README.md (#9196) 2024-08-28 12:25:00 +08:00
RustDesk
6a5d5875c8 Logo broken (#9195) 2024-08-28 12:23:40 +08:00
fufesou
cf06d1028f fix: web, reset cursor on disconn, back to main page (#9192)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-27 23:58:04 +08:00
9amm
fd178a7b6c Fix minor typo (#9191)
Co-authored-by: 9amm <>
2024-08-27 23:38:40 +08:00
rustdesk
f3a2733d75 start mac service more later 2024-08-27 20:25:01 +08:00
fufesou
55de573a01 fix: keyboard input, mulit windows (#9189)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-27 19:30:51 +08:00
fufesou
40239a1c41 feat: macos, mouse button, back&forward (#9185)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-27 15:20:29 +08:00
fufesou
c68ce7dd84 fix: web v2, keyboard mode (#9180)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-27 00:00:33 +08:00
rustdesk
690a2c8399 still find delegate failure when my mac restarted automatically sometimes 2024-08-26 17:07:02 +08:00
fufesou
4b4fd94f3e feat: web v2 keyboard (#9175)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-26 12:13:11 +08:00
21pages
5abe42f66c not run get window focus if no multiple displays (#9174)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-26 10:37:35 +08:00
21pages
48aec6484c refresh file transfer table on resume (#9167)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-25 21:29:41 +08:00
21pages
a946d4d0c9 file transfer status text overflow at start (#9166)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-25 20:46:21 +08:00
ELForcer
24f4b94082 Update ru.rs (#9163) 2024-08-25 15:12:08 +08:00
fufesou
aa1e122532 fix: revert key events to raw key events on Linux (#9161)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-25 00:03:31 +08:00
rustdesk
d400999b9c bump to 1.3.1 2024-08-24 19:02:04 +08:00
fufesou
1d416f6626 refact: flutter keyboard, map mode (#9160)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-24 12:10:36 +08:00
Kleofass
9d9741f18e Update lv.rs (#9155) 2024-08-23 21:27:52 +08:00
21pages
50aa8e12ad desktop file transfer, all columns respond to tap, add right click item border (#9153)
When right click selected item, the border is not obvious but can feel
some change.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-23 10:00:36 +08:00
jxdv
5931af460e Update trs (#9144)
* update sk tr

* update cz tr
2024-08-22 09:36:03 +08:00
fufesou
fc607d6789 fix: privacy mode 2, restore (#9141)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-22 09:35:50 +08:00
rustdesk
529e70910d build 47 2024-08-21 18:29:43 +08:00
fufesou
f300d797e2 Fix/ios query onlines (#9134)
* fix: ios query onlines

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-21 18:21:25 +08:00
Daniel Ehrhardt
e3cce2824d Added Public Server to Readme (#9132) 2024-08-21 09:28:02 +08:00
21pages
f34b8411a7 Fix new cm tab not replace the old persisted tab (#9127)
* This happens when after changing DesktopTab to StatefulWidget, 1.2.7
  and 1.3.0 have this problem.
* When `addConnection` in server_model.dart is called, the old closed
  client is removed, the client parameter of buildConnectionCard is new,
but client id inside Consumer is old.
* The only state in cm page is timer, its value is kept in test.
* There may be a better way to solve the ui update.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-20 15:34:10 +08:00
21pages
8745fcbb6a opt desktop file manager status list (#9117)
* Show delete file/dir log
* Show full path rather than base file name
* Show files count
* Opt status card layout
* Change selected color to accent

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-20 10:53:55 +08:00
flusheDData
2a0fd55af7 Update es.rs (#9114)
New terms added
2024-08-19 14:41:28 +08:00
RustDesk
da70cbcdda Update es.rs 2024-08-19 14:19:24 +08:00
flusheDData
921b64e1e0 Last tips (#9113)
* Update es.rs

New translations

* Update es.rs

Translation correction of 'Version'

* Update es.rs

Last tips

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-08-19 14:14:47 +08:00
XLion
c0de0aa108 Update tw.rs (#9112) 2024-08-19 14:13:31 +08:00
fufesou
715d475f49 fix: privacy mode 2, more error info (#9111)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-19 11:35:35 +08:00
Mr-Update
e3f09b3ec6 Update de.rs (#9107) 2024-08-19 03:12:56 +08:00
rustdesk
0a5fafb84f comment 2024-08-18 10:04:20 +08:00
rustdesk
4e084c5ee0 revert 2024-08-18 09:56:24 +08:00
rustdesk
d1fe617670 fixing https://github.com/rustdesk/rustdesk/issues/9103, maybe 2024-08-18 09:30:53 +08:00
bovirus
7744bdbbe0 Update Italian language (#9093) 2024-08-16 18:39:36 +08:00
FastAct
e1329c8157 Update nl.rs (#9092) 2024-08-16 15:19:18 +08:00
fufesou
f31e60af5b fix: crash, drop tokio RunTime in async context (#9091)
* fix: crash, drop tokio RunTime in async context

Signed-off-by: fufesou <linlong1266@gmail.com>

* chore

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-16 14:55:42 +08:00
21pages
ed18e3c786 file rename (#9089)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-16 12:55:58 +08:00
rustdesk
579e0fac36 fix https://github.com/rustdesk/rustdesk/issues/9088 2024-08-16 12:20:40 +08:00
fufesou
92752765ba fix: resize top edge (#9081)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-15 23:58:19 +08:00
kibeb
071f51cf6f Update cs.rs: more corrections, new phrases translated (#9075) 2024-08-15 17:34:39 +08:00
kibeb
dde3cce120 Update cs.rs: typo corrected (#9074) 2024-08-15 14:55:44 +08:00
21pages
bb1b9858d5 only main window show tabbar border, change cm tabbar color (#9073)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-15 13:25:59 +08:00
fufesou
a31c27be73 fix: windows, remote window, resizing, #9061 (#9062)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-15 13:24:42 +08:00
21pages
85ae3916cb fix get_custom_server_from_string, relay= is not used (#9069)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-15 09:57:12 +08:00
jkh0kr
cc9b7e64eb Update ko.rs (#9067)
Additional Korean translations
2024-08-15 09:25:03 +08:00
flusheDData
5e22a49e49 Translation correction (#9064)
* Update es.rs

New translations

* Update es.rs

Translation correction of 'Version'
2024-08-15 09:24:42 +08:00
21pages
07cf1b4db5 fix wallpaper get sid (#9058)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-14 18:09:47 +08:00
RustDesk
f6ab5cdcb2 Update config.toml (#9057) 2024-08-14 17:32:05 +08:00
Mr-Update
b477aded0b Update de.rs (#9054) 2024-08-14 10:39:05 +08:00
rustdesk
65318efd67 update README 2024-08-13 21:42:04 +08:00
bovirus
dbd195a46e Update Italian language (#9047) 2024-08-13 14:22:08 +08:00
flusheDData
0651ad492f Update es.rs (#9045)
New translations
2024-08-13 14:11:36 +08:00
fufesou
a3c5adb1f4 refact: remove dup translation (#9043)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-13 11:27:39 +08:00
fufesou
a771abcdc2 fix: win, multi monitors, maximize, may fix 9033 (#9038)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-13 00:18:35 +08:00
21pages
b8b3a089f3 android telebot setting (#9035)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-12 22:20:35 +08:00
fufesou
8f00067266 fix: build (#9036)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-12 22:15:59 +08:00
fufesou
83bf067d18 fix: not plug virtual dislay, non win, installed (#9034)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-12 21:53:41 +08:00
21pages
1729ee337f trust this device to skip 2fa (#9012)
* trust this device to skip 2fa

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Update connection.rs

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-08-12 18:08:33 +08:00
Xp96
57834840b8 Update ptbr.rs (#9031) 2024-08-12 17:22:00 +08:00
fufesou
99d7b62d79 fix: privacy mode 2, resolution changed (#9027)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-11 23:28:02 +08:00
fufesou
6625aca994 fix: win, virtual display (#9023)
1. Default resolution 1920x1080.
2. Restore on conn & disconn.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-11 19:26:41 +08:00
FastAct
ce56be6507 Update nl.rs (#9022) 2024-08-11 06:49:52 +08:00
mehdi-song
fd69b14623 Update fa.rs (#9021) 2024-08-11 06:49:34 +08:00
bovirus
7521bbe15f Update Italian language (#9016) 2024-08-10 20:52:27 +08:00
fufesou
3c6ddd7403 fix: multi-displays, displays changed, switch idx (#9006)
Use init display index as the primary index.

But when displays changed, the primary display may also changes.

No need to change the old primary index.
But we need to make sure that the old primary index does not exceed the display number.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-09 21:17:06 +08:00
Mr-Update
6820e2f4c7 Update de.rs (#9004) 2024-08-09 08:43:18 +08:00
Ivan Beà
77f3ebaf1a Update ca.rs (#8997)
* Update ca.rs

update catalan translation

* Update ca.rs

minor changes
2024-08-08 23:18:01 +08:00
fufesou
e7e244d4f2 fix: update pub desktop_multi_window (#9002)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-08 23:06:33 +08:00
fufesou
f4c40d733e Fix/exe upgrade options (#9001)
* fix: exe upgrade, use previous options

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: msi, shortcuts options, swap pos

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-08 22:07:06 +08:00
fufesou
049c334db3 fix: privacy mode, windows vd, init resolution (#8992)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-08 15:31:11 +08:00
rustdesk
6197832317 update hwcodec 2024-08-08 11:58:12 +08:00
Vasyl Gello
2fd53f9825 Pass JVM to ffmpeg (#8985)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-08 11:53:49 +08:00
Yevhen Popok
ae16b8975b Update Ukrainian translation (#8980)
Sync with #8977
2024-08-08 10:24:40 +08:00
solokot
171177c76f Update ru.rs (#8987) 2024-08-08 10:24:28 +08:00
bovirus
ade1d8c0c7 Update Italian language (#8988) 2024-08-08 10:24:15 +08:00
rustdesk
9a194f0850 res/users.py 2024-08-07 23:26:17 +08:00
21pages
76d5a8b205 unlock with PIN (#8977)
* add custom password to unlock settings

* If not set, use admin password; if set, use custom settings password.
* At least 4 characters.
* Set with gui or command line.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Update cn.rs

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-08-07 16:21:38 +08:00
jkh0kr
bc6ce6c7ee Update ko.rs (#8974)
Additional Korean translation
2024-08-07 10:12:43 +08:00
rustdesk
025cdfa25b fix ci 2024-08-07 01:19:29 +08:00
rustdesk
2f432e941d hide-tray option 2024-08-07 01:08:36 +08:00
21pages
96edca8f74 update sysinfo rlim_max, which causing debian 13 pkexec not work (#8968)
* update sysinfo rlim_max, which causing debian 13 pkexec not work

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Update Cargo.toml

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-08-06 18:19:35 +08:00
fufesou
51b250435d refact: audio input, combobox instead of radio (#8965)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-06 17:07:05 +08:00
21pages
5a2121501d fix build (#8964)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-06 12:36:39 +08:00
rustdesk
877b3e2ce5 fix ci 2024-08-06 12:19:06 +08:00
rustdesk
421ddc0016 fix https://github.com/rustdesk/rustdesk/issues/4863 2024-08-06 12:11:03 +08:00
fufesou
2662abc5a3 fix: reset voice call state, on conn (#8961)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-06 00:28:31 +08:00
fufesou
b3e1c8a907 Refact/msi more install options (#8949)
* refact: msi, more install options

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: msi, reg values on upgrade/modify

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: msi, silent repair/upgrade, RemoveInstallFolder()

Signed-off-by: fufesou <linlong1266@gmail.com>

* Options support both 1/0 and Y/N

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: msi, preprocess, open file with explicit encoding

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: msi, read previous options

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: mis, install folder, read previous option

Signed-off-by: fufesou <linlong1266@gmail.com>

* Comment on Control -> Checkbox

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: UI, checkbox options, read previous values

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: shortcuts options, init state

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: shortcuts, init state

Signed-off-by: fufesou <linlong1266@gmail.com>

* Better shortcuts property conditions

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-05 17:49:48 +08:00
rustdesk
2266fde26f fix address_book_name cli 2024-08-04 19:31:13 +08:00
rustdesk
e58e75eea9 add address_book_name, address_book_tag to cli, https://github.com/rustdesk/rustdesk/discussions/7866, need to use with server Pro 1.4.2 (not ready) 2024-08-04 18:45:49 +08:00
rustdesk
eafebdba21 run universal for upload only 2024-08-04 16:32:05 +08:00
rustdesk
7bf5e69444 revert 2024-08-04 16:23:58 +08:00
rustdesk
cb0dc46d08 turn it off since it does not work, please test it before submit PR 2024-08-04 16:22:42 +08:00
rustdesk
9b8209b61b log::trace audio buffer cleared 2024-08-04 16:17:59 +08:00
21pages
b6ba9978e3 set max audio buffer to 150ms, clear audio buffer if full (#8947)
The device should have the capability to play a sufficient audio buffer during each period to meet the audio config, so the playback speed is not slow.

The audio delay is caused by network jitter. The controlled  side sends audio data every 10ms, but it often happens that multiple packets are sent together. During underrun periods, the controlling side plays extra silence data instead, resulting in the device playing more audio than the configured amount.
2024-08-04 16:11:00 +08:00
Vasyl Gello
0d1d7a9b87 Guard parameters for universal apk (#8943)
* Guard parameters for universal apk

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* Free space before android builds

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

---------

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-04 12:34:08 +08:00
fufesou
508dd5b383 fix: custom client, msi, conn type (#8944)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-03 22:29:30 +08:00
Vasyl Gello
31a1b7a80b Fix copy step for universal apk (#8942)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-03 19:00:27 +08:00
Vasyl Gello
ba43424781 Build universal apk (#8941)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-03 18:39:12 +08:00
rustdesk
f899b2a962 fix https://github.com/rustdesk/rustdesk/issues/8856 2024-08-03 18:05:32 +08:00
Yevhen Popok
2dd3d8c11e Use correct locale code for Ukrainian translation file name (#8932)
Rename Ukrainian translation file ua.rs to uk.rs to avoid confusion.
This complements commits [2333ee2](2333ee2c07) and [b6035fb](b6035fbbdf)
2024-08-02 22:58:45 +08:00
fufesou
6eea425280 fix: clipboard cm ipc data, raw bytes (#8930)
* fix: clipboard cm ipc data, raw bytes

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove useless check

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-02 21:36:49 +08:00
Vasyl Gello
5e7d4fd2d6 vcpkg deps (#8926)
* vcpkg: bump opus to 1.5.2

Should fix flakes caused by https://github.com/android/ndk/issues/2032

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* vcpkg: actually use cached artifacts

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* Print all vcpkg log files on errors

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

---------

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-02 18:43:30 +08:00
rustdesk
d9fba50606 fix https://github.com/rustdesk/rustdesk-server-pro/issues/334 again 2024-08-02 15:43:55 +08:00
21pages
b6035fbbdf update supportedLocales (#8925)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-02 15:12:48 +08:00
Yevhen Popok
e67b694f06 Update Ukrainian translation (#8921) 2024-08-02 09:31:37 +08:00
rustdesk
2333ee2c07 fix https://github.com/rustdesk/rustdesk/issues/8923 2024-08-02 09:29:51 +08:00
Vasyl Gello
61ccc2152e Bump Android NDK to r27 (#8918)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-01 21:33:59 +08:00
21pages
f6aca4ca8e fix click cm audio permission button before auth (#8917)
1. When not authenticated, clicking the audio permission button on the cm will send audio data
2. Keep the cursor position code unchanged, because `show_remote_cursor` is false before auth, so subscription will not happen.
3. Keep the clipboard code unchanged, because the keyboard permission will also be determined in `try_sub_services`. If the clipboard permission is clicked before auth and the keyboard permission is clicked after auth, the clipboard service will not be subscribed.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-08-01 18:17:02 +08:00
Vasyl Gello
1707987a7b Fix typo in android-x86 lib copy step (#8914)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-01 15:09:33 +08:00
fufesou
85604dee79 refact: msi, custom client props (#8913)
* refact: msi, custom client props

Signed-off-by: fufesou <linlong1266@gmail.com>

* format

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-08-01 15:08:52 +08:00
Hamir Mahal
a12969be30 fix: usage of actions/checkout@v3 (#8912)
* chore: changes from formatting on save

* fix: usage of `actions/checkout@v3`
2024-08-01 09:12:11 +08:00
rustdesk
9f91eada89 fix https://github.com/rustdesk/rustdesk-server-pro/issues/338 2024-08-01 09:09:45 +08:00
Vasyl Gello
cb6a6aa42a flutter-build: Parameterize Android build matrix (#8907)
As @rustdesk noted debug builds are no-go in official RD repo
but this change makes it possible to filter jobs only relevant to
Android from flutter-build.yml to build only Android in a separate
mirror of Rustdesk.

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-31 20:38:00 +08:00
21pages
4fec8abad4 update repo to rustdesk-org (#8905)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-31 17:25:10 +08:00
solokot
35571dc8d7 Update ru.rs (#8901) 2024-07-31 14:16:55 +08:00
FastAct
a103b83647 Update nl.rs (#8892) 2024-07-30 20:23:49 +08:00
21pages
e1e4bf599b add libatomic1 to linux armv7 sciter depends (#8890)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-30 14:52:06 +08:00
rustdesk
cba8aaa410 tooltip for https://github.com/rustdesk/rustdesk/issues/8600, and change
dialog error to richtext with link support
2024-07-30 14:42:21 +08:00
fufesou
8ced4ddaa2 simple refact (#8888)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-30 13:06:37 +08:00
fufesou
15404ecab4 fix: clipboard, windows, controlled side, formats (#8885)
* fix: clipboard, windows, controlled side, formats

Signed-off-by: fufesou <linlong1266@gmail.com>

* Clipboard, reuse ipc conn and send_raw()

Signed-off-by: fufesou <linlong1266@gmail.com>

* Clipboard, merge content buffer

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: clipboard service, ipc stream

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-30 11:35:39 +08:00
Generalworks Inc.
97772f9ac5 Fix typo on ja.rs (#8886) 2024-07-30 00:27:28 +08:00
rustdesk
764fbe2c9d addressing https://github.com/rustdesk/rustdesk/issues/8883 2024-07-30 00:27:01 +08:00
fufesou
e03344d85b fix: clipboard, read as much as possible (#8881)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-29 21:19:29 +08:00
fufesou
0e98a51775 fix: clipboard, set formats and enable option (#8873)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-29 15:40:02 +08:00
XLion
0a1d3c4afb Update tw.rs and cn.rs (#8877)
* Update tw.rs

* Update cn.rs
2024-07-29 15:34:26 +08:00
rustdesk
fd9b5f3c57 fix https://github.com/rustdesk/rustdesk-server-pro/issues/334 2024-07-28 22:55:14 +08:00
Mr-Update
73f6afd4c0 Update de.rs (#8870) 2024-07-28 21:09:36 +08:00
fufesou
50dd2b3aad chore (#8868)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-28 19:33:00 +08:00
fufesou
c0e9445602 refact: embed crate mouce, uinput (#8836)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-28 17:27:09 +08:00
fufesou
541d9c6b86 feat: clipboard, multi formats (#8733)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-28 17:26:54 +08:00
fufesou
8c91e5c5ca refact: update crate tfc (#8867)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-28 17:26:18 +08:00
jxdv
19d1605d8c Update trs (#8866)
* update sk-tr

* update cs-tr
2024-07-28 17:13:15 +08:00
bovirus
0faf82f109 Update Italian language (#8863) 2024-07-28 17:13:01 +08:00
21pages
ee5314de20 use selectableText for some errors (#8862)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-28 11:13:19 +08:00
21pages
7e8d3bd2ac remove calling refreshCurrentUser when connect status become ready (#8849)
When refreshCurrentUser throw error, show check network in ab and group
tab.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-28 10:15:09 +08:00
Vasyl Gello
9750e1409c Use VCPKG_FORCE_SYSTEM_BINARIES in actual RD sciter build step (#8859)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-28 08:44:16 +08:00
Vasyl Gello
67d4e061fb Try to detach /opt/artifacts from sciter (#8857)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-27 23:57:53 +08:00
Vasyl Gello
f67f2be0cb Add built CMake to PATH in actual RD build step (#8855)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-27 21:28:34 +08:00
Vasyl Gello
aa42bf548e Limit cmake buildstep to 1 threads to check OOM (#8852)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-27 16:29:53 +08:00
rustdesk
d679e8fa7d WIN_RUST_VERSION -> SCITER_RUST_VERSION 2024-07-27 16:16:31 +08:00
Vasyl Gello
1357ef5d6f Try to fix sciter-armhf build (#8850)
* Bootstrap CMake 3.29.7 for ubuntu/bionic/armhf
  * vcpkg exevutable now needs GCC 8 but all product binaries
    are still OK with GCC 7
  * Remove res/vcpkg/linux.cmake

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-27 15:32:26 +08:00
21pages
30a5d1e0e1 avoid call refreshCurrentUser twice at startup (#8848)
refreshCurrentUser will be called at these 2 position:

1. runMainApp or runMobileApp in main.dart
2. when connect status is ready

Both of these two happens at startup, when connect status is ready and startup time < 5 seconds, not call refreshCurrentUser

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-27 09:50:06 +08:00
FastAct
9f0985c842 Update nl.rs (#8843) 2024-07-26 17:17:05 +08:00
21pages
be06c0d738 tab border for desktop subwindow (#8842)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-26 11:20:16 +08:00
zuiyu
f0f50f0f03 fix: remove getRadio todo and delete contentPadding param (#8839) 2024-07-26 10:31:01 +08:00
bovirus
1850d32f49 Update Italian language (#8838) 2024-07-26 10:00:18 +08:00
fufesou
3999d498be fix: keep window pos after new conn (#8834)
1. Keep window pos
2. Do some init in StatefulWidget constructor.
If try init in its state class, it may be too late. Because I see the
init function is called after building the widget tree.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-25 21:52:57 +08:00
rustdesk
bbdce8d57b Send clipboard keystroke, https://github.com/rustdesk/rustdesk/discussions/5451 2024-07-25 15:21:28 +08:00
dignow
baf70da2fe fix: trackpad, reverse horizontal scroll (#8827)
Signed-off-by: dignow <linlong1265@gmail.com>
2024-07-25 14:44:58 +08:00
dignow
b967d496cc refact: init values from initState to Constractor (#8817)
* refact: init values from initState to Constractor

Signed-off-by: dignow <linlong1265@gmail.com>

* fix: move RxBool init into Constructor

Signed-off-by: dignow <linlong1265@gmail.com>

* peer sort option

Signed-off-by: dignow <linlong1265@gmail.com>

* Remove empty initState()

Signed-off-by: dignow <linlong1265@gmail.com>

---------

Signed-off-by: dignow <linlong1265@gmail.com>
2024-07-25 10:45:51 +08:00
dignow
2aef79688b fix: mobile, conn page, jump on exiting remote (#8820)
Signed-off-by: dignow <linlong1265@gmail.com>
2024-07-25 00:43:14 +08:00
dignow
0451a1c45f fix: mobile, server page, jumps on loading (#8819)
Signed-off-by: dignow <linlong1265@gmail.com>
2024-07-25 00:13:22 +08:00
dignow
f7e9057a39 fix: video service, do not skip refresh message (#8815)
Signed-off-by: dignow <linlong1265@gmail.com>
2024-07-24 21:59:25 +08:00
dignow
d73e0e1e5a fix: video service (#8812)
1. Unset refresh flag if just refreshed.
2. Reduce the scope of the lock.

Signed-off-by: dignow <linlong1265@gmail.com>
2024-07-24 18:41:52 +08:00
dignow
39dbd89287 fix: siwtching display, clear last old image (#8810)
* fix: siwtching display, clear last old image

1. Clear last old image.
2. Try refresh after switching display.
3. Add an interval check before refresh video service.

Signed-off-by: dignow <linlong1265@gmail.com>

* simple changes

Signed-off-by: dignow <linlong1265@gmail.com>

---------

Signed-off-by: dignow <linlong1265@gmail.com>
2024-07-24 17:35:06 +08:00
21pages
c04f460bbd fix more bool options (#8809)
* fix more bool options
* hide sort ab tags because it's already sorted

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-24 17:20:58 +08:00
dignow
79a1f888d6 fix: flutter remove setState in initState (#8807)
Signed-off-by: dignow <linlong1265@gmail.com>
2024-07-24 14:00:49 +08:00
21pages
57d1b1ecc4 fix nextRgba not called when switching to texture render (#8792)
Because rgba buffer render doesn't support multi display, when switch to
multi display, it is possible that rgba.valid has been set to valid but
nextRgab is not called, when switching back to single display,
rgba.valid is still true.

Fix by rgba buffer, rgba texture and  gpu texture using different messages.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-24 12:06:16 +08:00
Vasyl Gello
8a4a2b5732 Remove existing /opt/artifacts/vcpkg in sciter builds (#8804)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-24 11:23:49 +08:00
Vasyl Gello
51a60a7eed Fix arm32 sciter build (#8803)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-24 10:16:25 +08:00
bovirus
b26acde450 Update Italian language (#8800) 2024-07-24 09:41:43 +08:00
Mr-Update
2de81045ea Update de.rs (#8801) 2024-07-24 09:41:25 +08:00
Vasyl Gello
a72a8906b0 vcpkg deps (#8764)
* Revert "Revert vcpkg ffmpeg (#8751)"

This reverts commit 5c16a8302e.

* vcpkg: Reland ffmpeg and try to fix sciter build

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* vcpkg: Detect AVX2 by requiring __m256i

(ubuntu18.04 sciter)

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* Install nasm from debian buster and python3.7

... from ubuntu universe

[Skip CI]

* vcpkg: Add libyuv port with fix for windows

From
abc59feabf

Found by @deep-soft

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* Bump vcpkg baseline to 2024.07.12

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* Fix F-Droid version action

I thought the latest release will be updated by the time hook starts
but it is not the case. Get tag from GITHUB_REF instead if GITHUB_REF_TYPE
is "tag".

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

---------

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-23 23:31:36 +08:00
dignow
614086a216 fix: mobile, query onlines, on active (#8796)
* fix: mobile, query onlines, on active

Signed-off-by: dignow <linlong1265@gmail.com>

* Update peer_tab_page.dart

---------

Signed-off-by: dignow <linlong1265@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-07-23 23:28:03 +08:00
rustdesk
2ffc2ad85b enhance https://github.com/rustdesk/rustdesk-server-pro/discussions/325 2024-07-23 22:26:23 +08:00
21pages
eef091d4e8 fix hide ab tag when custom client startup (#8795)
not custom: 'Y', ''
custom: 'Y', 'N'
should not use isNotEmpty

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-23 21:35:05 +08:00
rustdesk
97f26f880b default-connect-password option 2024-07-23 10:46:50 +08:00
rustdesk
22c6f5e589 aggresive online query for self-hosting 2024-07-23 10:26:56 +08:00
お餅のCreeeper
b828768fa9 Update ja.rs and Add "About Rustdesk" (#8784)
* Update ja.rs

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>

* Update ja.rs

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>

* Add 'About RustDesk'

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>

---------

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>
2024-07-23 08:58:48 +08:00
dignow
31e7b6acf1 refact: msi, revision version (#8782)
* refact: msi, revision version

Signed-off-by: dignow <linlong1265@gmail.com>

* check revision version

Signed-off-by: dignow <linlong1265@gmail.com>

* refact: msi, default revision number

Signed-off-by: dignow <linlong1265@gmail.com>

* Simple refact

Signed-off-by: dignow <linlong1265@gmail.com>

---------

Signed-off-by: dignow <linlong1265@gmail.com>
2024-07-22 23:37:25 +08:00
21pages
14b505130b remove end slash when setting server config (#8779)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-22 17:11:32 +08:00
21pages
22f3425ace fix custom client show ip whiltelist warning (#8778)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-22 17:00:29 +08:00
rustdesk
4723d6a830 mouse wrong commit ref 2024-07-22 10:29:00 +08:00
DiegoGZ
c4f3c0f133 Update ca.rs - Added new translation into Catalan and a few corrections (#8777)
Added new translation into Catalan and a few corrections
2024-07-22 09:58:04 +08:00
Bot_wxt1221
de375c91bb fix: nixos's headless mode. (#8774) 2024-07-21 17:05:32 +08:00
dignow
d3454f07d3 fix: move some crates to rustdesk-org (#8772)
Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
2024-07-20 23:49:40 +08:00
fufesou
cf8ef2533a fix: keyboard, linux, repeated keys, #6793 (#8757)
* fix: keyboard, linux, repeat keys, #6793

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: keyboard, linux->linux, may also repeat keys with bad network

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-20 00:42:49 +08:00
お餅のCreeeper
6ad662260e Update ja.rs (#8758)
* Update ja.rs

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>

* Update README-JP.md

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>

---------

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>
2024-07-19 23:56:39 +08:00
21pages
2b54a553c7 buildin options and add to mobile (#8759)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-19 23:55:52 +08:00
rustdesk
85ded0a3e5 no idea why publish sciter windows tar.gz, remove it 2024-07-19 00:32:19 +08:00
21pages
5c16a8302e Revert vcpkg ffmpeg (#8751)
* Revert "fix linux ffmpeg link, still link ffmpeg in hwcodec (#8747)"

This reverts commit 48464835f5.

* Revert "fix linux armv7 ffmpeg arch, linux x64 sciter add hwcodec feature (#8744)"

This reverts commit b68d7a3054.

* Revert "arm linux remove cuda-llvm option (#8735)"

This reverts commit bc672b3367.

* Reapply "try fix arm64 linux ci and publish error log (#8730)" (#8732)

This reverts commit e283d33f28.

* Revert "try fix arm64 linux ci and publish error log (#8730)"

This reverts commit a4565bf0da.

* Revert "F-Droid: enable hwcodec for future builds (#8726)"

This reverts commit 188f85b042.

* Revert "remove unused vcpkg ffmpeg code (#8725)"

This reverts commit 72c96f22b6.

* Revert "install ffmpeg lib with vcpkg (#8724)"

This reverts commit 0143eaf601.

* update hwcodec to reverted

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-18 22:28:35 +08:00
21pages
04c175c62e fix custom client settings tab visibility (#8752)
* fix custom client settings tab visibility

only control with tabKeys

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix preset-user-name typo

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-18 22:27:31 +08:00
fufesou
2be05608d8 fix: windows, headless, not work when exiting RDP (#8753)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-18 22:23:45 +08:00
21pages
c3c99ba107 fix missing retry (#8750)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-18 17:16:25 +08:00
fufesou
a81d6468cc fix: desktop settings, disconnect, timeout (#8749)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-18 15:57:36 +08:00
21pages
48464835f5 fix linux ffmpeg link, still link ffmpeg in hwcodec (#8747)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-18 13:18:21 +08:00
rustdesk
edc5d86ee7 fix hide-help-cards and fix https://github.com/rustdesk/rustdesk/issues/8687 2024-07-18 11:26:11 +08:00
fufesou
e9c8ba5393 fix: macos, remove unused workaround (#8746)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-18 10:38:15 +08:00
rustdesk
a72bc0fb28 hide-help-cards 2024-07-18 09:47:04 +08:00
rustdesk
5a8c8cbf7c hide-help-cards 2024-07-18 09:47:04 +08:00
21pages
b68d7a3054 fix linux armv7 ffmpeg arch, linux x64 sciter add hwcodec feature (#8744)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-17 22:37:05 +08:00
rustdesk
9e931a6f04 change remove-preset-password-warning default value to Y 2024-07-17 21:17:54 +08:00
rustdesk
f0587796e2 OPTION_PRESET_STRATEGY_NAME, OPTION_PRESET_USERNAME 2024-07-17 20:11:30 +08:00
fufesou
875ac28ab5 fix: macos, remove useless workaround (#8738)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-17 19:13:03 +08:00
お餅のCreeeper
6821bef5e5 Update README-JP.md and ja.rs (#8737)
* Update README-JP.md

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>

* Update ja.rs

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>

* Fix README-JP.md

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>

---------

Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com>
2024-07-17 19:11:04 +08:00
rustdesk
930561f431 remove unused protobuf 2024-07-17 18:17:30 +08:00
21pages
bc672b3367 arm linux remove cuda-llvm option (#8735)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-17 17:19:21 +08:00
21pages
e283d33f28 Revert "try fix arm64 linux ci and publish error log (#8730)" (#8732)
setup a selfhost and will fix it later

This reverts commit a4565bf0da.
2024-07-17 13:35:32 +08:00
fufesou
901505e8be fix: macos, load multi dylib instances (#8731)
Multiple dylib instances will cause some global instances to be invalid.

eg. lazy_static objects in rust side, will be created more than once.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-17 11:49:11 +08:00
21pages
a4565bf0da try fix arm64 linux ci and publish error log (#8730)
FFmepg can be built on arm64 ubuntu vm, possible reason is that the
condition of nvcodec can't be satisfied. If this still can't work, nvcodec can
be removed for arm linux.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-17 11:40:50 +08:00
fufesou
092e4089c7 fix: try workaround, macos, subwindow, frozen (#8729)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-17 09:55:46 +08:00
Vasyl Gello
188f85b042 F-Droid: enable hwcodec for future builds (#8726)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-16 16:35:10 +08:00
21pages
72c96f22b6 remove unused vcpkg ffmpeg code (#8725)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-16 15:46:20 +08:00
21pages
0143eaf601 install ffmpeg lib with vcpkg (#8724)
* use vcpkg to install ffmpeg and build sdk from source, so no prebuild lib in hwcodec.
* link ffmpeg in rustdesk directly, ffmpeg can be used as basic library.
* for windows developers, `VCPKG_DEFAULT_HOST_TRIPLET` env need to be set to `x64-windows-static` during installation.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-16 15:10:24 +08:00
21pages
09466680d3 mobile virtual display, resolution menu, proxy setting (#8717)
1. Merge code of mobile and desktop virtual display menu.
2. Mobile add seperate resolution menu, only support changing
   resolutions.
3. Android add proxy setting

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-15 18:53:14 +08:00
fufesou
eec879a801 refact: macos, hide&show on leaving view (#8712)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-15 10:50:54 +08:00
21pages
3f11d9cdb6 remove Instant sub (#8714)
which cause crash when connect to windows just startup

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-15 10:49:09 +08:00
21pages
8512c2b2b0 fix lan peers batch deletion (#8715)
`peers.map((p) async {})` is not sync and peers are loaded before deletion.

```
let mut peers = config::LanPeers::load().peers;
peers.retain(|x| x.id != id);
```

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-15 10:47:19 +08:00
fufesou
e2a7e38a39 fix: build ios (#8709)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-14 15:05:56 +08:00
21pages
3a0ece1447 mobile view mode menu (#8707)
* mobile add view mode menu, disable same menus as desktop
* show forbidden cursor when not view mode && no keyboard permission &&
  not show remote cursor
* hide keyboard/mouse bottom action when keyboard disabled
* fix not listen keyboard permission change
* mobile missing changing resolution and http proxy setting, will add if
  needed.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-14 15:04:35 +08:00
rustdesk
d0a54a6cc6 hc 2024-07-14 05:47:42 +08:00
RustDesk
bed214bd37 Update playground.yml 2024-07-14 04:56:03 +08:00
RustDesk
5f31211db3 Update playground.yml 2024-07-14 04:35:23 +08:00
RustDesk
29b8875c1c Update playground.yml 2024-07-14 04:28:22 +08:00
RustDesk
3367c541b2 Update playground.yml 2024-07-14 04:08:11 +08:00
fufesou
30afe4f779 refact: seperate audio device for voice call (#8703)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-14 04:07:02 +08:00
fufesou
d18e95703e fix: mouse forward back (#8705)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-14 03:49:16 +08:00
RustDesk
9adc083def Update playground.yml 2024-07-14 03:47:15 +08:00
RustDesk
0dc664474a Update playground.yml 2024-07-14 03:46:48 +08:00
Vasyl Gello
5e8fe239fa build_fdroid.sh: Fix review points by Licaon_Kter (#8701)
* NDK should be downloaded via verified Android SDK's sdkmanager
  * vcpkg should be built from sources not prebuilt

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-13 20:46:05 +08:00
RustDesk
7a3100a87c Update playground.yml 2024-07-13 16:08:07 +08:00
RustDesk
8a1acedae5 Update playground.yml 2024-07-13 15:51:51 +08:00
RustDesk
f5bcc17636 Update playground.yml 2024-07-13 15:45:59 +08:00
RustDesk
883c630206 Update playground.yml 2024-07-13 15:35:24 +08:00
RustDesk
a95a6ab733 Update playground.yml 2024-07-13 15:24:01 +08:00
RustDesk
46605fab1b Update playground.yml 2024-07-13 14:33:17 +08:00
RustDesk
9d26fec631 Update playground.yml 2024-07-13 11:33:15 +08:00
RustDesk
294a6ce9bc Update playground.yml 2024-07-13 11:15:00 +08:00
RustDesk
183ea47ba4 Update playground.yml 2024-07-13 01:29:17 +08:00
RustDesk
06e04143a8 Update playground.yml 2024-07-13 01:26:09 +08:00
RustDesk
a532b36e28 Update playground.yml 2024-07-13 01:03:43 +08:00
fufesou
c873b69662 refact: unwrap offstage for cursor paint (#8693)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-13 00:44:08 +08:00
RustDesk
b30f84623b Update playground.yml 2024-07-12 23:44:25 +08:00
fufesou
888e993534 fix: unable to close on fullscreen (#8690)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-12 22:04:11 +08:00
rustdesk
1d59a7fe5f adjust uni_link_desktop version to make work with old flutter 2024-07-12 18:07:27 +08:00
RustDesk
2c027cdcf5 Update playground.yml 2024-07-12 17:55:25 +08:00
RustDesk
fe513dd967 Update playground.yml 2024-07-12 17:26:00 +08:00
RustDesk
d652b99d5b Update playground.yml 2024-07-12 16:33:55 +08:00
RustDesk
c2716c2509 Update playground.yml 2024-07-12 15:44:51 +08:00
21pages
821f7245b0 videotoolbox ram codec (#8683)
* h265 encoding: the second frame always fails, use repeat encoding to achieve real-time encoding
* h264 encoding: Not supported because encoding fails too frequently, about 50%, with one failure followed by another success.
* h264/h265 decoding: No issues found.
* Does not support dynamically changing the bitrate and changing the quality by resetting the encoder.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-12 11:08:51 +08:00
RustDesk
0ea88ce6ff Update playground.yml 2024-07-12 11:01:12 +08:00
RustDesk
21f41e98a0 Update playground.yml 2024-07-12 10:44:04 +08:00
RustDesk
282ea02ebf Update playground.yml 2024-07-12 10:41:13 +08:00
RustDesk
170200fa49 Update playground.yml 2024-07-12 10:14:25 +08:00
rustdesk
d8cee6507d test old flutter for mac intel freeze issue 2024-07-12 10:07:55 +08:00
fufesou
2391b18046 fix: ubuntu 18.04, service, Exec format error (#8680)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-11 23:13:41 +08:00
fufesou
b5a7165015 feat: android, controlled side, clipboard text (#8677)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-11 12:02:19 +08:00
RustDesk
ef4d84657b Revert "feat: clipboard, multi format (#8672)" (#8673)
This reverts commit 011647511c.
2024-07-11 00:36:29 +08:00
fufesou
011647511c feat: clipboard, multi format (#8672)
* feat: clipboard, multi format

Signed-off-by: fufesou <linlong1266@gmail.com>

* inline

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-11 00:05:25 +08:00
21pages
e2d217a138 fix wayland Display::all() comment (#8664)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-09 22:10:39 +08:00
21pages
f07936a911 wayland not call check_get_displays_changed_msg (#8637)
* wayland not call check_get_displays_changed_msg

For wayland, call Display::all() in video service will cause block, I
don't know the reason.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Update display_service.rs

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-07-09 18:01:30 +08:00
21pages
0bb4d43e9e add custom client option to allow remote cm modification (#8660)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-09 15:19:16 +08:00
Vasyl Gello
6f74080a2d build_fdroid: Move sudo-deps back to f-droid spec (#8659)
... as requested by linsui here:
https://gitlab.com/fdroid/fdroiddata/-/merge_requests/15343#note_1988918695

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-09 14:36:05 +08:00
rustdesk
8a370e640a add hide-username-on-card, https://github.com/rustdesk/rustdesk-server-pro/issues/284#issuecomment-2216521407 2024-07-09 13:45:54 +08:00
Vasyl Gello
d007408061 Factor out F-Droid buildscript (#8625)
Now RustDesk maintainers can make proactive changes in F-Droid
packaging script so F-Droid side is (fully) automated.

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-09 11:45:08 +08:00
fufesou
02572e9032 fix: obx, no rx value (#8652)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-08 21:51:16 +08:00
21pages
af66d2a73b main window add block mask, cm add keyboard block (#8640)
* block window body only so user can still click minisize button.
* cm doesn't show mask
* Remove focusable Offstage in tabbar_widget.dart

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-08 20:08:05 +08:00
bovirus
eb5ab4d7d9 Update Italian language (#8645) 2024-07-08 19:59:29 +08:00
rustdesk
c02b4f994a fix two tray icon on macos 2024-07-08 14:46:28 +08:00
XLion
d093fdc256 Translation (#8634)
* Update tw.rs

* Update en.rs: Using `.` instead of `,`

* Update tw.rs: Fix formatting
2024-07-08 12:10:02 +08:00
FastAct
2af799f46e Update nl.rs (#8632) 2024-07-08 12:09:50 +08:00
rustdesk
3c7e24c605 add MOUSE_MOVE_TIME to key event, may work for
https://github.com/rustdesk/rustdesk/issues/8633 when window resume via
keyboard rather than mouse
2024-07-08 11:38:42 +08:00
rustdesk
7d961d895b add assign function in device.py for user/strategy/ab 2024-07-07 16:35:57 +08:00
rustdesk
53dbc2fa6f add OPTION_DISABLE_UDP 2024-07-07 09:25:27 +08:00
rustdesk
024220e58a add more fine-grained hide options for https://github.com/rustdesk/rustdesk-server-pro/issues/276 2024-07-06 23:41:53 +08:00
rustdesk
8621b93436 add display-name option for https://github.com/rustdesk/rustdesk-server-pro/issues/277 2024-07-06 23:10:50 +08:00
rustdesk
ac88121c4a fix ci 2024-07-06 11:46:33 +08:00
Vasilis553
90df80ed78 Update el.rs (#8628)
* Update el.rs

* Update el.rs
2024-07-06 11:35:29 +08:00
rustdesk
d4f3a87276 change to remove-preset-password-warning 2024-07-05 20:09:26 +08:00
rustdesk
48efdcf1f0 add remove-preset-password-warning-of 2024-07-05 19:26:20 +08:00
fufesou
0511cdbb21 feat: clipboard svg (#8615)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-05 09:30:59 +08:00
Thadah D. Denyse
8747b9847f Add basque language (#8597)
* Add basque language

* Update lang.rs
2024-07-05 09:30:07 +08:00
RustDesk
92d0fe1c3f Revert "feat: clipboard svg (#8608)" (#8612)
This reverts commit a9015bcf70.
2024-07-04 21:31:19 +08:00
fufesou
a9015bcf70 feat: clipboard svg (#8608)
* feat: clipboard svg

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: is_last_plain, reset on clipboard event

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-04 21:23:08 +08:00
flusheDData
f8f2686267 Update es.rs (#8609)
* Update es.rs

New terms added

* Update es.rs

Mistype correction
2024-07-04 20:21:28 +08:00
solokot
c2bd1b8965 Update ru.rs (#8610) 2024-07-04 20:21:18 +08:00
Nevaran
4eeee5b7ee Update bg.rs (#8611)
Added more translated parts, some might need more context to be more accurate
2024-07-04 20:21:06 +08:00
fufesou
dfc224ec01 fix: #8599 (#8603)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-04 20:20:25 +08:00
fufesou
86ff768241 clear unwrap (#8605)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-04 20:18:53 +08:00
jxdv
94addb162b update sk && cz tr (#8595)
* update sk translations

* update cz translations
2024-07-04 11:43:09 +08:00
rustdesk
bea65f8739 fix ci 2024-07-03 21:47:13 +08:00
rustdesk
92f570831d return x11 for tty session 2024-07-03 21:37:25 +08:00
Kleofass
9349210a87 Update lv.rs (#8591) 2024-07-03 20:40:57 +08:00
Vasyl Gello
95f4274eca vcpkg: sort triplets and restore local opus port for 1.5.1 (#8585)
* ci/flutter-build: Sort out triplets in job matrices

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* Restore local port for opus 1.5.1

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

---------

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-03 17:57:58 +08:00
Vasyl Gello
a6febb2816 [WIP] Fix F-Droid version file workflow (#8583) 2024-07-03 17:12:21 +08:00
Vasyl Gello
e294dafe7c Bump vcpkg baseline to 2024.06.15 (#8582)
* res/vcpkg: drop old opus port overlay

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* res/vcpkg: bump aom to 3.9.1

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* res/vcpkg: bump libvpx to 1.14.1

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* Bump vcpkg baseline to 2024.06.15

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

---------

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-07-03 15:53:17 +08:00
21pages
d00582e929 linux x11 rgb565 capture (#8580)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-03 14:20:41 +08:00
Vasyl Gello
6d2e985593 res/vcpkg: Bump aom to 3.9.1, opus to 1.5.1 and libvpx to 1.14.1 (#8555)
* res/vcpkg: drop old opus port overlay

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* res/vcpkg: bump aom to 3.9.1

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* res/vcpkg: bump libvpx to 1.14.1

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

---------

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-03 14:12:19 +08:00
Mr-Update
182e8c4ac0 Update de.rs (#8577) 2024-07-03 11:55:59 +08:00
bovirus
40019b80f6 Update Italian language (#8576) 2024-07-03 11:55:47 +08:00
fufesou
2f40b9dc04 fix: build ios (#8570)
ld: Undefined symbols:
     _kSecMatchSubjectWholeString, referenced from:
         security_framework::item::ItemSearchOptions::search::he568de2b0004b0c0 in liblibrustdesk.a[872](security_framework-88ef6afe340eb2ab.security_framework.e4562ffe63567184-cgu.0.rcgu.o)

TODO:

There's also a warning after commit "2116fec20b5a2165df97e329c156baf308668efc".
No idea how to fix it. But it does not stop building.

ld: warning: Could not find or use auto-linked framework 'CoreAudioTypes': framework 'CoreAudioTypes' not found

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-02 15:37:45 +08:00
21pages
8602b036bd remove special treatment when only use permanent passwrod but no password set (#8566)
1. Remove special treatment when only use permanent passwrod  but no password set, it has  no need and `Connection not allowd` prompt make user confusing.
2. When only use permanent password is chosen and the permanent password
   is empty, pop up the set-password dialog, if still not set in the
   dialog, back to the old choice
3. Add cancel confirm for 2fa and telegram bot

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-02 14:32:22 +08:00
fufesou
51db8e706d fix: win, clipboard image (#8561)
The window must belong to the current thread for clipboard-master.

https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage#:~:text=The%20window%20must%20belong%20to%20the%20current%20thread.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-02 00:46:55 +08:00
21pages
a0dc38f749 try fix linux arm64 build (#8560)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-02 00:19:18 +08:00
Mr-Update
625b610cfd Update de.rs (#8559) 2024-07-02 00:18:54 +08:00
fufesou
62a8349739 fix: clipboard data, decompress, buf too small (#8556)
* fix: clipboard data, decompress, buf too small

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: compress image

Signed-off-by: fufesou <linlong1266@gmail.com>

* decompress image, use default level

Signed-off-by: fufesou <linlong1266@gmail.com>

* chore

Signed-off-by: fufesou <linlong1266@gmail.com>

* decompress, zstd::decode_all

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-07-02 00:18:38 +08:00
rustdesk
0ab500c27c bring fdroid.yml back 2024-07-01 16:50:27 +08:00
21pages
285e974d1a ci for linux x86_64 sciter deb and flatpak (#8545)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-07-01 11:01:35 +08:00
rustdesk
e71d86c124 move clipboard in common.rs to clipboard.rs 2024-07-01 02:14:58 +08:00
rustdesk
14343e89d4 fix ci 2024-07-01 01:52:39 +08:00
rustdesk
3f2dfa521c fix ci 2024-07-01 01:43:16 +08:00
rustdesk
cd73368cb9 make clipboard dropped after no sub 2024-07-01 00:24:23 +08:00
rustdesk
84b5cd70ed initialize change_count to 1 so that first data always got 2024-06-30 23:27:33 +08:00
rustdesk
01672bc697 clipboard image, not tested, todo: set_html 2024-06-30 21:57:32 +08:00
21pages
763174657b add type to all Getx put/get/delete/isRegistered (#8550)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-30 21:24:18 +08:00
rustdesk
15fa80fb26 fix ci 2024-06-30 19:17:24 +08:00
FastAct
d537e2563d Update nl.rs (#8546) 2024-06-30 19:04:22 +08:00
rustdesk
1719e478e3 remove debug code 2024-06-30 17:07:23 +08:00
rustdesk
1f129e6ef3 change update_clipboard to threaded, since creating a context may take long 2024-06-30 17:05:09 +08:00
21pages
25d0ced8ba fix ci (#8543)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-30 13:54:40 +08:00
rustdesk
2116fec20b wl-clipboard-rs 0.9 and refresh cargo.lock 2024-06-30 12:07:32 +08:00
rustdesk
1252f45506 fix 100% cpu usage of clipboard disabled on remote menu 2024-06-30 10:57:15 +08:00
fufesou
1f4c62e480 fix: linux, arboard::new(), retry (#8538)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-30 01:13:45 +08:00
fufesou
bd334769fa fix: win, virtual display, privacy mode (#8537)
Restore display settings.

Only restore by the display registry settings.

Do not change display settings twice when turning off privacy mode.

Only restore the registry values.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-30 00:44:10 +08:00
jxdv
750368af7b Update cs tr (#8536)
* update common tr

* update tips
2024-06-29 22:26:06 +08:00
jxdv
2fb35c3596 Update sk tr (#8535)
* translate normal

* translate bot tips
2024-06-29 22:16:40 +08:00
21pages
5114a9d369 shared memory size use the max resolution of all displays (#8534)
The case of setting a monitor inserted later as the primary monitor is not taken into account

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-29 21:50:21 +08:00
solokot
4b6ba7938f Update ru.rs (#8533)
* Update ru.rs

* Update ru.rs
2024-06-29 21:41:09 +08:00
Vedant
1e400d2a64 Update winget-releaser to latest (#8532) 2024-06-29 21:17:10 +08:00
fufesou
967e63266f fix: win, multi-display settings changed (#8531)
1. Windows, multi-display, as the controlled side.
2. Connect
3. Turn on privacy mode, with mode 2.
4. Turn off privacy mode.

Then the display settings are continues changed when connect &
disconnect.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-29 20:16:22 +08:00
21pages
f9b0a88213 fix switch to primary display but can't see UAC (#8527)
How to reproduce: elevate at primary display->switch to another
display->
trigger UAC->switch to primary display->can't see UAC

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-29 17:14:09 +08:00
21pages
d67afa49b4 portable service only run on main display (#8525)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-29 14:17:24 +08:00
fufesou
1fd170b089 fix: mobile more actions, check version 1.2.7 (#8526)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-29 11:50:40 +08:00
rustdesk
a632718e80 typo 2024-06-29 10:46:21 +08:00
rustdesk
9f72d05749 do not run self-host in ci commit flow 2024-06-28 23:32:23 +08:00
fufesou
c062813c6d Upgrade arboard (#8522)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-06-28 23:15:33 +08:00
21pages
3ae1638125 fix extracted forground window not foreground (#8521)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-28 22:36:29 +08:00
bovirus
96aff38862 Update Italian language (#8520) 2024-06-28 16:28:43 +08:00
rustdesk
ed3fb1efa4 bump to 1.2.7 2024-06-28 09:59:10 +08:00
fufesou
d689bbf38e refact: mobile more actions, divider (#8512)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-28 00:57:16 +08:00
fufesou
c1bbdaf9ae refact: mobile min cursor size, 12 (#8510)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-27 23:19:37 +08:00
21pages
ab9e1013b2 remove dpiAware from manifest in ci for self-extracted executable (#8508)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-27 22:23:25 +08:00
Samuel FORESTIER
e1140b1bea Tidy up configuration files UNIX permissions (#7983)
* Add missing libpam package causing build to fail using Docker image

```
wrapper.h:1:10: fatal error: 'security/pam_appl.h' file not found
```

Signed-off-by: Samuel FORESTIER <samuel+dev@forestier.app>

* Tidy up configuration files UNIX permissions

Signed-off-by: Samuel FORESTIER <samuel+dev@forestier.app>

---------

Signed-off-by: Samuel FORESTIER <samuel+dev@forestier.app>
2024-06-27 20:14:32 +08:00
21pages
cfd27c8d87 wakelock not block system sleep (#8504)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-27 20:09:51 +08:00
rustdesk
a18947eed2 fix typo 2024-06-27 18:24:04 +08:00
bovirus
f8592e0d5b Update Italian language (#8502)
* Update Italian language

* Fix typo
2024-06-27 18:10:41 +08:00
rustdesk
5bfdf05ff2 /hello rather than hello to activate bot 2024-06-27 18:01:14 +08:00
rustdesk
9e851542ec telegram bot works now 2024-06-27 17:36:50 +08:00
rustdesk
e79946b4e4 telegram bot ui settings and code sending 2024-06-27 16:18:41 +08:00
solokot
aed212d8f8 Update ru.rs (#8497) 2024-06-27 15:04:53 +08:00
fufesou
c5d3c7f390 Feat/android more actions (#8496)
* feat: android volume and power actions

Signed-off-by: fufesou <linlong1266@gmail.com>

* Add translations and refact action menus

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove divider

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: recover deleted translations

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-27 13:28:05 +08:00
fufesou
b047730830 Refact/android input changed notify clients (#8494)
* refact: android, input control changed, notify clients

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: android init input perm

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-27 13:05:45 +08:00
21pages
9c7d4ef1f7 not use nwg default features (#8492)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-27 12:11:08 +08:00
21pages
12d3c59172 windows portable loading ui (#8490)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-27 11:04:55 +08:00
Stas Solovey
ef06b7d5d0 add Belarusian locale (#8480)
* Update ru.rs

* Update ru.rs

* Update ru.rs

* Update ru.rs

* Update ru.rs

* Update ru.rs

* Update ru.rs

* Create be.rs (Belarussian locale)

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update be.rs

* Update lang.rs

* Update lang.rs

* Update be.rs

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-06-26 20:32:05 +08:00
fufesou
f17e17a6b9 fix: mobile cursor, check null (#8481)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-26 19:49:52 +08:00
rustdesk
faf363cfd2 add TelegramBot 2024-06-26 18:49:41 +08:00
fufesou
dbbd9179b7 fix: android cursor scale (#8478)
* fix: android cursor scale

Signed-off-by: fufesou <linlong1266@gmail.com>

* Min scale restriction for mobile cursor

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-26 18:42:08 +08:00
rustdesk
49f848a453 refactor 2024-06-26 16:35:29 +08:00
rustdesk
ef56aea74f fix https://github.com/rustdesk/rustdesk/issues/8479 2024-06-26 16:28:31 +08:00
rustdesk
cb5fa85ac2 build 45 2024-06-26 09:59:35 +08:00
fufesou
11bdd3cfcd fix: ios keyboard, composing input (#8471)
* fix: ios keyboard, composing input

Signed-off-by: fufesou <linlong1266@gmail.com>

* Incorrect changes

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-26 00:24:57 +08:00
fufesou
f0dcc91907 fix: wrong use of Instant sub, just after booting (#8470)
* fix: wrong use of Instant sub, just after booting

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: ThrottledInterval, first next tick

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-25 20:33:38 +08:00
21pages
c1c2d26ec7 fix, check video_queue len rather than len when refresh (#8469)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-25 18:30:32 +08:00
21pages
93133b9a6c if fps is adjusted below 30, not set fps to 30 when changing image quality (#8468)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-25 17:25:50 +08:00
fufesou
245f08055f fix: mobile, chat menu, hide after tapping (#8465)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-25 00:31:41 +08:00
fufesou
00ddd63372 fix: ios, chat window position, wrong member value (#8464)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-24 22:56:13 +08:00
21pages
1765c7bbf4 fix multi display fps control (#8455)
* Calculate fps without distinguish displays, use one fps control
  because the controlled side control fps of all displays with one FPS
  variable.
* Because all displays decode frame in one thread, when there are N
  displays, the video frames received in one second is `fps * N`, so the
  calculated decode fps should be divided by N. Because the actual
  display count is not obvious in rust, when no data frame is received for 5 seconds, the display is considered inactive, and only the active display is used as the dividend.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-24 19:41:15 +08:00
Yevhen Popok
65edd55516 Update Ukrainian translation (#8453) 2024-06-24 17:32:00 +08:00
rustdesk
4947cf8718 fix https://github.com/rustdesk/rustdesk/issues/8452 2024-06-23 22:50:54 +08:00
jxdv
65dd2b8993 update sk.rs (#8449) 2024-06-23 18:05:28 +08:00
jxdv
ef82cfa034 update cs.rs (#8450) 2024-06-23 18:05:06 +08:00
rustdesk
1a69d525af fix tile type droplist and change to build 44 2024-06-23 11:39:44 +08:00
fufesou
307827be3c fix: mobile actions hide and mobile theme (#8447)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-23 11:26:15 +08:00
fufesou
40cb59336f fix: mobile actions, position (#8446)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-23 11:06:47 +08:00
XLion
a9e0ea8520 Update tw.rs (#8444) 2024-06-23 09:09:08 +08:00
rustdesk
baeee642dd build 43 2024-06-22 20:51:41 +08:00
Mr-Update
416efe9fd3 Update de.rs (#8443) 2024-06-22 20:51:33 +08:00
solokot
8b5ac390d1 Update ru.rs (#8442) 2024-06-22 20:05:56 +08:00
rustdesk
212e8e7559 fix one missing file 2024-06-22 12:45:32 +08:00
rustdesk
41a20b50ea split web js to v1 and v2 2024-06-22 12:29:20 +08:00
21pages
3742b51d58 quality monitor, delay displays as 0 when fps is 0 (#8441)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-22 09:39:02 +08:00
bovirus
1a21dff5d4 Update Italian language (#8439) 2024-06-22 08:11:09 +08:00
fufesou
bbf7d9e08a fix: android, no voice call under android 11 (#8440)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-22 08:10:54 +08:00
21pages
ffed29e632 fix typo (#8436)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-21 23:58:00 +08:00
21pages
0f6538c1a7 add enable directx option, android software encoding half resolution option (#8435)
* add option enable directx capture screen, default true

Signed-off-by: 21pages <sunboeasy@gmail.com>

* option android software encoding half scale, check isStart flag

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-21 18:54:32 +08:00
rustdesk
ff2e055a5a use flutter 3.13 for android because its video super slow on my phone 2024-06-21 18:14:58 +08:00
rustdesk
cdf97f8717 try out 3.13.9 with master 2024-06-21 17:42:58 +08:00
rustdesk
b2af79a3c5 try out 3.16.0 2024-06-21 17:05:51 +08:00
fufesou
74cc5abd09 fix: android prompt "Failed to stop voice call" on conn ended (#8434)
* fix: android prompt "Failed to stop voice call" on conn ended

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove invalid comment

Signed-off-by: fufesou <linlong1266@gmail.com>

* Better control of voice call status

Signed-off-by: fufesou <linlong1266@gmail.com>

* Better voice call status control

Signed-off-by: fufesou <linlong1266@gmail.com>

* better end conn for voice call

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-21 16:43:54 +08:00
rustdesk
32c4712d5e fix ci 2024-06-21 12:42:27 +08:00
rustdesk
3244395bfb try 3.22.2 playground 2024-06-21 12:34:00 +08:00
rustdesk
1cb0e1ce7b try out 3.22.2 2024-06-21 12:33:24 +08:00
fufesou
42394fcbdd fix: android, two finger pan, scale (#8429)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-21 09:07:32 +08:00
rustdesk
0b32e741f7 test 3.13.9 on master for android 2024-06-21 01:29:00 +08:00
rustdesk
80c5d59916 fix ci 2024-06-21 01:13:05 +08:00
rustdesk
e95823f543 for try out flutter 3.22.2 2024-06-21 01:00:46 +08:00
rustdesk
06fe972683 try out 3.22.2 for android 2024-06-21 00:45:45 +08:00
rustdesk
3057396c02 try latest 2024-06-21 00:19:05 +08:00
rustdesk
7db9543fee change back to old settings, only use flutter 3.13.9 2024-06-21 00:15:21 +08:00
rustdesk
58d86acf0d change back to 3.16.9 2024-06-20 23:51:58 +08:00
rustdesk
859020583d fix ci 2024-06-20 23:21:08 +08:00
rustdesk
0cab620ba5 try old flutter and vcpkg 2024-06-20 23:09:38 +08:00
rustdesk
4338fcc51a check if vpckg overrided 2024-06-20 23:06:42 +08:00
rustdesk
5f6f1e8d36 use prebuilt vcpkg to overwrite 2024-06-20 22:45:07 +08:00
fufesou
a91f244f35 fix: android, touch mode, one finger pan, start pos (#8427)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-20 22:21:14 +08:00
fufesou
82bf04da81 fix: android, touch mode, correct cursor input, on soft keyboard shows (#8426)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-20 22:18:57 +08:00
rustdesk
a679e4a5e3 vcpkg_root 2024-06-20 22:16:04 +08:00
rustdesk
3c79404534 use my vcpkg 2024-06-20 21:53:43 +08:00
rustdesk
ba707d1149 try out different ndk 2024-06-20 21:28:27 +08:00
rustdesk
93d88f30b4 test more commits 2024-06-20 21:10:43 +08:00
rustdesk
f5bc136b07 fix ci 2024-06-20 20:32:00 +08:00
rustdesk
ae69cbb207 fix ci 2024-06-20 20:30:55 +08:00
fufesou
39e3da1eb0 android, secure keyboard on remote input (#8425)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-20 20:20:32 +08:00
rustdesk
c1322b47c3 more commits 2024-06-20 20:13:57 +08:00
rustdesk
67f83bd5dd more commits 2024-06-20 19:48:40 +08:00
rustdesk
e424d01f3d publish missed 2024-06-20 19:41:43 +08:00
rustdesk
a424830893 fix ci 2024-06-20 19:23:17 +08:00
rustdesk
3c5810cc01 prepare android old version test 2024-06-20 19:16:51 +08:00
21pages
30bd4e1cef update hwcodec, use ms as pts like vpx (#8422)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-20 13:57:56 +08:00
RustDesk
7956953669 Revert "fix: android, touch mode, move cursor (#8419)" (#8421)
This reverts commit dcba4615a2.
2024-06-20 12:22:36 +08:00
fufesou
dcba4615a2 fix: android, touch mode, move cursor (#8419)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-20 08:29:07 +08:00
rustdesk
0bf9de8256 also check --server in loop 2024-06-19 21:29:54 +08:00
rustdesk
77f1c7e74c add crate::platform::quit_gui(); for double sure 2024-06-19 21:21:51 +08:00
rustdesk
27478946ea open new window not always work, so give it a little time before exit 2024-06-19 21:17:26 +08:00
rustdesk
1f25a8af86 fix macos stop service on gui not restart 2024-06-19 20:23:05 +08:00
rustdesk
d75caad71f move --server check into daemon.plist 2024-06-19 19:54:30 +08:00
rustdesk
adf0226641 fix ci and make macos service time check more aggressive 2024-06-19 19:10:44 +08:00
rustdesk
137f58a84a refactor macos service for delegate again, remove runme in install service of linux 2024-06-19 18:49:49 +08:00
21pages
7c45a68870 linux install service, stop service before start (#8414)
If the stop-service option before installation is "", after
installation --sever is also started up. When clicking to
start service, restart --server to make it read the config file,
otherwise the service can't be started util --server is restarted.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-19 16:29:40 +08:00
rustdesk
99edab4b61 hide docker from tao, this may fix https://github.com/rustdesk/rustdesk/issues/8399 2024-06-19 16:25:48 +08:00
fufesou
e50b72622c fix: android, touch mode, soft keyboard, no pointer events (#8409)
* fix: android, touch mode, soft keyboard, no pointer events

Signed-off-by: fufesou <linlong1266@gmail.com>

* Reset lastIsBlocked on touch mode toggled

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: reset lastIsBlocked when updating keyHelpToolsRect

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-19 15:58:23 +08:00
rustdesk
60dc40f47f try hide docker in tao delegate because hide in rustdesk side a bit late so that still seeing it sometimes
refactor service to make it restart after login to avoid delegate caught for seconds after login
also make main windows Close event restart itself for above case
2024-06-19 15:42:53 +08:00
21pages
841c331981 fix sleep duration when receive ipc close (#8410)
unit is second

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-19 14:02:13 +08:00
21pages
4eafa5a585 fix ci (#8407)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-19 10:12:10 +08:00
rustdesk
5a740e891e make main window can be reopen if killed by --server for creating ipc 2024-06-19 09:27:29 +08:00
rustdesk
1fcc7001bd use exit(-1) in Data::Close to make sure --server can restart 2024-06-18 22:42:42 +08:00
rustdesk
e57854422a fix kill main window in --server 2024-06-18 22:04:34 +08:00
21pages
8c39979848 fix get mac display scale, find screen from display id (#8401) 2024-06-18 19:37:15 +08:00
Stas Solovey
2c38648e39 update ru.rs (#8400)
* Update ru.rs

* Update ru.rs

* Update ru.rs

* Update ru.rs

* Update ru.rs

* Update ru.rs

* Update ru.rs

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-06-18 19:36:32 +08:00
rustdesk
97aa739d69 revert https://github.com/rustdesk/rustdesk/pull/8368 2024-06-18 18:21:29 +08:00
Stas Solovey
b0042f29fb Update ru.rs (#8398)
* Update ru.rs

* Update ru.rs

* Update ru.rs
2024-06-18 17:08:44 +08:00
rustdesk
e3ca82945f fix https://github.com/rustdesk/rustdesk/issues/2680 2024-06-18 16:30:56 +08:00
rustdesk
bf6a3a7067 fix stupid flutter 2024-06-18 15:06:43 +08:00
rustdesk
d25670c79a fix https://github.com/rustdesk/rustdesk/issues/2680 2024-06-18 14:39:56 +08:00
XLion
32b26e4ad3 Update translation (#8394)
* Update tw.rs

* Update cn.rs

Add spacing
2024-06-18 09:54:03 +08:00
rustdesk
818439db48 fix ci 2024-06-18 08:43:19 +08:00
21pages
e23a9da1a8 sync get option in android setting (#8393)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-18 08:29:10 +08:00
21pages
37ebac2a9e update hwcodec, remove AVCodecParserContext (#8389)
It was used to decode different resolution with same decoder, but may
cause crash.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-17 23:53:43 +08:00
Kleofass
46bf552afc Update lv.rs (#8383) 2024-06-17 17:31:38 +08:00
rustdesk
70151e3dd8 add Push Notifications capability though we do not use it explictly, but our dep used it, have to enable it, otherwise review will refuse us 2024-06-17 12:50:29 +08:00
rustdesk
e933f0baf2 build 41 2024-06-17 11:16:33 +08:00
rustdesk
f2a612c3d9 add voice_call start_pa 2024-06-17 10:57:25 +08:00
flusheDData
4a648f0068 New terms added (#8377)
* Update es.rs

New term and tip added

* Update es.rs

change representación por renderizado (render)

* Update es.rs

New terms added
2024-06-17 10:36:10 +08:00
21pages
5b52742cf7 fix mobile ab/group not update when login with 2fa/email (#8378)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-17 10:35:57 +08:00
rustdesk
237d234277 add rustc 1.78 ABI change comment 2024-06-17 09:44:36 +08:00
rustdesk
ed0cba281f start dbus only for main 2024-06-16 23:59:09 +08:00
rustdesk
2e0eaed322 call _ffiBind.mainStartPa only for --cm 2024-06-16 23:51:00 +08:00
21pages
e2a6d66805 make mobile ab dropdown button text vertical center (#8376)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-16 23:41:40 +08:00
21pages
8d6de9ca59 opt android ab ui (#8374)
* multiline error banner
* mobile remove ab permission icons due to hard to press
* center ab dropdown button text

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-16 23:13:46 +08:00
fufesou
db108d964b fix: build 09f452b055 (#8373)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-06-16 22:37:14 +08:00
solokot
f016d453fa Update ru.rs (#8370) 2024-06-16 15:14:45 +08:00
21pages
60ea8d2c2b mac scale factor of each screen (#8368)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-16 12:01:41 +08:00
jxdv
12ff1319f1 update sk.rs (#8364) 2024-06-15 16:03:56 +08:00
jxdv
f224d8872e update cs.rs (#8365) 2024-06-15 16:03:42 +08:00
rustdesk
5cf2d5f062 change back to 1.75 since sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html 2024-06-15 14:03:33 +08:00
fufesou
92dd0ee1dd fix: non texture, multi window, switch display (#8353)
* fix: non texture, multi window, switch display

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix build

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-14 17:55:03 +08:00
bovirus
70c20fc76f Update Italian language (#8352) 2024-06-14 17:39:24 +08:00
fufesou
07e0b5ac10 fix: desktop, remote toolbar, remember collapse (#8349)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-14 00:28:59 +08:00
fufesou
12f7fc3d33 fix: push rgba only on desktop (#8348)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-13 23:22:03 +08:00
fufesou
60f47cb549 fix: desktop, remote toolbar autohide (#8347)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-13 21:04:00 +08:00
Mr-Update
d33fa3f073 Update de.rs (#8346) 2024-06-13 21:01:24 +08:00
21pages
2e4fafcf46 add missing call of androidUpdatekeepScreenOn (#8345)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-13 19:24:38 +08:00
21pages
ab451b9056 android keep screen on option (#8344)
* android keep screen on option

Keep screen on option relays on floating window.

Three options: Never, During controlled(default), During service is on

Signed-off-by: 21pages <sunboeasy@gmail.com>

* When rustdesk is in forground, be consistent with the settings

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-13 18:30:29 +08:00
fufesou
bc875a35b0 Refact/multi window soft rendering (#8343)
* refact: multi_window_soft_rendering

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: window pos, potential wait for image

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* remove debug print

Signed-off-by: fufesou <linlong1266@gmail.com>

* explicitly set rgba_data.size_got to false after init

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: multi window, merge images, render with texture

Signed-off-by: fufesou <linlong1266@gmail.com>

* revert, flutter.rs, rgba valid

Signed-off-by: fufesou <linlong1266@gmail.com>

* Add displays index before sending capture msg

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: multi window, soft rendering

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: build

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-13 18:03:41 +08:00
rustdesk
8e12a34634 upgrade our ipc-parity and tokio-socks crate to tokio 1.38 2024-06-13 13:17:34 +08:00
rustdesk
77204127f2 use latest rust for non-windows, and upgrade tokio to 3.18 which fix a mpsc channel bug 2024-06-13 13:05:35 +08:00
fufesou
65c2ccdc93 fix: try fix, macos, flutter, focus changed, no reponse (#8338)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-13 10:42:50 +08:00
fufesou
964d4f1f87 try fix cursor id, int.parse, exceeds limit (#8333)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-13 00:34:23 +08:00
21pages
f559e9c74a disable hardware encoding if encoding fails too many times (#8327)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-12 23:37:51 +08:00
21pages
610009528b hwcodec, only process that start ipc server start check process (#8325)
check process send config to ipc server, other process get config from ipc server. Process will save config to toml, and the toml will be used if the config is none.

when start check process: ipc server process start or option changed
from disable to enable

when get config: main window start or option changed from disable to
enable, start_video_audio_threads.

Only windows implements signature, which is used to mark whether the gpu software and hardware information changes. After reboot, the signature doesn't change. https://asawicki.info/news_1773_how_to_programmatically_check_graphics_driver_version, use dxgi way to get software version, it's not consistent with the visible driver version, after updating intel driver with small version change, the signature doesn't change. Linux doesn't use toml file.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-12 20:40:35 +08:00
rustdesk
0f10a88b23 remove elevation/installation requirement for --get-id 2024-06-12 20:35:04 +08:00
Kleofass
60049c8cc5 Update lv.rs (#8323) 2024-06-12 17:20:29 +08:00
rustdesk
50aa5880de always call platformFFI.nextRgba no matter what to avoid dead lock because of unknown reason 2024-06-12 02:34:15 +08:00
rustdesk
47143318ba ensure nextRgba called no matter if image created 2024-06-12 01:40:54 +08:00
fufesou
c27791a9ac comments (#8316)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-12 00:53:54 +08:00
fufesou
b19d732a3a fix: audio rechannel len (#8315)
* fix: audio rechannel len

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-12 00:32:10 +08:00
rustdesk
cd3db3a686 try to fix https://github.com/rustdesk/rustdesk-server-pro/issues/266 2024-06-11 19:51:33 +08:00
fufesou
35fb9f8897 fix: peer option, individual_windows, use 'N' instead of '' (#8307)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-10 23:01:55 +08:00
21pages
ec042434be use sihost.exe as fallback for run_as_user if no explorer.exe (#8305)
* There is no relevant information, but I found that each session has a unique sihost.exe, and the user name of the process is consistent with the user name of the session, and after using the task manager to kill this process, it will automatically restart. Checking sessionUserName=siHost UserName may be unnecessary, but since there is no evidence, check it anyway.
* GetFallbackUserPid is called only when explorer.exe does not exist.
* ProcessHacker shows that the tokens of explorer.exe and sihost.exe are the same, I know little about it.

Signed-off-by: 21pages <pages21@163.com>
2024-06-10 20:29:53 +08:00
rustdesk
f8041a3de5 fix merge problem of last commit 2024-06-10 19:53:02 +08:00
rustdesk
dd90096e13 remove useless stop-rendezvous-service 2024-06-10 16:12:08 +08:00
fufesou
9ab5512bfa fix: custom client, option to bool (#8303)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-10 11:01:39 +08:00
fufesou
32ab56f864 fix: custom client, options, option2bool() (#8302)
* fix: custom client, options, option2bool()

Signed-off-by: fufesou <linlong1266@gmail.com>

* format

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-10 00:11:59 +08:00
rustdesk
78d7bfac01 fix https://github.com/rustdesk/rustdesk/discussions/8031 2024-06-09 19:48:42 +08:00
21pages
57570c3ba6 is_ipc_file_exist quote Config::ipc_path (#8295)
Signed-off-by: 21pages <pages21@163.com>
2024-06-08 21:56:47 +08:00
21pages
ffac670f95 fix nt_terminate_process missing CloseHandle (#8294)
Signed-off-by: 21pages <pages21@163.com>
2024-06-08 21:15:01 +08:00
Yevhen Popok
be16f1be44 Update Ukrainian translation (#8293) 2024-06-08 20:51:06 +08:00
21pages
fd0f85d565 no explorer.exe, judge by pid retrived from cpp (#8291)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-08 16:50:35 +08:00
21pages
8de5f3f0d3 not close connection if failed to start cm due to no explorer.exe (#8290)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-08 16:11:51 +08:00
21pages
0bb537b872 fix kill occupied ipc process, find with enumerate, kill with NtTerminateProcess (#8289)
* I reproduced the issue, that process did't have title, couldn't be connected
  to and taskkill not work
* Test whether ipc is opccupied with enumerating named pipe
* With NtTerminateProcess, it was killed successfully.
* There is a way to find the exact process which occupy the ipc, I have
  not check it, it's from https://github.com/winsiderss/systeminformer

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-08 14:09:16 +08:00
Mr-Update
987da00be0 Update de.rs (#8286) 2024-06-08 09:43:50 +08:00
jxdv
e9e2214d29 update cs.rs (#8285) 2024-06-08 09:43:32 +08:00
jxdv
ac9f3317f1 update sk.rs (#8284) 2024-06-08 09:43:17 +08:00
bovirus
7da85d277e Update Italian language (#8282) 2024-06-07 20:48:19 +08:00
solokot
274244b055 Update ru.rs (#8281) 2024-06-07 15:49:52 +08:00
fufesou
8fa611daed refact: Wayland, not support multiple displays (#8280)
* refact: Wayland, not support multiple displays

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: Wayland disable multiple for RemoteDesktop

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-07 12:59:42 +08:00
21pages
64d0fb17f7 add floating window setting (#8279)
* Set `disable-floating-window` in client ui, it shows enabled when
  option is enabled and has floating window permission.
* Remove ignore battery setting because not work on every device.
* When the phone orientation changes, make the Y coordinate change
  proportionally, when changing back, the floating window position is still the original one.
* Add custom client option `floating-window-untouchable` to make the
  click event pass through the floating window automically. Set it untouchable automically when transparency is 0.
* On my phone, floating window size 16 no works and 32 works, so keep
  the size range [32, 320]

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-07 11:04:18 +08:00
fufesou
6d1d844b14 refact: Wayland, do not show multi displays (#8277)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-07 09:54:50 +08:00
fufesou
686dd11d8e fix: peer menu, hidden by wrong check (#8275)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-07 00:20:55 +08:00
21pages
9d42ee9df8 vram avoid always fallback to gdi (#8272)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-06 22:52:31 +08:00
21pages
9562768a04 android floating window (#8268)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-05 23:11:44 +08:00
Kleofass
54b8daede4 Update lv.rs (#8262) 2024-06-05 18:44:47 +08:00
21pages
bd51afe86c fix rustPointerInput jni parameter declaration, call new_string in with_local_frame (#8266)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-05 18:09:01 +08:00
fufesou
a84b9bd2c8 fix: setMovable only on macos (#8261)
* fix: setMovable only on macos

Signed-off-by: fufesou <linlong1266@gmail.com>

* Refact and comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* Refact comments

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-05 14:52:56 +08:00
rustdesk
ce1dac3b86 attempt to fix local reference table overflow, https://github.com/rustdesk/rustdesk/issues/4118 2024-06-05 00:38:54 +08:00
rustdesk
2dcd9f02cd bump to 1.2.6 2024-06-03 21:01:41 +08:00
rustdesk
416d57bec6 https://github.com/rustdesk/rustdesk/pull/7971 2024-06-03 13:40:14 +08:00
21pages
7d56717cf5 msgbox responds to numpadEnter as submit (#8248)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-03 11:19:35 +08:00
fufesou
32ef5f47f8 fix: macos tab drag (#8242)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-02 10:56:29 +08:00
fufesou
32346c23e0 fix: macos, terminate, close main then remote windows (#8240)
* fix: macos, terminate, close main then remote windows

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-02 10:35:54 +08:00
fufesou
b6ebf61d6c fix: macos, fullscreen, remove mimimize button (#8238)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-01 21:28:35 +08:00
fufesou
d79efcedef fix: close all windows, obfuscated window id (#8237)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-01 21:28:03 +08:00
21pages
18464ec570 windows replace uni with cmd to start up main window after installation (#8235)
This is to avoid uni link not working

Other change: The install window now has the title "appname - Install" to distinguish it from the main application window.

Signed-off-by: 21pages <pages21@163.com>
2024-06-01 20:28:21 +08:00
fufesou
ed5487a1fc refact: mobile actions (#8236)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-01 20:23:58 +08:00
fufesou
df36580451 fix: tab, disable dragging (#8228)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-01 00:10:18 +08:00
rustdesk
bd7790c1eb https://github.com/rustdesk/rustdesk-server-pro/issues/261 2024-05-31 20:58:55 +08:00
FastAct
67d66c6750 Update nl.rs (#8224) 2024-05-31 19:57:26 +08:00
21pages
1c00d7aa1a android broadcast display changed immediately when change scale (#8219)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-31 16:44:42 +08:00
21pages
68cabe596d update hwcodec, fix wrong gop, which causes FFmpeg nvenc vram encode delay (#8220)
Signed-off-by: 21pages <pages21@163.com>
2024-05-31 16:44:18 +08:00
21pages
8919ea65e3 fix, mac hwcodec decoding align use dst_align (#8215)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-30 23:40:25 +08:00
bovirus
d4dda94e2a Update Italian language (#8214) 2024-05-30 16:57:14 +08:00
XLion
ee58b37d1e Update translations (#8210)
* Update translations

Add additional hint for `texture_render_tip` to let user know that it may be an option to fix issue.

* fix formatting for tw.rs
2024-05-30 15:09:12 +08:00
rustdesk
06cb49ec71 preset-address-book-name preset-address-book-tag https://github.com/rustdesk/rustdesk-server-pro/issues/257 2024-05-30 14:27:47 +08:00
jxdv
caca7e5860 update cs.rs (#8208) 2024-05-30 08:33:32 +08:00
jxdv
3ddb4c9799 update sk.rs (#8207) 2024-05-30 08:30:54 +08:00
21pages
74c24caae9 not show paste menu when mobile control mobile (#8204)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-29 20:38:56 +08:00
æLtorio
48e8a25f6e Add missing french language tips (#8201)
* french language tips

* makes spaces conformed to original file

---------

Co-authored-by: Ronan <ronan.le_meillat@highcanfly.club>
2024-05-29 19:10:47 +08:00
fufesou
fd045043a1 fix: flutter image pub, version 3 to 4, get bytes (#8202)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-05-29 19:10:15 +08:00
Mr-Update
0e97696b47 Translation missing (#8199) 2024-05-29 17:11:13 +08:00
æLtorio
804764d529 french translations (#8198)
* french translations

* typo

---------

Co-authored-by: Ronan <ronan.le_meillat@highcanfly.club>
2024-05-29 16:52:40 +08:00
Yevhen Popok
98a38754d4 Update Ukrainian translation (#8195) 2024-05-29 15:07:37 +08:00
21pages
837382349e fix the bottom of FFmpeg vram qsv encoding being cropped (#8191)
Signed-off-by: 21pages <pages21@163.com>
2024-05-29 13:34:22 +08:00
fufesou
d2f119b85e fix: no forcely adaptive view style, if peer is android (#8189)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-05-29 12:24:33 +08:00
fufesou
89d855d085 fix: deactive widget ancester (#8187)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-05-29 08:16:36 +08:00
bovirus
1bfafaf07c Update Italian language (#8185) 2024-05-29 08:15:17 +08:00
solokot
cf4f073153 Update ru.rs (#8183) 2024-05-29 08:14:57 +08:00
flusheDData
aca9ba1a49 term change (#8181)
* Update es.rs

New term and tip added

* Update es.rs

change representación por renderizado (render)
2024-05-29 08:14:31 +08:00
flusheDData
399e20a14a Update es.rs (#8179)
New term and tip added
2024-05-28 22:33:52 +08:00
fufesou
7ca5a0b977 fix: switch texture, do not input os password (#8178)
* refact: switch texture, do not input os password

Signed-off-by: fufesou <linlong1266@gmail.com>

* Switch texture, refresh video

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-05-28 22:33:28 +08:00
Mr-Update
74d4505b3d Update de.rs (#8177) 2024-05-28 21:56:36 +08:00
rustdesk
df74a38b90 disable-group-panel for https://github.com/rustdesk/rustdesk-server-pro/issues/250 2024-05-28 20:10:12 +08:00
rustdesk
e8d02905fe pre-elevate-service for https://github.com/rustdesk/rustdesk-server-pro/issues/252 2024-05-28 19:58:47 +08:00
rustdesk
123a45149d bump to 1.2.5 2024-05-28 17:14:55 +08:00
fufesou
72ec86b58d refact: texture render as an option (#8168)
* refact: texture render as an option

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: texture render, translation

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: texture render as option

Signed-off-by: fufesou <linlong1266@gmail.com>

* Update ui_interface.rs

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-05-28 16:42:30 +08:00
21pages
010b17509a fix android get raw data (#8171)
If `std::ptr::copy_nonoverlapping` is not in `take`, it's not protected by the lock.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-28 16:35:59 +08:00
rustdesk
278d593580 adjust log file days from 7 days to 31 days 2024-05-28 15:52:50 +08:00
FastAct
f997a1ff52 Update nl.rs (#8170) 2024-05-28 15:40:04 +08:00
21pages
036d10cfbe android: for software encoding and screen size > 1200, display size reduced to half (#8167)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-28 12:43:13 +08:00
fufesou
b0c21e927b https://github.com/rustdesk-org/rustdesk_desktop_multi_window/pull/11 (#8166)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-05-28 10:30:56 +08:00
21pages
fcece3732c android request external stroage when start service for log (#8161)
This will not influence file transfer permission. Requesting external storage permissions does not turn on file transfer permissions, and turning off file transfer permissions does not turn off external storage permissions.

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-27 21:01:09 +08:00
21pages
c7308dbbc9 fix mediacodec bad encoding quality (#8159)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-27 19:34:40 +08:00
fufesou
9ce62dc584 fix: window backgroud color (#8155)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-05-27 09:29:31 +08:00
21pages
0442f7012b fix mac render memory, dispose old decoded image (#8140)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-27 09:27:30 +08:00
mehdi-song
e7f0f0ff8d Update fa.rs (#8141) 2024-05-25 09:58:53 +08:00
21pages
4fd4b24fa2 call capture_displays when no enable flutter_texture_render (#8146)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-25 09:45:12 +08:00
21pages
17d18f1dd8 impore fps control, quicker send refresh and clear video queue (#8137)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-24 17:21:16 +08:00
21pages
902e166f0c fix mediacodec check_bitrate_range not work (#8133)
should check with mc_name, and that cause the highest custom bitrate
being invalid

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-24 14:07:48 +08:00
fufesou
1efce51222 fix: restore window, on connection (#8129)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-05-23 22:11:40 +08:00
21pages
7da09f6296 sort address book name dropdown (#8127)
Signed-off-by: 21pages <pages21@163.com>
2024-05-23 17:44:48 +08:00
fufesou
b8d9c4c378 fix: mutlwindow, fullscreen (#8123)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-05-23 09:51:19 +08:00
21pages
49b0630752 fix android mediacodec encoding align (#8121)
* update ffmpeg, mediacodec encode align 64
* more d3d11 decode availablity check
* remove unused mediacodec info

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-22 20:18:09 +08:00
Yevhen Popok
91f07b4b03 Update Ukrainian translation (#8118) 2024-05-22 17:17:59 +08:00
21pages
b2f4ba0882 fix cursor menu divider and Auto (null) (#8116)
Signed-off-by: 21pages <pages21@163.com>
2024-05-22 15:07:47 +08:00
fufesou
1acd7bd19c fix: macos fullscreen state (#8110)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-21 23:38:27 +08:00
fufesou
534fc9c40c fix: linux, login wayland, server (#8111)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-21 23:35:47 +08:00
fufesou
e192f10c56 fix: proxy, username, enable (#8109)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-21 21:27:32 +08:00
fufesou
676ee99709 fix: revert window border width on Win and Mac (#8100)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-20 16:24:28 +08:00
fufesou
6e5622a97a fix: user option, enable file copy paste (#8096)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-19 20:41:19 +08:00
fufesou
d8c9250aab refact: custom client proxy (#8093)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-19 16:40:49 +08:00
fufesou
986b9fb0e0 refact: custom client, remove enable-check-update (#8092)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-19 16:32:15 +08:00
fufesou
3c502c6fc2 refact: replace some option keys (#8090)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-19 14:07:42 +08:00
fufesou
72d59af7b0 fix: popup menu, nav, pop (#8089)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-18 23:55:05 +08:00
21pages
d3eaa6600d fix, windows try kill flutter main window process only when --server's ipc is (#8086)
occupied

Signed-off-by: 21pages <pages21@163.com>
2024-05-18 23:14:42 +08:00
fufesou
96f41fcc02 refact: custom client, more advanced settings (#8085)
* refact: custom client, more advanced settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* feat: custom client, more advanced settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-18 23:13:54 +08:00
21pages
c2b7810c33 windows kill flutter main window when --server close (#8077)
Signed-off-by: 21pages <pages21@163.com>
2024-05-18 08:24:28 +08:00
fufesou
44d4e13fa7 fix: windows, restore windows pos (#8075)
* fix: windows, restore windows pos

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: restore window pos, a better way to get cur scale factor

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* chore

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-18 08:23:09 +08:00
ddsardella
1dd7cd9384 Update Dockerfile for missing pam dev lib (#8078)
After failing to build with this due to the missing pam development library package I have added it to the DockerFile for the build image
2024-05-18 08:21:40 +08:00
fufesou
8357d4675a Fix/custom client advanced settings (#8066)
* fix: custom client, advanced settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: custom client, default options

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: cargo test

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: remove prefix $ and unify option keys

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: custom client, advanced options

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* debug custom client, advanced settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* custom client, advanced settings. Add filter-transfer to display settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* custom client, advanced settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: custom client, advanced settings, codec

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: custom client, advanced settings, whitelist

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-17 14:19:11 +08:00
21pages
3a4390e0c7 fix wrong feature to call update_supported_encoding (#8067)
Signed-off-by: 21pages <pages21@163.com>
2024-05-16 16:05:18 +08:00
XLion
68fa688c96 Update tw.rs (#8065) 2024-05-16 15:48:39 +08:00
Mr-Update
42428261d7 Update de.rs (#8063) 2024-05-15 17:20:03 +08:00
flusheDData
e01b1ed04d Android new call tip (#8057)
* Update es.rs

New terms added

* Update es.rs

New tip added

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-05-15 09:42:07 +08:00
Mikhail Samodurov
4e5dcd827b CI: Remove unused apt package (#8048)
* Update bridge.yml

Removed apt instructions for metapackages clang and llvm in favor of specific version package

* Replaced apt instructions to use specific llvm and libclang version
2024-05-15 07:38:31 +08:00
flusheDData
e8003510ef Update es.rs (#8047)
New terms added

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-05-15 07:15:26 +08:00
bovirus
da23e26a70 Update Italian language (#8052) 2024-05-15 07:12:33 +08:00
jxdv
c5b781fb02 update sk.rs (#8053) 2024-05-15 07:12:14 +08:00
jxdv
3bb1c22f49 update cs.rs (#8054) 2024-05-15 07:11:46 +08:00
fufesou
53647fd58e fix: mobile chat icon (#8041)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-14 15:35:34 +08:00
fufesou
0500bf070e refact: android audio input, voice call (#8037)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-14 09:20:27 +08:00
21pages
d70b0cdd4f Not require both max-width and max-height of mediacodec larger than (#8036)
screen width and screen height

* Only use hardware codec, when api < 29, judge with codec name prefix.

Signed-off-by: 21pages <pages21@163.com>
2024-05-13 20:24:50 +08:00
21pages
7e09809ad8 auto codec, h265 > h264 > vp9/vp8 (#8032)
Signed-off-by: 21pages <pages21@163.com>
2024-05-13 16:27:48 +08:00
21pages
a7499c2de8 add ffmpeg mediacodec h264/h265 encode (#8028)
* Check available when app start from kotlin via get codec info
* For latency free, repeat encode 10 frame at most when capture return WouldBlock
* For changing quality, kotlin support but jni doesn't support, rerun video service when quality is manualy
  changed
* 3 or 6 times bitrate for mediacodec because its quality is poor

Signed-off-by: 21pages <pages21@163.com>
2024-05-13 12:39:04 +08:00
rustdesk
4c99b8c70e upgrade tokio to 3.17 for a windows named pipe race condition,
https://github.com/tokio-rs/mio/pull/1760, https://github.com/tokio-rs/tokio/issues/6369
2024-05-12 14:08:21 +08:00
XLion
8b6913d31f Update tw.rs (#8022) 2024-05-12 10:49:49 +08:00
rustdesk
97f0642a8b remove fdroid, no idea why do we need this. 2024-05-11 16:27:44 +08:00
rustdesk
a04dd6ad31 split fdroid ci out 2024-05-11 16:24:04 +08:00
rustdesk
9827c76514 fix ci 2024-05-11 16:17:55 +08:00
21pages
712bfbae92 fix, not unregister gpu texture when closeSession is false (#8018)
Signed-off-by: 21pages <pages21@163.com>
2024-05-11 11:11:12 +08:00
jxdv
0152e937ec update cs.rs (#8013) 2024-05-11 08:55:30 +08:00
jxdv
c066dc8c24 update sk.rs (#8012) 2024-05-11 08:55:16 +08:00
Kleofass
0b96b3f345 Update lv.rs (#8010) 2024-05-11 08:54:59 +08:00
Mr-Update
b5781933b6 Update de.rs (#8007) 2024-05-11 08:54:44 +08:00
fufesou
d851bf8b69 fix: move tab to new window, black screen (#8016)
* fix: move tab to new window, black screen

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: can remove

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-11 08:54:17 +08:00
fufesou
b6d6a4360f fix: linux resizable (#8015)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-11 08:53:19 +08:00
fufesou
69b11e8dc6 fix: linux, custom client, incoming only, resizable (#8005)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-10 16:40:29 +08:00
rustdesk
6e78037770 fix linux custom client tray 2024-05-10 15:12:07 +08:00
fufesou
bed0375f45 fix: android, stop capture, release surface (#8004)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-10 14:37:00 +08:00
rustdesk
42d723f82f less cpu usage of sciter version build 2024-05-10 14:19:06 +08:00
rustdesk
fb0ce8c974 for saving disk space 2024-05-10 14:03:40 +08:00
21pages
d55770f12b remove android resolution scale (#8002)
Signed-off-by: 21pages <pages21@163.com>
2024-05-10 10:18:20 +08:00
fufesou
125c275623 fix: android media project dialog every conn on Android 14 (#7997)
* fix: android media project dialog every conn on Android 14

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* comments

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* comments

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-09 23:33:34 +08:00
21pages
c640f7ed12 fix android crash caused by outdated video frame ptr (#7996)
Signed-off-by: 21pages <pages21@163.com>
2024-05-09 22:53:01 +08:00
fufesou
73662ed7d9 fix: linux, custom client, incoming, window size (#7995)
* fix: linux, custom client, incoming, window size

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* comments

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-09 22:51:53 +08:00
21pages
a595d83232 fix android possible waiting for image issue (#7994)
Signed-off-by: 21pages <pages21@163.com>
2024-05-09 18:59:51 +08:00
solokot
6743b4f290 Update ru.rs (#7993) 2024-05-09 18:09:44 +08:00
rustdesk
e8ecb738d0 remove msi beta 2024-05-09 18:09:17 +08:00
bovirus
9c20968e05 Update Italian language (#7992) 2024-05-09 16:31:04 +08:00
rustdesk
9f80690d8a fix flatpak 2024-05-09 16:09:19 +08:00
rustdesk
e58a95e7d9 fix flatpak 2024-05-09 16:03:12 +08:00
fufesou
d9b96d2f31 fix: build (#7989)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-09 11:13:35 +08:00
fufesou
bbe9017318 fix: wayland delete restore token (#7988)
* fix: wayland delete restore token

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Wayland close session when clearing restore token

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix build

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Refact Wayland option

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Wayland clear screen selection, fake token

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix build web

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: build

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* chore

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-09 11:03:59 +08:00
21pages
a31a68ba17 If there is hardware ram encoder as fallback, not require all adapters (#7987)
support the codec format

Signed-off-by: 21pages <pages21@163.com>
2024-05-09 09:02:25 +08:00
bovirus
9d56e29a09 Update Italian language (#7984) 2024-05-09 07:33:12 +08:00
Mr-Update
32fca6f9b3 Update de.rs (#7982) 2024-05-09 07:32:54 +08:00
Kleofass
72c2de575a Update lv.rs (#7979) 2024-05-09 07:32:38 +08:00
rustdesk
93d4987dcc remove package to image 2024-05-08 21:29:33 +08:00
rustdesk
2a25034039 fix flatpak 2024-05-08 20:45:09 +08:00
21pages
5ba1c2587d opt supported encoding update, consider multi connections (#7978)
Signed-off-by: 21pages <pages21@163.com>
2024-05-08 20:31:39 +08:00
rustdesk
1b0a3e610e refactor flatpak/appimage deb path 2024-05-08 20:07:57 +08:00
RustDesk
2820f3f798 Update flutter-build.yml 2024-05-08 18:09:29 +08:00
21pages
09f3850250 installed windows client save incoming recording to a specific directory (#7974)
Signed-off-by: 21pages <pages21@163.com>
2024-05-08 17:04:53 +08:00
RustDesk
35832f8f7f Revert "fix: bump winget releaser version & add dependabot (#4848)" (#7971)
This reverts commit 7466d8cb3a.
2024-05-08 14:19:09 +08:00
sitiom
7466d8cb3a fix: bump winget releaser version & add dependabot (#4848)
* fix: bump winget releaser version

* ci: add dependabot
2024-05-08 14:13:55 +08:00
21pages
ea3786457b cm window always on top (#7953)
Signed-off-by: 21pages <pages21@163.com>
2024-05-08 12:08:37 +08:00
fufesou
01322146c0 refact: window frame border (#7946)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-08 09:59:05 +08:00
bovirus
26adc557bf Update Italian language (#7943) 2024-05-08 09:52:04 +08:00
jxdv
36e52d8165 update sk.rs (#7949) 2024-05-08 09:51:50 +08:00
jxdv
4c0211fa23 update cs.rs (#7950) 2024-05-08 09:51:33 +08:00
fufesou
a3b4dcf762 fix: wayland, accept conn (#7951)
Show prompt window after the controlled side accepting the connection.

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-08 09:50:55 +08:00
fufesou
5537c525ca refact: comments, wayland, restore token (#7947)
We cannot store restore_token if the server is not running.

Because `org.freedesktop.portal.RemoteDesktop` does not support persist
mode for now.

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-08 09:49:16 +08:00
21pages
3746fd88b5 Show current codec in menu when auto codec is chosen (#7942)
* change negotiated codec name to negotiated codec format

Signed-off-by: 21pages <pages21@163.com>

* fallback to vp9 directly if failed to create encoder

Current fallback method is clear hwcodec config

Signed-off-by: 21pages <pages21@163.com>

* show current codec in menu when auto codec is chosen

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
2024-05-07 20:34:23 +08:00
21pages
e373144350 android 13+ request notification permission when start service (#7941)
Signed-off-by: 21pages <pages21@163.com>
2024-05-07 18:45:05 +08:00
fufesou
3be4bfc821 fix: ci, ios (#7940)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-07 16:53:57 +08:00
fufesou
2c1595d0d5 fix: voice call, select audio input device (#7922)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-07 16:18:48 +08:00
fufesou
f08933f93c fix: translate, proxy (#7923)
* fix: translate, proxy

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: proxy server, change placeholder to tooltip

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-07 15:21:38 +08:00
21pages
51813b7d7e set vp9 as auto codec (#7928)
Signed-off-by: 21pages <pages21@163.com>
2024-05-07 15:20:55 +08:00
21pages
4e30418f79 disable av1 for all 32 bit platforms (#7921)
Signed-off-by: 21pages <pages21@163.com>
2024-05-06 23:09:09 +08:00
21pages
0af370d736 x86 sciter set not support av1 due to slow (#7920)
Signed-off-by: 21pages <pages21@163.com>
2024-05-06 22:03:49 +08:00
rustdesk
ac04a032ad more for custom client of linux 2024-05-06 22:02:13 +08:00
21pages
f6223a6f71 dropdown menu for tabs that cannot be displayed (#7918)
* The visibility threshold is 75%
* Used in remote, file transfer, port forward and cm

Signed-off-by: 21pages <pages21@163.com>
2024-05-06 17:10:02 +08:00
fufesou
937cea5a01 refact: remove virtual_display_driver (#7915)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-06 15:26:21 +08:00
fufesou
846522037f fix: mstsc switch, plug in virtual displays (#7913)
* fix: mstsc switch, plug in virtual displays

Signed-off-by: fufesou <shuanglongchen@yeah.ne>

* Update display_service.rs

---------

Signed-off-by: fufesou <shuanglongchen@yeah.ne>
Co-authored-by: fufesou <shuanglongchen@yeah.ne>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-05-06 14:01:59 +08:00
fufesou
b5a88d00af fix: headless, plug in multiple virtual displays (#7912)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-06 13:34:31 +08:00
bovirus
7391271107 Update Italian language (#7910) 2024-05-06 10:59:55 +08:00
21pages
991694aca5 update hwcodec, qsv support changing bitrate (#7911)
Signed-off-by: 21pages <pages21@163.com>
2024-05-06 10:59:25 +08:00
rustdesk
29b13d19d6 modify GIO_MODULE_DIR 2024-05-05 23:33:58 +08:00
rustdesk
b0ff74c799 fix appimage 2024-05-05 16:44:59 +08:00
rustdesk
75c60660e7 remove unused code 2024-05-05 15:47:17 +08:00
rustdesk
eacc840c54 to make server work on wayland, have to use host's gstreamer 2024-05-05 15:46:30 +08:00
rustdesk
6d09c0adcf fix ci 2024-05-04 19:34:10 +08:00
rustdesk
fe1cf1105a fix ci 2024-05-04 19:10:32 +08:00
rustdesk
9454334ddd try out APPRUN_LD_LIBRARY_PATH 2024-05-04 18:29:22 +08:00
rustdesk
cc482c589c XDG_SESSION_TYPE as final backup 2024-05-04 17:22:43 +08:00
fufesou
b387bc1038 fix: linux, check load dylib in main.cc (#7901)
* fix: linux, check load dylib in main.cc

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Better error message

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact error message

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Better error message

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* check if "libnsl.so.1" is required, mainly for AppImage and yum

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-04 14:07:29 +08:00
fufesou
890b735985 fix: test case, test_if_valid_server (#7903)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-04 14:06:41 +08:00
XLion
eb81e00d20 Add /r tip at id_input_tip (#7900) 2024-05-03 23:45:00 +08:00
fufesou
c619bfc05c fix: flutter, process hangup on loading dylib failed (#7898)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-03 22:58:49 +08:00
rustdesk
b5935eb4c5 fix ci 2024-05-03 16:12:36 +08:00
rustdesk
73b3023c2d do not run ci if update res/ 2024-05-03 14:37:01 +08:00
21pages
4cf709fc26 update hwcodec, build linux ffmpeg lib on ubuntu 18 (#7897)
* Build linux ffmpeg lib on ubuntu 18 container
* Fix ffmpeg ram nvnec not work if the display connected to nvidia gpu
  is not the primary display
* Fix windows mfx lib link error if vram feature is not enabled.

Signed-off-by: 21pages <pages21@163.com>
2024-05-03 13:05:21 +08:00
rustdesk
b471b934f2 fix service unit again 2024-05-03 04:51:01 +08:00
rustdesk
abbd4c8934 fix ci 2024-05-03 03:24:22 +08:00
rustdesk
ed8c852d35 fix ci 2024-05-03 03:12:47 +08:00
rustdesk
c3598b3513 fix ci 2024-05-03 02:57:24 +08:00
rustdesk
b5f6a9c91a fix one flatpak feature missing 2024-05-03 02:52:41 +08:00
rustdesk
d18810b612 fix appimage --cm 2024-05-03 02:13:44 +08:00
rustdesk
3c17d713b3 try out APPIMAGE and ARGV0 2024-05-03 01:50:58 +08:00
flusheDData
d46b2d52ce New terms (#7894)
* Update es.rs

New terms added

* Update es.rs

A slight correction
2024-05-03 01:40:02 +08:00
rustdesk
e07d0b3b25 refactor 2024-05-03 00:42:20 +08:00
rustdesk
2ff3b24b33 fix --server not killed after service stop or uninstallation 2024-05-03 00:36:29 +08:00
rustdesk
115fb05c85 SPA_PLUGIN_DIR works for finding support/libspa-support.so, I tested
manually, though crash because used spa0.2
2024-05-02 22:41:13 +08:00
rustdesk
1e7e72883d fix libspa-support.so 2024-05-02 20:50:06 +08:00
rustdesk
1edb7639f6 try libspa-support again 2024-05-02 20:35:09 +08:00
rustdesk
33c5ceecec pibpipewire-0.2-1 2024-05-02 19:47:56 +08:00
rustdesk
a8a7f306fe fix ci 2024-05-02 19:32:53 +08:00
rustdesk
9888e51b93 fix ci 2024-05-02 19:32:00 +08:00
rustdesk
c3124314ef try to fix libspa-support.so not found in arm64 appimage when connection
started
2024-05-02 18:47:39 +08:00
rustdesk
975cd183bc fix ci 2024-05-02 18:19:36 +08:00
rustdesk
aa9da0b0c8 fix ci 2024-05-02 18:14:42 +08:00
rustdesk
d83b106607 fix ci 2024-05-02 17:12:05 +08:00
rustdesk
1a0a6cedc7 fix ci 2024-05-02 17:06:50 +08:00
rustdesk
d4e1a83a5a fix ci 2024-05-02 17:06:01 +08:00
rustdesk
03e93d546a fix ci 2024-05-02 17:03:20 +08:00
rustdesk
a1b957b0f7 fix ci 2024-05-02 16:51:37 +08:00
rustdesk
eeffb11841 fix ci 2024-05-02 16:50:11 +08:00
rustdesk
860dc7dbc2 fix ci, we have no arm64 makepkg image 2024-05-02 16:02:22 +08:00
rustdesk
220f267976 no need to publish deb in unsigned any more since we removed
appimage/flatpak/headless feature
2024-05-02 15:28:04 +08:00
rustdesk
b5bd6e008b fix ci 2024-05-02 14:57:17 +08:00
rustdesk
9d20bc6441 fix ci 2024-05-02 14:23:59 +08:00
rustdesk
1c36734092 fix ci 2024-05-02 14:23:27 +08:00
rustdesk
9102b6e6b8 fix ci 2024-05-02 13:54:09 +08:00
rustdesk
cd78fa9762 fix ci 2024-05-02 13:46:01 +08:00
rustdesk
407e2d4b7b fix ci 2024-05-02 13:45:14 +08:00
rustdesk
62d6ee46c4 fix ci 2024-05-02 13:36:48 +08:00
21pages
f15bf8166f fix visibilty of address book menu divider (#7890)
* fix visibilty of address book menu divider

Signed-off-by: 21pages <pages21@163.com>

* update hwcodec, fix nvenc use d3d11 without judging windows or linux

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
2024-05-02 13:24:33 +08:00
rustdesk
7ce0b225ef remove linux_headless/appimage/flatpak feature 2024-05-02 13:23:32 +08:00
21pages
f853b29fd9 fix list type peer view (#7887)
* Layout list type peer view with layout rather than calculated width
* Hide menu sync from recent and web console if no write permission
* Pull ab twice because it's force updated when refreshCurrentUser is called on startup and
  connection status changing
2024-05-01 23:38:39 +08:00
rustdesk
f9cc8de93e fix ci 2024-05-01 21:42:21 +08:00
rustdesk
a924a876ae fix app name refactor 2024-05-01 21:29:11 +08:00
rustdesk
1f15d2c66e also --disable-flutter-texture-render for mac arm 2024-05-01 20:49:10 +08:00
rustdesk
ffd8354208 optimize ci 2024-05-01 19:40:30 +08:00
21pages
b3b1f4664c update hwcodec, fix wrong linux-x86_64 ffmpeg lib (#7883)
Signed-off-by: 21pages <pages21@163.com>
2024-05-01 19:15:21 +08:00
rustdesk
03410ded9a fix ci 2024-05-01 18:53:50 +08:00
rustdesk
09c9671376 fix ci 2024-05-01 16:38:04 +08:00
rustdesk
736b3b93e9 r26b -> r26d 2024-05-01 14:52:02 +08:00
rustdesk
4a71ca6b81 fix ci 2024-05-01 14:23:08 +08:00
RustDesk
a828f9b1f9 Update build-macos-arm64.yml 2024-05-01 14:06:44 +08:00
rustdesk
7ce5f8e8b6 fix ci 2024-05-01 13:05:50 +08:00
rustdesk
08b7e036b4 test vcpkg new in android 2024-05-01 12:58:27 +08:00
rustdesk
b1722a085e fix ci 2024-05-01 12:35:58 +08:00
rustdesk
9d985585b9 test android ci 2024-05-01 12:25:53 +08:00
rustdesk
de057b7234 enable makepkg for aarch64 2024-05-01 10:35:19 +08:00
rustdesk
04ec49e18e change to jobs 3 for selfhost linux arm 2024-05-01 10:20:44 +08:00
21pages
34c7c25908 update hwcodec, add windows ffmpeg vram encoding (#7876)
* windows add ffmpeg vram encoding
* windows add missing nvenc and qsv ram encoding, linux add vaapi, current codec table:  https://github.com/21pages/hwcodec?tab=readme-ov-file#codec

Signed-off-by: 21pages <pages21@163.com>
2024-05-01 00:07:09 +08:00
rustdesk
f74374e759 refactor ci 2024-04-30 23:19:59 +08:00
rustdesk
cce74f5631 fix ci 2024-04-30 23:14:37 +08:00
fufesou
003b9e48e1 fix: macos, crash, key input. --server also has UI. (#7878)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-30 22:53:42 +08:00
rustdesk
28bf73cd5a disable flutter texture render of osx intel 2024-04-30 22:50:59 +08:00
rustdesk
511b982dce --jobs=1 for arm64 linux selfhost 2024-04-30 22:42:09 +08:00
fufesou
dcd176f95c fix: keyboard input method, for flutter input (#7875)
* fix: keyboard input method, for flutter input

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* comment

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-30 21:06:24 +08:00
fufesou
d394aa8a15 feat: msi reg QuietUninstallString (#7873)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-30 17:42:28 +08:00
rustdesk
c8b35d5ce1 fix ci 2024-04-30 16:27:44 +08:00
fufesou
6cd107c3e3 Fix/msi service (#7872)
* refact: msi service, shell execute control

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: msi, skip adding rules when uninstalling

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* line indentation

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-30 15:57:12 +08:00
rustdesk
a884e32816 fix ci 2024-04-30 15:50:02 +08:00
rustdesk
45d2d3cbb5 refactor unsigned to prepare for flathub 2024-04-30 15:47:36 +08:00
rustdesk
382f75d249 shorten github ci time 2024-04-30 12:55:08 +08:00
rustdesk
e90010f0c0 no flatpak build for no upload 2024-04-30 12:44:10 +08:00
rustdesk
d6aaed1d82 fix ci 2024-04-30 12:03:53 +08:00
rustdesk
5082ed3b9e test flatpak arm64 2024-04-30 12:02:20 +08:00
rustdesk
a858e07db0 try out newer flatpak since error of "error: Nothing matches org.freedesktop.Platform in remote flathub" 2024-04-30 11:58:48 +08:00
rustdesk
df8f785731 rename some ci 2024-04-30 09:16:22 +08:00
rustdesk
332674a4a1 refactor linux build ci 2024-04-30 09:08:32 +08:00
fufesou
a686849bb6 fix: msi, delete service (#7867)
* fix: msi, delete service

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: msi, replace 1060 to ERROR_SERVICE_DOES_NOT_EXIST

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-30 08:21:34 +08:00
rustdesk
54d041701c print dlopen error 2024-04-29 18:00:34 +08:00
rustdesk
496eec17a1 add libc6:arm64 to same to x64 2024-04-29 15:43:57 +08:00
rustdesk
25eb67c582 try out ubuntu 20.04 for appimage arm64 2024-04-29 15:40:55 +08:00
XLion
a26fab3cce Update translation (#7861) 2024-04-29 14:42:26 +08:00
Mateusz Prais
7cb67cf8fb Add missing translations, improve wording and fix typos (#7860) 2024-04-29 11:26:46 +08:00
rustdesk
96ec1e937f libnsl.so.1 is included in libc6(already added), but it did not work, we
still need to install libnsl in fedora, however, installing libnsl on
x64 works, but on arm64 crashed (though no librustdesk.so any more)
2024-04-28 23:06:37 +08:00
rustdesk
b6616ed2cc change to libnsl 2024-04-28 21:12:04 +08:00
rustdesk
7e263af75f VideoConnCount for future use 2024-04-28 21:08:49 +08:00
rustdesk
b4c51f3d41 add libnsl2 for fedora 40 2024-04-28 20:24:39 +08:00
fufesou
1dfbaa1e02 fix: test if valid server, control if try test with proxy (#7858)
* fix: test if valid server, control if try test with proxy

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: build

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-28 14:22:21 +08:00
21pages
bd717349a7 update hwcodec, fix screen jitter (#7857)
Signed-off-by: 21pages <pages21@163.com>
2024-04-28 13:26:55 +08:00
rustdesk
99d7752e25 try libcanberra-gtk3-module 2024-04-28 12:58:15 +08:00
fufesou
22e58d7623 fix: test if valid server (#7856)
* fix: #7853, test if valid server

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact comments

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-28 12:17:39 +08:00
rustdesk
30ad142868 change to github m1 instead of selfhost m1 2024-04-28 12:14:00 +08:00
rustdesk
e084ff4f7b x64 mac 2024-04-28 11:43:53 +08:00
rustdesk
3bbec4081a add gtk modules 2024-04-28 10:44:14 +08:00
21pages
45137d5506 fix switch to setting page (#7849)
Signed-off-by: 21pages <pages21@163.com>
2024-04-27 23:24:07 +08:00
21pages
e9d9a656ab fix mouse wheel scroll up not work when controlling mac hidpi (#7848)
Signed-off-by: 21pages <pages21@163.com>
2024-04-27 17:31:35 +08:00
jxdv
ade458b820 update cs.rs (#7847) 2024-04-27 16:42:17 +08:00
jxdv
b022dcbb70 update sk.rs (#7846) 2024-04-27 16:40:24 +08:00
fufesou
b403a7a25d fix: scroll percent is auto reset after detecting displays change (#7845)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-27 15:01:21 +08:00
fufesou
a6632632fa fix: multi-window, click-move (#7844)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-27 13:45:44 +08:00
bovirus
b863ea51ad Update Italian language (#7838) 2024-04-27 13:13:21 +08:00
fufesou
796b66b057 fix: multi-window, init perms (#7839)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-26 22:34:11 +08:00
21pages
2626dcbc5f fix black screen issue when controlling the second screen on versions that lack multiple display support while using vram decoding (#7836)
* avoid create unnecessary video decoder

Signed-off-by: 21pages <pages21@163.com>

* controlled side uses the most frequent selected codec

Signed-off-by: 21pages <pages21@163.com>

* fix black screen when control old version's second screen

For versions that do not support multiple displays, the display parameter is always 0, need set type of current display

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
2024-04-26 19:42:47 +08:00
rustdesk
105a758914 disable flutter build of ios, since ios sdk not installed yet 2024-04-26 19:16:52 +08:00
rustdesk
98cce186c7 fix ci 2024-04-26 19:07:16 +08:00
rustdesk
474e13f8b9 move ios ci to arm64 mac 2024-04-26 19:00:38 +08:00
rustdesk
09f87238dc vcpkg -> FLUTTER_ELINUX_COMMIT_ID, and remove FLUTTER_ELINUX_COMMIT 2024-04-26 18:15:16 +08:00
fufesou
2b3f87d6f2 fix: msi, custom client, license (#7834)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-26 13:49:53 +08:00
LelieL91
9d3c823603 Update it.rs (#7833)
Fix typo
2024-04-26 11:57:26 +08:00
Kleofass
7e00d70f4d Update lv.rs (#7832) 2024-04-26 11:57:15 +08:00
Mr-Update
0828f747e3 Update de.rs (#7831) 2024-04-26 11:57:05 +08:00
rustdesk
d7de0c2578 improe job 2024-04-26 11:54:31 +08:00
fufesou
4dfc82f684 fix: msi, app icon (#7830)
* fix: msi, app icon

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: check if icon.ico exists

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-25 22:35:51 +08:00
solokot
92748de7d4 Update ru.rs (#7827) 2024-04-25 20:17:25 +08:00
21pages
0e7e27f99d update hwcodec, fix gpu/cpu stuck caused by nv codec (#7826)
* Disable all nv codec encoding on windows except nv sdk encoding,
  because it doesn't use CUContext
* Keep nv codec on linux, because I didn't reproduce the stuck on it
* Add ffmpeg d3d11 vram decoding

Signed-off-by: 21pages <pages21@163.com>
2024-04-25 20:16:48 +08:00
FastAct
23147f2328 Update nl.rs (#7824) 2024-04-25 19:04:48 +08:00
Sahil Yeole
3811f41076 Feat: Follow remote cursor and window focus | Auto display switch (#7717)
* feat: auto switch display on follow remote cursor

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* feat: auto switch display on follow remote window focus

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix build and remove unused imports

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix build

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix build

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix linux get_focused_window_id

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* lock show remote cursor when follow remote cursor is enabled

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix config

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* prevent auto display switch on show all display and displays as individual windows

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix options

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix options

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* remove unused function

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* remove unwraps and improve iterations

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* set updateCursorPos to false to avoid interrupting remote cursor

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* update lang

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix web build

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* update checks for options and enable in view mode

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* use focused display index for window focus service

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* use window center for windows display focused

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* remove unused imports

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix build

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* use libxdo instead of xdotool

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix multi monitor check

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* enable show cursor when follow cursor is default

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* remove show_all_displays,use runtime state instead

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix show cursor lock state on default

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* remove view mode with follow options

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix build

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* use separate message for follow current display

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix options

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* sciter support for follow remote cursor and window

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* add check for ui session handlers count

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* use cached displays and remove peer info write

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* No follow options when show all displays

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* No follow options when multi ui session

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* turn off follow options when not used|prevent msgs

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* use window center for switch in linux

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* use subbed display count to prevent switch msgs

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix build

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix web build

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* move subbed displays count

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* fix build

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* add noperms for window focus

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* add subscribe for window focus

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* remove window_focus message and unsub on multi ui

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* add multi ui session field

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

---------

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-04-25 13:26:02 +08:00
yuluo
43a0a4f8e0 fix: http/https proxy (#7821)
* add http(s) proxy

* Add front-end translation

* fix ui description

* For linux platform, add rustls support

* fix: Fix the proxy address test function.

* add: Added default prompts for agency agreement and some multi-language translations

* add: Http proxy request client

* fix: add async http proxy func and format the code

* add: Preliminary support for flutter front-end calling rust back-end http request

* Optimize HTTP calls

* Optimize HTTP calls

* fix: Optimize HTTP requests, refine translations, and fix dependencies

* fix: Win and macOS compilation errors

* fix: web platforms

* fix: Optimize import

* fix: Fix web platform issues

* fix: Fix web platform issues

* fix: update ci

* fix: test ci

* test: test CI

* Revert "fix: update ci"

This reverts commit 2e5f247b2e.

* test: test CI

* test: test CI

* fix: fix lock file

* fix: Optimize imports
2024-04-25 11:46:21 +08:00
whosehang
66d1af63b0 chore: fix some typos in comments (#7814)
Signed-off-by: whosehang <whosehang@outlook.com>
2024-04-24 14:16:19 +08:00
Cong Zhang
bc68f400f6 fix: add support for headless in docker (#7811) 2024-04-24 09:47:45 +08:00
rustdesk
67ee3a5a67 refresh_process_specifics 2024-04-23 20:26:21 +08:00
fufesou
b407893db5 refact: win idd, x86 on x64 (#7796)
* refact: win idd, x86 on x64

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* comments

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* typo

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: win idd, check if x64 and deviceinstaller64 exits

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: win idd

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: win idd, add logs

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-23 17:00:41 +08:00
fufesou
1b4a41b522 fix: portable, clear old files (#7802)
* fix: portable, clear old files

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix build

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-23 16:54:43 +08:00
RustDesk
ac7f8a6447 Revert "http/https proxy (#7600)" (#7801)
This reverts commit da57fcb641.
2024-04-23 15:26:16 +08:00
yuluo
da57fcb641 http/https proxy (#7600)
* add http(s) proxy

* Add front-end translation

* fix ui description

* For linux platform, add rustls support

* fix: Fix the proxy address test function.

* add: Added default prompts for agency agreement and some multi-language translations

* add: Http proxy request client

* fix: add async http proxy func and format the code

* add: Preliminary support for flutter front-end calling rust back-end http request

* Optimize HTTP calls

* Optimize HTTP calls

* fix: Optimize HTTP requests, refine translations, and fix dependencies
2024-04-23 15:00:23 +08:00
rustdesk
f11c332cb4 continue to remove --service runtime 2024-04-23 13:52:36 +08:00
flusheDData
3b4006b821 New terms added (#7797)
* Update es.rs

tip translation added

* Update es.rs
2024-04-22 23:01:00 +08:00
rustdesk
4c62d8c1b2 optimize runtime performance of macOS --service 2024-04-22 21:31:53 +08:00
fufesou
4f47d4482b refact: win, idd control (#7789)
* refact: win, idd control

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: win device control, better addr of

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: simple refact

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-22 10:37:08 +08:00
fufesou
ad062486ff Fix/win query arch (#7786)
* fix: win, query arch with GetNativeSystemInfo

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: idd, ci

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-21 14:55:42 +08:00
Kleofass
33c8bdfabf Update lv.rs (#7784) 2024-04-21 14:53:22 +08:00
rustdesk
0365c94407 remove IsWow64Process2 2024-04-20 19:24:44 +08:00
rustdesk
c81c4f9114 visual studio 2019 -> 2022 2024-04-20 18:20:09 +08:00
fufesou
d920953df1 typo (#7783)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-20 15:28:33 +08:00
rustdesk
f02a2e44d8 remove temp support_windows_specific_session 2024-04-20 15:13:04 +08:00
fufesou
fb1aa9c028 Fix. Multi-display connection, resolutions (#7782)
* fix: multi-display, change resolution

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: multi-displays, resolutions of displays

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: build

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: Function rename

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact. Function rename

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-20 14:54:23 +08:00
jxdv
d4a1d4cd7e update cs.rs (#7780) 2024-04-20 11:31:07 +08:00
jxdv
8a6d9a1496 update sk.rs (#7779) 2024-04-20 11:30:57 +08:00
Mr-Update
a8eefbc9f0 Update de.rs (#7778) 2024-04-20 11:30:48 +08:00
bovirus
75521fe363 Update Italian language (#7776) 2024-04-20 11:30:38 +08:00
fufesou
c75778943f refact: msi, version and build date, check (#7775)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-19 18:16:07 +08:00
solokot
0e6fa37ae4 Update ru.rs (#7771) 2024-04-19 17:58:06 +08:00
fufesou
819eea9456 refact: msi (#7774)
* refact: msi

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Remove unused coment

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-19 17:57:04 +08:00
rustdesk
25eb8dc9b0 typo 2024-04-19 14:35:50 +08:00
rustdesk
40067f5aa2 no countdown in scam warning of android for custom build 2024-04-19 13:49:45 +08:00
rustdesk
1ffc10e44f flutter 19.6 2024-04-19 13:25:02 +08:00
21pages
5d5547ffef put andriod custom client logo to settings section list (#7768)
Signed-off-by: 21pages <pages21@163.com>
2024-04-19 12:44:52 +08:00
fufesou
e83c28bf54 refact: win, virtual display (#7767)
* refact: win, virtual display

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Update flutter-build.yml

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-04-19 11:31:52 +08:00
fufesou
a3c0911529 fix: msi, explicit wide char api (#7764)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-19 00:47:13 +08:00
rustdesk
ae23f0de03 Improve android threading, https://github.com/rustdesk/rustdesk/issues/4118#issuecomment-1515666629
todo: should we add some condition to assure imageReader not be closed
while callback is running?
2024-04-18 22:38:32 +08:00
rustdesk
edb5529df3 flutter_icons does not generate mipmap-ldpi, so I drop it also 2024-04-18 17:32:15 +08:00
21pages
4d3fb77786 remove hwcodec for sciter armv7 (#7753)
Signed-off-by: 21pages <pages21@163.com>
2024-04-18 16:19:29 +08:00
rustdesk
24ac7c1626 remove google-services.json 2024-04-18 15:23:12 +08:00
writegr
9b2ec62be9 chore: fix some typos in comments (#7752)
Signed-off-by: writegr <wellweek@outlook.com>
2024-04-18 14:39:38 +08:00
21pages
4252b5e273 enable ffmpeg native h26x software decoders for all platforms (#7750)
* enable ffmpeg native h26x software decoders for all platforms

* h26x software decoders depend on hwcodec feature, so all platforms
  enable it, software h26x decoders are always available like vpx, no available check and no option
* ffmpeg:
	- build: mac arm64 build ffmpeg with my m1, others build with ci
	- version: win/linux use ffmpeg release/5.1, becaues higher version require higher nvidia driver,  other platforms use release/7.0
* test:
	- ios not test.
	- android: sometimes the screen will appear blurry, but it will recover after a while.
	- arm64 linux: test a example of hwcodec repo

Signed-off-by: 21pages <pages21@163.com>

* check hwcodec only when enabled and immediately when clicked enabled

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
2024-04-18 13:12:45 +08:00
rustdesk
4e8cbe3db1 buildPresetPasswordWarning for android 2024-04-17 22:01:09 +08:00
rustdesk
bdf8bbe26f custom android 2024-04-17 12:49:25 +08:00
Kleofass
990c05fc3d Update lv.rs (#7745) 2024-04-17 10:20:31 +08:00
rustdesk
7bb4e22a77 fix ci 2024-04-16 22:53:01 +08:00
rustdesk
736503df1b fix ci 2024-04-16 22:38:47 +08:00
rustdesk
414455a8fb fix ci 2024-04-16 22:25:10 +08:00
rustdesk
bc0ab88e74 do not load empty custom_client_config 2024-04-16 22:17:05 +08:00
rustdesk
01ec539065 load android custom client for jvm startServer 2024-04-16 21:46:54 +08:00
rustdesk
b9792fc17d bridge rust version 2024-04-16 20:18:17 +08:00
rustdesk
c656c3c087 typo 2024-04-16 18:46:46 +08:00
rustdesk
a6c1d2d486 customClientConfig in dart 2024-04-16 18:42:35 +08:00
rustdesk
44bce59777 more java_ffi_FFI 2024-04-16 18:22:36 +08:00
fufesou
9b5e5aa474 fix: Msi custom app, different component guids (#7738)
* fix: Msi custom app, different component guids

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: msi, update readme

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-16 14:12:39 +08:00
fufesou
a5d02998ad Fix. Msi, remove RustDesk words (#7732)
* Fix. Msi, remove RustDesk words

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Fix. Replace RustDesk in langs

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-16 13:09:20 +08:00
flusheDData
1d4c129e9c Update es.rs (#7733)
tip translation added
2024-04-16 11:10:31 +08:00
rustdesk
e9a6ca8ebc typo 2024-04-15 21:19:46 +08:00
rustdesk
28340c80dd fix me 2024-04-15 15:58:07 +08:00
rustdesk
05f6fde467 ffi.RustDesk -> ffi.FFI 2024-04-15 15:49:37 +08:00
rustdesk
56d353cb64 fix me 2024-04-15 15:39:25 +08:00
rustdesk
260d0cdc67 fix me 2024-04-15 15:37:33 +08:00
rustdesk
cdd92303b8 refactor android ffi 2024-04-15 15:20:27 +08:00
alewicki95
0dba37f4f7 Update pl.rs (#7723) 2024-04-15 11:13:32 +08:00
jxdv
7f3775a061 update system2 (#7722) 2024-04-15 00:52:27 +08:00
fufesou
a7a10f4eaa Refact. Win, file copy paste, default true (#7719)
* Refact. Win, file copy paste, default true

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Fix. File copy and paste menu, compatible with 1.2.3

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-14 21:25:26 +08:00
fufesou
5166083406 Fix. Msi, reg add SoftwareSASGeneration (#7708)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-14 21:03:51 +08:00
21pages
5c4d95ac0f not use av1 as auto codec on x86 sciter (#7714)
Signed-off-by: 21pages <pages21@163.com>
2024-04-13 21:10:36 +08:00
rustdesk
71c4d74759 fix typo 2024-04-13 14:43:27 +08:00
Mr-Update
725e8221a5 Update de.rs (#7712) 2024-04-13 13:12:40 +08:00
jxdv
92ae41cc13 update cs.rs (#7711) 2024-04-13 13:12:31 +08:00
jxdv
333b9130fe update sk.rs (#7710) 2024-04-13 13:12:20 +08:00
fufesou
60ae903cf3 Fix. Msi, reg values in HKCR (#7709)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-13 13:11:50 +08:00
fufesou
24ea55b010 Fix. Msi, firewall outbound profile (#7704)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-12 18:44:18 +08:00
fufesou
8231d07706 Fix. Msi. Terminate brokers. (#7693)
* Fix. Msi. Terminate brokers.

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Fix. Msi, remove tray shortcut in startmenu

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Msi. format

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Feat. Msi, set property

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Fix. Mis, only do InstallValidate if is Install

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-12 17:42:26 +08:00
21pages
98df2b111e hwcodec uses one repository (#7701)
* update hwcodec, gpucodec repo is merged to hwcodec

Signed-off-by: 21pages <pages21@163.com>

* rename gpucodec.rs to vram.rs

Signed-off-by: 21pages <pages21@163.com>

* rename all gpucodec to vram, because vram is a feature of hwcodec

Signed-off-by: 21pages <pages21@163.com>

* use one check process and one config file

* set check encode image size to 720p

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
2024-04-12 17:26:24 +08:00
rustdesk
d8875f381b protobuf 3.4 2024-04-12 11:38:19 +08:00
fufesou
6e3d16173a Fix. Mis, do not InstallValidate when uninstall (#7692)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-11 23:27:09 +08:00
FastAct
a88b189664 Update nl.rs (#7691) 2024-04-11 20:42:21 +08:00
fufesou
48da00eb66 Fix. Msi, error 5 on uninstall (#7690)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-11 20:41:23 +08:00
RustDesk
5322332c5d Update flutter-build.yml 2024-04-11 20:23:08 +08:00
fufesou
c92cfbb20a Fix. Github ci (#7689)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-11 19:26:29 +08:00
fufesou
f673647072 Feat/msi (#7688)
* Feat. Msi, check is self-installed

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Feat. Msi.

1. Check if is self-installation.
2. Add firewall rule by custom action.

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Feat. Msi, github ci

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Feat. Msi, github ci

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Feat. Msi, github ci

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Feat. Msi, refact preprocess.py

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Feat. Msi

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Trivial, renames

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-11 18:54:32 +08:00
rustdesk
7ea5a9bba3 devices.py 2024-04-11 18:39:46 +08:00
fufesou
aa002c5d60 Refact. Msi, remove unused code (#7685)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-11 12:06:10 +08:00
fufesou
64020758d9 Feat. Msi (#7684)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-11 11:51:35 +08:00
bovirus
a4f357fd80 Update Italian language (#7678) 2024-04-11 10:50:40 +08:00
XLion
4c6fdfd76a Update tw.rs (#7677) 2024-04-11 10:50:26 +08:00
Tom-Brian Garcia
c972452310 fix: do not call WakeLock on Web (mobile) (#7668)
* fix: do not call WakeLock on Web (mobile)

* fix: do not call WakeLock on Web (mobile) - replaced isMobile by !isWeb
2024-04-10 21:32:05 +08:00
Alen Bajo
6ba6a16836 Update hr.rs (#7675)
Some small things changed
2024-04-10 17:27:59 +08:00
solokot
c63f1dfc53 Update ru.rs (#7674) 2024-04-10 15:19:19 +08:00
21pages
ac79c45529 remove tooltip of main window tab, opt some tooltip by raising its hierarchy (#7672)
Signed-off-by: 21pages <pages21@163.com>
2024-04-10 14:04:12 +08:00
Integral
7f58737f1f Update cn.rs (#7669) 2024-04-10 11:00:14 +08:00
RustDesk
ead8a48436 Update lang.rs 2024-04-10 00:23:45 +08:00
rustdesk
178d33155f fix check_process for empty arg since on mac, p.cmd() get "/Applications/RustDesk.app/Contents/MacOS/RustDesk", "XPC_SERVICE_NAME=com.carriez.RustDesk_server" 2024-04-09 20:38:07 +08:00
rustdesk
07ab8e508c fix me 2024-04-09 19:48:58 +08:00
rustdesk
ea1d4adfa6 fix check_process on mac since normal user can not get system's
process's command line arguments
2024-04-09 19:33:39 +08:00
rustdesk
f34a8ef0e5 fix me 2024-04-09 19:02:17 +08:00
mehdi-song
0df4b39bcc Update fa.rs (#7662) 2024-04-09 18:37:56 +08:00
rustdesk
07eca00bd5 feat: allow-only-conn-window-open https://github.com/rustdesk/rustdesk/discussions/7033 2024-04-09 18:34:44 +08:00
rustdesk
cf8386aa50 remove useless code in is_installed windows 2024-04-09 16:33:03 +08:00
SOZEL
44de6a5549 docs: update README-VN.md (#7655) 2024-04-08 21:38:47 +08:00
RustDesk
a41a9bcbf7 Update build-macos-arm64.yml 2024-04-08 21:13:56 +08:00
RustDesk
a679fae9c0 Update build-macos-arm64.yml 2024-04-08 21:11:34 +08:00
RustDesk
0f906f3937 Update build-macos-arm64.yml 2024-04-08 21:08:40 +08:00
RustDesk
c907daa741 Update build-macos-arm64.yml 2024-04-08 21:05:19 +08:00
RustDesk
df579de147 Update build-macos-arm64.yml 2024-04-08 21:03:54 +08:00
rustdesk
e884bdbbc4 fix https://github.com/rustdesk/rustdesk/pull/7654 2024-04-08 16:41:46 +08:00
Alen Bajo
84fc70e9a9 Create hr.rs (#7654)
Croatian translation (Hrvatski)
2024-04-08 16:30:50 +08:00
BrazilianArmy
4fc5d3f03b Update ptbr.rs (#7649)
Atualização de algumas KEYS para português BR
2024-04-08 11:56:12 +08:00
fufesou
9402516acd Refact. Flutter pub upgrade web (#7648)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-07 22:03:50 +08:00
fufesou
65f7541ec7 Fix. Compare cert blob before deleting it. (#7643)
* Fix. Compare cert blob before deleting it.

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Rename function name

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-07 14:31:09 +08:00
rustdesk
22da14e3f7 fix https://github.com/rustdesk/rustdesk/discussions/7628, https://github.com/rustdesk/rustdesk/discussions/7490 2024-04-07 11:31:51 +08:00
fufesou
fddad09167 Refact. Remove unused memset (#7633)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-06 23:19:50 +08:00
fufesou
f36f065508 Refact. Delete check reg fingerprint (#7631)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-06 22:58:42 +08:00
21pages
0c294eefae reordered peer tab (#7604)
* reordered peer tab

Signed-off-by: 21pages <pages21@163.com>

* opt peer tab visible menu, avoid checkbox value splash

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
2024-04-06 17:53:03 +08:00
fufesou
5a0333ddaf Feat. msi (#7610)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-06 16:30:33 +08:00
Integral
e0b9a9a82f Update cn.rs (#7614) 2024-04-06 16:29:55 +08:00
fufesou
ab07eb6f4a Fix. Remove strange cert dir created by 1.2.3 (#7620)
* Fix. Remove strange cert dir created by 1.2.3

1. Remove `install_cert()`.
2. https://github.com/rustdesk/rustdesk/discussions/6444#discussioncomment-9017532

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* comments

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-06 16:22:59 +08:00
Yevhen Popok
bddd800769 Update Ukrainian translation (#7611)
* Update Ukrainian translation

* Update Ukrainian translation

* Update Ukrainian translation
2024-04-06 16:21:11 +08:00
fufesou
5533ebf86a Fix. Remote toolbar, keyboard mode (#7626)
Move modeOnly to the scope it is needed.
Get modeOnly everytime of dropdown menu.

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-06 11:37:31 +08:00
fufesou
2397fdc495 Fix. Remote toolbar, menu refresh (#7605)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-05 09:28:35 +08:00
fufesou
129548764e fix build (#7597)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-03 22:20:56 +08:00
fufesou
ed257e39d0 Feat. msi (#7590)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-03 14:27:35 +08:00
rustdesk
33eaf2e05c bump to flutter 3.19.5 for wrong cpu instruction used in flutter 3.16 -> 3.19.4, which cause not running on some CPU 2024-04-03 14:25:09 +08:00
21pages
41d99d5108 fix forget password toast error when personal ab doesn't contain this id (#7589)
Signed-off-by: 21pages <pages21@163.com>
2024-04-02 23:18:52 +08:00
RustDesk
edefa5f786 Revert "Refact. CI, print sha256sum (#7580)" (#7587)
This reverts commit 2b20714f50.
2024-04-02 22:20:07 +08:00
21pages
d7b47b49d2 opt password sync, opt ab widgets (#7582)
* Opt sync conctrl with password source, add some comments
* For sync from recent, legacy ab remove forceRelay, rdpPort, rdpUsername,
  because it's not used, personal ab add sync hash
* Opt style of add Id dialog

Signed-off-by: 21pages <pages21@163.com>
2024-04-02 22:08:47 +08:00
fufesou
74af7ef8b2 Fix. Custom client, connection status (#7586)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-02 21:59:17 +08:00
redistay
1cfc9b6139 chore: fix some typos (#7585)
Signed-off-by: redistay <wujunjing@outlook.com>
2024-04-02 21:41:54 +08:00
fufesou
db6bf547a9 Feat. msi (#7584)
* Feat. msi

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* remove ununsed file

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-02 18:10:09 +08:00
fufesou
2b20714f50 Refact. CI, print sha256sum (#7580)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-02 15:06:37 +08:00
FastAct
cfa72ad7d1 Update nl.rs (#7581) 2024-04-02 14:47:40 +08:00
fufesou
2e11a8b458 Fix. Linux, run_cmds, trim new line (#7579)
* Fix. Linux, run_cmds, trim new line

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* add tests

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-02 14:47:13 +08:00
XLion
58fe95d6fd Uppercase API (#7577) 2024-04-02 11:44:27 +08:00
21pages
de65c8b2cf fix resolution restoration (#7572)
* Fix resolution restore. Screen resolution changes may be slow and cause errors in judgment. Remove the consideration of resolution changes caused by other processes.
* Keep the design unchange: when all connections end, revert to the resolution when there were no connections.
* Resolution menu use `scaledRect` for retina
* I can't reproduce the restoration failure of retina with 3rd software, but it maybe the same reason of slow resolution change.

Signed-off-by: 21pages <pages21@163.com>
2024-04-01 17:20:19 +08:00
rustdesk
57510980ed move pkill tray to execstop of the service for linux 2024-04-01 17:07:10 +08:00
hzhou
26a7f9dd46 kill tray in postrm of linux 2024-04-01 16:36:34 +08:00
hzhou
7fb2464d84 fix sysinfo update when id change, and add debug info for pk change 2024-04-01 16:21:05 +08:00
fufesou
9e7137ce09 Fix. Custom resolution removed after 2rd conn (#7571)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-04-01 13:27:52 +08:00
mehdi-song
80747fc306 Update fa.rs (#7566) 2024-03-31 21:37:22 +08:00
RustDesk
0d75f71d16 Update build-macos-arm64.yml 2024-03-30 18:27:23 +08:00
RustDesk
46c2288f50 Update build-macos-arm64.yml 2024-03-30 18:09:55 +08:00
RustDesk
d76c9ad1db Update build-macos-arm64.yml 2024-03-30 17:39:44 +08:00
RustDesk
3faa18c020 Update build-macos-arm64.yml 2024-03-30 17:19:03 +08:00
RustDesk
d699496a52 Update build-macos-arm64.yml 2024-03-30 17:13:23 +08:00
RustDesk
19bf5f1a23 Update build-macos-arm64.yml 2024-03-30 17:07:34 +08:00
RustDesk
080b56a0cb Update build-macos-arm64.yml 2024-03-30 16:55:28 +08:00
fufesou
82d9b033f2 Fix. Custom client, get app name (#7562)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-30 13:39:34 +08:00
FastAct
bf6e465970 Update nl.rs (#7554) 2024-03-29 19:43:06 +08:00
fufesou
3ef9824d8e Flutter web, custom cursor (#7545)
* Flutter web, custom cursor

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* trivial changes

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Flutter web, custom cursor, use date after 'updateGetKey()'

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* trivial changes

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-29 10:52:32 +08:00
21pages
4b0e88ce46 fix not saving cache after changing current address book (#7544)
and get address book mode from cache

Signed-off-by: 21pages <pages21@163.com>
2024-03-28 20:50:53 +08:00
21pages
0c32a889a9 fix ab cache not work, set current ab name after load cache (#7542)
Signed-off-by: 21pages <pages21@163.com>
2024-03-28 19:37:25 +08:00
rustdesk
6186b41e3f modify comment 2024-03-28 11:52:43 +08:00
fufesou
6e44a91d0b Refact. Flutter web desktop (#7539)
* Refact. Flutter web desktop

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Flutter web, prevent default context menu

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-28 11:38:11 +08:00
fufesou
810b980e6b Try fix, click tray icon, run main window (#7540)
* Try fix, click tray icon, run main window

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Remove start uni link on Windows, use 'run_me' instead

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-28 11:14:03 +08:00
21pages
22356940d9 ab show loading LinearProgressIndicator (#7538)
* Display the LinearProgressIndicator at the top of ab if the current ab is both empty and in a loading state.
* Add pull error like the legacy ab.
* When forcefully reading the 'ab' list, retrieve the personal ab guid to judge the current mode  to prevent server upgrades while the main window remains open

Signed-off-by: 21pages <pages21@163.com>
2024-03-27 21:28:21 +08:00
21pages
cc30f7aa02 handle boot args if initialLink is empty (#7537)
Signed-off-by: 21pages <pages21@163.com>
2024-03-27 15:51:30 +08:00
RustDesk
6e689400b6 Revert "macos support uni command "rustdesk rustdesk://xxxx" without mainwind…" (#7536)
This reverts commit 50fbf8833b.
2024-03-27 14:24:14 +08:00
21pages
50fbf8833b macos support uni command "rustdesk rustdesk://xxxx" without mainwindow (#7534)
Signed-off-by: 21pages <pages21@163.com>
2024-03-27 13:42:50 +08:00
fufesou
d7137990b9 Refact. Flutter web, peer cards (#7525)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-27 11:26:29 +08:00
flusheDData
8ecc107c06 New terms added (#7526)
* Update es.rs

New terms added

* Update es.rs

mistype correction

* Update es.rs

New term added

* Update es.rs

New terms added

* Update es.rs

New terms added

* Update es.rs

New terms added
2024-03-27 11:26:11 +08:00
rustdesk
1fa3837bb0 revert back to async_trait since having to use 1.75 because of win7
support
2024-03-27 03:36:33 +08:00
rustdesk
5aec5b084a refactor --service/--server start time check (my stupid to restart
--server)
2024-03-27 03:02:36 +08:00
rustdesk
b77add97db change to nightly-2023-10-13, the day of 1.2.3 2024-03-27 02:22:38 +08:00
rustdesk
b306d808fb change to nightly-2023-12-28, the day of 1.75.0 2024-03-27 02:13:25 +08:00
rustdesk
1729c3f76d have to use nightly for win32 2024-03-27 02:05:54 +08:00
rustdesk
2eb591391e fix ci 2024-03-27 01:48:34 +08:00
rustdesk
075194fea8 x86 rust toolchain 2024-03-27 01:45:17 +08:00
rustdesk
28b376d2d2 stick to rust 1.75 for win7/8, https://github.com/rustdesk/rustdesk/discussions/7503 2024-03-27 01:33:01 +08:00
crazeteam
4f3de4cf54 chore: fix some typos (#7518)
Signed-off-by: crazeteam <lilujing@outlook.com>
2024-03-27 01:08:56 +08:00
rustdesk
85f387bfb9 improve https://github.com/rustdesk/rustdesk/discussions/5631#discussioncomment-8916273 2024-03-27 00:38:13 +08:00
rustdesk
81aec8402b Temp solution for https://github.com/rustdesk/rustdesk/discussions/5631 2024-03-26 22:31:58 +08:00
rustdesk
d407dfed0a remove main.cc since not found the way to code sign it since g++ no ENABLE_HARDENED_RUNTIME 2024-03-26 21:27:02 +08:00
rustdesk
dcd4d0886e Close -> "Toggle tags" 2024-03-26 16:57:29 +08:00
rustdesk
9ad240951e fix uni link when mac service started, by use
applicationShouldOpenUntitledFile delegate
2024-03-26 14:11:02 +08:00
fufesou
e86d4657da Refact. Flutter web, key input (#7511)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-26 10:45:06 +08:00
rustdesk
d36e33a7cf rustdesk -> RustDesk in agent/daemon process since in App, the exe is
RustDesk rather than lowercase
2024-03-25 21:18:45 +08:00
21pages
376bcefc14 fix peer tab Tooltip persist via move it up (#7510)
Signed-off-by: 21pages <pages21@163.com>
2024-03-25 20:58:55 +08:00
21pages
c0c110fe7e opt pull ab (#7509)
* get ab settings only on new mode
* avoid pull dummpy current ab if list not initialized

Signed-off-by: 21pages <pages21@163.com>
2024-03-25 20:38:26 +08:00
rustdesk
3dc6e576df check isDisableAccount and isDisableAb before pull ab / group 2024-03-25 20:14:34 +08:00
21pages
49f717fcf8 opt pull ab (#7508)
1. Test legacy/new mode only upon logging out.
2. Avoid pulling all data unnecessarily:
* On startup: Retrieve list, current, and personal data.
* On refresh: Retrieve list and current data.
* On changing AB/switching tabs: Attempt to pull current data if not initialized.
3. Cache only personal and current AB.
4. Synchronize current AB from recent.
5. Remove AB loading CircularProgressIndicator.

Signed-off-by: 21pages <pages21@163.com>
2024-03-25 19:59:21 +08:00
rustdesk
00152e0db4 upload unsigned dmg since tar.gz does not work app failure 2024-03-25 15:43:18 +08:00
rustdesk
1964203848 fix unsigned mac 2024-03-25 15:04:34 +08:00
rustdesk
495444abd0 fix ci 2024-03-25 12:28:27 +08:00
rustdesk
09ec0b6325 fix https://github.com/rustdesk/rustdesk/discussions/7501 2024-03-25 12:15:51 +08:00
rustdesk
cad0f15a83 fix ci 2024-03-25 12:06:49 +08:00
rustdesk
b087dcd328 upload unsigned macos app 2024-03-25 11:21:01 +08:00
fufesou
8dff263a0c Refact. Flutter web, mid commit (#7502)
* Refact. Flutter web, mid commit

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Refact. Flutter web, mid commit

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-25 10:47:53 +08:00
XLion
d546b23bd8 Update translation (#7500) 2024-03-25 10:45:06 +08:00
solokot
bb9400745e Update ru.rs (#7496) 2024-03-25 10:43:01 +08:00
rustdesk
30f84e78e1 fix custom tray of mac 2024-03-24 22:17:40 +08:00
rustdesk
30af020ba8 fix config::is_incoming_only 2024-03-24 20:28:27 +08:00
rustdesk
ef27cd6c5c custom.txt and tray for mac 2024-03-24 20:09:37 +08:00
rustdesk
770289cd67 replace Assets.xcassets/AppIcon.appiconset with AppIcon.icns for custom
client
2024-03-24 15:23:24 +08:00
rustdesk
25b5c96648 remove default create-dmg in build.py 2024-03-24 14:04:05 +08:00
fufesou
85cafda168 Refact. Flutter web, mid commit (#7494)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-24 11:23:06 +08:00
21pages
96e1b5b0f6 remove deserializing config option using default config (#7491)
Signed-off-by: 21pages <pages21@163.com>
Co-authored-by: squarer3 <squalerblack@gmail.com>
2024-03-24 11:20:12 +08:00
fufesou
dd90e84f6a Fix. Peer tab, match name and icon (#7489)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-23 22:23:30 +08:00
rustdesk
05969c2f99 Hostname -> Server 2024-03-23 17:10:52 +08:00
fufesou
05ef1c3924 Feat. ci web (#7486)
* Feat. ci web

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Update README.md

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-03-23 14:28:25 +08:00
fufesou
8018920a64 Fix. Platform is MacOS and is Linux (#7485)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-23 10:27:17 +08:00
21pages
dcb7e389eb opt sytle of ab name (#7484)
Signed-off-by: 21pages <pages21@163.com>
2024-03-23 10:10:22 +08:00
fufesou
a15cd62fd4 Refact. Flutter web, mid commit (#7482)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-23 10:08:55 +08:00
rustdesk
1c3a2e475d safer handle_intranet 2024-03-23 00:56:18 +08:00
FastAct
83c45d772f Update nl.rs (#7475) 2024-03-22 23:36:59 +08:00
21pages
96c8d4de6a fix restore ab afer pullAb (#7480)
* Opt ab name drop downdrop, more width

Signed-off-by: 21pages <pages21@163.com>
2024-03-22 23:32:59 +08:00
fufesou
9558974080 Refact. Build flutter web (#7472)
* Refact. Build flutter web

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Refact. Flutter web, wrap Platform.xx

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-22 13:16:37 +08:00
21pages
285e298a8b fix ab tag flashing when refreshing (#7471)
* opt isFull, fix web console menu

Signed-off-by: 21pages <pages21@163.com>

* fix ab tag flashing when refreshing

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
2024-03-22 11:02:22 +08:00
jxdv
57b17b1798 update cs.rs (#7467) 2024-03-22 09:53:37 +08:00
jxdv
7e13642098 update sk.rs (#7466)
* update sk.rs

* update pw warn
2024-03-22 09:53:21 +08:00
Mr-Update
26b5f53b12 Update de.rs (#7461) 2024-03-22 09:52:33 +08:00
bovirus
896b321cdc Italian language update (#7462) 2024-03-22 09:52:02 +08:00
Kleofass
4115a5936c Update lv.rs (#7460) 2024-03-22 09:51:46 +08:00
FastAct
0e0cf7df09 Update nl.rs (#7459) 2024-03-21 16:59:02 +08:00
21pages
0c232be0c4 opt web console menu text (#7452)
Signed-off-by: 21pages <pages21@163.com>
2024-03-21 14:44:13 +08:00
21pages
67bc26ed57 remove address book operation code, as it duplicates the the functionality of web console (#7451)
Signed-off-by: 21pages <pages21@163.com>
2024-03-21 14:01:18 +08:00
rustdesk
f421a14659 fix lang 2024-03-21 11:18:48 +08:00
RustDesk
81b74cf46c Update README.md 2024-03-21 11:17:09 +08:00
solokot
b1a3ce323b Update ru.rs (#7442) 2024-03-21 10:12:47 +08:00
Kleofass
1f58826444 Update lv.rs (#7440) 2024-03-21 10:12:33 +08:00
Nadahar
2ea853b1e6 Fixed CPU performance montor on non-English Windows (#7446)
Signed-off-by: Nadahar <Nadahar@users.noreply.github.com>
2024-03-21 10:12:17 +08:00
Daniel Marschall
a2e1647faa Update de.rs (#7447)
* Update de.rs

* Update de.rs
2024-03-21 10:08:43 +08:00
21pages
4e060203ad ab share rules, use user/group name rather than level (#7443)
Signed-off-by: 21pages <pages21@163.com>
2024-03-21 07:23:08 +08:00
RustDesk
0540e2cf6c Revert "missing quotes around MACOS_CODESIGN_IDENTIRY (#7416)" (#7438)
This reverts commit 794d3ffcac.
2024-03-20 21:20:39 +08:00
rustdesk
21e4679b6c improve tfa 2024-03-20 20:00:29 +08:00
RustDesk
8b9728b40b Revert "Fix how 2FA codes are verified (#7429)" (#7436)
This reverts commit e0739709e7.
2024-03-20 19:54:47 +08:00
p0358
e0739709e7 Fix how 2FA codes are verified (#7429)
Signed-off-by: p0358 <p0358@users.noreply.github.com>
2024-03-20 19:50:55 +08:00
21pages
41da6d552f shared address book (#7229)
Signed-off-by: 21pages <pages21@163.com>
2024-03-20 15:05:54 +08:00
solokot
ecb70b43df Update ru.rs (#7433) 2024-03-20 11:28:10 +08:00
FastAct
b2290f5b4c Update nl.rs (#7426) 2024-03-19 18:53:35 +08:00
Lee Jong Mun
c6f415e9df kor translation update (#7424) 2024-03-19 18:53:08 +08:00
XLion
3f7d622512 Small fix for langs (#7418) 2024-03-19 18:52:36 +08:00
Ronan LE MEILLAT
794d3ffcac missing quotes around MACOS_CODESIGN_IDENTIRY (#7416) 2024-03-19 18:51:33 +08:00
RustDesk
ee2f87ef32 Update build-macos-arm64.yml 2024-03-17 11:28:12 +08:00
rustdesk
87da2a165d fix ci 2024-03-17 11:20:25 +08:00
rustdesk
130deb8e22 add build arm64 macos 2024-03-17 11:17:23 +08:00
rustdesk
b65d65cb12 fix ci 2024-03-17 00:56:59 +08:00
Kleofass
3caa5776c8 Update lv.rs (#7408) 2024-03-16 22:38:08 +08:00
rustdesk
6cbbb6498f init_log once 2024-03-16 22:20:50 +08:00
rustdesk
fb12ee7f2a fix loadLogo 2024-03-16 21:55:21 +08:00
bovirus
48842b6f2a Update Italian language (#7407) 2024-03-16 17:41:26 +08:00
fufesou
fc90526258 Fix. Custom client, maxmize of incoming only (#7406)
* Fix. Custom client, maxmize of incoming only

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* format

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-16 17:33:58 +08:00
fufesou
efd1f24ab2 Fix. Custom client, ui bugs (#7405)
* Fix. Custom client, ui bugs

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* trivial

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-16 15:37:23 +08:00
rustdesk
0808672b6e modify warn 2024-03-16 11:59:12 +08:00
rustdesk
fe0de47805 preset password warning 2024-03-16 11:52:48 +08:00
jxdv
ccf5b0ef53 update cs.rs (#7404) 2024-03-16 10:13:12 +08:00
jxdv
218f4872b8 update sk.rs (#7403) 2024-03-16 10:12:12 +08:00
rustdesk
077569dd5d better option save 2024-03-16 01:51:16 +08:00
rustdesk
a3f79065c5 better error handle in job.py 2024-03-16 00:30:30 +08:00
rustdesk
fbfcf862cb add divider of statusbar back and add quit button for qs 2024-03-16 00:24:18 +08:00
RustDesk
7307b84dad Update flutter-build.yml 2024-03-15 23:36:03 +08:00
Daniel Marschall
6b15a9b6d2 Made censoring in screenshot nicer (Fixes #7398) (#7399) 2024-03-15 22:20:43 +08:00
fufesou
e3420fd563 Refact. Linux, x11, user --server (#7400)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-15 22:18:14 +08:00
RustDesk
31120f51c9 Update flutter-build.yml 2024-03-15 18:07:17 +08:00
fufesou
3d279ec127 fix build (#7397)
* fix build

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fmt

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-15 17:46:36 +08:00
fufesou
48cd76b938 Fix. Linux run commands, getent passwd (#7396)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-15 17:26:33 +08:00
fufesou
8e5fbc8480 Fix. Use rounded (seconds) timestamp for test (#7394)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-15 14:24:22 +08:00
bovirus
cdea968c24 Add files via upload (#7393)
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-03-15 10:19:46 +08:00
DeDuplicate
a6aabed6a1 add hebrew translation (#7391)
* add hebrew translation

* Update lang.rs

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-03-15 10:18:08 +08:00
Mr-Update
cde235896f Update de.rs (#7390)
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-03-15 10:16:24 +08:00
jxdv
ca48aaf5ee update cs.rs (#7388)
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-03-15 10:14:44 +08:00
jxdv
8a705a6a9a update sk.rs (#7387)
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-03-15 10:14:03 +08:00
rustdesk
b3adbc1110 move outgoing_only_desk_tip to end 2024-03-15 10:12:58 +08:00
rustdesk
2bbc13ccf6 adjust logo and statusbar 2024-03-15 09:04:49 +08:00
fufesou
411384f0e7 Fix. MacOS, incoming only, restore pos (#7392)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-15 08:42:41 +08:00
rustdesk
a9b0bc0409 fix some minors of custom client 2024-03-15 00:26:53 +08:00
shuangcui
228582296c chore: remove repetitive word (#7386)
Signed-off-by: shuangcui <fliter@qq.com>
2024-03-14 21:36:55 +08:00
XLion
d67108cd20 Update translation (#7385)
Add translation for tw and add `to` to the sentence to make it more clear.
2024-03-14 21:33:08 +08:00
rustdesk
175f185cb1 fix https://github.com/rustdesk/rustdesk/discussions/7381 2024-03-14 20:38:57 +08:00
rustdesk
56f27ec356 fix ci 2024-03-14 11:45:16 +08:00
fufesou
5253d67e04 Fix/custom client styles (#7373)
* Fix. qs styles

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* custom client, options

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Move logo.svg to icon.svg

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Refact. Custom client, connection status ui.

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client ui. Disable settings, hide "Change password"

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client, logo align center

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client, refact, outgoing ui

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client, outgoing, settings icon

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client, powered by RustDesk

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client, remove unused SizeBox

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Update config.rs

* Update flutter_ffi.rs

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-03-14 11:36:14 +08:00
rustdesk
a314f59db9 fix https://github.com/rustdesk/rustdesk/issues/7377 2024-03-14 11:03:47 +08:00
rustdesk
da4e268e63 fix job 2024-03-14 06:59:56 +08:00
XLion
c186378d43 Correct copyright year (#7375)
* Correct copyright year

* Correct name
2024-03-13 21:12:04 +08:00
rustdesk
482bb5f64f fix CI 2024-03-13 09:41:28 +08:00
n-connect
3c1492e9bc Update hu.rs - fixing ID Server and others (#7366)
After a failed client setup in Network settings - due to wrong translation
2024-03-12 23:19:23 +08:00
rustdesk
be301b825f load tray icon from asset 2024-03-12 22:13:28 +08:00
rustdesk
fc875f7ad6 more on custom client 2024-03-12 21:47:29 +08:00
rustdesk
f644053811 fix hostname for mac 2024-03-11 01:36:43 +08:00
rustdesk
c0e5e78d2b no sign for build ci 2024-03-10 15:49:07 +08:00
rustdesk
d103872c61 avoid upload debug file 2024-03-10 15:39:49 +08:00
rustdesk
d47104f3f3 new sign 2024-03-10 15:06:49 +08:00
rustdesk
d4288f89d3 fix ci 2024-03-10 14:03:28 +08:00
rustdesk
7060257051 Remove some arc, and add some custom client 2024-03-10 12:48:00 +08:00
Dimitris Apostolou
866ec097c0 fix: fix crate vulnerabilities (#7344) 2024-03-09 17:52:18 +08:00
21pages
dcbe2805e6 improve waiting for image when only one image received (#7340)
* fix padding of mobile server page PopupMenuItem

Signed-off-by: 21pages <pages21@163.com>

* improve waiting for image when only one image received

* For flutter texture late creation: create texture between session add and session start, it works not in principle but in test.
* For late PeerInfo handling
	a. rgba texture render: allow zero size in on_rgba
	b. gpu texture render and rgba buffer render doesn't check size currently
* Fix wrong valid flag of first frame in rgba texture render

Other issues:
* decodeImageFromPixels may fail on first image
* Correct width/height when resolution changes

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
2024-03-08 17:24:02 +08:00
rustdesk
262814391a fix ci 2024-03-08 00:47:21 +08:00
rustdesk
b32cf403e6 overwrite config 2024-03-08 00:22:52 +08:00
rustdesk
921ee34779 unify winres 2024-03-07 17:00:32 +08:00
rustdesk
75a871c437 format 2024-03-07 16:37:51 +08:00
rustdesk
b5a47efe8e remove share rdp from strategy, useless and leading to confusion 2024-03-07 16:33:46 +08:00
Andrzej Rudnik
8ff24d876c Update pl.rs (#7329) 2024-03-07 14:09:09 +08:00
21pages
dd44bb2a0c flutter windows main.cpp get app name from rust (#7316)
Signed-off-by: 21pages <pages21@163.com>
2024-03-06 17:57:05 +08:00
rustdesk
f3686b23c7 fix tw 2024-03-06 17:38:33 +08:00
XLion
fccdc8ca1f Fix lang detection for zh-TW (#7326)
It is ``TW``, not ``tw``
2024-03-06 17:35:40 +08:00
rustdesk
fc149666d9 remove icon.rc and add compression level 2024-03-05 23:22:15 +08:00
jxdv
4292838e80 update sk.rs (#7317)
* fix typo

* update translations
2024-03-05 11:59:38 +08:00
fufesou
befac94473 Fix. macos, workaround fn (#7319)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-03-05 11:31:41 +08:00
rustdesk
c0128d86f4 fix ci 2024-03-04 22:55:25 +08:00
rustdesk
794636e208 use winres in portable 2024-03-04 22:19:18 +08:00
rustdesk
eff6ac254f improve generate.py 2024-03-04 21:16:14 +08:00
rustdesk
fa575be289 fix ci 2024-03-04 19:53:17 +08:00
rustdesk
060ad68570 add rustdesk.tar.gz to ci 2024-03-04 18:12:34 +08:00
21pages
1620394736 fix windows ci, downgrade flutter-action (#7312)
Signed-off-by: 21pages <pages21@163.com>
2024-03-04 09:17:56 +08:00
RustDesk
1e6e91e4bd Update clear-cache.yml 2024-03-03 22:29:31 +08:00
rustdesk
b0cbe2ae70 upgrade clear-acache 2024-03-03 22:18:49 +08:00
rustdesk
1391202985 clear-cache action 2024-03-03 22:17:00 +08:00
mehdi-song
d9ad24f563 Update fa.rs (#7309)
;-)
For all those who live in the world
2024-03-03 19:38:03 +08:00
21pages
61279752e2 upgrade uni link desktop (#7307)
* fix macos uni not work since flutter 3.16, upgrade uni_links_desktop

Signed-off-by: 21pages <pages21@163.com>

* Connect when clicking enter (drop-down is not considered)

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
2024-03-03 17:31:21 +08:00
rustdesk
1794b65182 remove devcontainer from readme, since it is not maintained and not good
for use
2024-03-01 15:50:25 +08:00
rustdesk
6bb5629bb4 update readme 2024-03-01 15:44:19 +08:00
rustdesk
0f3db29e75 fix https://github.com/rustdesk/rustdesk/discussions/7045 2024-02-29 11:27:21 +08:00
Yevhen Popok
c6cdf77a68 Update Ukrainian UI translation (#7288) 2024-02-29 09:57:48 +08:00
XLion
48682faf71 Update tw.rs (#7287) 2024-02-28 22:52:13 +08:00
21pages
49d396538c port forward, wait until TestDelay is replied (#7282)
* Revert "fix rdp not work, caused by test_delay_timer (#7274)"

This reverts commit 7fca0ba588.

* port forward, wait until TestDelay is replied

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
2024-02-28 12:18:13 +08:00
flusheDData
30802820b0 new terms (#7275)
* Update es.rs

New terms added

* Update es.rs

mistype correction

* Update es.rs

New term added

* Update es.rs

New terms added

* Update es.rs

New terms added
2024-02-28 09:12:16 +08:00
21pages
7fca0ba588 fix rdp not work, caused by test_delay_timer (#7274)
* fix rdp not work, caused by test_delay_timer

Signed-off-by: 21pages <pages21@163.com>

* Update connection.rs

---------

Signed-off-by: 21pages <pages21@163.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-02-28 00:11:02 +08:00
21pages
50d080d098 enable retina scale factor (#7269)
* enable retina scale factor

* enabled only when there are only one video service running
* scale mouse event
* scale cursor position
* scale remote menu display button
* adjust resolution

Signed-off-by: 21pages <pages21@163.com>

* Update server.rs

---------

Signed-off-by: 21pages <pages21@163.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-02-27 22:28:23 +08:00
fufesou
96792bec78 Refact. Qs, do not check qs on startup. (#7272)
Process will not exit if early return on the flutter side.

core_main.rs has checked qs already.

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-27 17:29:12 +08:00
fufesou
e6953c8883 Feat. Quick support, ui (#7267)
* Feat. QS ui

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Remove 'Quick support'

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* add help card

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* use addPostFrameCallback to get child size

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Fix. qs, set home window size

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Qs, set setResizable for settings page

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Qs, help cards margin bottom

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Qs, online status, padding

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Qs, online status, padding

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Qs, online status, use margin instead of padding

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Qs, fix, start cm window

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-27 17:02:10 +08:00
21pages
1d9b1781d4 update runas to 1.2.0 (#7271)
Signed-off-by: 21pages <pages21@163.com>
2024-02-27 15:42:35 +08:00
rustdesk
6720bb9f6b fix ci 2024-02-26 19:01:42 +08:00
rustdesk
2217e5303d fix ci 2024-02-26 18:12:28 +08:00
rustdesk
c8392a714c license.rs -> custom_server.rs to remove misunderstanding 2024-02-26 18:05:54 +08:00
rustdesk
749a1d2da2 support logo.png 2024-02-26 17:49:12 +08:00
21pages
2920e33d71 fix is_physical_console_session (#7266)
Signed-off-by: 21pages <pages21@163.com>
2024-02-26 13:34:26 +08:00
21pages
1bc11a207a fix rdp session ctrl+alt+delete (#7258)
Signed-off-by: 21pages <pages21@163.com>
2024-02-26 13:22:21 +08:00
fufesou
ddbd4e79a2 Refact. ci, third party, upload artifact (#7265)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-26 10:25:45 +08:00
fufesou
49c107bb5f Refact/ci remove third party topmost window (#7263)
* Refact. Remove third-party prebuilt dep, topmost window

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix ci

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Update third-party-RustDeskTempTopMostWindow.yml

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-02-26 02:24:00 +08:00
fufesou
1b99d28c9b Refact. build.py, skip portable packing (#7264)
ci and build.py do the packing.

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-26 02:20:37 +08:00
21pages
7b8e983412 fix calling getWindowName before bind init (#7259)
Signed-off-by: 21pages <pages21@163.com>
2024-02-26 00:02:42 +08:00
rustdesk
f1373234dd remove useless is_rdp_service_open 2024-02-25 20:10:36 +08:00
21pages
b511a52e09 remove is_rdp_service_open judgement for option share rdp (#7257)
Signed-off-by: 21pages <pages21@163.com>
2024-02-25 20:09:30 +08:00
rustdesk
4c8c2aa323 translate rustdesk 2024-02-25 15:06:55 +08:00
rustdesk
a28c9f8f36 remove more "rustdesk" built-in 2024-02-25 13:29:06 +08:00
fufesou
9ff1dfe019 Test. More comment (#7251)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-24 23:03:56 +08:00
fufesou
3ae52dacfc test RustDeskInterval with interval_at (#7249)
* test RustDeskInterval with interval_at

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Test. RustDeskInterval, to tokio interval

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Add comment

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-24 22:44:46 +08:00
rustdesk
aa6f879504 fix config string in exe file name 2024-02-24 20:36:18 +08:00
fufesou
1dde5c5689 Refact. get_version_number support '- patch version' (#7197)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-24 20:11:27 +08:00
FastAct
913b422d74 Update nl.rs (#7248) 2024-02-24 17:08:54 +08:00
fufesou
2459bcd206 Fix. Multi sub windows, sync peer options (#7247)
* Fix. Multi window, sync peer options

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Remove unused `use`

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-23 22:49:53 +08:00
wangweijie
cce1ce0ee0 Add loongarch64 support (#7244)
Signed-off-by: wangweijie <wangweijie@loongson.cn>
2024-02-23 20:08:35 +08:00
21pages
8518513aff fix haredware codec memory leak (#7240)
Signed-off-by: 21pages <pages21@163.com>
2024-02-23 13:33:01 +08:00
jxdv
ce5e28c543 update cs.rs (#7234) 2024-02-23 10:15:53 +08:00
jxdv
9b7a8203f6 update sk.rs (#7233) 2024-02-23 10:14:49 +08:00
solokot
fd8f2422ea Update ru.rs (#7232) 2024-02-23 00:51:10 +08:00
fufesou
09325fadaf Fix. Text clipboard is not working correctly (#7226)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-22 13:44:44 +08:00
Mr-Update
d7d7be9366 Update de.rs (#7225) 2024-02-22 11:44:16 +08:00
fufesou
25afdda2b2 Fix/arboard clipboard context timeout (#7217)
* Fix. Set custom timeout for arboard clipboard

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix build

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Do not block tokio runtime

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-21 22:05:27 +08:00
bovirus
c690d5e940 Italian language update (#7211) 2024-02-21 11:30:06 +08:00
Kleofass
243a8b60b1 Update lv.rs (#7210) 2024-02-21 11:29:54 +08:00
21pages
7b317619ac fix windows ci wrong sign directory since update to flutter 3.16.9 (#7209)
Signed-off-by: 21pages <pages21@163.com>
2024-02-20 21:05:19 +08:00
grummbeer
c987bed965 Automate year of copyright (#7206) 2024-02-20 18:00:07 +08:00
NicKoehler
ada4bafb94 Update italian readme (#7205)
* updated it readme

* fix link

* fix other links
2024-02-20 17:28:49 +08:00
fufesou
27f5d0fd54 Fix. Remove unused idd feature in ci. (#7198)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-20 03:19:38 +08:00
480 changed files with 48919 additions and 16091 deletions

View File

@@ -1,8 +1,14 @@
[target.x86_64-pc-windows-msvc]
rustflags = ["-Ctarget-feature=+crt-static"]
[target.i686-pc-windows-msvc]
rustflags = ["-Ctarget-feature=+crt-static"]
rustflags = ["-C", "target-feature=+crt-static", "-C", "link-args=/NODEFAULTLIB:MSVCRT"]
[target.'cfg(target_os="macos")']
rustflags = [
"-C", "link-args=-sectcreate __CGPreLoginApp __cgpreloginapp /dev/null",
]
#[target.'cfg(target_os="linux")']
# glibc-static required, this may fix https://github.com/rustdesk/rustdesk/issues/9103, but I do not want this big change
# this is unlikely to help also, because the other so files still use libc dynamically
#rustflags = [
# "-C", "link-args=-Wl,-Bstatic -lc -Wl,-Bdynamic"
#]

View File

@@ -6,8 +6,9 @@ on:
workflow_call:
env:
FLUTTER_VERSION: "3.16.9"
FLUTTER_VERSION: "3.19.6"
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503
jobs:
generate_bridge:
@@ -37,10 +38,8 @@ jobs:
git \
g++ \
libclang-10-dev \
libclang-dev \
libgtk-3-dev \
llvm-10-dev \
llvm-dev \
nasm \
ninja-build \
pkg-config \
@@ -49,9 +48,9 @@ jobs:
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
toolchain: ${{ env.RUST_VERSION }}
targets: ${{ matrix.job.target }}
components: ''
components: "rustfmt"
- uses: Swatinem/rust-cache@v2
with:

View File

@@ -4,9 +4,9 @@ env:
# MIN_SUPPORTED_RUST_VERSION: "1.46.0"
# CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2023.10.19
# vcpkg version: 2024.06.15
# for multiarch gcc compatibility
VCPKG_COMMIT_ID: "8eb57355a4ffb410a2e94c07b4dca2dffbee8e50"
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
on:
workflow_dispatch:
@@ -21,6 +21,9 @@ on:
- ".github/**"
- "docs/**"
- "README.md"
- "res/**"
- "appimage/**"
- "flatpak/**"
jobs:
# ensure_cargo_fmt:
@@ -71,12 +74,12 @@ jobs:
# - { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
# - { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true }
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
# - { target: i686-pc-windows-msvc , os: windows-2019 }
# - { target: i686-pc-windows-msvc , os: windows-2022 }
# - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
# - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
# - { target: x86_64-apple-darwin , os: macos-10.15 }
# - { target: x86_64-pc-windows-gnu , os: windows-2019 }
# - { target: x86_64-pc-windows-msvc , os: windows-2019 }
# - { target: x86_64-pc-windows-gnu , os: windows-2022 }
# - { target: x86_64-pc-windows-msvc , os: windows-2022 }
- { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04 }
# - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
steps:
@@ -103,11 +106,14 @@ jobs:
gcc \
git \
g++ \
libpam0g-dev \
libasound2-dev \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
libgtk-3-dev \
libpulse-dev \
libva-dev \
libvdpau-dev \
libxcb-randr0-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \

37
.github/workflows/clear-cache.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Clear cache
on:
workflow_dispatch:
permissions:
actions: write
jobs:
clear-cache:
runs-on: ubuntu-latest
steps:
- name: Clear cache
uses: actions/github-script@v7
with:
script: |
console.log("About to clear")
const caches = await github.rest.actions.getActionsCacheList({
owner: context.repo.owner,
repo: context.repo.repo,
})
for (const cache of caches.data.actions_caches) {
console.log(cache)
github.rest.actions.deleteActionsCacheById({
owner: context.repo.owner,
repo: context.repo.repo,
cache_id: cache.id,
})
}
console.log("Clear completed")
- name: Purge cache # Above seems not clear thouroughly, so add this to double clear
uses: MyAlbum/purge-cache@v2
with:
accessed: true # Purge caches by their last accessed time (default)
created: false # Purge caches by their created time (default)
max-age: 1 # in seconds

39
.github/workflows/fdroid.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Fdroid version file generation
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:
# https://gitlab.com/fdroid/fdroiddata/-/blob/master/metadata/com.carriez.flutter_hbb.yml
# Finds latest release and transforms F-Droid version code from version as follows:
# X.Y.Z-A => X * 1e6 + Y * 1e4 + Z * 1e2 + A
update-fdroid-version-file:
name: Publish RustDesk version file for F-Droid updater
runs-on: ubuntu-latest
steps:
- name: Generate RustDesk version file
run: |
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
UPSTREAM_VERNAME="${GITHUB_REF##refs/tags/}"
UPSTREAM_VERNAME="${UPSTREAM_VERNAME##v}"
else
UPSTREAM_VERNAME="$(curl https://api.github.com/repos/rustdesk/rustdesk/releases/latest | jq -r .tag_name | sed 's/^v//')"
fi
UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr '.' ' ' | tr '-' ' ' | while read -r MAJOR MINOR PATCH REV; do [ -z "$MAJOR" ] && MAJOR=0; [ -z "$MINOR" ] && MINOR=0; [ -z "$PATCH" ] && PATCH=0; [ -z "$REV" ] && REV=0; echo "$(( 1000000 * $MAJOR + 10000 * $MINOR + 100 * $PATCH + $REV ))"; done)"
echo "versionName=$UPSTREAM_VERNAME" > rustdesk-version.txt
echo "versionCode=$UPSTREAM_VERCODE" >> rustdesk-version.txt
shell: bash
- name: Publish RustDesk version file
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: "fdroid-version"
files: |
./rustdesk-version.txt

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,9 @@ on:
- ".github/**"
- "docs/**"
- "README.md"
- "res/**"
- "appimage/**"
- "flatpak/**"
jobs:
run-ci:

View File

@@ -15,24 +15,4 @@ jobs:
secrets: inherit
with:
upload-artifact: true
upload-tag: ${{ env.GITHUB_REF_NAME }}
update-fdroid-version-file:
name: Publish RustDesk version file for F-Droid updater
runs-on: ubuntu-latest
steps:
- name: Generate RustDesk version file
run: |
UPSTREAM_VERNAME="$GITHUB_REF_NAME"
UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr -d '.')"
echo "versionName=$UPSTREAM_VERNAME" > rustdesk-version.txt
echo "versionCode=$UPSTREAM_VERCODE" >> rustdesk-version.txt
shell: bash
- name: Publish RustDesk version file
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: "fdroid-version"
files: |
./rustdesk-version.txt
upload-tag: ${{ github.ref_name }}

View File

@@ -1,84 +0,0 @@
name: Flutter Windows History Build
on: [workflow_dispatch]
env:
LLVM_VERSION: "10.0"
FLUTTER_VERSION: "3.16.9"
TAG_NAME: "tmp"
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
VERSION: "1.2.4"
jobs:
build-for-history-windows:
name: ${{ matrix.job.date }}
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { target: x86_64-pc-windows-msvc, os: windows-2019, arch: x86_64, date: 2023-08-04, ref: 72c198a1e94cc1e0242fce88f92b3f3caedcd0c3 }
steps:
- name: Checkout source code
uses: actions/checkout@v4
with:
ref: ${{ matrix.job.ref }}
- name: Install LLVM and Clang
uses: KyleMayes/install-llvm-action@v1
with:
version: ${{ env.LLVM_VERSION }}
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
components: rustfmt
profile: minimal # minimal component installation (ie, no documentation)
- name: Install flutter rust bridge deps
run: |
cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid"
Push-Location flutter ; flutter pub get ; Pop-Location
~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart
- name: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11
with:
vcpkgDirectory: C:\vcpkg
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
- name: Install vcpkg dependencies
run: |
$VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed"
shell: bash
- name: Build rustdesk
run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver
- name: Build self-extracted executable
shell: bash
run: |
pushd ./libs/portable
python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe
popd
mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ matrix.job.date }}-${{ matrix.job.target }}.exe
- name: Publish Release
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
./SignOutput/rustdesk-*.exe

416
.github/workflows/playground.yml vendored Normal file
View File

@@ -0,0 +1,416 @@
name: playground
on:
#schedule:
# schedule build every night
# - cron: "0/6 * * * *"
workflow_dispatch:
env:
RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503
CARGO_NDK_VERSION: "3.1.2"
LLVM_VERSION: "15.0.6"
FLUTTER_VERSION: "3.22.2"
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
# for arm64 linux because official Dart SDK does not work
FLUTTER_ELINUX_VERSION: "3.16.9"
TAG_NAME: "nightly"
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2024.06.15
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
VERSION: "1.3.2"
NDK_VERSION: "r26d"
#signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
# To make a custom build with your own servers set the below secret values
RS_PUB_KEY: "${{ secrets.RS_PUB_KEY }}"
RENDEZVOUS_SERVER: "${{ secrets.RENDEZVOUS_SERVER }}"
API_SERVER: "${{ secrets.API_SERVER }}"
UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}"
SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}"
jobs:
build-for-macOS:
name: ${{ matrix.job.target }}
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- {
target: x86_64-apple-darwin,
os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel
extra-build-args: "",
arch: x86_64,
flutter: "3.13.9",
ref: "f6509e3fd6917aa976bad2fc684182601ebf2434",
bridge: "1.80.1",
date: "20231219"
}
- {
target: x86_64-apple-darwin,
os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel
extra-build-args: "",
arch: x86_64,
flutter: "3.10.6",
ref: "f6509e3fd6917aa976bad2fc684182601ebf2434",
bridge: "1.80.1",
date: "20231219"
}
- {
target: x86_64-apple-darwin,
os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel
extra-build-args: "",
arch: x86_64,
flutter: "3.10.6",
ref: "85ddfc0739f052cab0029c46b899b959ee94eeb8",
bridge: "1.80.1",
date: "20231119"
}
- {
target: x86_64-apple-darwin,
os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel
extra-build-args: "",
arch: x86_64,
flutter: "3.13.9",
ref: "85ddfc0739f052cab0029c46b899b959ee94eeb8",
bridge: "1.80.1",
date: "20231119"
}
steps:
- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Checkout source code
uses: actions/checkout@v3
with:
ref: ${{ matrix.job.ref }}
- name: Import the codesign cert
if: env.MACOS_P12_BASE64 != null
uses: apple-actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.MACOS_P12_BASE64 }}
p12-password: ${{ secrets.MACOS_P12_PASSWORD }}
keychain: rustdesk
- name: Check sign and import sign key
if: env.MACOS_P12_BASE64 != null
run: |
security default-keychain -s rustdesk.keychain
security find-identity -v
- name: Import notarize key
if: env.MACOS_P12_BASE64 != null
uses: timheuer/base64-to-file@v1.2
with:
# https://gregoryszorc.com/docs/apple-codesign/stable/apple_codesign_rcodesign.html#notarizing-and-stapling
fileName: rustdesk.json
fileDir: ${{ github.workspace }}
encodedString: ${{ secrets.MACOS_NOTARIZE_JSON }}
- name: Install rcodesign tool
if: env.MACOS_P12_BASE64 != null
shell: bash
run: |
pushd /tmp
wget https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz
tar -zxvf apple-codesign-0.22.0-macos-universal.tar.gz
mv apple-codesign-0.22.0-macos-universal/rcodesign /usr/local/bin
popd
- name: Install build runtime
run: |
brew install llvm create-dmg nasm cmake gcc wget ninja pkg-config
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ matrix.job.flutter }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
targets: ${{ matrix.job.target }}
components: "rustfmt"
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- name: Install flutter rust bridge deps
shell: bash
run: |
sed -i '' 's/3.1.0/2.17.0/g' flutter/pubspec.yaml;
cargo install flutter_rust_bridge_codegen --version ${{ matrix.job.bridge }} --features "uuid"
# below works for mac to make buildable on 3.13.9
# pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd;
pushd flutter && flutter pub get && popd
~/.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
- name: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
- name: Install vcpkg dependencies
run: |
$VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed"
- name: Restore from cache and install vcpkg
uses: lukka/run-vcpkg@v7
if: false
with:
setupOnly: true
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
- name: Install vcpkg dependencies
if: false
run: |
$VCPKG_ROOT/vcpkg install libvpx libyuv opus aom
- name: Show version information (Rust, cargo, Clang)
shell: bash
run: |
clang --version || true
rustup -V
rustup toolchain list
rustup default
cargo -V
rustc -V
- name: Build rustdesk
run: |
./build.py --flutter ${{ matrix.job.extra-build-args }}
- name: create unsigned dmg
run: |
CREATE_DMG="$(command -v create-dmg)"
CREATE_DMG="$(readlink -f "$CREATE_DMG")"
sed -i -e 's/MAXIMUM_UNMOUNTING_ATTEMPTS=3/MAXIMUM_UNMOUNTING_ATTEMPTS=7/' "$CREATE_DMG"
create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app
- name: Codesign app and create signed dmg
if: env.MACOS_P12_BASE64 != null
run: |
# Patch create-dmg to give more attempts to unmount image
CREATE_DMG="$(command -v create-dmg)"
CREATE_DMG="$(readlink -f "$CREATE_DMG")"
sed -i -e 's/MAXIMUM_UNMOUNTING_ATTEMPTS=3/MAXIMUM_UNMOUNTING_ATTEMPTS=7/' "$CREATE_DMG"
# Unlock keychain
security default-keychain -s rustdesk.keychain
security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain
# start sign the rustdesk.app and dmg
rm -rf *.dmg || true
codesign --force --options runtime -s ${{ secrets.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-${{ env.VERSION }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app
codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict rustdesk-${{ env.VERSION }}.dmg -vvv
# notarize the rustdesk-${{ env.VERSION }}.dmg
rcodesign notary-submit --api-key-path ${{ github.workspace }}/rustdesk.json --staple rustdesk-${{ env.VERSION }}.dmg
- name: Rename rustdesk
run: |
for name in rustdesk*??.dmg; do
mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}-flutter${{ matrix.job.flutter }}-flutter${{ matrix.job.date }}.dmg"
done
- name: Publish DMG package
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
rustdesk*-${{ matrix.job.arch }}*.dmg
build-rustdesk-android:
if: false
name: build rustdesk android apk ${{ matrix.job.target }}
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- {
arch: aarch64,
target: aarch64-linux-android,
os: ubuntu-20.04,
openssl-arch: android-arm64,
ref: master, # latest
}
steps:
- name: Checkout source code
uses: actions/checkout@v3
with:
ref: ${{ matrix.job.ref }}
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
clang \
cmake \
curl \
gcc-multilib \
git \
g++ \
g++-multilib \
libayatana-appindicator3-dev\
libasound2-dev \
libc6-dev \
libclang-10-dev \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
libgtk-3-dev \
libpam0g-dev \
libpulse-dev \
libva-dev \
libvdpau-dev \
libxcb-randr0-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \
libxdo-dev \
libxfixes-dev \
llvm-10-dev \
nasm \
yasm \
ninja-build \
openjdk-11-jdk-headless \
pkg-config \
tree \
wget
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
components: "rustfmt"
- name: Install flutter rust bridge deps
run: |
git config --global core.longpaths true
cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid"
sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml
pushd flutter/lib; find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g'; popd;
pushd flutter ; flutter pub get ; popd
~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: ${{ env.NDK_VERSION }}
add-to-path: true
- name: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11
with:
vcpkgDirectory: /opt/artifacts/vcpkg
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
- name: Install vcpkg dependencies
run: |
case ${{ matrix.job.target }} in
aarch64-linux-android)
./flutter/build_android_deps.sh arm64-v8a
;;
armv7-linux-androideabi)
./flutter/build_android_deps.sh armeabi-v7a
;;
esac
shell: bash
- name: Clone deps
shell: bash
run: |
pushd /opt
git clone https://github.com/rustdesk-org/rustdesk_thirdparty_lib.git --depth=1
ls -ls /opt/artifacts/vcpkg/installed/arm64-android/lib/
# cp -rf /opt/rustdesk_thirdparty_lib/vcpkg/* /opt/artifacts/vcpkg/
ls -ls /opt/artifacts/vcpkg/installed/arm64-android/lib/
- name: Build rustdesk lib
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
run: |
rustup target add ${{ matrix.job.target }}
cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }}
case ${{ matrix.job.target }} in
aarch64-linux-android)
./flutter/ndk_arm64.sh
mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
;;
armv7-linux-androideabi)
./flutter/ndk_arm.sh
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
;;
esac
- name: Build rustdesk
shell: bash
env:
JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64
run: |
export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH
# temporary use debug sign config
sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle
case ${{ matrix.job.target }} in
aarch64-linux-android)
mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
# build flutter
pushd flutter
flutter build apk --release --target-platform android-arm64 --split-per-abi
mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
;;
armv7-linux-androideabi)
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/arm-linux-androideabi/libc++_shared.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
# build flutter
pushd flutter
flutter build apk --release --target-platform android-arm --split-per-abi
mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
;;
esac
popd
mkdir -p signed-apk; pushd signed-apk
mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ./rustdesk-test-${{ matrix.job.ref }}-${{ matrix.job.ndk }}.apk
- uses: r0adkll/sign-android-release@v1
name: Sign app APK
if: env.ANDROID_SIGNING_KEY != null
id: sign-rustdesk
with:
releaseDirectory: ./signed-apk
signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }}
alias: ${{ secrets.ANDROID_ALIAS }}
keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }}
env:
# override default build-tools version (29.0.3) -- optional
BUILD_TOOLS_VERSION: "30.0.2"
- name: Publish signed apk package
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
${{steps.sign-rustdesk.outputs.signedReleaseFile}}

View File

@@ -0,0 +1,60 @@
name: build RustDeskTempTopMostWindow
on:
workflow_call:
inputs:
upload-artifact:
type: boolean
default: true
target:
description: 'Target'
required: true
type: string
default: 'windows-2022'
configuration:
description: 'Configuration'
required: true
type: string
default: 'Release'
platform:
description: 'Platform'
required: true
type: string
default: 'x64'
target_version:
description: 'TargetVersion'
required: true
type: string
default: 'Windows10'
env:
project_path: WindowInjection/WindowInjection.vcxproj
jobs:
build-RustDeskTempTopMostWindow:
runs-on: ${{ inputs.target }}
strategy:
fail-fast: false
env:
build_output_dir: RustDeskTempTopMostWindow/WindowInjection/${{ inputs.platform }}/${{ inputs.configuration }}
steps:
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v2
- name: Download the source code
run: |
git clone https://github.com/rustdesk-org/RustDeskTempTopMostWindow RustDeskTempTopMostWindow
# Build. commit 53b548a5398624f7149a382000397993542ad796 is tag v0.3
- name: Build the project
run: |
cd RustDeskTempTopMostWindow && git checkout 53b548a5398624f7149a382000397993542ad796
msbuild ${{ env.project_path }} -p:Configuration=${{ inputs.configuration }} -p:Platform=${{ inputs.platform }} /p:TargetVersion=${{ inputs.target_version }}
- name: Archive build artifacts
uses: actions/upload-artifact@master
if: ${{ inputs.upload-artifact }}
with:
name: topmostwindow-artifacts
path: |
./${{ env.build_output_dir }}/WindowInjection.dll

View File

@@ -4,9 +4,9 @@ on:
types: [released]
jobs:
publish:
runs-on: windows-latest # action can only be run on windows
runs-on: ubuntu-latest
steps:
- uses: vedantmgoyal2009/winget-releaser@v1
- uses: vedantmgoyal9/winget-releaser@main
with:
identifier: RustDesk.RustDesk
version: ${{ github.event.release.tag_name }}

5
.gitignore vendored
View File

@@ -51,4 +51,7 @@ lib/generated_bridge.dart
# build cache in examples
examples/**/target/
# ===
vcpkg_installed
vcpkg_installed
flutter/lib/generated_plugin_registrant.dart
libsciter.dylib
flutter/web/

3185
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,10 @@
[package]
name = "rustdesk"
version = "1.2.4"
version = "1.3.2"
authors = ["rustdesk <info@rustdesk.com>"]
edition = "2021"
build= "build.rs"
description = "A remote control software."
description = "RustDesk Remote Desktop"
default-run = "rustdesk"
rust-version = "1.75"
@@ -19,19 +19,14 @@ path = "src/naming.rs"
[features]
inline = []
cli = []
flutter_texture_render = []
appimage = []
flatpak = []
use_samplerate = ["samplerate"]
use_rubato = ["rubato"]
use_dasp = ["dasp"]
flutter = ["flutter_rust_bridge"]
default = ["use_dasp"]
hwcodec = ["scrap/hwcodec"]
gpucodec = ["scrap/gpucodec"]
vram = ["scrap/vram"]
mediacodec = ["scrap/mediacodec"]
linux_headless = ["pam" ]
virtual_display_driver = ["virtual_display"]
plugin_framework = []
linux-pkg-config = ["magnum-opus/linux-pkg-config", "scrap/linux-pkg-config"]
unix-file-copy-paste = [
@@ -45,7 +40,8 @@ unix-file-copy-paste = [
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
whoami = "1.4"
async-trait = "0.1"
whoami = "1.5.0"
scrap = { path = "libs/scrap", features = ["wayland"] }
hbb_common = { path = "libs/hbb_common" }
serde_derive = "1.0"
@@ -57,7 +53,6 @@ lazy_static = "1.4"
sha2 = "0.10"
repng = "0.2"
parity-tokio-ipc = { git = "https://github.com/rustdesk-org/parity-tokio-ipc" }
runas = "=1.0" # https://github.com/mitsuhiko/rust-runas/issues/13
magnum-opus = { git = "https://github.com/rustdesk-org/magnum-opus" }
dasp = { version = "0.11", features = ["signal", "interpolate-linear", "interpolate"], optional = true }
rubato = { version = "0.12", optional = true }
@@ -65,14 +60,13 @@ samplerate = { version = "0.2", optional = true }
uuid = { version = "1.3", features = ["v4"] }
clap = "4.2"
rpassword = "7.2"
base64 = "0.21"
num_cpus = "1.15"
bytes = { version = "1.4", features = ["serde"] }
default-net = "0.14"
wol-rs = "1.0"
flutter_rust_bridge = { version = "=1.80", features = ["uuid"], optional = true}
errno = "0.3"
rdev = { git = "https://github.com/fufesou/rdev" }
rdev = { git = "https://github.com/rustdesk-org/rdev" }
url = { version = "2.3", features = ["serde"] }
crossbeam-queue = "0.3"
hex = "0.4"
@@ -95,18 +89,35 @@ sys-locale = "0.3"
enigo = { path = "libs/enigo", features = [ "with_serde" ] }
clipboard = { path = "libs/clipboard" }
ctrlc = "3.2"
arboard = { version = "3.2", features = ["wayland-data-control"] }
# arboard = { version = "3.4.0", features = ["wayland-data-control"] }
arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] }
clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master" }
system_shutdown = "4.0"
qrcode-generator = "4.1"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winuser", "wincrypt", "shellscalingapi", "pdh", "synchapi", "memoryapi", "shellapi"] }
winapi = { version = "0.3", features = [
"winuser",
"wincrypt",
"shellscalingapi",
"pdh",
"synchapi",
"memoryapi",
"shellapi",
"devguid",
"setupapi",
"cguid",
"cfgmgr32",
"ioapiset",
] }
winreg = "0.11"
windows-service = "0.6"
virtual_display = { path = "libs/virtual_display", optional = true }
impersonate_system = { git = "https://github.com/21pages/impersonate-system" }
virtual_display = { path = "libs/virtual_display" }
impersonate_system = { git = "https://github.com/rustdesk-org/impersonate-system" }
shared_memory = "0.12"
tauri-winrt-notification = "0.1.2"
runas = "1.2"
[target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2"
@@ -127,44 +138,47 @@ image = "0.24"
keepawake = { git = "https://github.com/rustdesk-org/keepawake-rs" }
[target.'cfg(any(target_os = "windows", target_os = "linux"))'.dependencies]
wallpaper = { git = "https://github.com/21pages/wallpaper.rs" }
wallpaper = { git = "https://github.com/rustdesk-org/wallpaper.rs" }
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
# https://github.com/rustdesk/rustdesk-server-pro/issues/189, using native-tls for better tls support
reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "json", "native-tls", "gzip"], default-features=false }
reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "socks", "json", "native-tls", "gzip"], default-features=false }
[target.'cfg(not(any(target_os = "macos", target_os = "windows")))'.dependencies]
reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "json", "rustls-tls", "rustls-tls-native-roots", "gzip"], default-features=false }
reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "socks", "json", "rustls-tls", "rustls-tls-native-roots", "gzip"], default-features=false }
[target.'cfg(target_os = "linux")'.dependencies]
psimple = { package = "libpulse-simple-binding", version = "2.27" }
pulse = { package = "libpulse-binding", version = "2.27" }
rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" }
async-process = "1.7"
mouce = { git="https://github.com/fufesou/mouce.git" }
evdev = { git="https://github.com/fufesou/evdev" }
evdev = { git="https://github.com/rustdesk-org/evdev" }
dbus = "0.9"
dbus-crossroads = "0.5"
pam = { git="https://github.com/fufesou/pam", optional = true }
pam = { git="https://github.com/rustdesk-org/pam" }
users = { version = "0.11" }
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch", optional = true}
x11rb = {version = "0.12", features = ["all-extensions"], optional = true}
percent-encoding = {version = "2.3", optional = true}
once_cell = {version = "1.18", optional = true}
nix = { version = "0.29", features = ["term", "process"]}
gtk = "0.18"
termios = "0.3"
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.13"
jni = "0.21"
android-wakelock = { git = "https://github.com/21pages/android-wakelock" }
android-wakelock = { git = "https://github.com/rustdesk-org/android-wakelock" }
[workspace]
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/portable"]
exclude = ["vdi/host", "examples/custom_plugin"]
[package.metadata.winres]
LegalCopyright = "Copyright © 2023 Purslane, Inc."
# this FileDescription overrides package.description
FileDescription = "RustDesk"
LegalCopyright = "Copyright © 2024 Purslane Ltd. All rights reserved."
ProductName = "RustDesk"
FileDescription = "RustDesk Remote Desktop"
OriginalFilename = "rustdesk.exe"
[target.'cfg(target_os="windows")'.build-dependencies]
winres = "0.1"

View File

@@ -18,6 +18,7 @@ RUN apt update -y && \
libxcb-shape0-dev \
libxcb-xfixes0-dev \
libasound2-dev \
libpam0g-dev \
libpulse-dev \
make \
cmake \

View File

@@ -1,6 +1,6 @@
<p align="center">
<img src="res/logo-header.svg" alt="RustDesk - Your remote desktop"><br>
<a href="#free-public-servers">Servers</a> •
<a href="#public-servers">Servers</a> •
<a href="#raw-steps-to-build">Build</a> •
<a href="#how-to-build-with-docker">Docker</a> •
<a href="#file-structure">Structure</a> •
@@ -13,8 +13,6 @@ Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitt
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
[![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Frustdesk%2Fbounties%3Fstatus%3Dopen)](https://console.algora.io/org/rustdesk/bounties?status=open)
Yet another remote desktop software, written in Rust. Works out of the box, no configuration required. You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/server), or [write your own rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo).
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
@@ -31,22 +29,6 @@ RustDesk welcomes contribution from everyone. See [CONTRIBUTING.md](docs/CONTRIB
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Free Public Servers
Below are the servers you are using for free, they may change over time. If you are not close to one of these, your network may be slow.
| Location | Vendor | Specification |
| --------- | ------------- | ------------------ |
| Germany | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4 GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## Dev Container
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
If you already have VS Code and Docker installed, you can click the badge above to get started. Clicking will cause VS Code to automatically install the Dev Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use.
Go through [DEVCONTAINER.md](docs/DEVCONTAINER.md) for more info.
## Dependencies
Desktop versions use Flutter or Sciter (deprecated) for GUI, this tutorial is for Sciter only, since it is easier and more friendly to start. Check out our [CI](https://github.com/rustdesk/rustdesk/blob/master/.github/workflows/flutter-build.yml) for building Flutter version.
@@ -77,19 +59,19 @@ Please download Sciter dynamic library yourself.
```sh
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libpam0g-dev
```
### openSUSE Tumbleweed
```sh
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel pam-devel
```
### Fedora 28 (CentOS 8)
```sh
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel gstreamer1-devel gstreamer1-plugins-base-devel pam-devel
```
### Arch (Manjaro)
@@ -180,12 +162,16 @@ Please ensure that you are running these commands from the root of the RustDesk
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter code for desktop and mobile
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: JavaScript for Flutter web client
## Snapshots
## Screenshots
![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png)
![Connection Manager](https://github.com/rustdesk/rustdesk/assets/28412477/db82d4e7-c4bc-4823-8e6f-6af7eadf7651)
![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png)
![Connected to a Windows PC](https://github.com/rustdesk/rustdesk/assets/28412477/9baa91e9-3362-4d06-aa1a-7518edcbd7ea)
![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png)
![File Transfer](https://github.com/rustdesk/rustdesk/assets/28412477/39511ad3-aa9a-4f8c-8947-1cce286a46ad)
![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png)
![TCP Tunneling](https://github.com/rustdesk/rustdesk/assets/28412477/78e8708f-e87e-4570-8373-1360033ea6c5)
## [Public Servers](#public-servers)
RustDesk is supported by a free EU server, graciously provided by [Codext GmbH](https://codext.link/rustdesk?utm_source=github)

View File

@@ -2,7 +2,7 @@
version: 1
script:
- rm -rf ./AppDir || true
- bsdtar -zxvf ../rustdesk-1.2.4.deb
- bsdtar -zxvf rustdesk.deb
- tar -xvf ./data.tar.xz
- mkdir ./AppDir
- mv ./usr ./AppDir/usr
@@ -18,7 +18,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.2.4
version: 1.3.2
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:
@@ -26,18 +26,18 @@ AppDir:
- arm64
allow_unauthenticated: true
sources:
- sourceline: deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe multiverse
- sourceline: deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal main restricted universe multiverse
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32'
- sourceline: deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe multiverse
- sourceline: deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal-updates main restricted universe multiverse
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32'
- sourceline: deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted
- sourceline: deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal-backports main restricted
universe multiverse
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32'
- sourceline: deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted
- sourceline: deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal-security main restricted
universe multiverse
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32'
include:
- libc6
- libc6:arm64
- libgtk-3-0
- libxcb-randr0
- libxdo3
@@ -51,9 +51,15 @@ AppDir:
- libva-x11-2
- libvdpau1
- libgstreamer-plugins-base1.0-0
- gstreamer1.0-pipewire
- libwayland-client0
- libwayland-cursor0
- libwayland-egl1
- libpulse0
- packagekit-gtk3-module
- libcanberra-gtk3-module
- libpam0g
- libdrm2
exclude:
- humanity-icon-theme
- hicolor-icon-theme
@@ -69,8 +75,11 @@ AppDir:
- usr/share/doc/*/TODO.*
runtime:
env:
GIO_MODULE_DIR: $APPDIR/usr/lib/x86_64-linux-gnu/gio/modules/
GIO_MODULE_DIR: /lib64/gio/modules:/usr/lib/aarch64-linux-gnu/gio/modules:$APPDIR/usr/lib/aarch64-linux-gnu/gio/modules
GDK_BACKEND: x11
APPDIR_LIBRARY_PATH: /lib64:/usr/lib/aarch64-linux-gnu:$APPDIR/lib/aarch64-linux-gnu:$APPDIR/lib/aarch64-linux-gnu/security:$APPDIR/lib/systemd:$APPDIR/usr/lib/aarch64-linux-gnu:$APPDIR/usr/lib/aarch64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders:$APPDIR/usr/lib/aarch64-linux-gnu/gstreamer-1.0:$APPDIR/usr/lib/aarch64-linux-gnu/gtk-3.0/3.0.0/immodules:$APPDIR/usr/lib/aarch64-linux-gnu/gtk-3.0/3.0.0/printbackends:$APPDIR/usr/lib/aarch64-linux-gnu/krb5/plugins/preauth:$APPDIR/usr/lib/aarch64-linux-gnu/libcanberra-0.30:$APPDIR/usr/lib/aarch64-linux-gnu/pulseaudio:$APPDIR/usr/lib/aarch64-linux-gnu/sasl2:$APPDIR/usr/lib/aarch64-linux-gnu/vdpau:$APPDIR/usr/lib/rustdesk/lib:$APPDIR/lib/aarch64
GST_PLUGIN_PATH: /lib64/gstreamer-1.0:/usr/lib/aarch64-linux-gnu/gstreamer-1.0:$APPDIR/usr/lib/aarch64-linux-gnu/gstreamer-1.0
GST_PLUGIN_SYSTEM_PATH: /lib64/gstreamer-1.0:/usr/lib/aarch64-linux-gnu/gstreamer-1.0:$APPDIR/usr/lib/aarch64-linux-gnu/gstreamer-1.0
test:
fedora-30:
image: appimagecrafters/tests-env:fedora-30

View File

@@ -2,7 +2,7 @@
version: 1
script:
- rm -rf ./AppDir || true
- bsdtar -zxvf ../rustdesk-1.2.4.deb
- bsdtar -zxvf rustdesk.deb
- tar -xvf ./data.tar.xz
- mkdir ./AppDir
- mv ./usr ./AppDir/usr
@@ -18,7 +18,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.2.4
version: 1.3.2
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:
@@ -26,19 +26,20 @@ AppDir:
- amd64
allow_unauthenticated: true
sources:
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-backports main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted
universe multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-security main restricted
universe multiverse
- sourceline: deb http://ppa.launchpad.net/pipewire-debian/pipewire-upstream/ubuntu
bionic main
include:
# https://github.com/rustdesk/rustdesk/issues/9103
# Because of APPDIR_LIBRARY_PATH, this libc6 is not used, use LD_PRELOAD: $APPDIR/usr/lib/x86_64-linux-gnu/libc.so.6 may help, If you have time, please have a try.
# We modify APPDIR_LIBRARY_PATH to use system lib first because gst crashed if not doing so, but you can try to change it.
- libc6:amd64
- libgtk-3-0
- libxcb-randr0
@@ -54,9 +55,14 @@ AppDir:
- libvdpau1
- libgstreamer-plugins-base1.0-0
- gstreamer1.0-pipewire
- libwayland-client0
- libwayland-cursor0
- libwayland-egl1
- libpulse0
- packagekit-gtk3-module
- libcanberra-gtk3-module
- libpam0g
- libdrm2
exclude:
- humanity-icon-theme
- hicolor-icon-theme
@@ -72,8 +78,11 @@ AppDir:
- usr/share/doc/*/TODO.*
runtime:
env:
GIO_MODULE_DIR: $APPDIR/usr/lib/x86_64-linux-gnu/gio/modules/
GIO_MODULE_DIR: /lib64/gio/modules:/usr/lib/x86_64-linux-gnu/gio/modules:$APPDIR/usr/lib/x86_64-linux-gnu/gio/modules
GDK_BACKEND: x11
APPDIR_LIBRARY_PATH: /lib64:/usr/lib/x86_64-linux-gnu:$APPDIR/lib/x86_64-linux-gnu:$APPDIR/lib/x86_64-linux-gnu/security:$APPDIR/lib/systemd:$APPDIR/usr/lib/x86_64-linux-gnu:$APPDIR/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders:$APPDIR/usr/lib/x86_64-linux-gnu/gstreamer-1.0:$APPDIR/usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules:$APPDIR/usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/printbackends:$APPDIR/usr/lib/x86_64-linux-gnu/krb5/plugins/preauth:$APPDIR/usr/lib/x86_64-linux-gnu/libcanberra-0.30:$APPDIR/usr/lib/x86_64-linux-gnu/pulseaudio:$APPDIR/usr/lib/x86_64-linux-gnu/sasl2:$APPDIR/usr/lib/x86_64-linux-gnu/vdpau:$APPDIR/usr/lib/rustdesk/lib:$APPDIR/lib/x86_64
GST_PLUGIN_PATH: /lib64/gstreamer-1.0:/usr/lib/x86_64-linux-gnu/gstreamer-1.0:$APPDIR/usr/lib/x86_64-linux-gnu/gstreamer-1.0
GST_PLUGIN_SYSTEM_PATH: /lib64/gstreamer-1.0:/usr/lib/x86_64-linux-gnu/gstreamer-1.0:$APPDIR/usr/lib/x86_64-linux-gnu/gstreamer-1.0
test:
fedora-30:
image: appimagecrafters/tests-env:fedora-30

View File

@@ -25,17 +25,22 @@ flutter_build_dir_2 = f'flutter/{flutter_build_dir}'
skip_cargo = False
def get_arch() -> str:
custom_arch = os.environ.get("ARCH")
def get_deb_arch() -> str:
custom_arch = os.environ.get("DEB_ARCH")
if custom_arch is None:
return "amd64"
return custom_arch
def get_deb_extra_depends() -> str:
custom_arch = os.environ.get("DEB_ARCH")
if custom_arch == "armhf": # for arm32v7 libsciter-gtk.so
return ", libatomic1"
return ""
def system2(cmd):
err = os.system(cmd)
if err != 0:
print(f"Error occurred when executing: {cmd}. Exiting.")
exit_code = os.system(cmd)
if exit_code != 0:
sys.stderr.write(f"Error occurred when executing: `{cmd}`. Exiting.\n")
sys.exit(-1)
@@ -48,15 +53,7 @@ def get_version():
def parse_rc_features(feature):
available_features = {
'PrivacyMode': {
'platform': ['windows'],
'zip_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.3'
'/TempTopMostWindow_x64.zip',
'checksum_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.3/checksum_md5',
'include': ['WindowInjection.dll'],
}
}
available_features = {}
apply_features = {}
if not feature:
feature = []
@@ -80,8 +77,9 @@ def parse_rc_features(feature):
return get_all_features()
elif isinstance(feature, list):
if windows:
# force add PrivacyMode
feature.append('PrivacyMode')
# download third party is deprecated, we use github ci instead.
# feature.append('PrivacyMode')
pass
for feat in feature:
if isinstance(feat, str) and feat.upper() == 'ALL':
return get_all_features()
@@ -106,7 +104,7 @@ def make_parser():
nargs='+',
default='',
help='Integrate features, windows only.'
'Available: PrivacyMode. Special value is "ALL" and empty "". Default is empty.')
'Available: [Not used for now]. Special value is "ALL" and empty "". Default is empty.')
parser.add_argument('--flutter', action='store_true',
help='Build flutter package', default=False)
parser.add_argument(
@@ -116,9 +114,9 @@ def make_parser():
'' if windows or osx else ', need libva-dev, libvdpau-dev.')
)
parser.add_argument(
'--gpucodec',
'--vram',
action='store_true',
help='Enable feature gpucodec, only available on windows now.'
help='Enable feature vram, only available on windows now.'
)
parser.add_argument(
'--portable',
@@ -130,21 +128,17 @@ def make_parser():
action='store_true',
help='Build with unix file copy paste feature'
)
parser.add_argument(
'--flatpak',
action='store_true',
help='Build rustdesk libs with the flatpak feature enabled'
)
parser.add_argument(
'--appimage',
action='store_true',
help='Build rustdesk libs with the appimage feature enabled'
)
parser.add_argument(
'--skip-cargo',
action='store_true',
help='Skip cargo build process, only flutter version + Linux supported currently'
)
if windows:
parser.add_argument(
'--skip-portable-pack',
action='store_true',
help='Skip packing, only flutter version + Windows supported'
)
parser.add_argument(
"--package",
type=str
@@ -188,6 +182,9 @@ def generate_build_script_for_docker():
system2("bash /tmp/build.sh")
# Downloading third party resources is deprecated.
# We can use this function in an offline build environment.
# Even in an online environment, we recommend building third-party resources yourself.
def download_extract_features(features, res_dir):
import re
@@ -271,15 +268,10 @@ def get_features(args):
features = ['inline'] if not args.flutter else []
if args.hwcodec:
features.append('hwcodec')
if args.gpucodec:
features.append('gpucodec')
if args.vram:
features.append('vram')
if args.flutter:
features.append('flutter')
features.append('flutter_texture_render')
if args.flatpak:
features.append('flatpak')
if args.appimage:
features.append('appimage')
if args.unix_file_copy_paste:
features.append('unix-file-copy-paste')
print("features:", features)
@@ -291,14 +283,17 @@ def generate_control_file(version):
system2('/bin/rm -rf %s' % control_file_path)
content = """Package: rustdesk
Section: net
Priority: optional
Version: %s
Architecture: %s
Maintainer: rustdesk <info@rustdesk.com>
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, libappindicator3-1, gstreamer1.0-pipewire
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, gstreamer1.0-pipewire%s
Recommends: libayatana-appindicator3-1
Description: A remote control software.
""" % (version, get_arch())
""" % (version, get_deb_arch(), get_deb_extra_depends())
file = open(control_file_path, "w")
file.write(content)
file.close()
@@ -338,8 +333,6 @@ def build_flutter_deb(version, features):
'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
system2(
'cp ../res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop')
system2(
'cp ../res/com.rustdesk.RustDesk.policy tmpdeb/usr/share/polkit-1/actions/')
system2(
'cp ../res/startwm.sh tmpdeb/etc/rustdesk/')
system2(
@@ -383,8 +376,6 @@ def build_deb_from_folder(version, binary_folder):
'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
system2(
'cp ../res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop')
system2(
'cp ../res/com.rustdesk.RustDesk.policy tmpdeb/usr/share/polkit-1/actions/')
system2(
"echo \"#!/bin/sh\" >> tmpdeb/usr/share/rustdesk/files/polkit && chmod a+x tmpdeb/usr/share/rustdesk/files/polkit")
@@ -410,9 +401,11 @@ def build_flutter_dmg(version, features):
"cp target/release/liblibrustdesk.dylib target/release/librustdesk.dylib")
os.chdir('flutter')
system2('flutter build macos --release')
'''
system2(
"create-dmg --volname \"RustDesk Installer\" --window-pos 200 120 --window-size 800 400 --icon-size 100 --app-drop-link 600 185 --icon RustDesk.app 200 190 --hide-extension RustDesk.app rustdesk.dmg ./build/macos/Build/Products/Release/RustDesk.app")
os.rename("rustdesk.dmg", f"../rustdesk-{version}.dmg")
'''
os.chdir("..")
@@ -427,7 +420,7 @@ def build_flutter_arch_manjaro(version, features):
system2('HBB=`pwd`/.. FLUTTER=1 makepkg -f')
def build_flutter_windows(version, features):
def build_flutter_windows(version, features, skip_portable_pack):
if not skip_cargo:
system2(f'cargo build --features {features} --lib --release')
if not os.path.exists("target/release/librustdesk.dll"):
@@ -438,6 +431,8 @@ def build_flutter_windows(version, features):
os.chdir('..')
shutil.copy2('target/release/deps/dylib_virtual_display.dll',
flutter_build_dir_2)
if skip_portable_pack:
return
os.chdir('libs/portable')
system2('pip3 install -r requirements.txt')
system2(
@@ -487,7 +482,7 @@ def main():
os.chdir('../../..')
if flutter:
build_flutter_windows(version, features)
build_flutter_windows(version, features, args.skip_portable_pack)
return
system2('cargo build --release --features ' + features)
# system2('upx.exe target/release/rustdesk.exe')

View File

@@ -1,9 +1,11 @@
#[cfg(windows)]
fn build_windows() {
let file = "src/platform/windows.cc";
cc::Build::new().file(file).compile("windows");
let file2 = "src/platform/windows_delete_test_cert.cc";
cc::Build::new().file(file).file(file2).compile("windows");
println!("cargo:rustc-link-lib=WtsApi32");
println!("cargo:rerun-if-changed={}", file);
println!("cargo:rerun-if-changed={}", file2);
}
#[cfg(target_os = "macos")]

View File

@@ -0,0 +1,87 @@
# 贡献者公约行为准则
## 我们的承诺
身为社区成员、贡献者和领袖,我们承诺使社区参与者不受骚扰,无论其年龄、体型、可见或不可见的缺陷、族裔、性征、性别认同和表达、经验水平、教育程度、社会与经济地位、国籍、相貌、种族、种姓、肤色、宗教信仰、性倾向或性取向如何。
我们承诺以有助于建立开放、友善、多样化、包容、健康社区的方式行事和互动。
## 我们的标准
有助于为我们的社区创造积极环境的行为例子包括但不限于:
* 表现出对他人的同情和善意
* 尊重不同的主张、观点和感受
* 提出和大方接受建设性意见
* 承担责任并向受我们错误影响的人道歉
* 注重社区共同诉求,而非个人得失
不当行为例子包括:
* 使用情色化的语言或图像,及性引诱或挑逗
* 嘲弄、侮辱或诋毁性评论,以及人身或政治攻击
* 公开或私下的骚扰行为
* 未经他人明确许可,公布他人的私人信息,如物理或电子邮件地址
* 其他有理由认定为违反职业操守的不当行为
## 责任和权力
社区领袖有责任解释和落实我们所认可的行为准则,并妥善公正地对他们认为不当、威胁、冒犯或有害的任何行为采取纠正措施。
社区领导有权力和责任删除、编辑或拒绝或拒绝与本行为准则不相符的评论comment、提交commits、代码、维基wiki编辑、议题issues或其他贡献并在适当时机知采取措施的理由。
## 适用范围
本行为准则适用于所有社区场合,也适用于在公共场所代表社区时的个人。
代表社区的情形包括使用官方电子邮件地址、通过官方社交媒体帐户发帖或在线上或线下活动中担任指定代表。
## 监督
辱骂、骚扰或其他不可接受的行为可通过[info@rustdesk.com](mailto:info@rustdesk.com)向负责监督的社区领袖报告。 所有投诉都将得到及时和公平的审查和调查。
所有社区领袖都有义务尊重任何事件报告者的隐私和安全。
## 处理方针
社区领袖将遵循下列社区处理方针来明确他们所认定违反本行为准则的行为的处理方式:
### 1. 纠正
**社区影响**: 使用不恰当的语言或其他在社区中被认定为不符合职业道德或不受欢迎的行为。
**处理意见**: 由社区领袖发出非公开的书面警告,明确说明违规行为的性质,并解释举止如何不妥。或将要求公开道歉。
### 2. 警告
**社区影响**: 单个或一系列违规行为。
**处理意见**: 警告并对连续性行为进行处理。在指定时间内,不得与相关人员互动,包括主动与行为准则执行者互动。这包括避免在社区场所和外部渠道中的互动。违反这些条款可能会导致临时或永久封禁。
### 3. 临时封禁
**社区影响**: 严重违反社区准则,包括持续的不当行为。
**处理意见**: 在指定时间内,暂时禁止与社区进行任何形式的互动或公开交流。在此期间,不得与相关人员进行公开或私下互动,包括主动与行为准则执行者互动。违反这些条款可能会导致永久封禁。
### 4. 永久封禁
**社区影响**: 行为模式表现出违反社区准则,包括持续的不当行为、骚扰个人或攻击或贬低某个类别的个体。
**处理意见**: 永久禁止在社区内进行任何形式的公开互动。
## 参见
本行为准则改编自[参与者公约][homepage]2.0 版, 参见
[https://www.contributor-covenant.org/zh-cn/version/2/0/code_of_conduct.html][v2.0].
指导方针借鉴自[Mozilla纪检分级][Mozilla CoC].
有关本行为准则的常见问题的答案,参见 [https://www.contributor-covenant.org/faq][FAQ]。 其他语言翻译参见[https://www.contributor-covenant.org/translations][translations]。
[homepage]: https://www.contributor-covenant.org
[v2.0]: https://www.contributor-covenant.org/zh-cn/version/2/0/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

32
docs/CONTRIBUTING-ZH.md Normal file
View File

@@ -0,0 +1,32 @@
# 为RustDesk做贡献
Rust欢迎每一位贡献者如果您有意向为我们做出贡献请遵循以下指南
## 贡献方式
对 RustDesk 或其依赖项的贡献需要通过 GitHub 的 Pull Request (PR) 的形式提交。每个 PR 都会由核心贡献者(即有权限合并代码的人)进行审核,审核通过后代码会合并到主分支,或者您会收到需要修改的反馈。所有贡献者,包括核心贡献者,提交的代码都应遵循此流程。
如果您希望处理某个问题,请先在对应的 GitHub issue 下发表评论,声明您将处理该问题,以避免该问题被多位贡献者重复处理。
## PR 注意事项
- 从 master 分支创建一个新的分支并在提交PR之前如果需要将您的分支 变基(rebase) 到最新的 master 分支。如果您的分支无法顺利合并到 master 分支,您可能会被要求更新您的代码。
- 每次提交的改动应该尽可能少,并且要保证每次提交的代码都是正确的(即每个 commit 都应能成功编译并通过测试)。
- 每个提交都应附有开发者证书签名(http://developercertificate.org), 表明您(以及您的雇主,若适用)同意遵守项目[许可证条款](../LICENCE)。在使用 git 提交代码时,可以通过在 `git commit` 时使用 `-s` 选项加入签名
- 如果您的 PR 未被及时审核,或需要指定的人员进行审核,您可以通过在 PR 或评论中 @ 提到相关审核者,以及发送[电子邮件](mailto:info@rustdesk.com)的方式请求审核。
- 请为修复的 bug 或新增的功能添加相应的测试用例。
有关具体的 git 使用说明,请参考[GitHub workflow 101](https://github.com/servo/servo/wiki/GitHub-workflow).
## 行为准则
请遵守项目的[贡献者公约行为准则](./CODE_OF_CONDUCT-ZH.md)。
## 沟通渠道
RustDesk 的贡献者主要通过 [Discord](https://discord.gg/nDceKgxnkV) 进行交流。

View File

@@ -27,14 +27,6 @@
[**BINARY تنزيل**](https://github.com/rustdesk/rustdesk/releases)
## خوادم مفتوحة ومجانية
فيما يلي الخوادم التي تستخدمها مجانًا، وقد تتغير طوال الوقت. إذا لم تكن قريبًا من أحد هؤلاء، فقد تكون شبكتك بطيئة.
| الموقع | المورد | المواصفات |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## التبعيات
لواجهة المستخدم الرسومية [sciter](https://sciter.com/) نسخة سطح المكتب تستخدم

View File

@@ -22,14 +22,6 @@ Projekt RustDesk vítá přiložení ruky k dílu od každého. Jak začít se d
[**STAHOVÁNÍ ZKOMPILOVANÝCH APLIKACÍ**](https://github.com/rustdesk/rustdesk/releases)
## Veřejné, zdarma službu nabízející servery
Níže jsou uvedeny servery zdarma k vašemu použití (údaje se mohou v čase měnit). Pokud se nenacházíte v oblastech světa poblíž nich, spojení může být pomalé.
| umístění | dodavatel | parametry |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## Softwarové součásti, na kterých závisí
Varianta pro počítač používá pro grafické uživatelské rozhraní [sciter](https://sciter.com/) stáhněte si potřebnou knihovnu.

View File

@@ -19,14 +19,6 @@ RustDesk hilser bidrag fra alle velkommen. Se [`docs/CONTRIBUTING.md`](docs/CONT
[**PROGRAM DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
## Gratis offentlige servere
Nedenfor er de servere, du bruger gratis, det kan ændre sig med tiden. Hvis du ikke er tæt på en af disse, kan dit netværk være langsomt.
| Beliggenhed | Udbyder | Specifikation |
| ---------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
## Afhængigheder
Desktopversioner bruger [sciter](https://sciter.com/) eller Flutter til GUI, denne vejledning er kun for Sciter.

View File

@@ -29,22 +29,6 @@ RustDesk heißt jegliche Mitarbeit willkommen. Schauen Sie sich [CONTRIBUTING-DE
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Freie öffentliche Server
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 |
| --------- | ------------- | ------------------ |
| Deutschland | [Hetzner](https://www.hetzner.com/de/) | 2 vCPU / 4 GB RAM |
| Ukraine (Kiew) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## Dev-Container
[![In Dev-Containern öffnen](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
Wenn Sie VS Code und Docker bereits installiert haben, können Sie auf das Abzeichen oben klicken, um loszulegen. Wenn Sie darauf klicken, wird VS Code automatisch die Dev-Container-Erweiterung installieren, den Quellcode in ein Container-Volume klonen und einen Dev-Container für die Verwendung aufsetzen.
Weitere Informationen finden Sie in [DEVCONTAINER-DE.md](DEVCONTAINER-DE.md).
## Abhängigkeiten
Desktop-Versionen verwenden [Sciter](https://sciter.com/) oder Flutter für die GUI, dieses Tutorial ist nur für Sciter.

View File

@@ -19,14 +19,6 @@ RustDesk bonvenigas kontribuon de ĉiuj. Vidu [`docs/CONTRIBUTING.md`](CONTRIBUT
[**BINARA ELŜUTO**](https://github.com/rustdesk/rustdesk/releases)
## Senpagaj publikaj serviloj
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 |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dependantaĵoj
La labortabla versio uzas [sciter](https://sciter.com/) por la interfaco, bonvolu elŝuti la bibliotekon dinamikan sciter.

View File

@@ -25,15 +25,6 @@ RustDesk agradece la contribución de todo el mundo. Lee [`docs/CONTRIBUTING.md`
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Servidores gratis de uso público
A continuación se muestran los servidores gratuitos, pueden cambiar a medida que pasa el tiempo. Si no estás cerca de uno de ellos, tu conexión puede ser lenta.
| Ubicación | Compañía | Especificación |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dependencias
La versión Desktop usa [Sciter](https://sciter.com/) o Flutter para el GUI, este tutorial es solo para Sciter.

View File

@@ -25,13 +25,6 @@
[دریافت نرم‌افزار](https://github.com/rustdesk/rustdesk/releases)
## سرورهای عمومی رایگان
شما مي‌توانید از سرورهای زیر به رایگان استفاده کنید. این لیست ممکن است به مرور زمان تغییر می‌کند. اگر به این سرورها نزدیک نیستید، ممکن است اتصال شما کند باشد.
| موقعیت | سرویس دهنده | مشخصات |
| --------- | ------------- | ------------------ |
| آلمان | Hetzner | 2 vCPU / 4GB RAM |
## وابستگی ها
نسخه‌های رومیزی از [sciter](https://sciter.com/) برای رابط کاربری گرافیکی استفاده می‌کنند. خواهشمندیم کتابخانه‌ی پویای sciter را خودتان دانلود کنید از این منابع دریافت کنید.

View File

@@ -19,14 +19,6 @@ RustDesk toivottaa avustukset tervetulleiksi kaikilta. Katso lisätietoja [`docs
[**BINAARILATAUS**](https://github.com/rustdesk/rustdesk/releases)
## Vapaita julkisia palvelimia
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 |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Riippuvuudet
Desktop-versiot käyttävät [sciter](https://sciter.com/) graafisena käyttöliittymänä, lataa sciter-dynaaminen kirjasto itsellesi.

View File

@@ -19,14 +19,6 @@ RustDesk accueille les contributions de tout le monde. Voir [`docs/CONTRIBUTING.
[**TÉLÉCHARGEMENT BINAIRE**](https://github.com/rustdesk/rustdesk/releases)
## Serveurs publics libres
Ci-dessous se trouvent les serveurs que vous utilisez gratuitement, cela peut changer au fil du temps. Si vous n'êtes pas proche de l'un d'entre eux, votre réseau peut être lent.
| Location | Vendor | Specification |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
## Dépendances
Les versions de bureau utilisent [sciter](https://sciter.com/) pour l'interface graphique, veuillez télécharger la bibliothèque dynamique sciter vous-même.

View File

@@ -29,22 +29,6 @@
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Δωρεάν δημόσιοι διακομιστές
Παρακάτω είναι οι διακομιστές που χρησιμοποιούνται δωρεάν, ενδέχεται να αλλάξουν με την πάροδο του χρόνου. Εάν δεν είστε κοντά σε ένα από αυτούς, το δίκτυό σας ίσως να είναι αργό.
| Περιοχή | Πάροχος | Προδιαγραφές |
| --------- | ------------- | ------------------ |
| Γερμανία | Hetzner | 2 vCPU / 4GB RAM |
| Ουκρανία (Κίεβο) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dev Container
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
Αν έχετε εγκατεστημένα το VS Code και το Docker, μπορείτε να ξεκινήσετε κάνοντας κλικ στην παραπάνω εικόνα. Αυτό θα έχει ως αποτέλεσμα, το VS Code να εγκαταστήσει αυτόματα την επέκταση Dev Containers, εάν χρειάζεται, θα κλωνοποιήσει τον πηγαίο κώδικα σε έναν νέο container και θα εκκινήσει ένα Dev Container για χρήση προγραμματισμού.
Για περισσότερες πληροφορίες μεταβείτε στο [DEVCONTAINER.md](docs/DEVCONTAINER.md).
## Προαπαιτούμενα για build
Στις παραθυρικές εκδόσεις χρησιμοποιείται είτε το [sciter](https://sciter.com/) είτε το Flutter, τα παρακάτω βήματα είναι μόνο για το Sciter.

View File

@@ -27,14 +27,6 @@ A RustDesk szívesen fogad minden contributiont, támogatást mindenkitől. Lás
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Ingyenes publikus szerverek
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ó |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dependencies
Az asztali verziók [sciter](https://sciter.com/)-t használnak a GUI-hoz, kérlek telepítsd a dynamikus könyvtárat magad.

View File

@@ -31,20 +31,6 @@ RustDesk mengajak semua orang untuk ikut berkontribusi. Lihat [`docs/CONTRIBUTIN
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Server Publik Gratis
Di bawah ini merupakan server gratis yang bisa kamu gunakan, seiring dengan waktu mungkin akan terjadi perubahan spesifikasi pada setiap server yang ada. Jika lokasi kamu berada jauh dengan salah satu server yang tersedia, kemungkinan koneksi akan terasa lambat ketika melakukan proses remote.
| Lokasi | Penyedia | Spesifikasi |
| --------- | ------------- | ------------------ |
| Jerman | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4GB RAM |
| Ukraina (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dev Container
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
Apabila PC kamu sudah terinstal VS Code dan Docker, kamu bisa mengklik badge yang ada diatas untuk memulainya. Dengan mengklik badge tersebut secara otomatis akan menginstal ekstensi pada VS Code, lakukan kloning (clone) source code kedalam container volume, dan aktifkan dev container untuk menggunakannya.
## Dependensi
Pada versi desktop, antarmuka pengguna (GUI) menggunakan [Sciter](https://sciter.com/) atau flutter

View File

@@ -4,8 +4,8 @@
<a href="#passaggi-per-la-compilazione">Compilazione</a> •
<a href="#come-compilare-con-docker">Docker</a> •
<a href="#struttura-dei-file">Struttura</a> •
<a href="#screenshots">Schermate</a><br>
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
<a href="#schermate">Schermate</a><br>
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-DA.md">Dansk</a>] | [<a href="README-GR.md">Ελληνικά</a>] | [<a href="README-TR.md">Türkçe</a>]<br>
<b>Abbiamo bisogno del tuo aiuto per tradurre questo file README e la <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">UI RustDesk</a> nella tua lingua nativa</b>
</p>
@@ -13,28 +13,29 @@ Chatta con noi su: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
Ancora un altro software per il controllo remoto del desktop, scritto in Rust.
Funziona immediatamente, nessuna configurazione richiesta. Hai il pieno controllo dei tuoi dati, senza preoccupazioni per la sicurezza.
Puoi usare il nostro server rendezvous/relay, [configurare il tuo server](https://rustdesk.com/server) o [realizzare il tuo server rendezvous/relay](https://github.com/rustdesk/rustdesk-server-demo).
[![Bounties aperti](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Frustdesk%2Fbounties%3Fstatus%3Dopen)](https://console.algora.io/org/rustdesk/bounties?status=open)
RustDesk accoglie il contributo di tutti.
Per ulteriori informazioni su come iniziare a contribuire, vedi [`docs/CONTRIBUTING-IT.md`](CONTRIBUTING.md).
Ancora un altro software per il controllo remoto del desktop, scritto in Rust. Funziona immediatamente, nessuna configurazione richiesta. Hai il pieno controllo dei tuoi dati, senza preoccupazioni per la sicurezza. Puoi usare il nostro server rendezvous/relay, [configurare il tuo server](https://rustdesk.com/server) o [realizzare il tuo server rendezvous/relay](https://github.com/rustdesk/rustdesk-server-demo).
[**DOWNLOAD PROGRAMMA**](https://github.com/rustdesk/rustdesk/releases)
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
## Server pubblici gratuiti
RustDesk accoglie il contributo di tutti. Per ulteriori informazioni su come iniziare a contribuire, vedi [CONTRIBUTING.md](CONTRIBUTING-IT.md).
Qui sotto trovi i server che possono essere usati gratuitamente, la lista potrebbe cambiare nel tempo.
Se non sei vicino a uno di questi server, la connessione potrebbe essere lenta.
[**FAQ**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
| Posizione | Venditore | Specifiche |
| --------- | ------------- | ------------------ |
| Germania | Hetzner | 2 vCPU / 4GB RAM |
| Ucraina (Kyev) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
[**SCARICA PROGRAMMA**](https://github.com/rustdesk/rustdesk/releases)
[**SCARICA NIGHTLY**](https://github.com/rustdesk/rustdesk/releases/tag/nightly)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Dipendenze
La versione Desktop usa per la GUI [sciter](https://sciter.com/), per favore scarica la libreria dinamica sciter.
Le versioni desktop utilizzano Flutter o Sciter (deprecato) per l'interfaccia utente, questo tutorial è solo per Sciter, poiché è più facile per iniziare. Controlla il nostro [CI](https://github.com/rustdesk/rustdesk/blob/master/.github/workflows/flutter-build.yml) per la compilazione della versione Flutter.
Scarica la libreria dinamica Sciter.
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
@@ -51,12 +52,22 @@ La versione Desktop usa per la GUI [sciter](https://sciter.com/), per favore sca
- Esegui `cargo run`
## [Build](https://rustdesk.com/docs/en/dev/build/)
## Come compilare in Linux
### Ubuntu 18 (Debian 10)
```sh
sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
```
### openSUSE Tumbleweed
```sh
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
```
### Fedora 28 (CentOS 8)
@@ -125,10 +136,7 @@ Quindi, ogni volta che devi compilare l'applicazione, esegui il seguente comando
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
Tieni presente che la prima build potrebbe richiedere più tempo prima che le dipendenze vengano memorizzate nella cache, le build successive saranno più veloci.
Inoltre, se hai bisogno di specificare argomenti diversi per il comando build, puoi farlo alla fine del comando nella posizione `<OPTIONAL-ARGS>`.
Ad esempio, se vuoi creare una versione di rilascio ottimizzata, esegui il comando precedentemente indicato seguito da `--release`.
L'eseguibile generato sarà creato nella cartella destinazione del sistema e può essere eseguito con:
Tieni presente che la prima build potrebbe richiedere più tempo prima che le dipendenze vengano memorizzate nella cache, le build successive saranno più veloci. Inoltre, se hai bisogno di specificare argomenti diversi per il comando build, puoi farlo alla fine del comando nella posizione `<OPTIONAL-ARGS>`. Ad esempio, se vuoi creare una versione di rilascio ottimizzata, esegui il comando precedentemente indicato seguito da `--release`. L'eseguibile generato sarà creato nella cartella destinazione del sistema e può essere eseguito con:
```sh
target/debug/rustdesk
@@ -140,19 +148,21 @@ Oppure, se stai avviando un eseguibile di rilascio:
target/release/rustdesk
```
Assicurati di eseguire questi comandi dalla radice del repository RustDesk, altrimenti l'applicazione potrebbe non essere in grado di trovare le risorse richieste.
Nota inoltre che altri sottocomandi cargo come `install` o `run` non sono attualmente supportati tramite questo metodo poiché installerebbero o eseguirebbero il programma all'interno del container anziché nell'host.
Assicurati di eseguire questi comandi dalla radice del repository RustDesk, altrimenti l'applicazione potrebbe non essere in grado di trovare le risorse richieste. Nota inoltre che altri sottocomandi cargo come `install` o `run` non sono attualmente supportati tramite questo metodo poiché installerebbero o eseguirebbero il programma all'interno del container anziché nell'host.
## Struttura dei file
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: codec video, config, wrapper tcp/udp, protobuf, funzioni per il trasferimento file, e altre funzioni utili.
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: cattura dello schermo
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: controllo tastiera/mouse specifico della piattaforma
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
- **[libs/clipboard](https://github.com/rustdesk/rustdesk/tree/master/libs/clipboard)**: implementazione del copia e incolla dei file per Windows, Linux, macOS.
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: Sciter UI obsoleto (deprecato)
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: servizi audio/appunti/input/video e connessioni di rete
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: avvio di una connessione peer
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Comunica con [rustdesk-server](https://github.com/rustdesk/rustdesk-server), attende la connessione remota diretta (TCP hole punching) oppure indiretta (relayed)
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: comunica con [rustdesk-server](https://github.com/rustdesk/rustdesk-server), attende la connessione remota diretta (TCP hole punching) oppure indiretta (relayed)
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: codice specifico della piattaforma
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: codice Flutter per desktop e mobile
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: JavaScript per client web Flutter
## Schermate

View File

@@ -1,67 +1,73 @@
<p align="center">
<img src="../res/logo-header.svg" alt="RustDesk - Your remote desktop"><br>
<img src="../res/logo-header.svg" alt="RustDesk - あなたのためのリモートデスクトップ"><br>
<a href="#free-public-servers">Servers</a> •
<a href="#raw-steps-to-build">Build</a> •
<a href="#how-to-build-with-docker">Docker</a> •
<a href="#file-structure">Structure</a> •
<a href="#snapshot">Snapshot</a><br>
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
<b>このREADMEをあなたの母国語に翻訳するために、あなたの助けが必要です。</b>
[<a href="docs/README-UA.md">Українська</a>] | [<a href="docs/README-CS.md">česky</a>] | [<a href="docs/README-ZH.md">中文</a>] | [<a href="docs/README-HU.md">Magyar</a>] | [<a href="docs/README-ES.md">Español</a>] | [<a href="docs/README-FA.md">فارسی</a>] | [<a href="docs/README-FR.md">Français</a>] | [<a href="docs/README-DE.md">Deutsch</a>] | [<a href="docs/README-PL.md">Polski</a>] | [<a href="docs/README-ID.md">Indonesian</a>] | [<a href="docs/README-FI.md">Suomi</a>] | [<a href="docs/README-ML.md">മലയാളം</a>] | [<a href="docs/README-JP.md">日本語</a>] | [<a href="docs/README-NL.md">Nederlands</a>] | [<a href="docs/README-IT.md">Italiano</a>] | [<a href="docs/README-RU.md">Русский</a>] | [<a href="docs/README-PTBR.md">Português (Brasil)</a>] | [<a href="docs/README-EO.md">Esperanto</a>] | [<a href="docs/README-KR.md">한국어</a>] | [<a href="docs/README-AR.md">العربي</a>] | [<a href="docs/README-VN.md">Tiếng Việt</a>] | [<a href="docs/README-DA.md">Dansk</a>] | [<a href="docs/README-GR.md">Ελληνικά</a>] | [<a href="docs/README-TR.md">Türkçe</a>]<br>
<b>READMEや<a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a>、 <a href="https://github.com/rustdesk/doc.rustdesk.com">RustDesk Doc</a>の翻訳者を歓迎します!</b>
</p>
Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
私たちと話す: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
Rustで書かれた、設定不要ですぐに使えるリモートデスクトップソフトウェアです。自分のデータを完全にコントロールでき、セキュリティの心配もありません。私たちのランデブー/リレーサーバを使うことも、[自分で設定する](https://rustdesk.com/server) ことも、 [自分でランデブー/リレーサーバを書くこともできます](https://github.com/rustdesk/rustdesk-server-demo)。
Rustで書かれた、設定不要ですぐに使えるリモートデスクトップソフトウェアです。自分のデータを完全にコントロールでき、セキュリティの心配もありません。私たちのランデブー/リレーサーバを使うことも、[自分でサーバーをセットアップする](https://rustdesk.com/server) ことも、 [自分でランデブー/リレーサーバを作成する](https://github.com/rustdesk/rustdesk-server-demo)こともできます
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
RustDeskは誰からの貢献歓迎します。 貢献するには [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) を参照してください。
RustDeskは皆さんの貢献歓迎します。
貢献の方法については[CONTRIBUTING.md](docs/CONTRIBUTING.md)をご確認ください。
[**RustDeskはどの様に動くのか?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
[**よくある質問**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
[**パッケージのダウンロード**](https://github.com/rustdesk/rustdesk/releases)
## 無料のパブリックサーバー
[**ナイトリービルド**](https://github.com/rustdesk/rustdesk/releases/tag/nightly)
下記のサーバーは、無料で使用できますが、後々変更されることがあります。これらのサーバーから遠い場合、接続が遅い可能性があります。
| Location | Vendor | Specification |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="F-Droidで入手する"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## 依存関係
デスクトップ版ではGUIに [sciter](https://sciter.com/) が使われています。 sciter dynamic library をダウンロードしてください。
デスクトップ版ではGUIにFlutterまたはSciter(非推奨)を使用しますが、チュートリアルでは分かりやすく、簡単なSciterのみを対象に解説しています。Flutterでのビルド方法については[CI](https://github.com/rustdesk/rustdesk/blob/master/.github/workflows/flutter-build.yml)をご覧ください。
Sciter dynamic libraryを事前にダウンロードしてください。
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
モバイル版はFlutterを利用します。デスクトップ版もSciterからFlutterへマイグレーション予定です。
[macOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
## ビルド手順
- Rust開発環境とC ++ビルド環境を準備します
- Rust開発環境とC++ビルド環境を準備します
- [vcpkg](https://github.com/microsoft/vcpkg), をインストールし、 `VCPKG_ROOT` 環境変数を正しく設定します。
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
- Linux/MacOS: vcpkg install libvpx libyuv opus aom
- run `cargo run`
- [vcpkg](https://github.com/microsoft/vcpkg)をインストールし、環境変数に`VCPKG_ROOT`設定します。
その後、以下のコマンドを実行します。
- Windowsの場合: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
- Linux/macOSの場合: vcpkg install libvpx libyuv opus aom
- `cargo run`を実行します。
## [ビルド](https://rustdesk.com/docs/en/dev/build/)
## Linuxでのビルド手順
## Linuxでのビルド方法
### Ubuntu 18 (Debian 10)
```sh
sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
```
### openSUSE Tumbleweed
```sh
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
```
### Fedora 28 (CentOS 8)
@@ -76,7 +82,7 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pipewire
```
### Install vcpkg
### vcpkgのインストール
```sh
git clone https://github.com/microsoft/vcpkg
@@ -88,7 +94,7 @@ export VCPKG_ROOT=$HOME/vcpkg
vcpkg/vcpkg install libvpx libyuv opus aom
```
### Fix libvpx (For Fedora)
### libvpxの修正 (Fedoraのみ)
```sh
cd vcpkg/buildtrees/libvpx/src
@@ -114,9 +120,9 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
## Dockerでビルドする方法
## Dockerでビルド方法
リポジトリクローンを作成し、Dockerコンテナを構築することから始めます
リポジトリクローンし、Dockerコンテナを構築ます:
```sh
git clone https://github.com/rustdesk/rustdesk
@@ -124,44 +130,50 @@ cd rustdesk
docker build -t "rustdesk-builder" .
```
その後、アプリケーションをビルドする必要があるたびに、以下のコマンドを実行します
以下のコマンドを実行します:
```sh
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
このコマンドはRustDeskをビルドする度に実行する必要があります。
なお、最初のビルドでは、依存関係がキャッシュされるまで時間がかかることがありますが、その後のビルドではより速くなります。さらに、ビルドコマンドに別の引数を指定する必要がある場合は、コマンドの最後にある `<OPTIONAL-ARGS>` の位置で指定することができます。例えば、最適化されたリリースバージョンをビルドしたい場合は、上記のコマンドの後に
`--release` を実行します。できあがった実行ファイルはシステムのターゲットフォルダに格納され、のコマンドで実行できます。
初回ビルドは時間がかかるかもしれませんが、2回目以降は依存関係がキャッシュされるため、ビルドにかかる時間が短くなります。
ビルドコマンドに追加の引数を指定する必要がある場合は、コマンドの最後(`<OPTIONAL-ARGS>`の位置)で指定することができます。例えば、最適化されたリリースバージョンをビルドしたい場合は、上記のコマンドの後に `--release`追記し実行します。ビルドされた実行ファイルはあなたのシステムのターゲットフォルダに保存され、下記のコマンドで実行することができます。
デバッグビルドを起動する場合:
```sh
target/debug/rustdesk
```
あるいは、リリース用の実行ファイルを実行している場合:
リリースビルドを起動する場合:
```sh
target/release/rustdesk
```
これらのコマンドをRustDeskリポジトリのルートから実行していることを確認してください。そうしないと、アプリケーションが必要なリソースを見つけられない可能性があります。また、 `install``run` などの他の cargo サブコマンドは、ホストではなくコンテナ内プログラムをインストールまたは実行するため、現在の方法ではサポートされていないことに注意してください
コマンドをRustDeskリポジトリのルートから実行していることを確認してください。また、`install``run` などの他のcargoサブコマンドは、ホストではなくコンテナ内プログラムをインストール実行するため、現在の方法ではサポートされていません
## ファイル構造
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: ビデオコーデック、コンフィグ、tcp/udpラッパー、protobuf、ファイル転送用のfs関数その他のユーティリティ関数
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: ビデオコーデック、設定、tcp/udpラッパー、protobuf、ファイル転送に利用されるfs関数その他のユーティリティ関数
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: スクリーンキャプチャ
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: プラットフォーム固有のキーボード/マウスコントロール
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: オーディオ/クリップボード/入力/ビデオサービス、ネットワーク接続
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: プラットフォーム固有のキーボード/マウス操作
- **[libs/clipboard](https://github.com/rustdesk/rustdesk/tree/master/libs/clipboard)**: Windows、Linux、macOS向けのファイルのコピーと貼り付けの実装
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: 廃止された Sciter UI (非推奨)
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**:
オーディオ/クリップボード/入力/ビデオ サービスとネットワーク接続
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: ピア接続の開始
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: [rustdesk-server](https://github.com/rustdesk/rustdesk-server), と通信し、リモートダイレクト (TCP hole punching) または中継接続を待つ
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: [rustdesk-server](https://github.com/rustdesk/rustdesk-server)と通信し、リモートの直接接続(TCPホールパンチング)や中継接続を担う
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: プラットフォーム固有のコード
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: デスクトップとモバイル向けのFlutterコード
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Flutterウェブクライアント向けのJavaScript
## スナップショット
## スクリーンショット
![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png)
![Connection Manager](https://github.com/rustdesk/rustdesk/assets/28412477/db82d4e7-c4bc-4823-8e6f-6af7eadf7651)
![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png)
![Connected to a Windows PC](https://github.com/rustdesk/rustdesk/assets/28412477/9baa91e9-3362-4d06-aa1a-7518edcbd7ea)
![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png)
![File Transfer](https://github.com/rustdesk/rustdesk/assets/28412477/39511ad3-aa9a-4f8c-8947-1cce286a46ad)
![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png)
![TCP Tunneling](https://github.com/rustdesk/rustdesk/assets/28412477/78e8708f-e87e-4570-8373-1360033ea6c5)

View File

@@ -24,13 +24,6 @@ RustDesk는 모든 기여를 환영합니다. 기여하고자 한다면 [`docs/C
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
## 무료 퍼블릭 서버
표에 있는 서버는 무료로 사용할 수 있지만 추후 변경될 수도 있습니다. 이 서버에서 멀다면, 네트워크가 느려질 가능성도 있습니다.
| Location | Vendor | Specification |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
## 의존관계
데스크탑판에는 GUI에 [sciter](https://sciter.com/)가 사용되었습니다. sciter dynamic library 를 다운로드해주세요.

View File

@@ -19,13 +19,6 @@
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
## സൗജന്യ പൊതു സെർവറുകൾ
നിങ്ങൾ സൗജന്യമായി ഉപയോഗിക്കുന്ന സെർവറുകൾ ചുവടെയുണ്ട്, അത് സമയത്തിനനുസരിച്ച് മാറിയേക്കാം. നിങ്ങൾ ഇവയിലൊന്നിനോട് അടുത്തല്ലെങ്കിൽ, നിങ്ങളുടെ നെറ്റ്‌വർക്ക് സ്ലോ ആയേക്കാം.
| സ്ഥാനം | കച്ചവടക്കാരൻ | വിവരണം |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
## ഡിപെൻഡൻസികൾ
ഡെസ്‌ക്‌ടോപ്പ് പതിപ്പുകൾ GUI-യ്‌ക്കായി [sciter](https://sciter.com/) ഉപയോഗിക്കുന്നു, ദയവായി സ്‌സൈറ്റർ ഡൈനാമിക് ലൈബ്രറി സ്വയം ഡൗൺലോഡ് ചെയ്യുക.

View File

@@ -27,22 +27,6 @@ RustDesk verwelkomt bijdragen van iedereen. Zie [`docs/CONTRIBUTING.md`](CONTRIB
alt="Download het op F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Gratis openbare servers
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 |
| --------- | ------------- | ------------------ |
| Duitsland | Hetzner | 2 vCPU / 4GB RAM |
| Oekraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dev Container
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
Als u VS Code en Docker al hebt geinstalleerd, kunt u op de bovenstaande badge klikken om te beginnen. Door te klikken zal VS Code automatisch de Dev Containers-extensie installeren indien nodig, de broncode klonen naar een containervolume en een dev container opstarten voor gebruik.
Bekijk [DEVCONTAINER.md](docs/DEVCONTAINER.md) voor meer informatie.
## Afhankelijkheden
Desktop versies gebruiken [sciter](https://sciter.com/) of Flutter voor GUI, deze handleiding is alleen voor Sciter.

View File

@@ -29,22 +29,6 @@ RustDesk zaprasza do współpracy każdego. Zobacz [`docs/CONTRIBUTING-PL.md`](C
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Darmowe Serwery Publiczne
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 |
| --------- | ------------- | ------------------ |
| Niemcy | Hetzner | 2 vCPU / 4GB RAM |
| Ukraina (Kijów) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Konterner Programisty (Dev Container)
[![Otwórz w Kontenerze programisty](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
Jeżeli masz zainstalowany VS Code i Docker, możesz kliknąć w powyższy link, aby rozpocząć. Kliknięcie spowoduje automatyczną instalację rozszrzenia Kontenera Programisty w VS Code (jeżeli wymagany), sklonuje kod źródłowy do kontenera, i przygotuje kontener do użycia.
Więcej informacji w pliku [DEVCONTAINER-PL.md](docs/DEVCONTAINER-PL.md) for more info.
## Zależności
Wersje desktopowe używają [sciter](https://sciter.com/) dla GUI, proszę pobrać samodzielnie bibliotekę sciter.

View File

@@ -19,14 +19,6 @@ RustDesk acolhe contribuições de todos. Leia [`docs/CONTRIBUTING.md`](CONTRIBU
[**DOWNLOAD DE BINÁRIOS**](https://github.com/rustdesk/rustdesk/releases)
## Servidores Públicos Grátis
Abaixo estão os servidores que você está utilizando de graça, ele pode mudar com o tempo. Se você não está próximo de algum deles, sua conexão pode ser lenta.
| Localização | Fornecedor | Especificações |
| ----------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
## Dependências
Versões de desktop utilizam [sciter](https://sciter.com/) para a GUI, por favor baixe a biblioteca dinâmica sciter por conta própria.

View File

@@ -28,13 +28,6 @@ RustDesk приветствует вклад каждого. Ознакомьт
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Бесплатные общедоступные серверы
Ниже приведены бесплатные публичные сервера, используемые по умолчанию. Имейте ввиду, они могут меняться со временем. Также стоит отметить, что скорость работы сети зависит от вашего местоположения и расстояния до серверов. Подключение происходит к ближайшему доступному.
| Расположение | Поставщик | Технические характеристики |
| --------- | ------------- | ------------------ |
| Германия | Hetzner | 2 vCPU / 4GB RAM |
## Зависимости
Настольные версии используют [sciter](https://sciter.com/) для графического интерфейса, загрузите динамическую библиотеку sciter самостоятельно.

View File

@@ -30,23 +30,6 @@ RustDesk, herkesten katkıyı kabul eder. Başlamak için [CONTRIBUTING.md](docs
alt="F-Droid'de Alın"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Ücretsiz Genel Sunucular
Aşağıda ücretsiz olarak kullandığınız sunucular listelenmiştir, zaman içinde değişebilirler. Eğer bunlardan birine yakın değilseniz, ağınız yavaş olabilir.
| Konum | Sağlayıcı | Özellikler |
| --------- | ------------- | ------------------ |
| Almanya | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4 GB RAM |
| Almanya | [Codext](https://codext.de) | 4 vCPU / 8 GB RAM |
| Ukrayna (Kiev) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## Geliştirici Konteyneri
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
Eğer zaten VS Code ve Docker kurulu ise yukarıdaki rozete tıklayarak başlayabilirsiniz. Tıklamak, VS Code'un gerektiğinde Dev Konteyner eklentisini otomatik olarak yüklemesine, kaynak kodunu bir konteyner hacmine klonlamasına ve kullanım için bir geliştirici konteyneri başlatmasına neden olur.
Daha fazla bilgi için [DEVCONTAINER.md](docs/DEVCONTAINER-TR.md) belgesine bakabilirsiniz.
## Bağımlılıklar
Masaüstü sürümleri GUI için

View File

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

View File

@@ -1,64 +1,55 @@
<p align="center">
<img src="../res/logo-header.svg" alt="RustDesk - Phần mềm điểu khiển máy tính từ xa dành cho bạn"><br>
<a href="#free-public-servers">Máy chủ</a> •
<img src="../res/logo-header.svg" alt="RustDesk - Your remote desktop"><br>
<a href="#free-public-servers">Server</a> •
<a href="#raw-steps-to-build">Build</a> •
<a href="#how-to-build-with-docker">Docker</a> •
<a href="#file-structure">Cấu trúc tệp tin</a> •
<a href="#file-structure">Structure</a> •
<a href="#snapshot">Snapshot</a><br>
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
<b>Chúng tôi cần sự gíup đỡ của bạn để dịch trang README này, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> và <a href="https://github.com/rustdesk/doc.rustdesk.com">tài liệu</a> sang ngôn ngữ bản địa của bạn</b>
<b>Chúng tôi rất hoan nghênh sự hỗ trợ của bạn trong việc dịch trang README, trang giao diện người dùng của RustDesk - <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> và trang tài liệu của RustDesk - <a href="https://github.com/rustdesk/doc.rustdesk.com">RustDesk Doc</a> sang Tiếng Việt</b>
</p>
Chat với chúng tôi qua: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
Hãy trao đổi với chúng tôi qua: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
Một phần mềm điểu khiển máy tính từ xa, đuợc lập trình bằng ngôn ngữ Rust. Hoạt động tức thì, không cần phải cài đặt. Bạn có toàn quyền điểu khiển với dữ liệu của bạn mà không cần phải lo lắng về sự bảo mật. Bạn có thể sử dụng máy chủ rendezvous/relay của chúng tôi, [tự cài đặt máy chủ](https://rustdesk.com/server), hay thậm chí [tự tạo máy chủ rendezvous/relay](https://github.com/rustdesk/rustdesk-server-demo).
RustDesk là một phần mềm điểu khiển máy tính từ xa mã nguồn mở, được viết bằng Rust. Nó hoạt động ngay sau khi cài đặt, không yêu cầu cấu hình phức tạp. Bạn có toàn quyền kiểm soát với dữ liệu của mình mà không cần phải lo lắng về vấn đề bảo mật. Bạn có thể sử dụng máy chủ rendezvous/relay của chúng tôi hoặc [tự cài đặt máy chủ của riêng mình](https://rustdesk.com/server) hay thậm chí [tự tạo máy chủ rendezvous/relay cho riêng bạn](https://github.com/rustdesk/rustdesk-server-demo).
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
Mọi người đều đuợc chào đón để đóng góp vào RustDesk. Để bắt đầu, hãy đọc [`docs/CONTRIBUTING.md`](CONTRIBUTING.md).
**RustDesk** luôn hoan nghênh mọi đóng góp từ mọi người. Hãy xem tệp [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) để bắt đầu.
[**RustDesk hoạt động như thế nào?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
[**CÁC BẢN PHÂN PHÁT MÃ NHỊ PHÂN**](https://github.com/rustdesk/rustdesk/releases)
[**FAQ**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
[**NIGHTLY BUILD**](https://github.com/rustdesk/rustdesk/FAQreleases/tag/nightly)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## Các Máy Chủ Công Khai Miễn Phí
Dưới đây là những máy chủ mà bạn có thể sử dụng mà không mất phí, chú ý là máy chủ có thể thay đổi theo thời gian. Nếu địa điểm của bạn không gần một trong số những máy chủ này, thì kết nói có thể chậm.
| Địa điểm | Nhà cung cấp | Cấu hình |
| --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM |
## Dependencies
Phiên bản cho máy tính sử dụng [sciter](https://sciter.com/) cho giao diện của phần mềm, vậy nên bn cần tự tải về thư viện sciter.
Phiên bản máy tính sử dụng __Flutter__ hoặc __Sciter__ (đã lỗi thời) cho giao diện người dùng (GUI). Hướng dẫn này chỉ áp dụng cho phiên bn Sciter, vì nó thân thiện và dễ bắt đầu hơn. Hãy kiểm tra [CI](https://github.com/rustdesk/rustdesk/blob/master/.github/workflows/flutter-build.yml) của chúng tôi để xây dựng phiên bản Flutter.
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
Vui lòng tự tải thư viện `Sciter` về máy theo hướng dẫn cho từng hệ điều hành.
Phiên bản cho điện thoại sử dụng Flutter. Chúng tôi sẽ chuyển sang sử dụng Flutter thay cho Sciter cho phiên bản máy tính.
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) | [Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) | [MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
## Cách để build
## Các bước build cơ bản
- Chuẩn bị môi trường phát triển Rust và môi trường build C++
- Chuẩn bị môi trường phát triển Rust và môi trường biên dịch C++
- Tải và cài [vcpkg](https://github.com/microsoft/vcpkg), và đặt biến môi trường `VCPKG_ROOT` sao cho đúng.
- Đối với Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
- Đối với Linux/MacOS: vcpkg install libvpx libyuv opus aom
- Tải và cài đặt [`vcpkg`](https://github.com/microsoft/vcpkg), và thiết lập biến môi trường `VCPKG_ROOT`.
- Windows: `vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static`
- Linux/MacOS: `vcpkg install libvpx libyuv opus aom`
- Chạy lệnh `cargo run`
## [Build](https://rustdesk.com/docs/en/dev/build/)
## Cách để build cho Linux
## Cách build cho Linux
### Ubuntu 18 (Debian 10)
@@ -78,7 +69,7 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pipewire
```
### Cách cài vcpkg
### Cách cài đặt `vcpkg`
```sh
git clone https://github.com/microsoft/vcpkg
@@ -90,7 +81,7 @@ export VCPKG_ROOT=$HOME/vcpkg
vcpkg/vcpkg install libvpx libyuv opus aom
```
### Cách sửa lỗi libvpx (Dành cho hệ điều hành Fedora)
### Cách sửa lỗi `libvpx` (Dành cho hệ điều hành Fedora)
```sh
cd vcpkg/buildtrees/libvpx/src
@@ -103,7 +94,7 @@ cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/
cd
```
### Cách build
### Build
```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
@@ -116,9 +107,9 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
## Cách để build sử dụng Docker
## Cách build bằng Docker
Bắt đầu bằng cách sao chép repo này về máy tính và build cái Docker cointainer:
Bắt đầu bằng cách sao chép repo này về máy tính của bạn và tạo Docker container:
```sh
git clone https://github.com/rustdesk/rustdesk
@@ -126,37 +117,37 @@ cd rustdesk
docker build -t "rustdesk-builder" .
```
Rồi mỗi khi bạn chạy ứng dụng, thì hãy chạy lệnh này:
Sau đó, mỗi khi bạn chạy ứng dụng, thì hãy chạy dòng lệnh sau:
```sh
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
Chú ý: Lần build đầu tiên có thể sẽ mất lâu hơn truớc khi các dependecies đuợc lưu lại, nhng lần build sau sẽ nhanh hơn. Hơn nũa, nếu bạn cần cung cấp các cài đặt lệnh khác cho lệnh build, bạn có thể đặt những cài đặt lệnh này vào cuối lệnh ở phần `<OPTIONAL-ARGS>`. Ví dụ nếu bạn cần build phiên bản đuợc tối ưu hóa, bạn sẽ chạy lệnh trên cùng với cài đặt lệnh --release. Kết quả build sẽ được lưu trong thư mục target trên máy tính của bạn, và có thể chạy với lệnh:
Lưu ý rằng **lần build đầu tiên có thể mất thời gian hơn trước khi các dependencies được lưu vào bộ nhớ cache**, nhưng các lần build sau sẽ nhanh hơn. Ngoài ra, nếu bạn cần chỉ định các đối số khác cho lệnh build, bạn có thể thêm chúng vào cuối lệnh ở phần `<OPTIONAL-ARGS>`. Ví dụ, nếu bạn muốn build phiên bản tối ưu hóa, bạn sẽ chạy lệnh trên với tùy chọn `--release`. Kết quả biên dịch sẽ được lưu trong thư mục target trên máy tính của bạn, và có thể chạy với lệnh:
```sh
target/debug/rustdesk
```
Nếu bạn đang chạy bản build đuợc tối ưu hóa, thì bạn có thể chạy với lệnh:
Nếu bạn đang chạy bản build được tối ưu hóa, thì bạn có thể chạy với lệnh:
```sh
target/release/rustdesk
```
Hãy đảm bảo bạn đang chạy những lệnh này từ thu mục rễ của repo RustDesk, vì nếu không thì ứng dụng có thể sẽ không tìm đuợc những tệp tài nguyên cần thiết. Cũng như nhớ rằng những lệnh con của cargo như `install` hoặc `run` hiện chưa được hỗ trợ bởi phương pháp này vì chúng sẽ cài đặt hoặc chạy ứng dụng trong container thay vì trên máy tính của bạn.
Hãy đảm bảo rằng bạn đang chạy các lệnh này từ gốc của thư mục **RustDesk**, nếu không, ứng dụng có thể không thể tìm thấy các tệp tài nguyên cần thiết. Hãy lưu ý rằng các câu lệnh con khác của **cargo** như **install** hoặc **run** hiện không được hỗ trợ qua phương pháp này, vì chúng sẽ cài đặt hoặc chạy chương trình bên trong **container** thay vì trên máy tính của bạn.
## Cấu trúc tệp tin
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, cấu hình, tcp/udp wrapper, protobuf, fs functions để truyền file, và một số hàm tiện ích khác
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: để ghi lại màn hình
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: để điều khiển máy tính/con chuột trên những nền tảng khác nhau
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: ghi lại màn hình
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: điều khiển máy tính/chuột trên các nền tảng khác nhau
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: giao diện người dùng
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: các dịch vụ âm thanh, clipboard, đầu vào, video và các kết nối mạng
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: để bắt đầu kết nối với một peer
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Để liên lạc với [rustdesk-server](https://github.com/rustdesk/rustdesk-server), đợi cho kết nối trực tiếp (TCP hole punching) hoặc kết nối được relayed.
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: bắt đầu kết nối với một peer
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: giao tiếp với [rustdesk-server](https://github.com/rustdesk/rustdesk-server), đợi kết nối trực tiếp (TCP hole punching) hoặc kết nối được chuyển tiếp.
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: mã nguồn riêng cho mỗi nền tảng
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Mã Flutter dành cho điện thoại
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Mã Flutter dành máy tính và điện thoại
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Mã JavaScript dành cho giao diện trên web bằng Flutter
## Snapshot

View File

@@ -8,7 +8,7 @@
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
</p>
Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
与我们交流: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
@@ -18,7 +18,7 @@ Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https:
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
RustDesk 期待各位的贡献. 如何参与开发? 详情请看 [CONTRIBUTING.md](CONTRIBUTING.md).
RustDesk 期待各位的贡献. 如何参与开发? 详情请看 [CONTRIBUTING-ZH.md](CONTRIBUTING-ZH.md).
[**FAQ**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
@@ -30,26 +30,11 @@ RustDesk 期待各位的贡献. 如何参与开发? 详情请看 [CONTRIBUTING.m
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## 免费的公共服务器
以下是您可以使用的、免费的、会随时更新的公共服务器列表,在国内也许网速会很慢或者无法访问。
| Location | Vendor | Specification |
| --------- | ------------- | ------------------ |
| Germany | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4 GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## Dev Container
[![在 Dev Containers 中打开](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
如果你已经安装了 VS Code 和 Docker, 你可以点击上面的徽章开始使用. 点击后, VS Code 将自动安装 Dev Containers 扩展(如果需要),将源代码克隆到容器卷中, 并启动一个 Dev 容器供使用.
Go through [DEVCONTAINER.md](docs/DEVCONTAINER.md) for more info.
## 依赖
桌面版本界面使用[sciter](https://sciter.com/), 请自行下载
桌面版本使用 Flutter 或 Sciter已弃用作为 GUI本教程仅适用于 Sciter因为它更简单且更易于上手。查看我们的[CI](https://github.com/rustdesk/rustdesk/blob/master/.github/workflows/flutter-build.yml)以构建 Flutter 版本
请自行下载Sciter动态库。
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
@@ -224,12 +209,13 @@ target/release/rustdesk
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: 视频编解码, 配置, tcp/udp 封装, protobuf, 文件传输相关文件系统操作函数, 以及一些其他实用函数
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: 屏幕截取
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: 平台相关的鼠标键盘输入
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
- **[libs/clipboard](https://github.com/rustdesk/rustdesk/tree/master/libs/clipboard)**: Windows、Linux、macOS 的文件复制和粘贴实现
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: 过时的 Sciter UI已弃用
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: 被控端服务音频、剪切板、输入、视频服务、网络连接的实现
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: 控制端
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: 与[rustdesk-server](https://github.com/rustdesk/rustdesk-server)保持UDP通讯, 等待远程连接(通过打洞直连或者中继)
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: 平台服务相关代码
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: 移动版本的Flutter代码
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: 适用于桌面和移动设备的 Flutter 代码
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Flutter Web版本中的Javascript代码
## 截图

View File

@@ -8,29 +8,43 @@
"modules": [
"shared-modules/libappindicator/libappindicator-gtk3-12.10.json",
"xdotool.json",
{
"name": "pam",
"buildsystem": "simple",
"build-commands": [
"./configure --disable-selinux --prefix=/app && make -j4 install"
],
"sources": [
{
"type": "archive",
"url": "https://github.com/linux-pam/linux-pam/releases/download/v1.3.1/Linux-PAM-1.3.1.tar.xz",
"sha256": "eff47a4ecd833fbf18de9686632a70ee8d0794b79aecb217ebd0ce11db4cd0db"
}
]
},
{
"name": "rustdesk",
"buildsystem": "simple",
"build-commands": [
"bsdtar -zxvf rustdesk-1.2.4.deb",
"bsdtar -zxvf rustdesk.deb",
"tar -xvf ./data.tar.xz",
"cp -r ./usr/* /app/",
"mkdir -p /app/bin && ln -s /app/lib/rustdesk/rustdesk /app/bin/rustdesk",
"mv /app/share/applications/rustdesk.desktop /app/share/applications/com.rustdesk.RustDesk.desktop",
"sed -i '/^Icon=/ c\\Icon=com.rustdesk.RustDesk' /app/share/applications/com.rustdesk.RustDesk.desktop",
"sed -i '/^Icon=/ c\\Icon=com.rustdesk.RustDesk' /app/share/applications/rustdesk-link.desktop",
"mv /app/share/applications/rustdesk-link.desktop /app/share/applications/com.rustdesk.RustDesk-link.desktop",
"sed -i '/^Icon=/ c\\Icon=com.rustdesk.RustDesk' /app/share/applications/*.desktop",
"mv /app/share/icons/hicolor/scalable/apps/rustdesk.svg /app/share/icons/hicolor/scalable/apps/com.rustdesk.RustDesk.svg",
"for size in 16 24 32 48 64 128 256 512; do\n rsvg-convert -w $size -h $size -f png -o $size.png logo.svg\n install -Dm644 $size.png /app/share/icons/hicolor/${size}x${size}/apps/com.rustdesk.RustDesk.png\n done"
"for size in 16 24 32 48 64 128 256 512; do\n rsvg-convert -w $size -h $size -f png -o $size.png scalable.svg\n install -Dm644 $size.png /app/share/icons/hicolor/${size}x${size}/apps/com.rustdesk.RustDesk.png\n done"
],
"cleanup": ["/include", "/lib/pkgconfig", "/share/gtk-doc"],
"sources": [
{
"type": "file",
"path": "../rustdesk-1.2.4.deb"
"path": "./rustdesk.deb"
},
{
"type": "file",
"path": "../res/logo.svg"
"path": "../res/scalable.svg"
}
]
}

1
flutter/.gitignore vendored
View File

@@ -32,7 +32,6 @@
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols

View File

@@ -106,6 +106,6 @@ dependencies {
implementation "androidx.media:media:1.6.0"
implementation 'com.github.getActivity:XXPermissions:18.5'
implementation("org.jetbrains.kotlin:kotlin-stdlib") { version { strictly("$kotlin_version") } }
implementation 'com.caverock:androidsvg-aar:1.4'
}
apply plugin: 'com.google.gms.google-services'

View File

@@ -1,40 +0,0 @@
{
"project_info": {
"project_number": "768133699366",
"firebase_url": "https://rustdesk.firebaseio.com",
"project_id": "rustdesk",
"storage_bucket": "rustdesk.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:768133699366:android:5fc9015370e344457993e7",
"android_client_info": {
"package_name": "com.carriez.flutter_hbb"
}
},
"oauth_client": [
{
"client_id": "768133699366-s9gdfsijefsd5g1nura4kmfne42lencn.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAPOsKcXjrAR-7Z148sYr_gdB_JQZkamTM"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "768133699366-s9gdfsijefsd5g1nura4kmfne42lencn.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

View File

@@ -3,6 +3,7 @@
package="com.carriez.flutter_hbb">
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -80,6 +81,11 @@
android:name=".MainService"
android:enabled="true"
android:foregroundServiceType="mediaProjection" />
<service
android:name=".FloatingWindowService"
android:enabled="true" />
<!--
Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java

View File

@@ -0,0 +1,193 @@
package com.carriez.flutter_hbb
import ffi.FFI
import android.Manifest
import android.content.Context
import android.media.*
import android.content.pm.PackageManager
import android.media.projection.MediaProjection
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import android.os.Build
import android.util.Log
import kotlin.concurrent.thread
const val AUDIO_ENCODING = AudioFormat.ENCODING_PCM_FLOAT // ENCODING_OPUS need API 30
const val AUDIO_SAMPLE_RATE = 48000
const val AUDIO_CHANNEL_MASK = AudioFormat.CHANNEL_IN_STEREO
class AudioRecordHandle(private var context: Context, private var isVideoStart: ()->Boolean, private var isAudioStart: ()->Boolean) {
private val logTag = "LOG_AUDIO_RECORD_HANDLE"
private var audioRecorder: AudioRecord? = null
private var audioReader: AudioReader? = null
private var minBufferSize = 0
private var audioRecordStat = false
private var audioThread: Thread? = null
@RequiresApi(Build.VERSION_CODES.M)
fun createAudioRecorder(inVoiceCall: Boolean, mediaProjection: MediaProjection?): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
return false
}
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.RECORD_AUDIO
) != PackageManager.PERMISSION_GRANTED
) {
Log.d(logTag, "createAudioRecorder failed, no RECORD_AUDIO permission")
return false
}
var builder = AudioRecord.Builder()
.setAudioFormat(
AudioFormat.Builder()
.setEncoding(AUDIO_ENCODING)
.setSampleRate(AUDIO_SAMPLE_RATE)
.setChannelMask(AUDIO_CHANNEL_MASK).build()
);
if (inVoiceCall) {
builder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION)
} else {
mediaProjection?.let {
var apcc = AudioPlaybackCaptureConfiguration.Builder(it)
.addMatchingUsage(AudioAttributes.USAGE_MEDIA)
.addMatchingUsage(AudioAttributes.USAGE_ALARM)
.addMatchingUsage(AudioAttributes.USAGE_GAME)
.addMatchingUsage(AudioAttributes.USAGE_UNKNOWN).build();
builder.setAudioPlaybackCaptureConfig(apcc);
} ?: let {
Log.d(logTag, "createAudioRecorder failed, mediaProjection null")
return false
}
}
audioRecorder = builder.build()
Log.d(logTag, "createAudioRecorder done,minBufferSize:$minBufferSize")
return true
}
@RequiresApi(Build.VERSION_CODES.M)
private fun checkAudioReader() {
if (audioReader != null && minBufferSize != 0) {
return
}
// read f32 to byte , length * 4
minBufferSize = 2 * 4 * AudioRecord.getMinBufferSize(
AUDIO_SAMPLE_RATE,
AUDIO_CHANNEL_MASK,
AUDIO_ENCODING
)
if (minBufferSize == 0) {
Log.d(logTag, "get min buffer size fail!")
return
}
audioReader = AudioReader(minBufferSize, 4)
Log.d(logTag, "init audioData len:$minBufferSize")
}
@RequiresApi(Build.VERSION_CODES.M)
fun startAudioRecorder() {
checkAudioReader()
if (audioReader != null && audioRecorder != null && minBufferSize != 0) {
try {
FFI.setFrameRawEnable("audio", true)
audioRecorder!!.startRecording()
audioRecordStat = true
audioThread = thread {
while (audioRecordStat) {
audioReader!!.readSync(audioRecorder!!)?.let {
FFI.onAudioFrameUpdate(it)
}
}
// let's release here rather than onDestroy to avoid threading issue
audioRecorder?.release()
audioRecorder = null
minBufferSize = 0
FFI.setFrameRawEnable("audio", false)
Log.d(logTag, "Exit audio thread")
}
} catch (e: Exception) {
Log.d(logTag, "startAudioRecorder fail:$e")
}
} else {
Log.d(logTag, "startAudioRecorder fail")
}
}
fun onVoiceCallStarted(mediaProjection: MediaProjection?): Boolean {
if (!isSupportVoiceCall()) {
return false
}
// No need to check if video or audio is started here.
if (!switchToVoiceCall(mediaProjection)) {
return false
}
return true
}
fun onVoiceCallClosed(mediaProjection: MediaProjection?): Boolean {
// Return true if not supported, because is was not started.
if (!isSupportVoiceCall()) {
return true
}
if (isVideoStart()) {
switchOutVoiceCall(mediaProjection)
}
tryReleaseAudio()
return true
}
@RequiresApi(Build.VERSION_CODES.M)
fun switchToVoiceCall(mediaProjection: MediaProjection?): Boolean {
audioRecorder?.let {
if (it.getAudioSource() == MediaRecorder.AudioSource.VOICE_COMMUNICATION) {
return true
}
}
audioRecordStat = false
audioThread?.join()
audioThread = null
if (!createAudioRecorder(true, mediaProjection)) {
Log.e(logTag, "createAudioRecorder fail")
return false
}
startAudioRecorder()
return true
}
@RequiresApi(Build.VERSION_CODES.M)
fun switchOutVoiceCall(mediaProjection: MediaProjection?): Boolean {
audioRecorder?.let {
if (it.getAudioSource() != MediaRecorder.AudioSource.VOICE_COMMUNICATION) {
return true
}
}
audioRecordStat = false
audioThread?.join()
if (!createAudioRecorder(false, mediaProjection)) {
Log.e(logTag, "createAudioRecorder fail")
return false
}
startAudioRecorder()
return true
}
fun tryReleaseAudio() {
if (isAudioStart() || isVideoStart()) {
return
}
audioRecordStat = false
audioThread?.join()
audioThread = null
}
fun destroy() {
Log.d(logTag, "destroy audio record handle")
audioRecordStat = false
audioThread?.join()
}
}

View File

@@ -0,0 +1,378 @@
package com.carriez.flutter_hbb
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.PixelFormat
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.util.Log
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
import android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
import android.widget.ImageView
import android.widget.PopupMenu
import com.caverock.androidsvg.SVG
import ffi.FFI
import kotlin.math.abs
class FloatingWindowService : Service(), View.OnTouchListener {
private lateinit var windowManager: WindowManager
private lateinit var layoutParams: WindowManager.LayoutParams
private lateinit var floatingView: ImageView
private lateinit var originalDrawable: Drawable
private lateinit var leftHalfDrawable: Drawable
private lateinit var rightHalfDrawable: Drawable
private var dragging = false
private var lastDownX = 0f
private var lastDownY = 0f
private var viewCreated = false;
private var keepScreenOn = KeepScreenOn.DURING_CONTROLLED
companion object {
private val logTag = "floatingService"
private var firstCreate = true
private var viewWidth = 120
private var viewHeight = 120
private const val MIN_VIEW_SIZE = 32 // size 0 does not help prevent the service from being killed
private const val MAX_VIEW_SIZE = 320
private var viewUntouchable = false
private var viewTransparency = 1f // 0 means invisible but can help prevent the service from being killed
private var customSvg = ""
private var lastLayoutX = 0
private var lastLayoutY = 0
private var lastOrientation = Configuration.ORIENTATION_UNDEFINED
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
try {
if (firstCreate) {
firstCreate = false
onFirstCreate(windowManager)
}
Log.d(logTag, "floating window size: $viewWidth x $viewHeight, transparency: $viewTransparency, lastLayoutX: $lastLayoutX, lastLayoutY: $lastLayoutY, customSvg: $customSvg")
createView(windowManager)
handler.postDelayed(runnable, 1000)
Log.d(logTag, "onCreate success")
} catch (e: Exception) {
Log.d(logTag, "onCreate failed: $e")
}
}
override fun onDestroy() {
super.onDestroy()
if (viewCreated) {
windowManager.removeView(floatingView)
}
handler.removeCallbacks(runnable)
}
@SuppressLint("ClickableViewAccessibility")
private fun createView(windowManager: WindowManager) {
floatingView = ImageView(this)
viewCreated = true
originalDrawable = resources.getDrawable(R.drawable.floating_window, null)
if (customSvg.isNotEmpty()) {
try {
val svg = SVG.getFromString(customSvg)
Log.d(logTag, "custom svg info: ${svg.documentWidth} x ${svg.documentHeight}");
// This make the svg render clear
svg.documentWidth = viewWidth * 1f
svg.documentHeight = viewHeight * 1f
originalDrawable = svg.renderToPicture().let {
BitmapDrawable(
resources,
Bitmap.createBitmap(it.width, it.height, Bitmap.Config.ARGB_8888)
.also { bitmap ->
it.draw(Canvas(bitmap))
})
}
floatingView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Log.d(logTag, "custom svg loaded")
} catch (e: Exception) {
e.printStackTrace()
}
}
val originalBitmap = Bitmap.createBitmap(
originalDrawable.intrinsicWidth,
originalDrawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(originalBitmap)
originalDrawable.setBounds(
0,
0,
originalDrawable.intrinsicWidth,
originalDrawable.intrinsicHeight
)
originalDrawable.draw(canvas)
val leftHalfBitmap = Bitmap.createBitmap(
originalBitmap,
0,
0,
originalDrawable.intrinsicWidth / 2,
originalDrawable.intrinsicHeight
)
val rightHalfBitmap = Bitmap.createBitmap(
originalBitmap,
originalDrawable.intrinsicWidth / 2,
0,
originalDrawable.intrinsicWidth / 2,
originalDrawable.intrinsicHeight
)
leftHalfDrawable = BitmapDrawable(resources, leftHalfBitmap)
rightHalfDrawable = BitmapDrawable(resources, rightHalfBitmap)
floatingView.setImageDrawable(rightHalfDrawable)
floatingView.setOnTouchListener(this)
floatingView.alpha = viewTransparency * 1f
var flags = FLAG_LAYOUT_IN_SCREEN or FLAG_NOT_TOUCH_MODAL or FLAG_NOT_FOCUSABLE
if (viewUntouchable || viewTransparency == 0f) {
flags = flags or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
}
layoutParams = WindowManager.LayoutParams(
viewWidth / 2,
viewHeight,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY else WindowManager.LayoutParams.TYPE_PHONE,
flags,
PixelFormat.TRANSLUCENT
)
layoutParams.gravity = Gravity.TOP or Gravity.START
layoutParams.x = lastLayoutX
layoutParams.y = lastLayoutY
val keepScreenOnOption = FFI.getLocalOption("keep-screen-on").lowercase()
keepScreenOn = when (keepScreenOnOption) {
"never" -> KeepScreenOn.NEVER
"service-on" -> KeepScreenOn.SERVICE_ON
else -> KeepScreenOn.DURING_CONTROLLED
}
Log.d(logTag, "keepScreenOn option: $keepScreenOnOption, value: $keepScreenOn")
updateKeepScreenOnLayoutParams()
windowManager.addView(floatingView, layoutParams)
moveToScreenSide()
}
private fun onFirstCreate(windowManager: WindowManager) {
val wh = getScreenSize(windowManager)
val w = wh.first
val h = wh.second
// size
FFI.getLocalOption("floating-window-size").let {
if (it.isNotEmpty()) {
try {
val size = it.toInt()
if (size in MIN_VIEW_SIZE..MAX_VIEW_SIZE && size <= w / 2 && size <= h / 2) {
viewWidth = size
viewHeight = size
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
// untouchable
viewUntouchable = FFI.getLocalOption("floating-window-untouchable") == "Y"
// transparency
FFI.getLocalOption("floating-window-transparency").let {
if (it.isNotEmpty()) {
try {
val transparency = it.toInt()
if (transparency in 0..10) {
viewTransparency = transparency * 1f / 10
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
// custom svg
FFI.getLocalOption("floating-window-svg").let {
if (it.isNotEmpty()) {
customSvg = it
}
}
// position
lastLayoutX = 0
lastLayoutY = (wh.second - viewHeight) / 2
lastOrientation = resources.configuration.orientation
}
private fun performClick() {
showPopupMenu()
}
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
dragging = false
lastDownX = event.rawX
lastDownY = event.rawY
}
MotionEvent.ACTION_UP -> {
val clickDragTolerance = 10f
if (abs(event.rawX - lastDownX) < clickDragTolerance && abs(event.rawY - lastDownY) < clickDragTolerance) {
performClick()
} else {
moveToScreenSide()
}
}
MotionEvent.ACTION_MOVE -> {
val dx = event.rawX - lastDownX
val dy = event.rawY - lastDownY
// ignore too small fist start moving(some time is click)
if (!dragging && dx*dx+dy*dy < 25) {
return false
}
dragging = true
layoutParams.x = event.rawX.toInt()
layoutParams.y = event.rawY.toInt()
layoutParams.width = viewWidth
floatingView.setImageDrawable(originalDrawable)
windowManager.updateViewLayout(view, layoutParams)
lastLayoutX = layoutParams.x
lastLayoutY = layoutParams.y
}
}
return false
}
private fun moveToScreenSide(center: Boolean = false) {
val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
val wh = getScreenSize(windowManager)
val w = wh.first
if (layoutParams.x < w / 2) {
layoutParams.x = 0
floatingView.setImageDrawable(rightHalfDrawable)
} else {
layoutParams.x = w - viewWidth / 2
floatingView.setImageDrawable(leftHalfDrawable)
}
if (center) {
layoutParams.y = (wh.second - viewHeight) / 2
}
layoutParams.width = viewWidth / 2
windowManager.updateViewLayout(floatingView, layoutParams)
lastLayoutX = layoutParams.x
lastLayoutY = layoutParams.y
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
if (newConfig.orientation != lastOrientation) {
lastOrientation = newConfig.orientation
val wh = getScreenSize(windowManager)
Log.d(logTag, "orientation: $lastOrientation, screen size: ${wh.first} x ${wh.second}")
val newW = wh.first
val newH = wh.second
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE || newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
// Proportional change
layoutParams.x = (layoutParams.x.toFloat() / newH.toFloat() * newW.toFloat()).toInt()
layoutParams.y = (layoutParams.y.toFloat() / newW.toFloat() * newH.toFloat()).toInt()
}
moveToScreenSide()
}
}
private fun showPopupMenu() {
val popupMenu = PopupMenu(this, floatingView)
val idShowRustDesk = 0
popupMenu.menu.add(0, idShowRustDesk, 0, translate("Show RustDesk"))
val idStopService = 1
popupMenu.menu.add(0, idStopService, 0, translate("Stop service"))
popupMenu.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
idShowRustDesk -> {
openMainActivity()
true
}
idStopService -> {
stopMainService()
true
}
else -> false
}
}
popupMenu.setOnDismissListener {
moveToScreenSide()
}
popupMenu.show()
}
private fun openMainActivity() {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val pendingIntent = PendingIntent.getActivity(
this, 0, intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_ONE_SHOT
)
try {
pendingIntent.send()
} catch (e: PendingIntent.CanceledException) {
e.printStackTrace()
}
}
private fun stopMainService() {
MainActivity.flutterMethodChannel?.invokeMethod("stop_service", null)
}
enum class KeepScreenOn {
NEVER,
DURING_CONTROLLED,
SERVICE_ON,
}
private val handler = Handler(Looper.getMainLooper())
private val runnable = object : Runnable {
override fun run() {
if (updateKeepScreenOnLayoutParams()) {
windowManager.updateViewLayout(floatingView, layoutParams)
}
handler.postDelayed(this, 1000) // 1000 milliseconds = 1 second
}
}
private fun updateKeepScreenOnLayoutParams(): Boolean {
val oldOn = layoutParams.flags and FLAG_KEEP_SCREEN_ON != 0
val newOn = keepScreenOn == KeepScreenOn.SERVICE_ON || (keepScreenOn == KeepScreenOn.DURING_CONTROLLED && MainService.isStart)
if (oldOn != newOn) {
Log.d(logTag, "change keep screen on to $newOn")
if (newOn) {
layoutParams.flags = layoutParams.flags or FLAG_KEEP_SCREEN_ON
} else {
layoutParams.flags = layoutParams.flags and FLAG_KEEP_SCREEN_ON.inv()
}
return true
}
return false
}
}

View File

@@ -18,7 +18,9 @@ import android.widget.EditText
import android.view.accessibility.AccessibilityEvent
import android.view.ViewGroup.LayoutParams
import android.view.accessibility.AccessibilityNodeInfo
import android.view.KeyEvent as KeyEventAndroid
import android.graphics.Rect
import android.media.AudioManager
import android.accessibilityservice.AccessibilityServiceInfo
import android.accessibilityservice.AccessibilityServiceInfo.FLAG_INPUT_METHOD_EDITOR
import android.accessibilityservice.AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS
@@ -75,6 +77,8 @@ class InputService : AccessibilityService() {
private var fakeEditTextForTextStateCalculation: EditText? = null
private val volumeController: VolumeController by lazy { VolumeController(applicationContext.getSystemService(AUDIO_SERVICE) as AudioManager) }
@RequiresApi(Build.VERSION_CODES.N)
fun onMouseInput(mask: Int, _x: Int, _y: Int) {
val x = max(0, _x)
@@ -294,6 +298,18 @@ class InputService : AccessibilityService() {
Log.d(logTag, "onKeyEvent $keyEvent textToCommit:$textToCommit")
var ke: KeyEventAndroid? = null
if (Build.VERSION.SDK_INT < 33 || textToCommit == null) {
ke = KeyEventConverter.toAndroidKeyEvent(keyEvent)
}
ke?.let { event ->
if (tryHandleVolumeKeyEvent(event)) {
return
} else if (tryHandlePowerKeyEvent(event)) {
return
}
}
if (Build.VERSION.SDK_INT >= 33) {
getInputMethod()?.let { inputMethod ->
inputMethod.getCurrentInputConnection()?.let { inputConnection ->
@@ -302,7 +318,7 @@ class InputService : AccessibilityService() {
inputConnection.commitText(text, 1, null)
}
} else {
KeyEventConverter.toAndroidKeyEvent(keyEvent).let { event ->
ke?.let { event ->
inputConnection.sendKeyEvent(event)
}
}
@@ -311,7 +327,7 @@ class InputService : AccessibilityService() {
} else {
val handler = Handler(Looper.getMainLooper())
handler.post {
KeyEventConverter.toAndroidKeyEvent(keyEvent)?.let { event ->
ke?.let { event ->
val possibleNodes = possibleAccessibiltyNodes()
Log.d(logTag, "possibleNodes:$possibleNodes")
for (item in possibleNodes) {
@@ -325,6 +341,43 @@ class InputService : AccessibilityService() {
}
}
private fun tryHandleVolumeKeyEvent(event: KeyEventAndroid): Boolean {
when (event.keyCode) {
KeyEventAndroid.KEYCODE_VOLUME_UP -> {
if (event.action == KeyEventAndroid.ACTION_DOWN) {
volumeController.raiseVolume(null, true, AudioManager.STREAM_SYSTEM)
}
return true
}
KeyEventAndroid.KEYCODE_VOLUME_DOWN -> {
if (event.action == KeyEventAndroid.ACTION_DOWN) {
volumeController.lowerVolume(null, true, AudioManager.STREAM_SYSTEM)
}
return true
}
KeyEventAndroid.KEYCODE_VOLUME_MUTE -> {
if (event.action == KeyEventAndroid.ACTION_DOWN) {
volumeController.toggleMute(true, AudioManager.STREAM_SYSTEM)
}
return true
}
else -> {
return false
}
}
}
private fun tryHandlePowerKeyEvent(event: KeyEventAndroid): Boolean {
if (event.keyCode == KeyEventAndroid.KEYCODE_POWER) {
// Perform power dialog action when action is up
if (event.action == KeyEventAndroid.ACTION_UP) {
performGlobalAction(GLOBAL_ACTION_POWER_DIALOG);
}
return true
}
return false
}
private fun insertAccessibilityNode(list: LinkedList<AccessibilityNodeInfo>, node: AccessibilityNodeInfo) {
if (node == null) {
return
@@ -422,7 +475,7 @@ class InputService : AccessibilityService() {
return linkedList
}
private fun trySendKeyEvent(event: android.view.KeyEvent, node: AccessibilityNodeInfo, textToCommit: String?): Boolean {
private fun trySendKeyEvent(event: KeyEventAndroid, node: AccessibilityNodeInfo, textToCommit: String?): Boolean {
node.refresh()
this.fakeEditTextForTextStateCalculation?.setSelection(0,0)
this.fakeEditTextForTextStateCalculation?.setText(null)
@@ -487,10 +540,10 @@ class InputService : AccessibilityService() {
it.layout(rect.left, rect.top, rect.right, rect.bottom)
it.onPreDraw()
if (event.action == android.view.KeyEvent.ACTION_DOWN) {
if (event.action == KeyEventAndroid.ACTION_DOWN) {
val succ = it.onKeyDown(event.getKeyCode(), event)
Log.d(logTag, "onKeyDown $succ")
} else if (event.action == android.view.KeyEvent.ACTION_UP) {
} else if (event.action == KeyEventAndroid.ACTION_UP) {
val success = it.onKeyUp(event.getKeyCode(), event)
Log.d(logTag, "keyup $success")
} else {}

View File

@@ -37,6 +37,8 @@ object KeyEventConverter {
action = KeyEvent.ACTION_UP
}
// FIXME: The last parameter is the repeat count, not modifiers ?
// https://developer.android.com/reference/android/view/KeyEvent#KeyEvent(long,%20long,%20int,%20int,%20int)
return KeyEvent(0, 0, action, chrValue, 0, modifiers)
}
@@ -112,6 +114,10 @@ object KeyEventConverter {
ControlKey.Delete -> KeyEvent.KEYCODE_FORWARD_DEL
ControlKey.Clear -> KeyEvent.KEYCODE_CLEAR
ControlKey.Pause -> KeyEvent.KEYCODE_BREAK
ControlKey.VolumeMute -> KeyEvent.KEYCODE_VOLUME_MUTE
ControlKey.VolumeUp -> KeyEvent.KEYCODE_VOLUME_UP
ControlKey.VolumeDown -> KeyEvent.KEYCODE_VOLUME_DOWN
ControlKey.Power -> KeyEvent.KEYCODE_POWER
else -> 0 // Default to unknown.
}
}

View File

@@ -7,6 +7,8 @@ package com.carriez.flutter_hbb
* Inspired by [droidVNC-NG] https://github.com/bk138/droidVNC-NG
*/
import ffi.FFI
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@@ -15,10 +17,20 @@ import android.os.Build
import android.os.IBinder
import android.util.Log
import android.view.WindowManager
import android.media.MediaCodecInfo
import android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
import android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar
import android.media.MediaCodecList
import android.media.MediaFormat
import android.util.DisplayMetrics
import androidx.annotation.RequiresApi
import org.json.JSONArray
import org.json.JSONObject
import com.hjq.permissions.XXPermissions
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import kotlin.concurrent.thread
class MainActivity : FlutterActivity() {
@@ -30,6 +42,9 @@ class MainActivity : FlutterActivity() {
private val logTag = "mMainActivity"
private var mainService: MainService? = null
private var isAudioStart = false
private val audioRecordHandle = AudioRecordHandle(this, { false }, { isAudioStart })
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
if (MainService.isReady) {
@@ -42,6 +57,7 @@ class MainActivity : FlutterActivity() {
channelTag
)
initFlutterChannel(flutterMethodChannel!!)
thread { setCodecInfo() }
}
override fun onResume() {
@@ -217,10 +233,159 @@ class MainActivity : FlutterActivity() {
result.success(false)
}
}
GET_VALUE -> {
if (call.arguments is String) {
if (call.arguments == KEY_IS_SUPPORT_VOICE_CALL) {
result.success(isSupportVoiceCall())
} else {
result.error("-1", "No such key", null)
}
} else {
result.success(null)
}
}
"on_voice_call_started" -> {
onVoiceCallStarted()
}
"on_voice_call_closed" -> {
onVoiceCallClosed()
}
else -> {
result.error("-1", "No such method", null)
}
}
}
}
private fun setCodecInfo() {
val codecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
val codecs = codecList.codecInfos
val codecArray = JSONArray()
val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
val wh = getScreenSize(windowManager)
var w = wh.first
var h = wh.second
val align = 64
w = (w + align - 1) / align * align
h = (h + align - 1) / align * align
codecs.forEach { codec ->
val codecObject = JSONObject()
codecObject.put("name", codec.name)
codecObject.put("is_encoder", codec.isEncoder)
var hw: Boolean? = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
hw = codec.isHardwareAccelerated
} else {
// https://chromium.googlesource.com/external/webrtc/+/HEAD/sdk/android/src/java/org/webrtc/MediaCodecUtils.java#29
// https://chromium.googlesource.com/external/webrtc/+/master/sdk/android/api/org/webrtc/HardwareVideoEncoderFactory.java#229
if (listOf("OMX.google.", "OMX.SEC.", "c2.android").any { codec.name.startsWith(it, true) }) {
hw = false
} else if (listOf("c2.qti", "OMX.qcom.video", "OMX.Exynos", "OMX.hisi", "OMX.MTK", "OMX.Intel", "OMX.Nvidia").any { codec.name.startsWith(it, true) }) {
hw = true
}
}
if (hw != true) {
return@forEach
}
codecObject.put("hw", hw)
var mime_type = ""
codec.supportedTypes.forEach { type ->
if (listOf("video/avc", "video/hevc").contains(type)) { // "video/x-vnd.on2.vp8", "video/x-vnd.on2.vp9", "video/av01"
mime_type = type;
}
}
if (mime_type.isNotEmpty()) {
codecObject.put("mime_type", mime_type)
val caps = codec.getCapabilitiesForType(mime_type)
if (codec.isEncoder) {
// Encoders max_height and max_width are interchangeable
if (!caps.videoCapabilities.isSizeSupported(w,h) && !caps.videoCapabilities.isSizeSupported(h,w)) {
return@forEach
}
}
codecObject.put("min_width", caps.videoCapabilities.supportedWidths.lower)
codecObject.put("max_width", caps.videoCapabilities.supportedWidths.upper)
codecObject.put("min_height", caps.videoCapabilities.supportedHeights.lower)
codecObject.put("max_height", caps.videoCapabilities.supportedHeights.upper)
val surface = caps.colorFormats.contains(COLOR_FormatSurface);
codecObject.put("surface", surface)
val nv12 = caps.colorFormats.contains(COLOR_FormatYUV420SemiPlanar)
codecObject.put("nv12", nv12)
if (!(nv12 || surface)) {
return@forEach
}
codecObject.put("min_bitrate", caps.videoCapabilities.bitrateRange.lower / 1000)
codecObject.put("max_bitrate", caps.videoCapabilities.bitrateRange.upper / 1000)
if (!codec.isEncoder) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
codecObject.put("low_latency", caps.isFeatureSupported(MediaCodecInfo.CodecCapabilities.FEATURE_LowLatency))
}
}
if (!codec.isEncoder) {
return@forEach
}
codecArray.put(codecObject)
}
}
val result = JSONObject()
result.put("version", Build.VERSION.SDK_INT)
result.put("w", w)
result.put("h", h)
result.put("codecs", codecArray)
FFI.setCodecInfo(result.toString())
}
private fun onVoiceCallStarted() {
var ok = false
mainService?.let {
ok = it.onVoiceCallStarted()
} ?: let {
isAudioStart = true
ok = audioRecordHandle.onVoiceCallStarted(null)
}
if (!ok) {
// Rarely happens, So we just add log and msgbox here.
Log.e(logTag, "onVoiceCallStarted fail")
flutterMethodChannel?.invokeMethod("msgbox", mapOf(
"type" to "custom-nook-nocancel-hasclose-error",
"title" to "Voice call",
"text" to "Failed to start voice call."))
} else {
Log.d(logTag, "onVoiceCallStarted success")
}
}
private fun onVoiceCallClosed() {
var ok = false
mainService?.let {
ok = it.onVoiceCallClosed()
} ?: let {
isAudioStart = false
ok = audioRecordHandle.onVoiceCallClosed(null)
}
if (!ok) {
// Rarely happens, So we just add log and msgbox here.
Log.e(logTag, "onVoiceCallClosed fail")
flutterMethodChannel?.invokeMethod("msgbox", mapOf(
"type" to "custom-nook-nocancel-hasclose-error",
"title" to "Voice call",
"text" to "Failed to stop voice call."))
} else {
Log.d(logTag, "onVoiceCallClosed success")
}
}
override fun onStop() {
super.onStop()
val disableFloatingWindow = FFI.getLocalOption("disable-floating-window") == "Y"
if (!disableFloatingWindow && MainService.isReady) {
startService(Intent(this, FloatingWindowService::class.java))
}
}
override fun onStart() {
super.onStart()
stopService(Intent(this, FloatingWindowService::class.java))
}
}

View File

@@ -1,5 +1,7 @@
package com.carriez.flutter_hbb
import ffi.FFI
/**
* Capture screen,get video and audio,send to rust.
* Dispatch notifications
@@ -52,27 +54,19 @@ const val NOTIFY_ID_OFFSET = 100
const val MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_VP9
// video const
const val MAX_SCREEN_SIZE = 1200
const val VIDEO_KEY_BIT_RATE = 1024_000
const val VIDEO_KEY_FRAME_RATE = 30
// audio const
const val AUDIO_ENCODING = AudioFormat.ENCODING_PCM_FLOAT // ENCODING_OPUS need API 30
const val AUDIO_SAMPLE_RATE = 48000
const val AUDIO_CHANNEL_MASK = AudioFormat.CHANNEL_IN_STEREO
class MainService : Service() {
init {
System.loadLibrary("rustdesk")
}
@Keep
@RequiresApi(Build.VERSION_CODES.N)
fun rustPointerInput(kind: String, mask: Int, x: Int, y: Int) {
fun rustPointerInput(kind: Int, mask: Int, x: Int, y: Int) {
// turn on screen with LIFT_DOWN when screen off
if (!powerManager.isInteractive && (kind == "touch" || mask == LIFT_DOWN)) {
if (!powerManager.isInteractive && (kind == 0 || mask == LIFT_DOWN)) {
if (wakeLock.isHeld) {
Log.d(logTag, "Turn on Screen, WakeLock release")
wakeLock.release()
@@ -81,10 +75,10 @@ class MainService : Service() {
wakeLock.acquire(5000)
} else {
when (kind) {
"touch" -> {
0 -> { // touch
InputService.ctx?.onTouchInput(mask, x, y)
}
"mouse" -> {
1 -> { // mouse
InputService.ctx?.onMouseInput(mask, x, y)
}
else -> {
@@ -109,6 +103,9 @@ class MainService : Service() {
put("scale",SCREEN_INFO.scale)
}.toString()
}
"is_start" -> {
isStart.toString()
}
else -> ""
}
}
@@ -141,10 +138,51 @@ class MainService : Service() {
e.printStackTrace()
}
}
"update_voice_call_state" -> {
try {
val jsonObject = JSONObject(arg1)
val id = jsonObject["id"] as Int
val username = jsonObject["name"] as String
val peerId = jsonObject["peer_id"] as String
val inVoiceCall = jsonObject["in_voice_call"] as Boolean
val incomingVoiceCall = jsonObject["incoming_voice_call"] as Boolean
if (!inVoiceCall) {
if (incomingVoiceCall) {
voiceCallRequestNotification(id, "Voice Call Request", username, peerId)
} else {
if (!audioRecordHandle.switchOutVoiceCall(mediaProjection)) {
Log.e(logTag, "switchOutVoiceCall fail")
MainActivity.flutterMethodChannel?.invokeMethod("msgbox", mapOf(
"type" to "custom-nook-nocancel-hasclose-error",
"title" to "Voice call",
"text" to "Failed to switch out voice call."))
}
}
} else {
if (!audioRecordHandle.switchToVoiceCall(mediaProjection)) {
Log.e(logTag, "switchToVoiceCall fail")
MainActivity.flutterMethodChannel?.invokeMethod("msgbox", mapOf(
"type" to "custom-nook-nocancel-hasclose-error",
"title" to "Voice call",
"text" to "Failed to switch to voice call."))
}
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
"stop_capture" -> {
Log.d(logTag, "from rust:stop_capture")
stopCapture()
}
"half_scale" -> {
val halfScale = arg1.toBoolean()
if (isHalfScale != halfScale) {
isHalfScale = halfScale
updateScreenInfo(resources.configuration.orientation)
}
}
else -> {
}
}
@@ -156,38 +194,23 @@ class MainService : Service() {
private val powerManager: PowerManager by lazy { applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager }
private val wakeLock: PowerManager.WakeLock by lazy { powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "rustdesk:wakelock")}
// jvm call rust
private external fun init(ctx: Context)
/// When app start on boot, app_dir will not be passed from flutter
/// so pass a app_dir here to rust server
private external fun startServer(app_dir: String)
private external fun startService()
private external fun onVideoFrameUpdate(buf: ByteBuffer)
private external fun onAudioFrameUpdate(buf: ByteBuffer)
private external fun translateLocale(localeName: String, input: String): String
private external fun refreshScreen()
private external fun setFrameRawEnable(name: String, value: Boolean)
// private external fun sendVp9(data: ByteArray)
private fun translate(input: String): String {
Log.d(logTag, "translate:$LOCAL_NAME")
return translateLocale(LOCAL_NAME, input)
}
companion object {
private var _isReady = false // media permission ready status
private var _isStart = false // screen capture start status
private var _isAudioStart = false // audio capture start status
val isReady: Boolean
get() = _isReady
val isStart: Boolean
get() = _isStart
val isAudioStart: Boolean
get() = _isAudioStart
}
private val logTag = "LOG_SERVICE"
private val useVP9 = false
private val binder = LocalBinder()
private var reuseVirtualDisplay = Build.VERSION.SDK_INT > 33
// video
private var mediaProjection: MediaProjection? = null
@@ -198,10 +221,7 @@ class MainService : Service() {
private var virtualDisplay: VirtualDisplay? = null
// audio
private var audioRecorder: AudioRecord? = null
private var audioReader: AudioReader? = null
private var minBufferSize = 0
private var audioRecordStat = false
private val audioRecordHandle = AudioRecordHandle(this, { isStart }, { isAudioStart })
// notification
private lateinit var notificationManager: NotificationManager
@@ -210,8 +230,8 @@ class MainService : Service() {
override fun onCreate() {
super.onCreate()
Log.d(logTag,"MainService onCreate")
init(this)
Log.d(logTag,"MainService onCreate, sdk int:${Build.VERSION.SDK_INT} reuseVirtualDisplay:$reuseVirtualDisplay")
FFI.init(this)
HandlerThread("Service", Process.THREAD_PRIORITY_BACKGROUND).apply {
start()
serviceLooper = looper
@@ -223,16 +243,18 @@ class MainService : Service() {
// keep the config dir same with flutter
val prefs = applicationContext.getSharedPreferences(KEY_SHARED_PREFERENCES, FlutterActivity.MODE_PRIVATE)
val configPath = prefs.getString(KEY_APP_DIR_CONFIG_PATH, "") ?: ""
startServer(configPath)
FFI.startServer(configPath, "")
createForegroundNotification()
}
override fun onDestroy() {
checkMediaPermission()
stopService(Intent(this, FloatingWindowService::class.java))
super.onDestroy()
}
private var isHalfScale: Boolean? = null;
private fun updateScreenInfo(orientation: Int) {
var w: Int
var h: Int
@@ -265,7 +287,7 @@ class MainService : Service() {
Log.d(logTag,"updateScreenInfo:w:$w,h:$h")
var scale = 1
if (w != 0 && h != 0) {
if (w > MAX_SCREEN_SIZE || h > MAX_SCREEN_SIZE) {
if (isHalfScale == true && (w > MAX_SCREEN_SIZE || h > MAX_SCREEN_SIZE)) {
scale = 2
w /= scale
h /= scale
@@ -278,7 +300,7 @@ class MainService : Service() {
SCREEN_INFO.dpi = dpi
if (isStart) {
stopCapture()
refreshScreen()
FFI.refreshScreen()
startCapture()
}
}
@@ -306,7 +328,7 @@ class MainService : Service() {
createForegroundNotification()
if (intent.getBooleanExtra(EXT_INIT_FROM_BOOT, false)) {
startService()
FFI.startService()
}
Log.d(logTag, "service starting: ${startId}:${Thread.currentThread()}")
val mediaProjectionManager =
@@ -354,12 +376,13 @@ class MainService : Service() {
).apply {
setOnImageAvailableListener({ imageReader: ImageReader ->
try {
// If not call acquireLatestImage, listener will not be called again
imageReader.acquireLatestImage().use { image ->
if (image == null) return@setOnImageAvailableListener
if (image == null || !isStart) return@setOnImageAvailableListener
val planes = image.planes
val buffer = planes[0].buffer
buffer.rewind()
onVideoFrameUpdate(buffer)
FFI.onVideoFrameUpdate(buffer)
}
} catch (ignored: java.lang.Exception) {
}
@@ -370,6 +393,14 @@ class MainService : Service() {
}
}
fun onVoiceCallStarted(): Boolean {
return audioRecordHandle.onVoiceCallStarted(mediaProjection)
}
fun onVoiceCallClosed(): Boolean {
return audioRecordHandle.onVoiceCallClosed(mediaProjection)
}
fun startCapture(): Boolean {
if (isStart) {
return true
@@ -378,6 +409,7 @@ class MainService : Service() {
Log.w(logTag, "startCapture fail,mediaProjection is null")
return false
}
updateScreenInfo(resources.configuration.orientation)
Log.d(logTag, "Start Capture")
surface = createSurface()
@@ -389,51 +421,71 @@ class MainService : Service() {
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
startAudioRecorder()
if (!audioRecordHandle.createAudioRecorder(false, mediaProjection)) {
Log.d(logTag, "createAudioRecorder fail")
} else {
Log.d(logTag, "audio recorder start")
audioRecordHandle.startAudioRecorder()
}
}
checkMediaPermission()
_isStart = true
setFrameRawEnable("video",true)
setFrameRawEnable("audio",true)
FFI.setFrameRawEnable("video",true)
return true
}
@Synchronized
fun stopCapture() {
Log.d(logTag, "Stop Capture")
setFrameRawEnable("video",false)
setFrameRawEnable("audio",false)
FFI.setFrameRawEnable("video",false)
_isStart = false
// release video
virtualDisplay?.release()
surface?.release()
if (reuseVirtualDisplay) {
// The virtual display video projection can be paused by calling `setSurface(null)`.
// https://developer.android.com/reference/android/hardware/display/VirtualDisplay.Callback
// https://learn.microsoft.com/en-us/dotnet/api/android.hardware.display.virtualdisplay.callback.onpaused?view=net-android-34.0
virtualDisplay?.setSurface(null)
} else {
virtualDisplay?.release()
}
// suface needs to be release after `imageReader.close()` to imageReader access released surface
// https://github.com/rustdesk/rustdesk/issues/4118#issuecomment-1515666629
imageReader?.close()
imageReader = null
videoEncoder?.let {
it.signalEndOfInputStream()
it.stop()
it.release()
}
virtualDisplay = null
if (!reuseVirtualDisplay) {
virtualDisplay = null
}
videoEncoder = null
// suface needs to be release after `imageReader.close()` to imageReader access released surface
// https://github.com/rustdesk/rustdesk/issues/4118#issuecomment-1515666629
surface?.release()
// release audio
audioRecordStat = false
audioRecorder?.release()
audioRecorder = null
minBufferSize = 0
_isAudioStart = false
audioRecordHandle.tryReleaseAudio()
}
fun destroy() {
Log.d(logTag, "destroy service")
_isReady = false
_isAudioStart = false
stopCapture()
imageReader?.close()
imageReader = null
if (reuseVirtualDisplay) {
virtualDisplay?.release()
virtualDisplay = null
}
mediaProjection = null
checkMediaPermission()
stopForeground(true)
stopService(Intent(this, FloatingWindowService::class.java))
stopSelf()
}
@@ -459,11 +511,7 @@ class MainService : Service() {
Log.d(logTag, "startRawVideoRecorder failed,surface is null")
return
}
virtualDisplay = mp.createVirtualDisplay(
"RustDeskVD",
SCREEN_INFO.width, SCREEN_INFO.height, SCREEN_INFO.dpi, VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
surface, null, null
)
createOrSetVirtualDisplay(mp, surface!!)
}
private fun startVP9VideoRecorder(mp: MediaProjection) {
@@ -475,11 +523,28 @@ class MainService : Service() {
}
it.setCallback(cb)
it.start()
virtualDisplay = mp.createVirtualDisplay(
"RustDeskVD",
SCREEN_INFO.width, SCREEN_INFO.height, SCREEN_INFO.dpi, VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
surface, null, null
)
createOrSetVirtualDisplay(mp, surface!!)
}
}
// https://github.com/bk138/droidVNC-NG/blob/b79af62db5a1c08ed94e6a91464859ffed6f4e97/app/src/main/java/net/christianbeier/droidvnc_ng/MediaProjectionService.java#L250
// Reuse virtualDisplay if it exists, to avoid media projection confirmation dialog every connection.
private fun createOrSetVirtualDisplay(mp: MediaProjection, s: Surface) {
try {
virtualDisplay?.let {
it.resize(SCREEN_INFO.width, SCREEN_INFO.height, SCREEN_INFO.dpi)
it.setSurface(s)
} ?: let {
virtualDisplay = mp.createVirtualDisplay(
"RustDeskVD",
SCREEN_INFO.width, SCREEN_INFO.height, SCREEN_INFO.dpi, VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
s, null, null
)
}
} catch (e: SecurityException) {
Log.w(logTag, "createOrSetVirtualDisplay: got SecurityException, re-requesting confirmation");
// This initiates a prompt dialog for the user to confirm screen projection.
requestMediaProjection()
}
}
@@ -507,7 +572,6 @@ class MainService : Service() {
}
}
private fun createMediaCodec() {
Log.d(logTag, "MediaFormat.MIMETYPE_VIDEO_VP9 :$MIME_TYPE")
videoEncoder = MediaCodec.createEncoderByType(MIME_TYPE)
@@ -527,76 +591,6 @@ class MainService : Service() {
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun startAudioRecorder() {
checkAudioRecorder()
if (audioReader != null && audioRecorder != null && minBufferSize != 0) {
try {
audioRecorder!!.startRecording()
audioRecordStat = true
thread {
while (audioRecordStat) {
audioReader!!.readSync(audioRecorder!!)?.let {
onAudioFrameUpdate(it)
}
}
Log.d(logTag, "Exit audio thread")
}
} catch (e: Exception) {
Log.d(logTag, "startAudioRecorder fail:$e")
}
} else {
Log.d(logTag, "startAudioRecorder fail")
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun checkAudioRecorder() {
if (audioRecorder != null && audioRecorder != null && minBufferSize != 0) {
return
}
// read f32 to byte , length * 4
minBufferSize = 2 * 4 * AudioRecord.getMinBufferSize(
AUDIO_SAMPLE_RATE,
AUDIO_CHANNEL_MASK,
AUDIO_ENCODING
)
if (minBufferSize == 0) {
Log.d(logTag, "get min buffer size fail!")
return
}
audioReader = AudioReader(minBufferSize, 4)
Log.d(logTag, "init audioData len:$minBufferSize")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
mediaProjection?.let {
val apcc = AudioPlaybackCaptureConfiguration.Builder(it)
.addMatchingUsage(AudioAttributes.USAGE_MEDIA)
.addMatchingUsage(AudioAttributes.USAGE_ALARM)
.addMatchingUsage(AudioAttributes.USAGE_GAME)
.addMatchingUsage(AudioAttributes.USAGE_UNKNOWN).build()
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.RECORD_AUDIO
) != PackageManager.PERMISSION_GRANTED
) {
return
}
audioRecorder = AudioRecord.Builder()
.setAudioFormat(
AudioFormat.Builder()
.setEncoding(AUDIO_ENCODING)
.setSampleRate(AUDIO_SAMPLE_RATE)
.setChannelMask(AUDIO_CHANNEL_MASK).build()
)
.setAudioPlaybackCaptureConfig(apcc)
.setBufferSizeInBytes(minBufferSize).build()
Log.d(logTag, "createAudioRecorder done,minBufferSize:$minBufferSize")
return
}
}
Log.d(logTag, "createAudioRecorder fail")
}
private fun initNotification() {
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationChannel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -681,6 +675,21 @@ class MainService : Service() {
notificationManager.notify(getClientNotifyID(clientID), notification)
}
private fun voiceCallRequestNotification(
clientID: Int,
type: String,
username: String,
peerId: String
) {
val notification = notificationBuilder
.setOngoing(false)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentTitle(translate("Do you accept?"))
.setContentText("$type:$username-$peerId")
.build()
notificationManager.notify(getClientNotifyID(clientID), notification)
}
private fun getClientNotifyID(clientID: Int): Int {
return clientID + NOTIFY_ID_OFFSET
}

View File

@@ -0,0 +1,78 @@
package com.carriez.flutter_hbb
// Inspired by https://github.com/yosemiteyss/flutter_volume_controller/blob/main/android/src/main/kotlin/com/yosemiteyss/flutter_volume_controller/VolumeController.kt
import android.media.AudioManager
import android.os.Build
import android.util.Log
class VolumeController(private val audioManager: AudioManager) {
private val logTag = "volume controller"
fun getVolume(streamType: Int): Double {
val current = audioManager.getStreamVolume(streamType)
val max = audioManager.getStreamMaxVolume(streamType)
return current.toDouble() / max
}
fun setVolume(volume: Double, showSystemUI: Boolean, streamType: Int) {
val max = audioManager.getStreamMaxVolume(streamType)
audioManager.setStreamVolume(
streamType,
(max * volume).toInt(),
if (showSystemUI) AudioManager.FLAG_SHOW_UI else 0
)
}
fun raiseVolume(step: Double?, showSystemUI: Boolean, streamType: Int) {
if (step == null) {
audioManager.adjustStreamVolume(
streamType,
AudioManager.ADJUST_RAISE,
if (showSystemUI) AudioManager.FLAG_SHOW_UI else 0
)
} else {
val target = getVolume(streamType) + step
setVolume(target, showSystemUI, streamType)
}
}
fun lowerVolume(step: Double?, showSystemUI: Boolean, streamType: Int) {
if (step == null) {
audioManager.adjustStreamVolume(
streamType,
AudioManager.ADJUST_LOWER,
if (showSystemUI) AudioManager.FLAG_SHOW_UI else 0
)
} else {
val target = getVolume(streamType) - step
setVolume(target, showSystemUI, streamType)
}
}
fun getMute(streamType: Int): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
audioManager.isStreamMute(streamType)
} else {
audioManager.getStreamVolume(streamType) == 0
}
}
private fun setMute(isMuted: Boolean, showSystemUI: Boolean, streamType: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
audioManager.adjustStreamVolume(
streamType,
if (isMuted) AudioManager.ADJUST_MUTE else AudioManager.ADJUST_UNMUTE,
if (showSystemUI) AudioManager.FLAG_SHOW_UI else 0
)
} else {
audioManager.setStreamMute(streamType, isMuted)
}
}
fun toggleMute(showSystemUI: Boolean, streamType: Int) {
val isMuted = getMute(streamType)
setMute(!isMuted, showSystemUI, streamType)
}
}

View File

@@ -15,10 +15,14 @@ import android.os.Looper
import android.os.PowerManager
import android.provider.Settings
import android.provider.Settings.*
import android.util.DisplayMetrics
import android.util.Log
import android.view.WindowManager
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat.getSystemService
import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions
import ffi.FFI
import java.nio.ByteBuffer
import java.util.*
@@ -43,6 +47,9 @@ const val START_ACTION = "start_action"
const val GET_START_ON_BOOT_OPT = "get_start_on_boot_opt"
const val SET_START_ON_BOOT_OPT = "set_start_on_boot_opt"
const val SYNC_APP_DIR_CONFIG_PATH = "sync_app_dir"
const val GET_VALUE = "get_value"
const val KEY_IS_SUPPORT_VOICE_CALL = "KEY_IS_SUPPORT_VOICE_CALL"
const val KEY_SHARED_PREFERENCES = "KEY_SHARED_PREFERENCES"
const val KEY_START_ON_BOOT_OPT = "KEY_START_ON_BOOT_OPT"
@@ -56,6 +63,11 @@ data class Info(
var width: Int, var height: Int, var scale: Int, var dpi: Int
)
fun isSupportVoiceCall(): Boolean {
// https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#VOICE_COMMUNICATION
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
}
fun requestPermission(context: Context, type: String) {
XXPermissions.with(context)
.permission(type)
@@ -120,3 +132,26 @@ class AudioReader(val bufSize: Int, private val maxFrames: Int) {
}
}
}
fun getScreenSize(windowManager: WindowManager) : Pair<Int, Int>{
var w = 0
var h = 0
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val m = windowManager.maximumWindowMetrics
w = m.bounds.width()
h = m.bounds.height()
} else {
val dm = DisplayMetrics()
windowManager.defaultDisplay.getRealMetrics(dm)
w = dm.widthPixels
h = dm.heightPixels
}
return Pair(w, h)
}
fun translate(input: String): String {
Log.d("common", "translate:$LOCAL_NAME")
return FFI.translateLocale(LOCAL_NAME, input)
}

View File

@@ -0,0 +1,23 @@
// ffi.kt
package ffi
import android.content.Context
import java.nio.ByteBuffer
object FFI {
init {
System.loadLibrary("rustdesk")
}
external fun init(ctx: Context)
external fun startServer(app_dir: String, custom_client_config: String)
external fun startService()
external fun onVideoFrameUpdate(buf: ByteBuffer)
external fun onAudioFrameUpdate(buf: ByteBuffer)
external fun translateLocale(localeName: String, input: String): String
external fun refreshScreen()
external fun setFrameRawEnable(name: String, value: Boolean)
external fun setCodecInfo(info: String)
external fun getLocalOption(key: String): String
}

View File

@@ -0,0 +1,7 @@
<vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android" android:height="320dp" android:viewportHeight="32" android:viewportWidth="32" android:width="320dp">
<path android:fillColor="#ffffff" android:pathData="M16,0L16,0A16,16 0,0 1,32 16L32,16A16,16 0,0 1,16 32L16,32A16,16 0,0 1,0 16L0,16A16,16 0,0 1,16 0z" android:strokeColor="#00000000" android:strokeWidth="1"/>
<path android:fillColor="#1a1a1a" android:pathData="m23.89,10.135 l-1.807,1.795c-0.318,0.285 -0.472,0.744 -0.293,1.131 1.204,2.518 0.747,5.52 -1.228,7.494 -1.976,1.973 -4.981,2.429 -7.502,1.226 -0.371,-0.166 -0.807,-0.025 -1.093,0.265l-1.836,1.833c-0.216,0.211 -0.322,0.51 -0.288,0.809 0.034,0.3 0.206,0.567 0.463,0.723 4.326,2.618 9.882,1.951 13.463,-1.618 3.581,-3.568 4.264,-9.115 1.655,-13.443 -0.15,-0.263 -0.414,-0.442 -0.714,-0.484 -0.3,-0.043 -0.603,0.058 -0.819,0.269zM8.265,8.184c-3.599,3.554 -4.304,9.103 -1.709,13.441 0.15,0.264 0.413,0.443 0.714,0.485 0.3,0.042 0.603,-0.058 0.82,-0.27l1.797,-1.785c0.325,-0.285 0.484,-0.749 0.303,-1.141 -1.204,-2.518 -0.748,-5.52 1.228,-7.493 1.975,-1.973 4.981,-2.429 7.502,-1.227 0.367,0.165 0.797,0.028 1.084,-0.254l1.846,-1.844c0.216,-0.211 0.322,-0.509 0.288,-0.809 -0.035,-0.299 -0.206,-0.566 -0.463,-0.723 -4.334,-2.596 -9.881,-1.908 -13.448,1.668z" android:strokeWidth="0.987992"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

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

After

Width:  |  Height:  |  Size: 277 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="0 0 1024 1024"><path d="M608 160c141.16 0 256 114.84 256 256 0 17.67 14.33 32 32 32s32-14.33 32-32c0-85.48-33.29-165.83-93.73-226.27C773.83 129.29 693.47 96 608 96c-17.67 0-32 14.33-32 32s14.33 32 32 32zm-24 168c61.76 0 112 50.24 112 112 0 17.67 14.33 32 32 32s32-14.33 32-32c0-97.05-78.95-176-176-176-17.67 0-32 14.33-32 32s14.33 32 32 32z"/><path d="M808.3 561.21c-12.76-3.83-25.7-6.2-38.46-7.03-60.3-4.5-116.45 18.9-146.55 61.08-22.6 31.67-45.66 50.01-68.52 54.5-17.71 3.48-33.12-1.7-45.49-5.85-2.66-.9-5.18-1.74-7.68-2.49-93.84-28.17-156.49-108.42-155.9-199.7.16-24.14 16.38-45.98 42.34-56.99 43.75-18.56 77.35-54 92.17-97.22 7.02-20.48 9.65-41.57 7.8-62.68-2.66-31.78-15.1-61.85-35.96-86.96-21.1-25.39-49.51-44-82.16-53.8-4.07-1.22-8.22-2.31-12.35-3.23-30.63-6.87-62.7-4.49-92.73 6.88-29.24 11.07-54.56 29.86-73.23 54.33a476.073 476.073 0 0 0-36.42 55.34 477.675 477.675 0 0 0-17.24 33.81C109.84 312.17 95.73 376.76 96 443.15c.26 63.78 13.7 126.26 39.95 185.7 27.55 62.39 69.3 119.84 120.74 166.11 54.14 48.71 117.6 84.85 188.63 107.4C499.02 919.41 554.33 928 610.21 928c10.99 0 22.01-.33 33.03-1 17.64-1.07 31.08-16.23 30.01-33.87-1.07-17.64-16.22-31.08-33.87-30.01-59.19 3.57-117.96-3.75-174.69-21.76C342.78 802.66 244.31 715.78 194.5 603c-46.76-105.9-46.21-221.33 1.55-325.03 4.55-9.87 9.57-19.72 14.92-29.26 9.29-16.54 19.89-32.64 31.5-47.86 23.47-30.77 64.09-45.87 101.07-37.58 2.66.6 5.33 1.3 7.95 2.08 40.93 12.29 69.48 45.6 72.75 84.86 0 .05.01.1.01.15 1.07 12.15-.47 24.39-4.58 36.37-8.94 26.06-29.58 47.59-56.63 59.07-23.58 10.01-43.63 25.72-57.99 45.45-15.12 20.78-23.2 45-23.36 70.05-.37 57.15 19 114.29 54.53 160.91 36.46 47.83 87.28 82.58 146.96 100.49 1.5.45 3.44 1.1 5.69 1.86 29.79 10.01 108.9 36.59 186.49-72.13 16.95-23.75 52.2-37.26 89.81-34.42l.36.03c7.97.51 16.17 2.02 24.34 4.47 22.12 6.64 42.04 25.38 56.11 52.77 16.97 33.04 21.71 72.53 12.1 100.56l-.16.47c-5.54 16.05-17.78 29.48-34.47 37.8-15.82 7.89-22.24 27.1-14.36 42.92s27.1 22.24 42.92 14.36c31.78-15.85 55.36-42.19 66.41-74.2l.18-.53c15.23-44.4 9.22-102.11-15.68-150.61-22.07-43.02-55.68-73.15-94.62-84.84z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="-186 -186 1365 1365"><path d="M608 160c141.16 0 256 114.84 256 256 0 17.67 14.33 32 32 32s32-14.33 32-32c0-85.48-33.29-165.83-93.73-226.27C773.83 129.29 693.47 96 608 96c-17.67 0-32 14.33-32 32s14.33 32 32 32zm-24 168c61.76 0 112 50.24 112 112 0 17.67 14.33 32 32 32s32-14.33 32-32c0-97.05-78.95-176-176-176-17.67 0-32 14.33-32 32s14.33 32 32 32z"/><path d="M808.3 561.21c-12.76-3.83-25.7-6.2-38.46-7.03-60.3-4.5-116.45 18.9-146.55 61.08-22.6 31.67-45.66 50.01-68.52 54.5-17.71 3.48-33.12-1.7-45.49-5.85-2.66-.9-5.18-1.74-7.68-2.49-93.84-28.17-156.49-108.42-155.9-199.7.16-24.14 16.38-45.98 42.34-56.99 43.75-18.56 77.35-54 92.17-97.22 7.02-20.48 9.65-41.57 7.8-62.68-2.66-31.78-15.1-61.85-35.96-86.96-21.1-25.39-49.51-44-82.16-53.8-4.07-1.22-8.22-2.31-12.35-3.23-30.63-6.87-62.7-4.49-92.73 6.88-29.24 11.07-54.56 29.86-73.23 54.33a476.073 476.073 0 0 0-36.42 55.34 477.675 477.675 0 0 0-17.24 33.81C109.84 312.17 95.73 376.76 96 443.15c.26 63.78 13.7 126.26 39.95 185.7 27.55 62.39 69.3 119.84 120.74 166.11 54.14 48.71 117.6 84.85 188.63 107.4C499.02 919.41 554.33 928 610.21 928c10.99 0 22.01-.33 33.03-1 17.64-1.07 31.08-16.23 30.01-33.87-1.07-17.64-16.22-31.08-33.87-30.01-59.19 3.57-117.96-3.75-174.69-21.76C342.78 802.66 244.31 715.78 194.5 603c-46.76-105.9-46.21-221.33 1.55-325.03 4.55-9.87 9.57-19.72 14.92-29.26 9.29-16.54 19.89-32.64 31.5-47.86 23.47-30.77 64.09-45.87 101.07-37.58 2.66.6 5.33 1.3 7.95 2.08 40.93 12.29 69.48 45.6 72.75 84.86 0 .05.01.1.01.15 1.07 12.15-.47 24.39-4.58 36.37-8.94 26.06-29.58 47.59-56.63 59.07-23.58 10.01-43.63 25.72-57.99 45.45-15.12 20.78-23.2 45-23.36 70.05-.37 57.15 19 114.29 54.53 160.91 36.46 47.83 87.28 82.58 146.96 100.49 1.5.45 3.44 1.1 5.69 1.86 29.79 10.01 108.9 36.59 186.49-72.13 16.95-23.75 52.2-37.26 89.81-34.42l.36.03c7.97.51 16.17 2.02 24.34 4.47 22.12 6.64 42.04 25.38 56.11 52.77 16.97 33.04 21.71 72.53 12.1 100.56l-.16.47c-5.54 16.05-17.78 29.48-34.47 37.8-15.82 7.89-22.24 27.1-14.36 42.92s27.1 22.24 42.92 14.36c31.78-15.85 55.36-42.19 66.41-74.2l.18-.53c15.23-44.4 9.22-102.11-15.68-150.61-22.07-43.02-55.68-73.15-94.62-84.84z"/></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

564
flutter/build_fdroid.sh Executable file
View File

@@ -0,0 +1,564 @@
#!/bin/bash
set -x
#
# Script to build F-Droid release of RustDesk
#
# Copyright (C) 2024, The RustDesk Authors
# 2024, Vasyl Gello <vasek.gello@gmail.com>
#
# The script is invoked by F-Droid builder system ste-by-step.
#
# It accepts the following arguments:
#
# - versionName from https://github.com/rustdesk/rustdesk/releases/download/fdroid-version/rustdesk-version.txt
# - versionCode from https://github.com/rustdesk/rustdesk/releases/download/fdroid-version/rustdesk-version.txt
# - Android architecture to build APK for: armeabi-v7a arm64-v8av x86 x86_64
# - The build step to execute:
#
# + sudo-deps: as root, install needed Debian packages into builder VM
# + prebuild: patch sources and do other stuff before the build
# + build: perform actual build of APK file
#
# Parse command-line arguments
VERNAME="${1}"
VERCODE="${2}"
ANDROID_ABI="${3}"
BUILDSTEP="${4}"
if [ -z "${VERNAME}" ] || [ -z "${VERCODE}" ] || [ -z "${ANDROID_ABI}" ] ||
[ -z "${BUILDSTEP}" ]; then
echo "ERROR: Command-line arguments are all required to be non-empty!" >&2
exit 1
fi
# Set various architecture-specific identifiers
case "${ANDROID_ABI}" in
arm64-v8a)
FLUTTER_TARGET=android-arm64
NDK_TARGET=aarch64-linux-android
RUST_TARGET=aarch64-linux-android
RUSTDESK_FEATURES='flutter,hwcodec'
;;
armeabi-v7a)
FLUTTER_TARGET=android-arm
NDK_TARGET=arm-linux-androideabi
RUST_TARGET=armv7-linux-androideabi
RUSTDESK_FEATURES='flutter,hwcodec'
;;
x86_64)
FLUTTER_TARGET=android-x64
NDK_TARGET=x86_64-linux-android
RUST_TARGET=x86_64-linux-android
RUSTDESK_FEATURES='flutter'
;;
x86)
FLUTTER_TARGET=android-x86
NDK_TARGET=i686-linux-android
RUST_TARGET=i686-linux-android
RUSTDESK_FEATURES='flutter'
;;
*)
echo "ERROR: Unknown Android ABI '${ANDROID_ABI}'!" >&2
exit 1
;;
esac
# Check ANDROID_SDK_ROOT and sdkmanager present on PATH
if [ ! -d "${ANDROID_SDK_ROOT}" ] || ! command -v sdkmanager 1>/dev/null; then
echo "ERROR: Can not find Android SDK!" >&2
exit 1
fi
# Export necessary variables
export PATH="${PATH}:${HOME}/flutter/bin:${HOME}/depot_tools"
export VCPKG_ROOT="${HOME}/vcpkg"
# Now act depending on build step
# NOTE: F-Droid maintainers require explicit declaration of dependencies
# as root via `Builds.sudo` F-Droid metadata directive:
# https://gitlab.com/fdroid/fdroiddata/-/merge_requests/15343#note_1988918695
case "${BUILDSTEP}" in
prebuild)
# prebuild: patch sources and do other stuff before the build
#
# Extract required versions for NDK, Rust, Flutter from
# '.github/workflows/flutter-build.yml'
#
CARGO_NDK_VERSION="$(yq -r \
.env.CARGO_NDK_VERSION \
.github/workflows/flutter-build.yml)"
FLUTTER_VERSION="$(yq -r \
.env.ANDROID_FLUTTER_VERSION \
.github/workflows/flutter-build.yml)"
if [ -z "${FLUTTER_VERSION}" ]; then
FLUTTER_VERSION="$(yq -r \
.env.FLUTTER_VERSION \
.github/workflows/flutter-build.yml)"
fi
FLUTTER_RUST_BRIDGE_VERSION="$(yq -r \
.env.FLUTTER_RUST_BRIDGE_VERSION \
.github/workflows/flutter-build.yml)"
NDK_VERSION="$(yq -r \
.env.NDK_VERSION \
.github/workflows/flutter-build.yml)"
RUST_VERSION="$(yq -r \
.env.RUST_VERSION \
.github/workflows/flutter-build.yml)"
VCPKG_COMMIT_ID="$(yq -r \
.env.VCPKG_COMMIT_ID \
.github/workflows/flutter-build.yml)"
if [ -z "${CARGO_NDK_VERSION}" ] || [ -z "${FLUTTER_VERSION}" ] ||
[ -z "${FLUTTER_RUST_BRIDGE_VERSION}" ] ||
[ -z "${NDK_VERSION}" ] || [ -z "${RUST_VERSION}" ] ||
[ -z "${VCPKG_COMMIT_ID}" ]; then
echo "ERROR: Can not identify all required versions!" >&2
exit 1
fi
# Map NDK version to revision
NDK_VERSION="$(wget \
-qO- \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
'https://api.github.com/repos/android/ndk/releases' |
jq -r ".[] | select(.tag_name == \"${NDK_VERSION}\") | .body | match(\"ndkVersion \\\"(.*)\\\"\").captures[0].string")"
if [ -z "${NDK_VERSION}" ]; then
echo "ERROR: Can not map Android NDK codename to revision!" >&2
exit 1
fi
export ANDROID_NDK_HOME="${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}"
export ANDROID_NDK_ROOT="${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}"
#
# Install the components
#
set -e
# Install Android NDK
if [ ! -d "${ANDROID_NDK_ROOT}" ]; then
sdkmanager --install "ndk;${NDK_VERSION}"
fi
# Install Flutter
if [ ! -f "${HOME}/flutter/bin/flutter" ]; then
pushd "${HOME}"
git clone https://github.com/flutter/flutter
pushd flutter
git reset --hard "${FLUTTER_VERSION}"
flutter config --no-analytics
popd # flutter
popd # ${HOME}
fi
# Install Rust
if [ ! -f "${HOME}/rustup/rustup-init.sh" ]; then
pushd "${HOME}"
git clone --depth 1 https://github.com/rust-lang/rustup
popd # ${HOME}
fi
pushd "${HOME}/rustup"
bash rustup-init.sh -y \
--target "${RUST_TARGET}" \
--default-toolchain "${RUST_VERSION}"
popd
if ! command -v cargo 1>/dev/null 2>&1; then
. "${HOME}/.cargo/env"
fi
# Install cargo-ndk
cargo install \
cargo-ndk \
--version "${CARGO_NDK_VERSION}"
# Install rust bridge generator
cargo install cargo-expand
cargo install flutter_rust_bridge_codegen \
--version "${FLUTTER_RUST_BRIDGE_VERSION}" \
--features "uuid"
# Populate native vcpkg dependencies
if [ ! -d "${VCPKG_ROOT}" ]; then
pushd "${HOME}"
git clone \
https://github.com/Microsoft/vcpkg.git
git clone \
https://github.com/Microsoft/vcpkg-tool.git
pushd vcpkg-tool
mkdir build
pushd build
cmake \
-DCMAKE_BUILD_TYPE=Release \
-G 'Ninja' \
-DVCPKG_DEVELOPMENT_WARNINGS=OFF \
..
cmake --build .
popd # build
popd # vcpkg-tool
pushd vcpkg
git reset --hard "${VCPKG_COMMIT_ID}"
cp -a ../vcpkg-tool/build/vcpkg vcpkg
# disable telemetry
touch "vcpkg.disable-metrics"
popd # vcpkg
popd # ${HOME}
fi
# Install depot-tools for x86
if [ "${ANDROID_ABI}" = "x86" ]; then
if [ ! -d "${HOME}/depot_tools" ]; then
pushd "${HOME}"
git clone \
--depth 1 \
https://chromium.googlesource.com/chromium/tools/depot_tools.git
popd # ${HOME}
fi
fi
# Patch the RustDesk sources
git apply res/fdroid/patches/*.patch
sed \
-i \
-e '/gms/d' \
flutter/android/build.gradle \
flutter/android/app/build.gradle
sed \
-i \
-e '/firebase_analytics/d' \
flutter/pubspec.yaml
sed \
-i \
-e '/ firebase/,/ version/d' \
flutter/pubspec.lock
sed \
-i \
-e '/firebase/Id' \
flutter/lib/main.dart
if [ "${FLUTTER_VERSION}" = "3.13.9" ]; then
# Fix for android 3.13.9
# https://github.com/rustdesk/rustdesk/blob/285e974d1a52c891d5fcc28e963d724e085558bc/.github/workflows/flutter-build.yml#L862
sed \
-i \
-e 's/extended_text: .*/extended_text: 11.1.0/' \
-e 's/uni_links_desktop/#uni_links_desktop/g' \
flutter/pubspec.yaml
set --
while read -r _1; do
set -- "$@" "${_1}"
done 0<<.a
$(find flutter/lib/ -type f -name "*dart*")
.a
sed \
-i \
-e 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' \
"$@"
set --
fi
sed -i "s/FLUTTER_VERSION_PLACEHOLDER/${FLUTTER_VERSION}/" flutter-sdk/.gclient
;;
build)
# build: perform actual build of APK file
set -e
#
# Extract required versions for NDK, Rust, Flutter from
# '.github/workflows/flutter-build.yml'
#
FLUTTER_VERSION="$(yq -r \
.env.ANDROID_FLUTTER_VERSION \
.github/workflows/flutter-build.yml)"
if [ -z "${FLUTTER_VERSION}" ]; then
FLUTTER_VERSION="$(yq -r \
.env.FLUTTER_VERSION \
.github/workflows/flutter-build.yml)"
fi
NDK_VERSION="$(yq -r \
.env.NDK_VERSION \
.github/workflows/flutter-build.yml)"
# Map NDK version to revision
NDK_VERSION="$(wget \
-qO- \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
'https://api.github.com/repos/android/ndk/releases' |
jq -r ".[] | select(.tag_name == \"${NDK_VERSION}\") | .body | match(\"ndkVersion \\\"(.*)\\\"\").captures[0].string")"
if [ -z "${NDK_VERSION}" ]; then
echo "ERROR: Can not map Android NDK codename to revision!" >&2
exit 1
fi
export ANDROID_NDK_HOME="${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}"
export ANDROID_NDK_ROOT="${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}"
if ! command -v cargo 1>/dev/null 2>&1; then
. "${HOME}/.cargo/env"
fi
# Download Flutter dependencies
pushd flutter
flutter packages pub get
popd # flutter
# Generate FFI bindings
flutter_rust_bridge_codegen \
--rust-input ./src/flutter_ffi.rs \
--dart-output ./flutter/lib/generated_bridge.dart
# Build host android deps
bash flutter/build_android_deps.sh "${ANDROID_ABI}"
# Build rustdesk lib
cargo ndk \
--platform 21 \
--target "${RUST_TARGET}" \
--bindgen \
build \
--release \
--features "${RUSTDESK_FEATURES}"
mkdir -p "flutter/android/app/src/main/jniLibs/${ANDROID_ABI}"
cp "target/${RUST_TARGET}/release/liblibrustdesk.so" \
"flutter/android/app/src/main/jniLibs/${ANDROID_ABI}/librustdesk.so"
cp "${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/${NDK_TARGET}/libc++_shared.so" \
"flutter/android/app/src/main/jniLibs/${ANDROID_ABI}/"
"${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip" \
"flutter/android/app/src/main/jniLibs/${ANDROID_ABI}"/*
# Build flutter-jit-release for x86
if [ "${ANDROID_ABI}" = "x86" ]; then
pushd flutter-sdk
echo "## Sync flutter engine sources"
echo "### We need fakeroot because chromium base image is unpacked with weird uid/gid ownership"
sed -i "s/FLUTTER_VERSION_PLACEHOLDER/${FLUTTER_VERSION}/" .gclient
export FAKEROOTDONTTRYCHOWN=1
fakeroot gclient sync
unset FAKEROOTDONTTRYCHOWN
pushd src
echo "## Patch away Google Play dependencies"
rm \
flutter/shell/platform/android/io/flutter/app/FlutterPlayStoreSplitApplication.java \
flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java flutter/shell/platform/android/io/flutter/embedding/android/FlutterPlayStoreSplitApplication.java
sed \
-i \
-e '/PlayStore/d' \
flutter/tools/android_lint/project.xml \
flutter/shell/platform/android/BUILD.gn
sed \
-i \
-e '/com.google.android.play/d' \
flutter/tools/androidx/files.json
echo "## Configure android engine build"
flutter/tools/gn \
--android --android-cpu x86 --runtime-mode=jit_release \
--no-goma --no-enable-unittests
echo "## Perform android engine build"
ninja -C out/android_jit_release_x86
echo "## Configure host engine build"
flutter/tools/gn \
--android-cpu x86 --runtime-mode=jit_release \
--no-goma --no-enable-unittests
echo "## Perform android engine build"
ninja -C out/host_jit_release_x86
echo "## Rename host engine"
mv out/host_jit_release_x86 out/host_jit_release
echo "## Mimic jit_release engine to debug to use with flutter build apk"
pushd out/android_jit_release_x86
sed \
-e 's/jit_release/debug/' \
flutter_embedding_jit_release.maven-metadata.xml \
1>flutter_embedding_debug.maven-metadata.xml
sed \
-e 's/jit_release/debug/' \
flutter_embedding_jit_release.pom \
1>flutter_embedding_debug.pom
sed \
-e 's/jit_release/debug/' \
x86_jit_release.maven-metadata.xml \
1>x86_debug.maven-metadata.xml
sed \
-e 's/jit_release/debug/' \
x86_jit_release.pom \
1>x86_debug.pom
cp -a \
flutter_embedding_jit_release-sources.jar \
flutter_embedding_debug-sources.jar
cp -a \
flutter_embedding_jit_release.jar \
flutter_embedding_debug.jar
cp -a \
x86_jit_release.jar \
x86_debug.jar
popd # out/android_jit_release_x86
popd # src
popd # flutter-sdk
echo "# Clean up intermediate engine files and show free space"
rm -rf \
flutter-sdk/src/out/android_jit_release_x86/obj \
flutter-sdk/src/out/host_jit_release/obj
mv flutter-sdk/src/out flutter-out
rm -rf flutter-sdk
mkdir -p flutter-sdk/src/
mv flutter-out flutter-sdk/src/out
fi
# Build the apk
pushd flutter
if [ "${ANDROID_ABI}" = "x86" ]; then
flutter build apk \
--local-engine-src-path="$(readlink -mf "../flutter-sdk/src")" \
--local-engine=android_jit_release_x86 \
--debug \
--build-number="${VERCODE}" \
--build-name="${VERNAME}" \
--target-platform "${FLUTTER_TARGET}"
else
flutter build apk \
--release \
--build-number="${VERCODE}" \
--build-name="${VERNAME}" \
--target-platform "${FLUTTER_TARGET}"
fi
popd # flutter
rm -rf flutter-sdk
# Special step for fdroiddata CI builds to remove .gitconfig
rm -f /home/vagrant/.gitconfig
;;
*)
echo "ERROR: Unknown build step '${BUILDSTEP}'!" >&2
exit 1
;;
esac
# Report success
echo "All done!"

View File

@@ -139,4 +139,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: d4cb12ad5d3bdb3352770b1d3db237584e155156
COCOAPODS: 1.12.1
COCOAPODS: 1.15.2

View File

@@ -159,7 +159,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.networking.wifi-info</key>
<true/>
</dict>

View File

@@ -1,2 +1,2 @@
#!/usr/bin/env bash
cargo build --features flutter --release --target aarch64-apple-ios --lib
cargo build --features flutter,hwcodec --release --target aarch64-apple-ios --lib

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/peer_model.dart';
@@ -188,3 +189,79 @@ class RequestException implements Exception {
return "RequestException, statusCode: $statusCode, error: $cause";
}
}
enum ShareRule {
read(1),
readWrite(2),
fullControl(3);
const ShareRule(this.value);
final int value;
static String desc(int v) {
if (v == ShareRule.read.value) {
return translate('Read-only');
}
if (v == ShareRule.readWrite.value) {
return translate('Read/Write');
}
if (v == ShareRule.fullControl.value) {
return translate('Full Control');
}
return v.toString();
}
static String shortDesc(int v) {
if (v == ShareRule.read.value) {
return 'R';
}
if (v == ShareRule.readWrite.value) {
return 'RW';
}
if (v == ShareRule.fullControl.value) {
return 'F';
}
return v.toString();
}
static ShareRule? fromValue(int v) {
if (v == ShareRule.read.value) {
return ShareRule.read;
}
if (v == ShareRule.readWrite.value) {
return ShareRule.readWrite;
}
if (v == ShareRule.fullControl.value) {
return ShareRule.fullControl;
}
return null;
}
}
class AbProfile {
String guid;
String name;
String owner;
String? note;
int rule;
AbProfile(this.guid, this.name, this.owner, this.note, this.rule);
AbProfile.fromJson(Map<String, dynamic> json)
: guid = json['guid'] ?? '',
name = json['name'] ?? '',
owner = json['owner'] ?? '',
note = json['note'] ?? '',
rule = json['rule'] ?? 0;
}
class AbTag {
String name;
int color;
AbTag(this.name, this.color);
AbTag.fromJson(Map<String, dynamic> json)
: name = json['name'] ?? '',
color = json['color'] ?? '';
}

View File

@@ -10,16 +10,16 @@ class PrivacyModeState {
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxString>(tag: key)) {
final RxString state = ''.obs;
Get.put(state, tag: key);
Get.put<RxString>(state, tag: key);
}
}
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxString>(tag: key)) {
Get.delete<RxString>(tag: key);
} else {
Get.find<RxString>(tag: key).value = '';
}
@@ -33,9 +33,9 @@ class BlockInputState {
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxBool>(tag: key)) {
final RxBool state = false.obs;
Get.put(state, tag: key);
Get.put<RxBool>(state, tag: key);
} else {
Get.find<RxBool>(tag: key).value = false;
}
@@ -43,8 +43,8 @@ class BlockInputState {
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxBool>(tag: key)) {
Get.delete<RxBool>(tag: key);
}
}
@@ -56,9 +56,9 @@ class CurrentDisplayState {
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxInt>(tag: key)) {
final RxInt state = RxInt(0);
Get.put(state, tag: key);
Get.put<RxInt>(state, tag: key);
} else {
Get.find<RxInt>(tag: key).value = 0;
}
@@ -66,8 +66,8 @@ class CurrentDisplayState {
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxInt>(tag: key)) {
Get.delete<RxInt>(tag: key);
}
}
@@ -105,16 +105,16 @@ class ConnectionTypeState {
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<ConnectionType>(tag: key)) {
final ConnectionType collectionType = ConnectionType();
Get.put(collectionType, tag: key);
Get.put<ConnectionType>(collectionType, tag: key);
}
}
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<ConnectionType>(tag: key)) {
Get.delete<ConnectionType>(tag: key);
}
}
@@ -127,9 +127,9 @@ class FingerprintState {
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxString>(tag: key)) {
final RxString state = ''.obs;
Get.put(state, tag: key);
Get.put<RxString>(state, tag: key);
} else {
Get.find<RxString>(tag: key).value = '';
}
@@ -137,8 +137,8 @@ class FingerprintState {
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxString>(tag: key)) {
Get.delete<RxString>(tag: key);
}
}
@@ -150,9 +150,9 @@ class ShowRemoteCursorState {
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxBool>(tag: key)) {
final RxBool state = false.obs;
Get.put(state, tag: key);
Get.put<RxBool>(state, tag: key);
} else {
Get.find<RxBool>(tag: key).value = false;
}
@@ -160,8 +160,31 @@ class ShowRemoteCursorState {
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxBool>(tag: key)) {
Get.delete<RxBool>(tag: key);
}
}
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
}
class ShowRemoteCursorLockState {
static String tag(String id) => 'show_remote_cursor_lock_$id';
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered<RxBool>(tag: key)) {
final RxBool state = false.obs;
Get.put<RxBool>(state, tag: key);
} else {
Get.find<RxBool>(tag: key).value = false;
}
}
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered<RxBool>(tag: key)) {
Get.delete<RxBool>(tag: key);
}
}
@@ -173,10 +196,10 @@ class KeyboardEnabledState {
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxBool>(tag: key)) {
// Server side, default true
final RxBool state = true.obs;
Get.put(state, tag: key);
Get.put<RxBool>(state, tag: key);
} else {
Get.find<RxBool>(tag: key).value = true;
}
@@ -184,8 +207,8 @@ class KeyboardEnabledState {
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxBool>(tag: key)) {
Get.delete<RxBool>(tag: key);
}
}
@@ -197,9 +220,9 @@ class RemoteCursorMovedState {
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxBool>(tag: key)) {
final RxBool state = false.obs;
Get.put(state, tag: key);
Get.put<RxBool>(state, tag: key);
} else {
Get.find<RxBool>(tag: key).value = false;
}
@@ -207,8 +230,8 @@ class RemoteCursorMovedState {
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxBool>(tag: key)) {
Get.delete<RxBool>(tag: key);
}
}
@@ -220,9 +243,9 @@ class RemoteCountState {
static void init() {
final key = tag();
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxInt>(tag: key)) {
final RxInt state = 1.obs;
Get.put(state, tag: key);
Get.put<RxInt>(state, tag: key);
} else {
Get.find<RxInt>(tag: key).value = 1;
}
@@ -230,8 +253,8 @@ class RemoteCountState {
static void delete() {
final key = tag();
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxInt>(tag: key)) {
Get.delete<RxInt>(tag: key);
}
}
@@ -243,9 +266,9 @@ class PeerBoolOption {
static void init(String id, String opt, bool Function() init_getter) {
final key = tag(id, opt);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxBool>(tag: key)) {
final RxBool value = RxBool(init_getter());
Get.put(value, tag: key);
Get.put<RxBool>(value, tag: key);
} else {
Get.find<RxBool>(tag: key).value = init_getter();
}
@@ -253,8 +276,8 @@ class PeerBoolOption {
static void delete(String id, String opt) {
final key = tag(id, opt);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxBool>(tag: key)) {
Get.delete<RxBool>(tag: key);
}
}
@@ -267,9 +290,9 @@ class PeerStringOption {
static void init(String id, String opt, String Function() init_getter) {
final key = tag(id, opt);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxString>(tag: key)) {
final RxString value = RxString(init_getter());
Get.put(value, tag: key);
Get.put<RxString>(value, tag: key);
} else {
Get.find<RxString>(tag: key).value = init_getter();
}
@@ -277,8 +300,8 @@ class PeerStringOption {
static void delete(String id, String opt) {
final key = tag(id, opt);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxString>(tag: key)) {
Get.delete<RxString>(tag: key);
}
}
@@ -291,9 +314,9 @@ class UnreadChatCountState {
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
if (!Get.isRegistered<RxInt>(tag: key)) {
final RxInt state = RxInt(0);
Get.put(state, tag: key);
Get.put<RxInt>(state, tag: key);
} else {
Get.find<RxInt>(tag: key).value = 0;
}
@@ -301,8 +324,8 @@ class UnreadChatCountState {
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
if (Get.isRegistered<RxInt>(tag: key)) {
Get.delete<RxInt>(tag: key);
}
}
@@ -315,9 +338,10 @@ initSharedStates(String id) {
CurrentDisplayState.init(id);
KeyboardEnabledState.init(id);
ShowRemoteCursorState.init(id);
ShowRemoteCursorLockState.init(id);
RemoteCursorMovedState.init(id);
FingerprintState.init(id);
PeerBoolOption.init(id, 'zoom-cursor', () => false);
PeerBoolOption.init(id, kOptionZoomCursor, () => false);
UnreadChatCountState.init(id);
if (isMobile) ConnectionTypeState.init(id); // desktop in other places
}
@@ -327,10 +351,11 @@ removeSharedStates(String id) {
BlockInputState.delete(id);
CurrentDisplayState.delete(id);
ShowRemoteCursorState.delete(id);
ShowRemoteCursorLockState.delete(id);
KeyboardEnabledState.delete(id);
RemoteCursorMovedState.delete(id);
FingerprintState.delete(id);
PeerBoolOption.delete(id, 'zoom-cursor');
PeerBoolOption.delete(id, kOptionZoomCursor);
UnreadChatCountState.delete(id);
if (isMobile) ConnectionTypeState.delete(id);
}

View File

@@ -1,13 +1,18 @@
import 'dart:math';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:dynamic_layouts/dynamic_layouts.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
import 'package:flutter_hbb/common/widgets/peer_card.dart';
import 'package:flutter_hbb/common/widgets/peers_view.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
import 'package:flutter_hbb/models/ab_model.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:url_launcher/url_launcher_string.dart';
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
import 'package:get/get.dart';
import 'package:flex_color_picker/flex_color_picker.dart';
@@ -31,47 +36,42 @@ class AddressBook extends StatefulWidget {
class _AddressBookState extends State<AddressBook> {
var menuPos = RelativeRect.fill;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) => Obx(() {
if (!gFFI.userModel.isLogin) {
return Center(
child: ElevatedButton(
onPressed: loginDialog, child: Text(translate("Login"))));
} else if (gFFI.userModel.networkError.isNotEmpty) {
return netWorkErrorWidget();
} else {
if (gFFI.abModel.abLoading.value && gFFI.abModel.emtpy) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Column(
children: [
// NOT use Offstage to wrap LinearProgressIndicator
if (gFFI.abModel.retrying.value) LinearProgressIndicator(),
if (gFFI.abModel.currentAbLoading.value &&
gFFI.abModel.currentAbEmpty)
const LinearProgressIndicator(),
buildErrorBanner(context,
loading: gFFI.abModel.abLoading,
err: gFFI.abModel.pullError,
loading: gFFI.abModel.currentAbLoading,
err: gFFI.abModel.currentAbPullError,
retry: null,
close: () => gFFI.abModel.pullError.value = ''),
close: () => gFFI.abModel.currentAbPullError.value = ''),
buildErrorBanner(context,
loading: gFFI.abModel.abLoading,
err: gFFI.abModel.pushError,
retry: () => gFFI.abModel.pushAb(isRetry: true),
close: () => gFFI.abModel.pushError.value = ''),
loading: gFFI.abModel.currentAbLoading,
err: gFFI.abModel.currentAbPushError,
retry: null, // remove retry
close: () => gFFI.abModel.currentAbPushError.value = ''),
Expanded(
child: isDesktop
? _buildAddressBookDesktop()
: _buildAddressBookMobile())
child: Obx(() => stateGlobal.isPortrait.isTrue
? _buildAddressBookPortrait()
: _buildAddressBookLandscape()),
),
],
);
}
});
Widget _buildAddressBookDesktop() {
Widget _buildAddressBookLandscape() {
return Row(
children: [
Offstage(
@@ -82,19 +82,23 @@ class _AddressBookState extends State<AddressBook> {
border: Border.all(
color: Theme.of(context).colorScheme.background)),
child: Container(
width: 150,
width: 200,
height: double.infinity,
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
_buildTagHeader().marginOnly(left: 8.0, right: 0),
_buildAbDropdown(),
_buildTagHeader().marginOnly(
left: 8.0,
right: gFFI.abModel.legacyMode.value ? 8.0 : 0,
top: gFFI.abModel.legacyMode.value ? 8.0 : 0),
Expanded(
child: Container(
width: double.infinity,
height: double.infinity,
child: _buildTags(),
),
)
),
_buildAbPermission(),
],
),
),
@@ -104,7 +108,8 @@ class _AddressBookState extends State<AddressBook> {
);
}
Widget _buildAddressBookMobile() {
Widget _buildAddressBookPortrait() {
const padding = 8.0;
return Column(
children: [
Offstage(
@@ -115,10 +120,12 @@ class _AddressBookState extends State<AddressBook> {
border: Border.all(
color: Theme.of(context).colorScheme.background)),
child: Container(
padding: const EdgeInsets.all(8.0),
padding:
const EdgeInsets.fromLTRB(padding, 0, padding, padding),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildAbDropdown(),
_buildTagHeader().marginOnly(left: 8.0, right: 0),
Container(
width: double.infinity,
@@ -133,6 +140,163 @@ class _AddressBookState extends State<AddressBook> {
);
}
Widget _buildAbPermission() {
icon(IconData data, String tooltip) {
return Tooltip(
message: translate(tooltip),
waitDuration: Duration.zero,
child: Icon(data, size: 12.0).marginSymmetric(horizontal: 2.0));
}
return Obx(() {
if (gFFI.abModel.legacyMode.value) return Offstage();
if (gFFI.abModel.current.isPersonal()) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
icon(Icons.cloud_off, "Personal"),
],
);
} else {
List<Widget> children = [];
final rule = gFFI.abModel.current.sharedProfile()?.rule;
if (rule == ShareRule.read.value) {
children.add(
icon(Icons.visibility, ShareRule.desc(ShareRule.read.value)));
} else if (rule == ShareRule.readWrite.value) {
children
.add(icon(Icons.edit, ShareRule.desc(ShareRule.readWrite.value)));
} else if (rule == ShareRule.fullControl.value) {
children.add(icon(
Icons.security, ShareRule.desc(ShareRule.fullControl.value)));
}
final owner = gFFI.abModel.current.sharedProfile()?.owner;
if (owner != null) {
children.add(icon(Icons.person, "${translate("Owner")}: $owner"));
}
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: children,
);
}
});
}
Widget _buildAbDropdown() {
if (gFFI.abModel.legacyMode.value) {
return Offstage();
}
final names = gFFI.abModel.addressBookNames();
if (!names.contains(gFFI.abModel.currentName.value)) {
return Offstage();
}
// order: personal, divider, character order
// https://pub.dev/packages/dropdown_button2#3-dropdownbutton2-with-items-of-different-heights-like-dividers
final personalAddressBookName = gFFI.abModel.personalAddressBookName();
bool contains = names.remove(personalAddressBookName);
names.sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
if (contains) {
names.insert(0, personalAddressBookName);
}
Row buildItem(String e, {bool button = false}) {
return Row(
children: [
Expanded(
child: Tooltip(
waitDuration: Duration(milliseconds: 500),
message: gFFI.abModel.translatedName(e),
child: Text(
gFFI.abModel.translatedName(e),
style: button ? null : TextStyle(fontSize: 14.0),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: button ? TextAlign.center : null,
)),
),
],
);
}
final items = names
.map((e) => DropdownMenuItem(value: e, child: buildItem(e)))
.toList();
var menuItemStyleData = MenuItemStyleData(height: 36);
if (contains && items.length > 1) {
items.insert(1, DropdownMenuItem(enabled: false, child: Divider()));
List<double> customHeights = List.filled(items.length, 36);
customHeights[1] = 4;
menuItemStyleData = MenuItemStyleData(customHeights: customHeights);
}
final TextEditingController textEditingController = TextEditingController();
final isOptFixed = isOptionFixed(kOptionCurrentAbName);
return DropdownButton2<String>(
value: gFFI.abModel.currentName.value,
onChanged: isOptFixed
? null
: (value) {
if (value != null) {
gFFI.abModel.setCurrentName(value);
bind.setLocalFlutterOption(k: kOptionCurrentAbName, v: value);
}
},
customButton: Obx(() => Container(
height: stateGlobal.isPortrait.isFalse ? 48 : 40,
child: Row(children: [
Expanded(
child:
buildItem(gFFI.abModel.currentName.value, button: true)),
Icon(Icons.arrow_drop_down),
]),
)),
underline: Container(
height: 0.7,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
menuItemStyleData: menuItemStyleData,
items: items,
isExpanded: true,
isDense: true,
dropdownSearchData: DropdownSearchData(
searchController: textEditingController,
searchInnerWidgetHeight: 50,
searchInnerWidget: Container(
height: 50,
padding: const EdgeInsets.only(
top: 8,
bottom: 4,
right: 8,
left: 8,
),
child: TextFormField(
expands: true,
maxLines: null,
controller: textEditingController,
decoration: InputDecoration(
isDense: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
hintText: translate('Search'),
hintStyle: const TextStyle(fontSize: 12),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
),
searchMatchFn: (item, searchValue) {
return item.value
.toString()
.toLowerCase()
.contains(searchValue.toLowerCase());
},
),
);
}
Widget _buildTagHeader() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -154,11 +318,12 @@ class _AddressBookState extends State<AddressBook> {
return Obx(() {
final List tags;
if (gFFI.abModel.sortTags.value) {
tags = gFFI.abModel.tags.toList();
tags = gFFI.abModel.currentAbTags.toList();
tags.sort();
} else {
tags = gFFI.abModel.tags;
tags = gFFI.abModel.currentAbTags;
}
final editPermission = gFFI.abModel.current.canWrite();
tagBuilder(String e) {
return AddressBookTag(
name: e,
@@ -169,11 +334,12 @@ class _AddressBookState extends State<AddressBook> {
} else {
gFFI.abModel.selectedTags.add(e);
}
});
},
showActionMenu: editPermission);
}
final gridView = DynamicGridView.builder(
shrinkWrap: isMobile,
gridView(bool isPortrait) => DynamicGridView.builder(
shrinkWrap: isPortrait,
gridDelegate: SliverGridDelegateWithWrapping(),
itemCount: tags.length,
itemBuilder: (BuildContext context, int index) {
@@ -181,9 +347,9 @@ class _AddressBookState extends State<AddressBook> {
return tagBuilder(e);
});
final maxHeight = max(MediaQuery.of(context).size.height / 6, 100.0);
return isDesktop
? gridView
: LimitedBox(maxHeight: maxHeight, child: gridView);
return Obx(() => stateGlobal.isPortrait.isFalse
? gridView(false)
: LimitedBox(maxHeight: maxHeight, child: gridView(true)));
});
}
@@ -193,13 +359,13 @@ class _AddressBookState extends State<AddressBook> {
alignment: Alignment.topLeft,
child: AddressBookPeersView(
menuPadding: widget.menuPadding,
initPeers: gFFI.abModel.peers,
)),
);
}
@protected
MenuEntryBase<String> syncMenuItem() {
final isOptFixed = isOptionFixed(syncAbOption);
return MenuEntrySwitch<String>(
switchType: SwitchType.scheckbox,
text: translate('Sync with recent sessions'),
@@ -207,14 +373,16 @@ class _AddressBookState extends State<AddressBook> {
return shouldSyncAb();
},
setter: (bool v) async {
bind.mainSetLocalOption(key: syncAbOption, value: v ? 'Y' : '');
gFFI.abModel.setShouldAsync(v);
},
dismissOnClicked: true,
enabled: (!isOptFixed).obs,
);
}
@protected
MenuEntryBase<String> sortMenuItem() {
final isOptFixed = isOptionFixed(sortAbTagsOption);
return MenuEntrySwitch<String>(
switchType: SwitchType.scheckbox,
text: translate('Sort tags'),
@@ -222,15 +390,18 @@ class _AddressBookState extends State<AddressBook> {
return shouldSortTags();
},
setter: (bool v) async {
bind.mainSetLocalOption(key: sortAbTagsOption, value: v ? 'Y' : '');
bind.mainSetLocalOption(
key: sortAbTagsOption, value: v ? 'Y' : defaultOptionNo);
gFFI.abModel.sortTags.value = v;
},
dismissOnClicked: true,
enabled: (!isOptFixed).obs,
);
}
@protected
MenuEntryBase<String> filterMenuItem() {
final isOptFixed = isOptionFixed(filterAbTagOption);
return MenuEntrySwitch<String>(
switchType: SwitchType.scheckbox,
text: translate('Filter by intersection'),
@@ -238,21 +409,34 @@ class _AddressBookState extends State<AddressBook> {
return filterAbTagByIntersection();
},
setter: (bool v) async {
bind.mainSetLocalOption(key: filterAbTagOption, value: v ? 'Y' : '');
bind.mainSetLocalOption(
key: filterAbTagOption, value: v ? 'Y' : defaultOptionNo);
gFFI.abModel.filterByIntersection.value = v;
},
dismissOnClicked: true,
enabled: (!isOptFixed).obs,
);
}
void _showMenu(RelativeRect pos) {
final canWrite = gFFI.abModel.current.canWrite();
final items = [
getEntry(translate("Add ID"), abAddId),
getEntry(translate("Add Tag"), abAddTag),
if (canWrite) getEntry(translate("Add ID"), addIdToCurrentAb),
if (canWrite) getEntry(translate("Add Tag"), abAddTag),
getEntry(translate("Unselect all tags"), gFFI.abModel.unsetSelectedTags),
sortMenuItem(),
syncMenuItem(),
if (gFFI.abModel.legacyMode.value)
sortMenuItem(), // It's already sorted after pulling down
if (canWrite) syncMenuItem(),
filterMenuItem(),
if (!gFFI.abModel.legacyMode.value && canWrite)
MenuEntryDivider<String>(),
if (!gFFI.abModel.legacyMode.value && canWrite)
getEntry(translate("ab_web_console_tip"), () async {
final url = await bind.mainGetApiServer();
if (await canLaunchUrlString(url)) {
launchUrlString(url);
}
}),
];
mod_menu.showMenu(
@@ -271,17 +455,20 @@ class _AddressBookState extends State<AddressBook> {
);
}
void abAddId() async {
if (gFFI.abModel.isFull(true)) {
void addIdToCurrentAb() async {
if (gFFI.abModel.isCurrentAbFull(true)) {
return;
}
var isInProgress = false;
var passwordVisible = false;
IDTextEditingController idController = IDTextEditingController(text: '');
TextEditingController aliasController = TextEditingController(text: '');
final tags = List.of(gFFI.abModel.tags);
TextEditingController passwordController = TextEditingController(text: '');
final tags = List.of(gFFI.abModel.currentAbTags);
var selectedTag = List<dynamic>.empty(growable: true).obs;
final style = TextStyle(fontSize: 14.0);
String? errorMsg;
final isCurrentAbShared = !gFFI.abModel.current.isPersonal();
gFFI.dialogManager.show((setState, close, context) {
submit() async {
@@ -293,22 +480,51 @@ class _AddressBookState extends State<AddressBook> {
if (id.isEmpty) {
// pass
} else {
if (gFFI.abModel.idContainBy(id)) {
if (gFFI.abModel.idContainByCurrent(id)) {
setState(() {
isInProgress = false;
errorMsg = translate('ID already exists');
});
return;
}
gFFI.abModel.addId(id, aliasController.text.trim(), selectedTag);
gFFI.abModel.pushAb();
this.setState(() {});
var password = '';
if (isCurrentAbShared) {
password = passwordController.text;
}
String? errMsg2 = await gFFI.abModel.addIdToCurrent(
id, aliasController.text.trim(), password, selectedTag);
if (errMsg2 != null) {
setState(() {
isInProgress = false;
errorMsg = errMsg2;
});
return;
}
// final currentPeers
}
close();
}
double marginBottom = 4;
row({required Widget lable, required Widget input}) {
makeChild(bool isPortrait) => Row(
children: [
!isPortrait
? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 100),
child: lable.marginOnly(right: 10))
: SizedBox.shrink(),
Expanded(
child: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 200),
child: input),
),
],
).marginOnly(bottom: !isPortrait ? 8 : 0);
return Obx(() => makeChild(stateGlobal.isPortrait.isTrue));
}
return CustomAlertDialog(
title: Text(translate("Add ID")),
content: Column(
@@ -316,66 +532,112 @@ class _AddressBookState extends State<AddressBook> {
children: [
Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: Row(
children: [
Text(
'*',
style: TextStyle(color: Colors.red, fontSize: 14),
),
Text(
'ID',
style: style,
),
],
),
).marginOnly(bottom: marginBottom),
TextField(
controller: idController,
inputFormatters: [IDTextInputFormatter()],
decoration: InputDecoration(errorText: errorMsg),
),
Align(
alignment: Alignment.centerLeft,
child: Text(
row(
lable: Row(
children: [
Text(
'*',
style: TextStyle(color: Colors.red, fontSize: 14),
),
Text(
'ID',
style: style,
),
],
),
input: Obx(() => TextField(
controller: idController,
inputFormatters: [IDTextInputFormatter()],
decoration: InputDecoration(
labelText: stateGlobal.isPortrait.isFalse
? null
: translate('ID'),
errorText: errorMsg,
errorMaxLines: 5),
))),
row(
lable: Text(
translate('Alias'),
style: style,
),
).marginOnly(top: 8, bottom: marginBottom),
TextField(
controller: aliasController,
input: Obx(() => TextField(
controller: aliasController,
decoration: InputDecoration(
labelText: stateGlobal.isPortrait.isFalse
? null
: translate('Alias'),
),
)),
),
Align(
alignment: Alignment.centerLeft,
child: Text(
translate('Tags'),
style: style,
if (isCurrentAbShared)
row(
lable: Text(
translate('Password'),
style: style,
),
input: Obx(
() => TextField(
controller: passwordController,
obscureText: !passwordVisible,
decoration: InputDecoration(
labelText: stateGlobal.isPortrait.isFalse
? null
: translate('Password'),
suffixIcon: IconButton(
icon: Icon(
passwordVisible
? Icons.visibility
: Icons.visibility_off,
color: MyTheme.lightTheme.primaryColor),
onPressed: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
),
),
),
)),
if (gFFI.abModel.currentAbTags.isNotEmpty)
Align(
alignment: Alignment.centerLeft,
child: Text(
translate('Tags'),
style: style,
),
).marginOnly(top: 8, bottom: marginBottom),
if (gFFI.abModel.currentAbTags.isNotEmpty)
Align(
alignment: Alignment.centerLeft,
child: Wrap(
children: tags
.map((e) => AddressBookTag(
name: e,
tags: selectedTag,
onTap: () {
if (selectedTag.contains(e)) {
selectedTag.remove(e);
} else {
selectedTag.add(e);
}
},
showActionMenu: false))
.toList(growable: false),
),
),
).marginOnly(top: 8, bottom: marginBottom),
Align(
alignment: Alignment.centerLeft,
child: Wrap(
children: tags
.map((e) => AddressBookTag(
name: e,
tags: selectedTag,
onTap: () {
if (selectedTag.contains(e)) {
selectedTag.remove(e);
} else {
selectedTag.add(e);
}
},
showActionMenu: false))
.toList(growable: false),
),
),
],
),
const SizedBox(
height: 4.0,
),
if (!gFFI.abModel.current.isPersonal())
Row(children: [
Icon(Icons.info, color: Colors.amber).marginOnly(right: 4),
Text(
translate('share_warning_tip'),
style: TextStyle(fontSize: 12),
)
]).marginSymmetric(vertical: 10),
// NOT use Offstage to wrap LinearProgressIndicator
if (isInProgress) const LinearProgressIndicator(),
],
@@ -407,10 +669,7 @@ class _AddressBookState extends State<AddressBook> {
} else {
final tags = field.trim().split(RegExp(r"[\s,;\n]+"));
field = tags.join(',');
for (final tag in tags) {
gFFI.abModel.addTag(tag);
}
gFFI.abModel.pushAb();
gFFI.abModel.addTags(tags);
// final currentPeers
}
close();
@@ -491,7 +750,7 @@ class AddressBookTag extends StatelessWidget {
child: Obx(() => Container(
decoration: BoxDecoration(
color: tags.contains(name)
? gFFI.abModel.getTagColor(name)
? gFFI.abModel.getCurrentAbTagColor(name)
: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(4)),
margin: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0),
@@ -506,7 +765,7 @@ class AddressBookTag extends StatelessWidget {
shape: BoxShape.circle,
color: tags.contains(name)
? Colors.white
: gFFI.abModel.getTagColor(name)),
: gFFI.abModel.getCurrentAbTagColor(name)),
).marginOnly(right: radius / 2),
Expanded(
child: Text(name,
@@ -530,7 +789,8 @@ class AddressBookTag extends StatelessWidget {
if (newName == null || newName.isEmpty) {
return translate('Can not be empty');
}
if (newName != name && gFFI.abModel.tags.contains(newName)) {
if (newName != name &&
gFFI.abModel.currentAbTags.contains(newName)) {
return translate('Already exists');
}
return null;
@@ -538,7 +798,6 @@ class AddressBookTag extends StatelessWidget {
onSubmit: (String newName) {
if (name != newName) {
gFFI.abModel.renameTag(name, newName);
gFFI.abModel.pushAb();
}
Future.delayed(Duration.zero, () => Get.back());
},
@@ -548,7 +807,7 @@ class AddressBookTag extends StatelessWidget {
}),
getEntry(translate(translate('Change Color')), () async {
final model = gFFI.abModel;
Color oldColor = model.getTagColor(name);
Color oldColor = model.getCurrentAbTagColor(name);
Color newColor = await showColorPickerDialog(
context,
oldColor,
@@ -567,12 +826,10 @@ class AddressBookTag extends StatelessWidget {
);
if (oldColor != newColor) {
model.setTagColor(name, newColor);
model.pushAb();
}
}),
getEntry(translate("Delete"), () {
gFFI.abModel.deleteTag(name);
gFFI.abModel.pushAb();
Future.delayed(Duration.zero, () => Get.back());
}),
];

View File

@@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/platform_model.dart';
const _kWindowsSystemSound = 'System Sound';
typedef AudioINputSetDevice = void Function(String device);
typedef AudioInputBuilder = Widget Function(
List<String> devices, String currentDevice, AudioINputSetDevice setDevice);
class AudioInput extends StatelessWidget {
final AudioInputBuilder builder;
final bool isCm;
final bool isVoiceCall;
const AudioInput(
{Key? key,
required this.builder,
required this.isCm,
required this.isVoiceCall})
: super(key: key);
static String getDefault() {
if (isWindows) return translate('System Sound');
return '';
}
static Future<String> getAudioInput(bool isCm, bool isVoiceCall) {
if (isVoiceCall) {
return bind.getVoiceCallInputDevice(isCm: isCm);
} else {
return bind.mainGetOption(key: 'audio-input');
}
}
static Future<String> getValue(bool isCm, bool isVoiceCall) async {
String device = await getAudioInput(isCm, isVoiceCall);
if (device.isNotEmpty) {
return device;
} else {
return getDefault();
}
}
static Future<void> setDevice(
String device, bool isCm, bool isVoiceCall) async {
if (device == getDefault()) device = '';
if (isVoiceCall) {
await bind.setVoiceCallInputDevice(isCm: isCm, device: device);
} else {
await bind.mainSetOption(key: 'audio-input', value: device);
}
}
static Future<Map<String, Object>> getDevicesInfo(
bool isCm, bool isVoiceCall) async {
List<String> devices = (await bind.mainGetSoundInputs()).toList();
if (isWindows) {
devices.insert(0, translate(_kWindowsSystemSound));
}
String current = await getValue(isCm, isVoiceCall);
return {'devices': devices, 'current': current};
}
@override
Widget build(BuildContext context) {
return futureBuilder(
future: getDevicesInfo(isCm, isVoiceCall),
hasData: (data) {
String currentDevice = data['current'];
List<String> devices = data['devices'] as List<String>;
if (devices.isEmpty) {
return const Offstage();
}
return builder(devices, currentDevice, (devices) {
setDevice(devices, isCm, isVoiceCall);
});
},
);
}
}

View File

@@ -9,9 +9,6 @@ import 'package:flutter_hbb/common/widgets/peer_card.dart';
Future<List<Peer>> getAllPeers() async {
Map<String, dynamic> recentPeers = jsonDecode(bind.mainLoadRecentPeersSync());
Map<String, dynamic> lanPeers = jsonDecode(bind.mainLoadLanPeersSync());
Map<String, dynamic> abPeers = jsonDecode(bind.mainLoadAbSync());
Map<String, dynamic> groupPeers = jsonDecode(bind.mainLoadGroupSync());
Map<String, dynamic> combinedPeers = {};
void mergePeers(Map<String, dynamic> peers) {
@@ -42,8 +39,16 @@ Future<List<Peer>> getAllPeers() async {
mergePeers(recentPeers);
mergePeers(lanPeers);
mergePeers(abPeers);
mergePeers(groupPeers);
for (var p in gFFI.abModel.allPeers()) {
if (!combinedPeers.containsKey(p.id)) {
combinedPeers[p.id] = p.toJson();
}
}
for (var p in gFFI.groupModel.peers.map((e) => Peer.copy(e)).toList()) {
if (!combinedPeers.containsKey(p.id)) {
combinedPeers[p.id] = p.toJson();
}
}
List<Peer> parsedPeers = [];
@@ -181,10 +186,10 @@ class AutocompletePeerTileState extends State<AutocompletePeerTile> {
],
))));
final colors = _frontN(widget.peer.tags, 25)
.map((e) => gFFI.abModel.getTagColor(e))
.map((e) => gFFI.abModel.getCurrentAbTagColor(e))
.toList();
return Tooltip(
message: isMobile
message: !(isDesktop || isWebDesktop)
? ''
: widget.peer.tags.isNotEmpty
? '${translate('Tags')}: ${widget.peer.tags.join(', ')}'

View File

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

View File

@@ -14,7 +14,11 @@ class UppercaseValidationRule extends ValidationRule {
String get name => translate('uppercase');
@override
bool validate(String value) {
return value.contains(RegExp(r'[A-Z]'));
return value.runes.any((int rune) {
var character = String.fromCharCode(rune);
return character.toUpperCase() == character &&
character.toLowerCase() != character;
});
}
}
@@ -24,7 +28,11 @@ class LowercaseValidationRule extends ValidationRule {
@override
bool validate(String value) {
return value.contains(RegExp(r'[a-z]'));
return value.runes.any((int rune) {
var character = String.fromCharCode(rune);
return character.toLowerCase() == character &&
character.toUpperCase() != character;
});
}
}

View File

@@ -1,12 +1,16 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_hbb/common/shared_state.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
import 'package:qr_flutter/qr_flutter.dart';
@@ -78,7 +82,7 @@ void changeIdDialog() {
final Iterable violations = rules.where((r) => !r.validate(newId));
if (violations.isNotEmpty) {
setState(() {
msg = isDesktop
msg = (isDesktop || isWebDesktop)
? '${translate('Prompt')}: ${violations.map((r) => r.name).join(', ')}'
: violations.map((r) => r.name).join(', ');
});
@@ -103,7 +107,7 @@ void changeIdDialog() {
}
setState(() {
isInProgress = false;
msg = isDesktop
msg = (isDesktop || isWebDesktop)
? '${translate('Prompt')}: ${translate(status)}'
: translate(status);
});
@@ -140,7 +144,7 @@ void changeIdDialog() {
const SizedBox(
height: 8.0,
),
isDesktop
(isDesktop || isWebDesktop)
? Obx(() => Wrap(
runSpacing: 8,
spacing: 4,
@@ -175,11 +179,14 @@ void changeIdDialog() {
}
void changeWhiteList({Function()? callback}) async {
var newWhiteList = (await bind.mainGetOption(key: 'whitelist')).split(',');
var newWhiteListField = newWhiteList.join('\n');
final curWhiteList = await bind.mainGetOption(key: kOptionWhitelist);
var newWhiteListField = curWhiteList == defaultOptionWhitelist
? ''
: curWhiteList.split(',').join('\n');
var controller = TextEditingController(text: newWhiteListField);
var msg = "";
var isInProgress = false;
final isOptFixed = isOptionFixed(kOptionWhitelist);
gFFI.dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title: Text(translate("IP Whitelisting")),
@@ -199,6 +206,7 @@ void changeWhiteList({Function()? callback}) async {
errorText: msg.isEmpty ? null : translate(msg),
),
controller: controller,
enabled: !isOptFixed,
autofocus: true),
),
],
@@ -212,45 +220,53 @@ void changeWhiteList({Function()? callback}) async {
),
actions: [
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("Clear", onPressed: () async {
await bind.mainSetOption(key: 'whitelist', value: '');
callback?.call();
close();
}, isOutline: true),
dialogButton(
"OK",
onPressed: () async {
setState(() {
msg = "";
isInProgress = true;
});
newWhiteListField = controller.text.trim();
var newWhiteList = "";
if (newWhiteListField.isEmpty) {
// pass
} else {
final ips = newWhiteListField.trim().split(RegExp(r"[\s,;\n]+"));
// test ip
final ipMatch = RegExp(
r"^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)(\/([1-9]|[1-2][0-9]|3[0-2])){0,1}$");
final ipv6Match = RegExp(
r"^(((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7})(\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}$");
for (final ip in ips) {
if (!ipMatch.hasMatch(ip) && !ipv6Match.hasMatch(ip)) {
msg = "${translate("Invalid IP")} $ip";
setState(() {
isInProgress = false;
});
return;
}
}
newWhiteList = ips.join(',');
}
await bind.mainSetOption(key: 'whitelist', value: newWhiteList);
if (!isOptFixed)
dialogButton("Clear", onPressed: () async {
await bind.mainSetOption(
key: kOptionWhitelist, value: defaultOptionWhitelist);
callback?.call();
close();
},
),
}, isOutline: true),
if (!isOptFixed)
dialogButton(
"OK",
onPressed: () async {
setState(() {
msg = "";
isInProgress = true;
});
newWhiteListField = controller.text.trim();
var newWhiteList = "";
if (newWhiteListField.isEmpty) {
// pass
} else {
final ips =
newWhiteListField.trim().split(RegExp(r"[\s,;\n]+"));
// test ip
final ipMatch = RegExp(
r"^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)(\/([1-9]|[1-2][0-9]|3[0-2])){0,1}$");
final ipv6Match = RegExp(
r"^(((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7})(\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}$");
for (final ip in ips) {
if (!ipMatch.hasMatch(ip) && !ipv6Match.hasMatch(ip)) {
msg = "${translate("Invalid IP")} $ip";
setState(() {
isInProgress = false;
});
return;
}
}
newWhiteList = ips.join(',');
}
if (newWhiteList.trim().isEmpty) {
newWhiteList = defaultOptionWhitelist;
}
await bind.mainSetOption(
key: kOptionWhitelist, value: newWhiteList);
callback?.call();
close();
},
),
],
onCancel: close,
);
@@ -296,7 +312,7 @@ Future<String> changeDirectAccessPort(
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("OK", onPressed: () async {
await bind.mainSetOption(
key: 'direct-access-port', value: controller.text);
key: kOptionDirectAccessPort, value: controller.text);
close();
}),
],
@@ -343,7 +359,7 @@ Future<String> changeAutoDisconnectTimeout(String old) async {
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("OK", onPressed: () async {
await bind.mainSetOption(
key: 'auto-disconnect-timeout', value: controller.text);
key: kOptionAutoDisconnectTimeout, value: controller.text);
close();
}),
],
@@ -365,6 +381,7 @@ class DialogTextField extends StatelessWidget {
final FocusNode? focusNode;
final TextInputType? keyboardType;
final List<TextInputFormatter>? inputFormatters;
final int? maxLength;
static const kUsernameTitle = 'Username';
static const kUsernameIcon = Icon(Icons.account_circle_outlined);
@@ -382,6 +399,7 @@ class DialogTextField extends StatelessWidget {
this.hintText,
this.keyboardType,
this.inputFormatters,
this.maxLength,
required this.title,
required this.controller})
: super(key: key);
@@ -408,6 +426,7 @@ class DialogTextField extends StatelessWidget {
obscureText: obscureText,
keyboardType: keyboardType,
inputFormatters: inputFormatters,
maxLength: maxLength,
),
),
],
@@ -622,7 +641,7 @@ class _DialogVerificationCodeField extends State<DialogVerificationCodeField> {
// software secure keyboard will take the focus since flutter 3.13
// request focus again when android account password obtain focus
if (Platform.isAndroid && widget.reRequestFocus) {
if (isAndroid && widget.reRequestFocus) {
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
_timerReRequestFocus?.cancel();
@@ -664,6 +683,8 @@ class PasswordWidget extends StatefulWidget {
this.reRequestFocus = false,
this.hintText,
this.errorText,
this.title,
this.maxLength,
}) : super(key: key);
final TextEditingController controller;
@@ -671,6 +692,8 @@ class PasswordWidget extends StatefulWidget {
final bool reRequestFocus;
final String? hintText;
final String? errorText;
final String? title;
final int? maxLength;
@override
State<PasswordWidget> createState() => _PasswordWidgetState();
@@ -691,7 +714,7 @@ class _PasswordWidgetState extends State<PasswordWidget> {
}
// software secure keyboard will take the focus since flutter 3.13
// request focus again when android account password obtain focus
if (Platform.isAndroid && widget.reRequestFocus) {
if (isAndroid && widget.reRequestFocus) {
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
_timerReRequestFocus?.cancel();
@@ -714,7 +737,7 @@ class _PasswordWidgetState extends State<PasswordWidget> {
@override
Widget build(BuildContext context) {
return DialogTextField(
title: translate(DialogTextField.kPasswordTitle),
title: translate(widget.title ?? DialogTextField.kPasswordTitle),
hintText: translate(widget.hintText ?? 'Enter your password'),
controller: widget.controller,
prefixIcon: DialogTextField.kPasswordIcon,
@@ -733,6 +756,7 @@ class _PasswordWidgetState extends State<PasswordWidget> {
obscureText: !_passwordVisible,
errorText: widget.errorText,
focusNode: _focusNode,
maxLength: widget.maxLength,
);
}
}
@@ -1106,7 +1130,7 @@ void showRequestElevationDialog(
errorText: errPwd.isEmpty ? null : errPwd.value,
),
],
).marginOnly(left: isDesktop ? 35 : 0),
).marginOnly(left: stateGlobal.isPortrait.isFalse ? 35 : 0),
).marginOnly(top: 10),
],
),
@@ -1583,7 +1607,7 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
msgBoxCommon(ffi.dialogManager, 'Custom Image Quality', content, [btnClose]);
}
void deletePeerConfirmDialog(Function onSubmit, String title) async {
void deleteConfirmDialog(Function onSubmit, String title) async {
gFFI.dialogManager.show(
(setState, close, context) {
submit() async {
@@ -1631,7 +1655,7 @@ void editAbTagDialog(
List<dynamic> currentTags, Function(List<dynamic>) onSubmit) {
var isInProgress = false;
final tags = List.of(gFFI.abModel.tags);
final tags = List.of(gFFI.abModel.currentAbTags);
var selectedTag = currentTags.obs;
gFFI.dialogManager.show((setState, close, context) {
@@ -1751,9 +1775,70 @@ void renameDialog(
});
}
void changeBot({Function()? callback}) async {
if (bind.mainHasValidBotSync()) {
await bind.mainSetOption(key: "bot", value: "");
callback?.call();
return;
}
String errorText = '';
bool loading = false;
final controller = TextEditingController();
gFFI.dialogManager.show((setState, close, context) {
onVerify() async {
final token = controller.text.trim();
if (token == "") return;
loading = true;
errorText = '';
setState(() {});
final error = await bind.mainVerifyBot(token: token);
if (error == "") {
callback?.call();
close();
} else {
errorText = translate(error);
loading = false;
setState(() {});
}
}
final codeField = TextField(
autofocus: true,
controller: controller,
decoration: InputDecoration(
hintText: translate('Token'),
),
);
return CustomAlertDialog(
title: Text(translate("Telegram bot")),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(translate("enable-bot-desc"),
style: TextStyle(fontSize: 12))
.marginOnly(bottom: 12),
Row(children: [Expanded(child: codeField)]),
if (errorText != '')
Text(errorText, style: TextStyle(color: Colors.red))
.marginOnly(top: 12),
],
),
actions: [
dialogButton("Cancel", onPressed: close, isOutline: true),
loading
? CircularProgressIndicator()
: dialogButton("OK", onPressed: onVerify),
],
onCancel: close,
);
});
}
void change2fa({Function()? callback}) async {
if (bind.mainHasValid2FaSync()) {
await bind.mainSetOption(key: "2fa", value: "");
await bind.mainClearTrustedDevices();
callback?.call();
return;
}
@@ -1821,6 +1906,7 @@ void enter2FaDialog(
SessionID sessionId, OverlayDialogManager dialogManager) async {
final controller = TextEditingController();
final RxBool submitReady = false.obs;
final RxBool trustThisDevice = false.obs;
dialogManager.dismissAll();
dialogManager.show((setState, close, context) {
@@ -1830,7 +1916,7 @@ void enter2FaDialog(
}
submit() {
gFFI.send2FA(sessionId, controller.text.trim());
gFFI.send2FA(sessionId, controller.text.trim(), trustThisDevice.value);
close();
dialogManager.showLoading(translate('Logging in...'),
onCancel: closeConnection);
@@ -1844,9 +1930,27 @@ void enter2FaDialog(
onChanged: () => submitReady.value = codeField.isReady,
);
final trustField = Obx(() => CheckboxListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
title: Text(translate("Trust this device")),
value: trustThisDevice.value,
onChanged: (value) {
if (value == null) return;
trustThisDevice.value = value;
},
));
return CustomAlertDialog(
title: Text(translate('enter-2fa-title')),
content: codeField,
content: Column(
children: [
codeField,
if (bind.sessionGetEnableTrustedDevices(sessionId: sessionId))
trustField,
],
),
actions: [
dialogButton('Cancel',
onPressed: cancel,
@@ -1909,3 +2013,487 @@ void showWindowsSessionsDialog(
);
});
}
void addPeersToAbDialog(
List<Peer> peers,
) async {
Future<bool> addTo(String abname) async {
final mapList = peers.map((e) {
var json = e.toJson();
// remove password when add to another address book to avoid re-share
json.remove('password');
json.remove('hash');
return json;
}).toList();
final errMsg = await gFFI.abModel.addPeersTo(mapList, abname);
if (errMsg == null) {
showToast(translate('Successful'));
return true;
} else {
BotToast.showText(text: errMsg, contentColor: Colors.red);
return false;
}
}
// if only one address book and it is personal, add to it directly
if (gFFI.abModel.addressbooks.length == 1 &&
gFFI.abModel.current.isPersonal()) {
await addTo(gFFI.abModel.currentName.value);
return;
}
RxBool isInProgress = false.obs;
final names = gFFI.abModel.addressBooksCanWrite();
RxString currentName = gFFI.abModel.currentName.value.obs;
TextEditingController controller = TextEditingController();
if (gFFI.peerTabModel.currentTab == PeerTabIndex.ab.index) {
names.remove(currentName.value);
}
if (names.isEmpty) {
debugPrint('no address book to add peers to, should not happen');
return;
}
if (!names.contains(currentName.value)) {
currentName.value = names[0];
}
gFFI.dialogManager.show((setState, close, context) {
submit() async {
if (controller.text != gFFI.abModel.translatedName(currentName.value)) {
BotToast.showText(
text: 'illegal address book name: ${controller.text}',
contentColor: Colors.red);
return;
}
isInProgress.value = true;
if (await addTo(currentName.value)) {
close();
}
isInProgress.value = false;
}
cancel() {
close();
}
return CustomAlertDialog(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(IconFont.addressBook, color: MyTheme.accent),
Text(translate('Add to address book')).paddingOnly(left: 10),
],
),
content: Obx(() => Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// https://github.com/flutter/flutter/issues/145081
DropdownMenu(
initialSelection: currentName.value,
onSelected: (value) {
if (value != null) {
currentName.value = value;
}
},
dropdownMenuEntries: names
.map((e) => DropdownMenuEntry(
value: e, label: gFFI.abModel.translatedName(e)))
.toList(),
inputDecorationTheme: InputDecorationTheme(
isDense: true, border: UnderlineInputBorder()),
enableFilter: true,
controller: controller,
),
// NOT use Offstage to wrap LinearProgressIndicator
isInProgress.value ? const LinearProgressIndicator() : Offstage()
],
)),
actions: [
dialogButton(
"Cancel",
icon: Icon(Icons.close_rounded),
onPressed: cancel,
isOutline: true,
),
dialogButton(
"OK",
icon: Icon(Icons.done_rounded),
onPressed: submit,
),
],
onSubmit: submit,
onCancel: cancel,
);
});
}
void setSharedAbPasswordDialog(String abName, Peer peer) {
TextEditingController controller = TextEditingController(text: '');
RxBool isInProgress = false.obs;
RxBool isInputEmpty = true.obs;
bool passwordVisible = false;
controller.addListener(() {
isInputEmpty.value = controller.text.isEmpty;
});
gFFI.dialogManager.show((setState, close, context) {
change(String password) async {
isInProgress.value = true;
bool res =
await gFFI.abModel.changeSharedPassword(abName, peer.id, password);
isInProgress.value = false;
if (res) {
showToast(translate('Successful'));
}
close();
}
cancel() {
close();
}
return CustomAlertDialog(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.key, color: MyTheme.accent),
Text(translate(peer.password.isEmpty
? 'Set shared password'
: 'Change Password'))
.paddingOnly(left: 10),
],
),
content: Obx(() => Column(children: [
TextField(
controller: controller,
autofocus: true,
obscureText: !passwordVisible,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(
passwordVisible ? Icons.visibility : Icons.visibility_off,
color: MyTheme.lightTheme.primaryColor),
onPressed: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
),
),
),
if (!gFFI.abModel.current.isPersonal())
Row(children: [
Icon(Icons.info, color: Colors.amber).marginOnly(right: 4),
Text(
translate('share_warning_tip'),
style: TextStyle(fontSize: 12),
)
]).marginSymmetric(vertical: 10),
// NOT use Offstage to wrap LinearProgressIndicator
isInProgress.value ? const LinearProgressIndicator() : Offstage()
])),
actions: [
dialogButton(
"Cancel",
icon: Icon(Icons.close_rounded),
onPressed: cancel,
isOutline: true,
),
if (peer.password.isNotEmpty)
dialogButton(
"Remove",
icon: Icon(Icons.delete_outline_rounded),
onPressed: () => change(''),
buttonStyle: ButtonStyle(
backgroundColor: MaterialStatePropertyAll(Colors.red)),
),
Obx(() => dialogButton(
"OK",
icon: Icon(Icons.done_rounded),
onPressed:
isInputEmpty.value ? null : () => change(controller.text),
)),
],
onSubmit: isInputEmpty.value ? null : () => change(controller.text),
onCancel: cancel,
);
});
}
void CommonConfirmDialog(OverlayDialogManager dialogManager, String content,
VoidCallback onConfirm) {
dialogManager.show((setState, close, context) {
submit() {
close();
onConfirm.call();
}
return CustomAlertDialog(
content: Row(
children: [
Expanded(
child: Text(content,
style: const TextStyle(fontSize: 15),
textAlign: TextAlign.start),
),
],
).marginOnly(bottom: 12),
actions: [
dialogButton(translate("Cancel"), onPressed: close, isOutline: true),
dialogButton(translate("OK"), onPressed: submit),
],
onSubmit: submit,
onCancel: close,
);
});
}
void changeUnlockPinDialog(String oldPin, Function() callback) {
final pinController = TextEditingController(text: oldPin);
final confirmController = TextEditingController(text: oldPin);
String? pinErrorText;
String? confirmationErrorText;
final maxLength = bind.mainMaxEncryptLen();
gFFI.dialogManager.show((setState, close, context) {
submit() async {
pinErrorText = null;
confirmationErrorText = null;
final pin = pinController.text.trim();
final confirm = confirmController.text.trim();
if (pin != confirm) {
setState(() {
confirmationErrorText =
translate('The confirmation is not identical.');
});
return;
}
final errorMsg = bind.mainSetUnlockPin(pin: pin);
if (errorMsg != '') {
setState(() {
pinErrorText = translate(errorMsg);
});
return;
}
callback.call();
close();
}
return CustomAlertDialog(
title: Text(translate("Set PIN")),
content: Column(
children: [
DialogTextField(
title: 'PIN',
controller: pinController,
obscureText: true,
errorText: pinErrorText,
maxLength: maxLength,
),
DialogTextField(
title: translate('Confirmation'),
controller: confirmController,
obscureText: true,
errorText: confirmationErrorText,
maxLength: maxLength,
)
],
).marginOnly(bottom: 12),
actions: [
dialogButton(translate("Cancel"), onPressed: close, isOutline: true),
dialogButton(translate("OK"), onPressed: submit),
],
onSubmit: submit,
onCancel: close,
);
});
}
void checkUnlockPinDialog(String correctPin, Function() passCallback) {
final controller = TextEditingController();
String? errorText;
gFFI.dialogManager.show((setState, close, context) {
submit() async {
final pin = controller.text.trim();
if (correctPin != pin) {
setState(() {
errorText = translate('Wrong PIN');
});
return;
}
passCallback.call();
close();
}
return CustomAlertDialog(
content: Row(
children: [
Expanded(
child: PasswordWidget(
title: 'PIN',
controller: controller,
errorText: errorText,
hintText: '',
))
],
).marginOnly(bottom: 12),
actions: [
dialogButton(translate("Cancel"), onPressed: close, isOutline: true),
dialogButton(translate("OK"), onPressed: submit),
],
onSubmit: submit,
onCancel: close,
);
});
}
void confrimDeleteTrustedDevicesDialog(
RxList<TrustedDevice> trustedDevices, RxList<Uint8List> selectedDevices) {
CommonConfirmDialog(gFFI.dialogManager, '${translate('Confirm Delete')}?',
() async {
if (selectedDevices.isEmpty) return;
if (selectedDevices.length == trustedDevices.length) {
await bind.mainClearTrustedDevices();
trustedDevices.clear();
selectedDevices.clear();
} else {
final json = jsonEncode(selectedDevices.map((e) => e.toList()).toList());
await bind.mainRemoveTrustedDevices(json: json);
trustedDevices.removeWhere((element) {
return selectedDevices.contains(element.hwid);
});
selectedDevices.clear();
}
});
}
void manageTrustedDeviceDialog() async {
RxList<TrustedDevice> trustedDevices = (await TrustedDevice.get()).obs;
RxList<Uint8List> selectedDevices = RxList.empty();
gFFI.dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title: Text(translate("Manage trusted devices")),
content: trustedDevicesTable(trustedDevices, selectedDevices),
actions: [
Obx(() => dialogButton(translate("Delete"),
onPressed: selectedDevices.isEmpty
? null
: () {
confrimDeleteTrustedDevicesDialog(
trustedDevices,
selectedDevices,
);
},
isOutline: false)
.marginOnly(top: 12)),
dialogButton(translate("Close"), onPressed: close, isOutline: true)
.marginOnly(top: 12),
],
onCancel: close,
);
});
}
class TrustedDevice {
late final Uint8List hwid;
late final int time;
late final String id;
late final String name;
late final String platform;
TrustedDevice.fromJson(Map<String, dynamic> json) {
final hwidList = json['hwid'] as List<dynamic>;
hwid = Uint8List.fromList(hwidList.cast<int>());
time = json['time'];
id = json['id'];
name = json['name'];
platform = json['platform'];
}
String daysRemaining() {
final expiry = time + 90 * 24 * 60 * 60 * 1000;
final remaining = expiry - DateTime.now().millisecondsSinceEpoch;
if (remaining < 0) {
return '0';
}
return (remaining / (24 * 60 * 60 * 1000)).toStringAsFixed(0);
}
static Future<List<TrustedDevice>> get() async {
final List<TrustedDevice> devices = List.empty(growable: true);
try {
final devicesJson = await bind.mainGetTrustedDevices();
if (devicesJson.isNotEmpty) {
final devicesList = json.decode(devicesJson);
if (devicesList is List) {
for (var device in devicesList) {
devices.add(TrustedDevice.fromJson(device));
}
}
}
} catch (e) {
print(e.toString());
}
devices.sort((a, b) => b.time.compareTo(a.time));
return devices;
}
}
Widget trustedDevicesTable(
RxList<TrustedDevice> devices, RxList<Uint8List> selectedDevices) {
RxBool selectAll = false.obs;
setSelectAll() {
if (selectedDevices.isNotEmpty &&
selectedDevices.length == devices.length) {
selectAll.value = true;
} else {
selectAll.value = false;
}
}
devices.listen((_) {
setSelectAll();
});
selectedDevices.listen((_) {
setSelectAll();
});
return FittedBox(
child: Obx(() => DataTable(
columns: [
DataColumn(
label: Checkbox(
value: selectAll.value,
onChanged: (value) {
if (value == true) {
selectedDevices.clear();
selectedDevices.addAll(devices.map((e) => e.hwid));
} else {
selectedDevices.clear();
}
},
)),
DataColumn(label: Text(translate('Platform'))),
DataColumn(label: Text(translate('ID'))),
DataColumn(label: Text(translate('Username'))),
DataColumn(label: Text(translate('Days remaining'))),
],
rows: devices.map((device) {
return DataRow(cells: [
DataCell(Checkbox(
value: selectedDevices.contains(device.hwid),
onChanged: (value) {
if (value == null) return;
if (value) {
selectedDevices.remove(device.hwid);
selectedDevices.add(device.hwid);
} else {
selectedDevices.remove(device.hwid);
}
},
)),
DataCell(Text(device.platform)),
DataCell(Text(device.id)),
DataCell(Text(device.name)),
DataCell(Text(device.daysRemaining())),
]);
}).toList(),
)),
);
}

View File

@@ -112,6 +112,8 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
};
}
// FIXME: This debounce logic is not working properly.
// If we move our finger very fast, we won't be able to detect the "oneFingerPan" event sometimes.
void onOneFingerStartDebounce(ScaleUpdateDetails d) {
start(ScaleUpdateDetails d) {
_currentState = GestureState.oneFingerPan;

View File

@@ -142,11 +142,6 @@ class _WidgetOPState extends State<WidgetOP> {
String _failedMsg = '';
String _url = '';
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
@@ -455,7 +450,7 @@ Future<bool?> loginDialog() async {
}
if (isEmailVerification != null) {
if (isMobile) {
if (close != null) close(false);
if (close != null) close(null);
verificationCodeDialog(
resp.user, resp.secret, isEmailVerification);
} else {
@@ -712,6 +707,11 @@ Future<bool?> verificationCodeDialog(
dialogButton("Verify", onPressed: getOnSubmit()),
]);
});
// For verification code, desktop update other models in login dialog, mobile need to close login dialog first,
// otherwise the soft keyboard will jump out on each key press, so mobile update in verification code dialog.
if (isMobile && res == true) {
await UserModel.updateOtherModels();
}
return res;
}

View File

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
import 'package:flutter_hbb/common/widgets/login.dart';
import 'package:flutter_hbb/common/widgets/peers_view.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
import '../../common.dart';
@@ -23,11 +24,6 @@ class _MyGroupState extends State<MyGroup> {
RxString get searchUserText => gFFI.groupModel.searchUserText;
static TextEditingController searchUserController = TextEditingController();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Obx(() {
@@ -35,6 +31,8 @@ class _MyGroupState extends State<MyGroup> {
return Center(
child: ElevatedButton(
onPressed: loginDialog, child: Text(translate("Login"))));
} else if (gFFI.userModel.networkError.isNotEmpty) {
return netWorkErrorWidget();
} else if (gFFI.groupModel.groupLoading.value && gFFI.groupModel.emtpy) {
return const Center(
child: CircularProgressIndicator(),
@@ -47,13 +45,16 @@ class _MyGroupState extends State<MyGroup> {
err: gFFI.groupModel.groupLoadError,
retry: null,
close: () => gFFI.groupModel.groupLoadError.value = ''),
Expanded(child: isDesktop ? _buildDesktop() : _buildMobile())
Expanded(
child: Obx(() => stateGlobal.isPortrait.isTrue
? _buildPortrait()
: _buildLandscape())),
],
);
});
}
Widget _buildDesktop() {
Widget _buildLandscape() {
return Row(
children: [
Container(
@@ -82,14 +83,14 @@ class _MyGroupState extends State<MyGroup> {
child: Align(
alignment: Alignment.topLeft,
child: MyGroupPeerView(
menuPadding: widget.menuPadding,
initPeers: gFFI.groupModel.peers)),
menuPadding: widget.menuPadding,
)),
)
],
);
}
Widget _buildMobile() {
Widget _buildPortrait() {
return Column(
children: [
Container(
@@ -114,8 +115,8 @@ class _MyGroupState extends State<MyGroup> {
child: Align(
alignment: Alignment.topLeft,
child: MyGroupPeerView(
menuPadding: widget.menuPadding,
initPeers: gFFI.groupModel.peers)),
menuPadding: widget.menuPadding,
)),
)
],
);
@@ -159,14 +160,14 @@ class _MyGroupState extends State<MyGroup> {
}
return true;
}).toList();
final listView = ListView.builder(
shrinkWrap: isMobile,
listView(bool isPortrait) => ListView.builder(
shrinkWrap: isPortrait,
itemCount: items.length,
itemBuilder: (context, index) => _buildUserItem(items[index]));
var maxHeight = max(MediaQuery.of(context).size.height / 6, 100.0);
return isDesktop
? listView
: LimitedBox(maxHeight: maxHeight, child: listView);
return Obx(() => stateGlobal.isPortrait.isFalse
? listView(false)
: LimitedBox(maxHeight: maxHeight, child: listView(true)));
});
}

View File

@@ -1,6 +1,8 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:debounce_throttle/debounce_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
@@ -26,9 +28,12 @@ class DraggableChatWindow extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (draggablePositions.chatWindow.isInvalid()) {
draggablePositions.chatWindow.update(position);
}
return isIOS
? IOSDraggable(
position: position,
position: draggablePositions.chatWindow,
chatModel: chatModel,
width: width,
height: height,
@@ -45,7 +50,7 @@ class DraggableChatWindow extends StatelessWidget {
)
: Draggable(
checkKeyboard: true,
position: position,
position: draggablePositions.chatWindow,
width: width,
height: height,
chatModel: chatModel,
@@ -54,7 +59,7 @@ class DraggableChatWindow extends StatelessWidget {
resizeToAvoidBottomInset: false,
appBar: CustomAppBar(
onPanUpdate: onPanUpdate,
appBar: isDesktop
appBar: (isDesktop || isWebDesktop)
? _buildDesktopAppBar(context)
: _buildMobileAppBar(context),
),
@@ -166,15 +171,17 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
/// floating buttons of back/home/recent actions for android
class DraggableMobileActions extends StatelessWidget {
DraggableMobileActions(
{this.position = Offset.zero,
this.onBackPressed,
{this.onBackPressed,
this.onRecentPressed,
this.onHomePressed,
this.onHidePressed,
required this.position,
required this.width,
required this.height});
required this.height,
required this.scale});
final Offset position;
final double scale;
final DraggableKeyPosition position;
final double width;
final double height;
final VoidCallback? onBackPressed;
@@ -186,8 +193,8 @@ class DraggableMobileActions extends StatelessWidget {
Widget build(BuildContext context) {
return Draggable(
position: position,
width: width,
height: height,
width: scale * width,
height: scale * height,
builder: (_, onPanUpdate) {
return GestureDetector(
onPanUpdate: onPanUpdate,
@@ -197,7 +204,8 @@ class DraggableMobileActions extends StatelessWidget {
child: Container(
decoration: BoxDecoration(
color: MyTheme.accent.withOpacity(0.4),
borderRadius: BorderRadius.all(Radius.circular(15))),
borderRadius:
BorderRadius.all(Radius.circular(15 * scale))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
@@ -205,17 +213,20 @@ class DraggableMobileActions extends StatelessWidget {
color: Colors.white,
onPressed: onBackPressed,
splashRadius: kDesktopIconButtonSplashRadius,
icon: const Icon(Icons.arrow_back)),
icon: const Icon(Icons.arrow_back),
iconSize: 24 * scale),
IconButton(
color: Colors.white,
onPressed: onHomePressed,
splashRadius: kDesktopIconButtonSplashRadius,
icon: const Icon(Icons.home)),
icon: const Icon(Icons.home),
iconSize: 24 * scale),
IconButton(
color: Colors.white,
onPressed: onRecentPressed,
splashRadius: kDesktopIconButtonSplashRadius,
icon: const Icon(Icons.more_horiz)),
icon: const Icon(Icons.more_horiz),
iconSize: 24 * scale),
const VerticalDivider(
width: 0,
thickness: 2,
@@ -226,7 +237,8 @@ class DraggableMobileActions extends StatelessWidget {
color: Colors.white,
onPressed: onHidePressed,
splashRadius: kDesktopIconButtonSplashRadius,
icon: const Icon(Icons.keyboard_arrow_down)),
icon: const Icon(Icons.keyboard_arrow_down),
iconSize: 24 * scale),
],
),
)));
@@ -234,12 +246,98 @@ class DraggableMobileActions extends StatelessWidget {
}
}
class DraggableKeyPosition {
final String key;
Offset _pos;
late Debouncer<int> _debouncerStore;
DraggableKeyPosition(this.key)
: _pos = DraggablePositions.kInvalidDraggablePosition;
get pos => _pos;
_loadPosition(String k) {
final value = bind.getLocalFlutterOption(k: k);
if (value.isNotEmpty) {
final parts = value.split(',');
if (parts.length == 2) {
return Offset(double.parse(parts[0]), double.parse(parts[1]));
}
}
return DraggablePositions.kInvalidDraggablePosition;
}
load() {
_pos = _loadPosition(key);
_debouncerStore = Debouncer<int>(const Duration(milliseconds: 500),
onChanged: (v) => _store(), initialValue: 0);
}
update(Offset pos) {
_pos = pos;
_triggerStore();
}
// Adjust position to keep it in the screen
// Only used for desktop and web desktop
tryAdjust(double w, double h, double scale) {
final size = MediaQuery.of(Get.context!).size;
w = w * scale;
h = h * scale;
double x = _pos.dx;
double y = _pos.dy;
if (x + w > size.width) {
x = size.width - w;
}
final tabBarHeight = isDesktop ? kDesktopRemoteTabBarHeight : 0;
if (y + h > (size.height - tabBarHeight)) {
y = size.height - tabBarHeight - h;
}
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
if (x != _pos.dx || y != _pos.dy) {
update(Offset(x, y));
}
}
isInvalid() {
return _pos == DraggablePositions.kInvalidDraggablePosition;
}
_triggerStore() => _debouncerStore.value = _debouncerStore.value + 1;
_store() {
bind.setLocalFlutterOption(k: key, v: '${_pos.dx},${_pos.dy}');
}
}
class DraggablePositions {
static const kChatWindow = 'draggablePositionChat';
static const kMobileActions = 'draggablePositionMobile';
static const kIOSDraggable = 'draggablePositionIOS';
static const kInvalidDraggablePosition = Offset(-999999, -999999);
final chatWindow = DraggableKeyPosition(kChatWindow);
final mobileActions = DraggableKeyPosition(kMobileActions);
final iOSDraggable = DraggableKeyPosition(kIOSDraggable);
load() {
chatWindow.load();
mobileActions.load();
iOSDraggable.load();
}
}
DraggablePositions draggablePositions = DraggablePositions();
class Draggable extends StatefulWidget {
const Draggable(
Draggable(
{Key? key,
this.checkKeyboard = false,
this.checkScreenSize = false,
this.position = Offset.zero,
required this.position,
required this.width,
required this.height,
this.chatModel,
@@ -248,55 +346,53 @@ class Draggable extends StatefulWidget {
final bool checkKeyboard;
final bool checkScreenSize;
final Offset position;
final DraggableKeyPosition position;
final double width;
final double height;
final ChatModel? chatModel;
final Widget Function(BuildContext, GestureDragUpdateCallback) builder;
@override
State<StatefulWidget> createState() => _DraggableState();
State<StatefulWidget> createState() => _DraggableState(chatModel);
}
class _DraggableState extends State<Draggable> {
late Offset _position;
late ChatModel? _chatModel;
bool _keyboardVisible = false;
double _saveHeight = 0;
double _lastBottomHeight = 0;
@override
void initState() {
super.initState();
_position = widget.position;
_chatModel = widget.chatModel;
_DraggableState(ChatModel? chatModel) {
_chatModel = chatModel;
}
get position => widget.position.pos;
void onPanUpdate(DragUpdateDetails d) {
final offset = d.delta;
final size = MediaQuery.of(context).size;
double x = 0;
double y = 0;
if (_position.dx + offset.dx + widget.width > size.width) {
if (position.dx + offset.dx + widget.width > size.width) {
x = size.width - widget.width;
} else if (_position.dx + offset.dx < 0) {
} else if (position.dx + offset.dx < 0) {
x = 0;
} else {
x = _position.dx + offset.dx;
x = position.dx + offset.dx;
}
if (_position.dy + offset.dy + widget.height > size.height) {
if (position.dy + offset.dy + widget.height > size.height) {
y = size.height - widget.height;
} else if (_position.dy + offset.dy < 0) {
} else if (position.dy + offset.dy < 0) {
y = 0;
} else {
y = _position.dy + offset.dy;
y = position.dy + offset.dy;
}
setState(() {
_position = Offset(x, y);
widget.position.update(Offset(x, y));
});
_chatModel?.setChatWindowPosition(_position);
_chatModel?.setChatWindowPosition(position);
}
checkScreenSize() {}
@@ -307,13 +403,13 @@ class _DraggableState extends State<Draggable> {
// save
if (!_keyboardVisible && currentVisible) {
_saveHeight = _position.dy;
_saveHeight = position.dy;
}
// reset
if (_lastBottomHeight > 0 && bottomHeight == 0) {
setState(() {
_position = Offset(_position.dx, _saveHeight);
widget.position.update(Offset(position.dx, _saveHeight));
});
}
@@ -321,10 +417,10 @@ class _DraggableState extends State<Draggable> {
if (_keyboardVisible && currentVisible) {
final sumHeight = bottomHeight + widget.height;
final contextHeight = MediaQuery.of(context).size.height;
if (sumHeight + _position.dy > contextHeight) {
if (sumHeight + position.dy > contextHeight) {
final y = contextHeight - sumHeight;
setState(() {
_position = Offset(_position.dx, y);
widget.position.update(Offset(position.dx, y));
});
}
}
@@ -343,8 +439,8 @@ class _DraggableState extends State<Draggable> {
}
return Stack(children: [
Positioned(
top: _position.dy,
left: _position.dx,
top: position.dy,
left: position.dx,
width: widget.width,
height: widget.height,
child: widget.builder(context, onPanUpdate))
@@ -355,25 +451,25 @@ class _DraggableState extends State<Draggable> {
class IOSDraggable extends StatefulWidget {
const IOSDraggable(
{Key? key,
this.position = Offset.zero,
this.chatModel,
required this.position,
required this.width,
required this.height,
required this.builder})
: super(key: key);
final Offset position;
final DraggableKeyPosition position;
final ChatModel? chatModel;
final double width;
final double height;
final Widget Function(BuildContext) builder;
@override
IOSDraggableState createState() => IOSDraggableState();
IOSDraggableState createState() =>
IOSDraggableState(chatModel, width, height);
}
class IOSDraggableState extends State<IOSDraggable> {
late Offset _position;
late ChatModel? _chatModel;
late double _width;
late double _height;
@@ -381,28 +477,27 @@ class IOSDraggableState extends State<IOSDraggable> {
double _saveHeight = 0;
double _lastBottomHeight = 0;
@override
void initState() {
super.initState();
_position = widget.position;
_chatModel = widget.chatModel;
_width = widget.width;
_height = widget.height;
IOSDraggableState(ChatModel? chatModel, double w, double h) {
_chatModel = chatModel;
_width = w;
_height = h;
}
DraggableKeyPosition get position => widget.position;
checkKeyboard() {
final bottomHeight = MediaQuery.of(context).viewInsets.bottom;
final currentVisible = bottomHeight != 0;
// save
if (!_keyboardVisible && currentVisible) {
_saveHeight = _position.dy;
_saveHeight = position.pos.dy;
}
// reset
if (_lastBottomHeight > 0 && bottomHeight == 0) {
setState(() {
_position = Offset(_position.dx, _saveHeight);
position.update(Offset(position.pos.dx, _saveHeight));
});
}
@@ -410,10 +505,10 @@ class IOSDraggableState extends State<IOSDraggable> {
if (_keyboardVisible && currentVisible) {
final sumHeight = bottomHeight + _height;
final contextHeight = MediaQuery.of(context).size.height;
if (sumHeight + _position.dy > contextHeight) {
if (sumHeight + position.pos.dy > contextHeight) {
final y = contextHeight - sumHeight;
setState(() {
_position = Offset(_position.dx, y);
position.update(Offset(position.pos.dx, y));
});
}
}
@@ -428,14 +523,14 @@ class IOSDraggableState extends State<IOSDraggable> {
return Stack(
children: [
Positioned(
left: _position.dx,
top: _position.dy,
left: position.pos.dx,
top: position.pos.dy,
child: GestureDetector(
onPanUpdate: (details) {
setState(() {
_position += details.delta;
position.update(position.pos + details.delta);
});
_chatModel?.setChatWindowPosition(_position);
_chatModel?.setChatWindowPosition(position.pos);
},
child: Material(
child: Container(
@@ -491,8 +586,10 @@ class QualityMonitor extends StatelessWidget {
children: [
_row("Speed", qualityMonitorModel.data.speed ?? '-'),
_row("FPS", qualityMonitorModel.data.fps ?? '-'),
// let delay be 0 if fps is 0
_row(
"Delay", "${qualityMonitorModel.data.delay ?? '-'}ms",
"Delay",
"${qualityMonitorModel.data.delay == null ? '-' : (qualityMonitorModel.data.fps ?? "").replaceAll(' ', '').replaceAll('0', '').isEmpty ? 0 : qualityMonitorModel.data.delay}ms",
rightColor: Colors.green),
_row("Target Bitrate",
"${qualityMonitorModel.data.targetBitrate ?? '-'}kb"),

View File

@@ -1,10 +1,10 @@
import 'dart:io';
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common/widgets/dialog.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
@@ -23,6 +23,8 @@ enum PeerUiType { grid, tile, list }
final peerCardUiType = PeerUiType.grid.obs;
bool? hideUsernameOnCard;
class _PeerCard extends StatefulWidget {
final Peer peer;
final PeerTabIndex tab;
@@ -52,42 +54,44 @@ class _PeerCardState extends State<_PeerCard>
@override
Widget build(BuildContext context) {
super.build(context);
if (isDesktop) {
return _buildDesktop();
} else {
return _buildMobile();
}
return Obx(() =>
stateGlobal.isPortrait.isTrue ? _buildPortrait() : _buildLandscape());
}
Widget _buildMobile() {
final peer = super.widget.peer;
Widget gestureDetector({required Widget child}) {
final PeerTabModel peerTabModel = Provider.of(context);
final peer = super.widget.peer;
return GestureDetector(
onDoubleTap: peerTabModel.multiSelectionMode
? null
: () => widget.connect(context, peer.id),
onTap: () {
if (peerTabModel.multiSelectionMode) {
peerTabModel.select(peer);
} else {
if (isMobile) {
widget.connect(context, peer.id);
} else {
peerTabModel.select(peer);
}
}
},
onLongPress: () => peerTabModel.select(peer),
child: child);
}
Widget _buildPortrait() {
final peer = super.widget.peer;
return Card(
margin: EdgeInsets.symmetric(horizontal: 2),
child: GestureDetector(
onTap: () {
if (peerTabModel.multiSelectionMode) {
peerTabModel.select(peer);
} else {
if (!isWebDesktop) {
connectInPeerTab(context, peer.id, widget.tab);
}
}
},
onDoubleTap: isWebDesktop
? () => connectInPeerTab(context, peer.id, widget.tab)
: null,
onLongPress: () {
peerTabModel.select(peer);
},
child: gestureDetector(
child: Container(
padding: EdgeInsets.only(left: 12, top: 8, bottom: 8),
child: _buildPeerTile(context, peer, null)),
));
}
Widget _buildDesktop() {
final PeerTabModel peerTabModel = Provider.of(context);
Widget _buildLandscape() {
final peer = super.widget.peer;
var deco = Rx<BoxDecoration?>(
BoxDecoration(
@@ -116,45 +120,48 @@ class _PeerCardState extends State<_PeerCard>
),
);
},
child: GestureDetector(
onDoubleTap:
peerTabModel.multiSelectionMode || peerTabModel.isShiftDown
? null
: () => widget.connect(context, peer.id),
onTap: () => peerTabModel.select(peer),
onLongPress: () => peerTabModel.select(peer),
child: gestureDetector(
child: Obx(() => peerCardUiType.value == PeerUiType.grid
? _buildPeerCard(context, peer, deco)
: _buildPeerTile(context, peer, deco))),
);
}
Widget _buildPeerTile(
BuildContext context, Peer peer, Rx<BoxDecoration?>? deco) {
final name =
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
makeChild(bool isPortrait, Peer peer) {
final name = hideUsernameOnCard == true
? peer.hostname
: '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
final greyStyle = TextStyle(
fontSize: 11,
color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6));
final child = Row(
return Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
decoration: BoxDecoration(
color: str2color('${peer.id}${peer.platform}', 0x7f),
borderRadius: isMobile
? BorderRadius.circular(_tileRadius)
: BorderRadius.only(
topLeft: Radius.circular(_tileRadius),
bottomLeft: Radius.circular(_tileRadius),
decoration: BoxDecoration(
color: str2color('${peer.id}${peer.platform}', 0x7f),
borderRadius: isPortrait
? BorderRadius.circular(_tileRadius)
: BorderRadius.only(
topLeft: Radius.circular(_tileRadius),
bottomLeft: Radius.circular(_tileRadius),
),
),
alignment: Alignment.center,
width: isPortrait ? 50 : 42,
height: isPortrait ? 50 : null,
child: Stack(
children: [
getPlatformImage(peer.platform, size: isPortrait ? 38 : 30)
.paddingAll(6),
if (_shouldBuildPasswordIcon(peer))
Positioned(
top: 1,
left: 1,
child: Icon(Icons.key, size: 6, color: Colors.white),
),
),
alignment: Alignment.center,
width: isMobile ? 50 : 42,
height: isMobile ? 50 : null,
child: getPlatformImage(peer.platform, size: isMobile ? 38 : 30)
.paddingAll(6),
),
],
)),
Expanded(
child: Container(
decoration: BoxDecoration(
@@ -170,19 +177,19 @@ class _PeerCardState extends State<_PeerCard>
child: Column(
children: [
Row(children: [
getOnline(isMobile ? 4 : 8, peer.online),
getOnline(isPortrait ? 4 : 8, peer.online),
Expanded(
child: Text(
peer.alias.isEmpty ? formatID(peer.id) : peer.alias,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleSmall,
)),
]).marginOnly(top: isMobile ? 0 : 2),
]).marginOnly(top: isPortrait ? 0 : 2),
Align(
alignment: Alignment.centerLeft,
child: Text(
name,
style: isMobile ? null : greyStyle,
style: isPortrait ? null : greyStyle,
textAlign: TextAlign.start,
overflow: TextOverflow.ellipsis,
),
@@ -190,52 +197,65 @@ class _PeerCardState extends State<_PeerCard>
],
).marginOnly(top: 2),
),
isMobile
? checkBoxOrActionMoreMobile(peer)
: checkBoxOrActionMoreDesktop(peer, isTile: true),
isPortrait
? checkBoxOrActionMorePortrait(peer)
: checkBoxOrActionMoreLandscape(peer, isTile: true),
],
).paddingOnly(left: 10.0, top: 3.0),
),
)
],
);
final colors =
_frontN(peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList();
}
Widget _buildPeerTile(
BuildContext context, Peer peer, Rx<BoxDecoration?>? deco) {
hideUsernameOnCard ??=
bind.mainGetBuildinOption(key: kHideUsernameOnCard) == 'Y';
final colors = _frontN(peer.tags, 25)
.map((e) => gFFI.abModel.getCurrentAbTagColor(e))
.toList();
return Tooltip(
message: isMobile
message: !(isDesktop || isWebDesktop)
? ''
: peer.tags.isNotEmpty
? '${translate('Tags')}: ${peer.tags.join(', ')}'
: '',
child: Stack(children: [
deco == null
? child
: Obx(
() => Container(
Obx(
() => deco == null
? makeChild(stateGlobal.isPortrait.isTrue, peer)
: Container(
foregroundDecoration: deco.value,
child: child,
child: makeChild(stateGlobal.isPortrait.isTrue, peer),
),
),
),
if (colors.isNotEmpty)
Positioned(
top: 2,
right: isMobile ? 20 : 10,
child: CustomPaint(
painter: TagPainter(radius: 3, colors: colors),
),
)
Obx(() => Positioned(
top: 2,
right: stateGlobal.isPortrait.isTrue ? 20 : 10,
child: CustomPaint(
painter: TagPainter(radius: 3, colors: colors),
),
))
]),
);
}
Widget _buildPeerCard(
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
final name =
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
hideUsernameOnCard ??=
bind.mainGetBuildinOption(key: kHideUsernameOnCard) == 'Y';
final name = hideUsernameOnCard == true
? peer.hostname
: '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
final child = Card(
color: Colors.transparent,
elevation: 0,
margin: EdgeInsets.zero,
// to-do: memory leak here, more investigation needed.
// Continious rebuilds of `Obx()` will cause memory leak here.
// The simple demo does not have this issue.
child: Obx(
() => Container(
foregroundDecoration: deco.value,
@@ -299,7 +319,7 @@ class _PeerCardState extends State<_PeerCard>
style: Theme.of(context).textTheme.titleSmall,
)),
]).paddingSymmetric(vertical: 8)),
checkBoxOrActionMoreDesktop(peer, isTile: false),
checkBoxOrActionMoreLandscape(peer, isTile: false),
],
).paddingSymmetric(horizontal: 12.0),
)
@@ -310,14 +330,21 @@ class _PeerCardState extends State<_PeerCard>
),
);
final colors =
_frontN(peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList();
final colors = _frontN(peer.tags, 25)
.map((e) => gFFI.abModel.getCurrentAbTagColor(e))
.toList();
return Tooltip(
message: peer.tags.isNotEmpty
? '${translate('Tags')}: ${peer.tags.join(', ')}'
: '',
child: Stack(children: [
child,
if (_shouldBuildPasswordIcon(peer))
Positioned(
top: 4,
left: 12,
child: Icon(Icons.key, size: 12, color: Colors.white),
),
if (colors.isNotEmpty)
Positioned(
top: 4,
@@ -338,7 +365,7 @@ class _PeerCardState extends State<_PeerCard>
}
}
Widget checkBoxOrActionMoreMobile(Peer peer) {
Widget checkBoxOrActionMorePortrait(Peer peer) {
final PeerTabModel peerTabModel = Provider.of(context);
final selected = peerTabModel.isPeerSelected(peer.id);
if (peerTabModel.multiSelectionMode) {
@@ -366,7 +393,7 @@ class _PeerCardState extends State<_PeerCard>
}
}
Widget checkBoxOrActionMoreDesktop(Peer peer, {required bool isTile}) {
Widget checkBoxOrActionMoreLandscape(Peer peer, {required bool isTile}) {
final PeerTabModel peerTabModel = Provider.of(context);
final selected = peerTabModel.isPeerSelected(peer.id);
if (peerTabModel.multiSelectionMode) {
@@ -401,6 +428,12 @@ class _PeerCardState extends State<_PeerCard>
onPointerUp: (_) => _showPeerMenu(peer.id),
child: build_more(context));
bool _shouldBuildPasswordIcon(Peer peer) {
if (gFFI.peerTabModel.currentTab != PeerTabIndex.ab.index) return false;
if (gFFI.abModel.current.isPersonal()) return false;
return peer.password.isNotEmpty;
}
/// Show the peer menu and handle user's choice.
/// User might remove the peer or send a file to the peer.
void _showPeerMenu(String id) async {
@@ -431,7 +464,7 @@ abstract class BasePeerCard extends StatelessWidget {
peer: peer,
tab: tab,
connect: (BuildContext context, String id) =>
connectInPeerTab(context, id, tab),
connectInPeerTab(context, peer, tab),
popupMenuEntryBuilder: _buildPopupMenuEntry,
);
}
@@ -453,7 +486,6 @@ abstract class BasePeerCard extends StatelessWidget {
MenuEntryBase<String> _connectCommonAction(
BuildContext context,
String id,
String title, {
bool isFileTransfer = false,
bool isTcpTunneling = false,
@@ -467,7 +499,7 @@ abstract class BasePeerCard extends StatelessWidget {
proc: () {
connectInPeerTab(
context,
peer.id,
peer,
tab,
isFileTransfer: isFileTransfer,
isTcpTunneling: isTcpTunneling,
@@ -480,10 +512,9 @@ abstract class BasePeerCard extends StatelessWidget {
}
@protected
MenuEntryBase<String> _connectAction(BuildContext context, Peer peer) {
MenuEntryBase<String> _connectAction(BuildContext context) {
return _connectCommonAction(
context,
peer.id,
(peer.alias.isEmpty
? translate('Connect')
: '${translate('Connect')} ${peer.id}'),
@@ -491,20 +522,18 @@ abstract class BasePeerCard extends StatelessWidget {
}
@protected
MenuEntryBase<String> _transferFileAction(BuildContext context, String id) {
MenuEntryBase<String> _transferFileAction(BuildContext context) {
return _connectCommonAction(
context,
id,
translate('Transfer file'),
isFileTransfer: true,
);
}
@protected
MenuEntryBase<String> _tcpTunnelingAction(BuildContext context, String id) {
MenuEntryBase<String> _tcpTunnelingAction(BuildContext context) {
return _connectCommonAction(
context,
id,
translate('TCP tunneling'),
isTcpTunneling: true,
);
@@ -541,7 +570,7 @@ abstract class BasePeerCard extends StatelessWidget {
],
)),
proc: () {
connectInPeerTab(context, id, tab, isRDP: true);
connectInPeerTab(context, peer, tab, isRDP: true);
},
padding: menuPadding,
dismissOnClicked: true,
@@ -610,8 +639,8 @@ abstract class BasePeerCard extends StatelessWidget {
@protected
Future<bool> _isForceAlwaysRelay(String id) async {
return (await bind.mainGetPeerOption(id: id, key: kOptionForceAlwaysRelay))
.isNotEmpty;
return option2bool(kOptionForceAlwaysRelay,
(await bind.mainGetPeerOption(id: id, key: kOptionForceAlwaysRelay)));
}
@protected
@@ -648,9 +677,8 @@ abstract class BasePeerCard extends StatelessWidget {
onSubmit: (String newName) async {
if (newName != oldName) {
if (tab == PeerTabIndex.ab) {
gFFI.abModel.changeAlias(id: id, alias: newName);
await gFFI.abModel.changeAlias(id: id, alias: newName);
await bind.mainSetPeerAlias(id: id, alias: newName);
gFFI.abModel.pushAb();
} else {
await bind.mainSetPeerAlias(id: id, alias: newName);
showToast(translate('Successful'));
@@ -702,11 +730,7 @@ abstract class BasePeerCard extends StatelessWidget {
await bind.mainLoadLanPeers();
break;
case PeerTabIndex.ab:
gFFI.abModel.deletePeer(id);
final future = gFFI.abModel.pushAb();
if (await bind.mainPeerExists(id: peer.id)) {
gFFI.abModel.reSyncToast(future);
}
await gFFI.abModel.deletePeers([id]);
break;
case PeerTabIndex.group:
break;
@@ -716,7 +740,7 @@ abstract class BasePeerCard extends StatelessWidget {
}
}
deletePeerConfirmDialog(onSubmit,
deleteConfirmDialog(onSubmit,
'${translate('Delete')} "${peer.alias.isEmpty ? formatID(peer.id) : peer.alias}"?');
},
padding: menuPadding,
@@ -732,14 +756,16 @@ abstract class BasePeerCard extends StatelessWidget {
style: style,
),
proc: () async {
bool result = gFFI.abModel.changePassword(id, '');
bool succ = await gFFI.abModel.changePersonalHashPassword(id, '');
await bind.mainForgetPassword(id: id);
bool toast = false;
if (result) {
toast = tab == PeerTabIndex.ab;
gFFI.abModel.pushAb(toastIfFail: toast, toastIfSucc: toast);
if (succ) {
showToast(translate('Successful'));
} else {
if (tab.index == PeerTabIndex.ab.index) {
BotToast.showText(
contentColor: Colors.red, text: translate("Failed"));
}
}
if (!toast) showToast(translate('Successful'));
},
padding: menuPadding,
dismissOnClicked: true,
@@ -824,13 +850,7 @@ abstract class BasePeerCard extends StatelessWidget {
),
proc: () {
() async {
if (gFFI.abModel.isFull(true)) {
return;
}
if (!gFFI.abModel.idContainBy(peer.id)) {
gFFI.abModel.addPeer(peer);
gFFI.abModel.pushAb();
}
addPeersToAbDialog([Peer.copy(peer)]);
}();
},
padding: menuPadding,
@@ -858,25 +878,29 @@ class RecentPeerCard extends BasePeerCard {
Future<List<MenuEntryBase<String>>> _buildMenuItems(
BuildContext context) async {
final List<MenuEntryBase<String>> menuItems = [
_connectAction(context, peer),
_transferFileAction(context, peer.id),
_connectAction(context),
_transferFileAction(context),
];
final List favs = (await bind.mainGetFav()).toList();
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context, peer.id));
menuItems.add(_tcpTunnelingAction(context));
}
// menuItems.add(await _openNewConnInOptAction(peer.id));
menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (Platform.isWindows && peer.platform == kPeerPlatformWindows) {
if (!isWeb) {
menuItems.add(await _forceAlwaysRelayAction(peer.id));
}
if (isWindows && peer.platform == kPeerPlatformWindows) {
menuItems.add(_rdpAction(context, peer.id));
}
if (Platform.isWindows) {
if (isWindows) {
menuItems.add(_createShortCutAction(peer.id));
}
menuItems.add(MenuEntryDivider());
menuItems.add(_renameAction(peer.id));
if (isMobile || isDesktop || isWebDesktop) {
menuItems.add(_renameAction(peer.id));
}
if (await bind.mainPeerHasPassword(id: peer.id)) {
menuItems.add(_unrememberPasswordAction(peer.id));
}
@@ -888,9 +912,7 @@ class RecentPeerCard extends BasePeerCard {
}
if (gFFI.userModel.userName.isNotEmpty) {
if (!gFFI.abModel.idContainBy(peer.id)) {
menuItems.add(_addToAb(peer));
}
menuItems.add(_addToAb(peer));
}
menuItems.add(MenuEntryDivider());
@@ -915,22 +937,26 @@ class FavoritePeerCard extends BasePeerCard {
Future<List<MenuEntryBase<String>>> _buildMenuItems(
BuildContext context) async {
final List<MenuEntryBase<String>> menuItems = [
_connectAction(context, peer),
_transferFileAction(context, peer.id),
_connectAction(context),
_transferFileAction(context),
];
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context, peer.id));
menuItems.add(_tcpTunnelingAction(context));
}
// menuItems.add(await _openNewConnInOptAction(peer.id));
menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (Platform.isWindows && peer.platform == kPeerPlatformWindows) {
if (!isWeb) {
menuItems.add(await _forceAlwaysRelayAction(peer.id));
}
if (isWindows && peer.platform == kPeerPlatformWindows) {
menuItems.add(_rdpAction(context, peer.id));
}
if (Platform.isWindows) {
if (isWindows) {
menuItems.add(_createShortCutAction(peer.id));
}
menuItems.add(MenuEntryDivider());
menuItems.add(_renameAction(peer.id));
if (isMobile || isDesktop || isWebDesktop) {
menuItems.add(_renameAction(peer.id));
}
if (await bind.mainPeerHasPassword(id: peer.id)) {
menuItems.add(_unrememberPasswordAction(peer.id));
}
@@ -939,9 +965,7 @@ class FavoritePeerCard extends BasePeerCard {
}));
if (gFFI.userModel.userName.isNotEmpty) {
if (!gFFI.abModel.idContainBy(peer.id)) {
menuItems.add(_addToAb(peer));
}
menuItems.add(_addToAb(peer));
}
menuItems.add(MenuEntryDivider());
@@ -966,22 +990,24 @@ class DiscoveredPeerCard extends BasePeerCard {
Future<List<MenuEntryBase<String>>> _buildMenuItems(
BuildContext context) async {
final List<MenuEntryBase<String>> menuItems = [
_connectAction(context, peer),
_transferFileAction(context, peer.id),
_connectAction(context),
_transferFileAction(context),
];
final List favs = (await bind.mainGetFav()).toList();
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context, peer.id));
menuItems.add(_tcpTunnelingAction(context));
}
// menuItems.add(await _openNewConnInOptAction(peer.id));
menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (Platform.isWindows && peer.platform == kPeerPlatformWindows) {
if (!isWeb) {
menuItems.add(await _forceAlwaysRelayAction(peer.id));
}
if (isWindows && peer.platform == kPeerPlatformWindows) {
menuItems.add(_rdpAction(context, peer.id));
}
menuItems.add(_wolAction(peer.id));
if (Platform.isWindows) {
if (isWindows) {
menuItems.add(_createShortCutAction(peer.id));
}
@@ -992,9 +1018,7 @@ class DiscoveredPeerCard extends BasePeerCard {
}
if (gFFI.userModel.userName.isNotEmpty) {
if (!gFFI.abModel.idContainBy(peer.id)) {
menuItems.add(_addToAb(peer));
}
menuItems.add(_addToAb(peer));
}
menuItems.add(MenuEntryDivider());
@@ -1019,37 +1043,55 @@ class AddressBookPeerCard extends BasePeerCard {
Future<List<MenuEntryBase<String>>> _buildMenuItems(
BuildContext context) async {
final List<MenuEntryBase<String>> menuItems = [
_connectAction(context, peer),
_transferFileAction(context, peer.id),
_connectAction(context),
_transferFileAction(context),
];
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context, peer.id));
menuItems.add(_tcpTunnelingAction(context));
}
// menuItems.add(await _openNewConnInOptAction(peer.id));
menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (Platform.isWindows && peer.platform == kPeerPlatformWindows) {
// menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (isWindows && peer.platform == kPeerPlatformWindows) {
menuItems.add(_rdpAction(context, peer.id));
}
if (Platform.isWindows) {
if (isWindows) {
menuItems.add(_createShortCutAction(peer.id));
}
menuItems.add(MenuEntryDivider());
menuItems.add(_renameAction(peer.id));
if (peer.hash.isNotEmpty) {
menuItems.add(_unrememberPasswordAction(peer.id));
if (gFFI.abModel.current.canWrite()) {
menuItems.add(MenuEntryDivider());
if (isMobile || isDesktop || isWebDesktop) {
menuItems.add(_renameAction(peer.id));
}
if (gFFI.abModel.current.isPersonal() && peer.hash.isNotEmpty) {
menuItems.add(_unrememberPasswordAction(peer.id));
}
if (!gFFI.abModel.current.isPersonal()) {
menuItems.add(_changeSharedAbPassword());
}
if (gFFI.abModel.currentAbTags.isNotEmpty) {
menuItems.add(_editTagAction(peer.id));
}
}
if (gFFI.abModel.tags.isNotEmpty) {
menuItems.add(_editTagAction(peer.id));
final addressbooks = gFFI.abModel.addressBooksCanWrite();
if (gFFI.peerTabModel.currentTab == PeerTabIndex.ab.index) {
addressbooks.remove(gFFI.abModel.currentName.value);
}
if (addressbooks.isNotEmpty) {
menuItems.add(_addToAb(peer));
}
menuItems.add(_existIn());
if (gFFI.abModel.current.canWrite()) {
menuItems.add(MenuEntryDivider());
menuItems.add(_removeAction(peer.id));
}
menuItems.add(MenuEntryDivider());
menuItems.add(_removeAction(peer.id));
return menuItems;
}
// address book does not need to update
@protected
@override
void _update() => gFFI.abModel.pullAb(quiet: true);
void _update() =>
{}; //gFFI.abModel.pullAb(force: ForcePullAb.current, quiet: true);
@protected
MenuEntryBase<String> _editTagAction(String id) {
@@ -1060,8 +1102,7 @@ class AddressBookPeerCard extends BasePeerCard {
),
proc: () {
editAbTagDialog(gFFI.abModel.getPeerTags(id), (selectedTag) async {
gFFI.abModel.changeTagForPeer(id, selectedTag);
gFFI.abModel.pushAb();
await gFFI.abModel.changeTagForPeers([id], selectedTag);
});
},
padding: super.menuPadding,
@@ -1073,6 +1114,53 @@ class AddressBookPeerCard extends BasePeerCard {
@override
Future<String> _getAlias(String id) async =>
gFFI.abModel.find(id)?.alias ?? '';
MenuEntryBase<String> _changeSharedAbPassword() {
return MenuEntryButton<String>(
childBuilder: (TextStyle? style) => Text(
translate(
peer.password.isEmpty ? 'Set shared password' : 'Change Password'),
style: style,
),
proc: () {
setSharedAbPasswordDialog(gFFI.abModel.currentName.value, peer);
},
padding: super.menuPadding,
dismissOnClicked: true,
);
}
MenuEntryBase<String> _existIn() {
final names = gFFI.abModel.idExistIn(peer.id);
final text = names.join(', ');
return MenuEntryButton<String>(
childBuilder: (TextStyle? style) => Text(
translate('Exist in'),
style: style,
),
proc: () {
gFFI.dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title: Text(translate('Exist in')),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [Text(text)]),
actions: [
dialogButton(
"OK",
icon: Icon(Icons.done_rounded),
onPressed: close,
),
],
onSubmit: close,
onCancel: close,
);
});
},
padding: super.menuPadding,
dismissOnClicked: true,
);
}
}
class MyGroupPeerCard extends BasePeerCard {
@@ -1087,18 +1175,18 @@ class MyGroupPeerCard extends BasePeerCard {
Future<List<MenuEntryBase<String>>> _buildMenuItems(
BuildContext context) async {
final List<MenuEntryBase<String>> menuItems = [
_connectAction(context, peer),
_transferFileAction(context, peer.id),
_connectAction(context),
_transferFileAction(context),
];
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context, peer.id));
menuItems.add(_tcpTunnelingAction(context));
}
// menuItems.add(await _openNewConnInOptAction(peer.id));
// menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (Platform.isWindows && peer.platform == kPeerPlatformWindows) {
if (isWindows && peer.platform == kPeerPlatformWindows) {
menuItems.add(_rdpAction(context, peer.id));
}
if (Platform.isWindows) {
if (isWindows) {
menuItems.add(_createShortCutAction(peer.id));
}
// menuItems.add(MenuEntryDivider());
@@ -1107,9 +1195,7 @@ class MyGroupPeerCard extends BasePeerCard {
// menuItems.add(_unrememberPasswordAction(peer.id));
// }
if (gFFI.userModel.userName.isNotEmpty) {
if (!gFFI.abModel.idContainBy(peer.id)) {
menuItems.add(_addToAb(peer));
}
menuItems.add(_addToAb(peer));
}
return menuItems;
}
@@ -1120,6 +1206,7 @@ class MyGroupPeerCard extends BasePeerCard {
}
void _rdpDialog(String id) async {
final maxLength = bind.mainMaxEncryptLen();
final port = await bind.mainGetPeerOption(id: id, key: 'rdp_port');
final username = await bind.mainGetPeerOption(id: id, key: 'rdp_username');
final portController = TextEditingController(text: port);
@@ -1174,50 +1261,54 @@ void _rdpDialog(String id) async {
),
],
).marginOnly(bottom: isDesktop ? 8 : 0),
Row(
children: [
isDesktop
? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 140),
child: Text(
"${translate('Username')}:",
textAlign: TextAlign.right,
).marginOnly(right: 10))
: SizedBox.shrink(),
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: isDesktop ? null : translate('Username')),
controller: userController,
),
),
],
).marginOnly(bottom: isDesktop ? 8 : 0),
Row(
children: [
isDesktop
? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 140),
child: Text(
"${translate('Password')}:",
textAlign: TextAlign.right,
).marginOnly(right: 10))
: SizedBox.shrink(),
Expanded(
child: Obx(() => TextField(
obscureText: secure.value,
Obx(() => Row(
children: [
stateGlobal.isPortrait.isFalse
? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 140),
child: Text(
"${translate('Username')}:",
textAlign: TextAlign.right,
).marginOnly(right: 10))
: SizedBox.shrink(),
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: isDesktop ? null : translate('Password'),
suffixIcon: IconButton(
onPressed: () => secure.value = !secure.value,
icon: Icon(secure.value
? Icons.visibility_off
: Icons.visibility))),
controller: passwordController,
)),
),
],
)
labelText:
isDesktop ? null : translate('Username')),
controller: userController,
),
),
],
).marginOnly(bottom: stateGlobal.isPortrait.isFalse ? 8 : 0)),
Obx(() => Row(
children: [
stateGlobal.isPortrait.isFalse
? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 140),
child: Text(
"${translate('Password')}:",
textAlign: TextAlign.right,
).marginOnly(right: 10))
: SizedBox.shrink(),
Expanded(
child: Obx(() => TextField(
obscureText: secure.value,
maxLength: maxLength,
decoration: InputDecoration(
labelText:
isDesktop ? null : translate('Password'),
suffixIcon: IconButton(
onPressed: () =>
secure.value = !secure.value,
icon: Icon(secure.value
? Icons.visibility_off
: Icons.visibility))),
controller: passwordController,
)),
),
],
))
],
),
),
@@ -1305,24 +1396,32 @@ class TagPainter extends CustomPainter {
}
}
void connectInPeerTab(BuildContext context, String id, PeerTabIndex tab,
void connectInPeerTab(BuildContext context, Peer peer, PeerTabIndex tab,
{bool isFileTransfer = false,
bool isTcpTunneling = false,
bool isRDP = false}) async {
var password = '';
bool isSharedPassword = false;
if (tab == PeerTabIndex.ab) {
// If recent peer's alias is empty, set it to ab's alias
// Because the platform is not set, it may not take effect, but it is more important not to display if the connection is not successful
Peer? p = gFFI.abModel.find(id);
if (p != null &&
p.alias.isNotEmpty &&
(await bind.mainGetPeerOption(id: id, key: "alias")).isEmpty) {
if (peer.alias.isNotEmpty &&
(await bind.mainGetPeerOption(id: peer.id, key: "alias")).isEmpty) {
await bind.mainSetPeerAlias(
id: id,
alias: p.alias,
id: peer.id,
alias: peer.alias,
);
}
if (!gFFI.abModel.current.isPersonal()) {
if (peer.password.isNotEmpty) {
password = peer.password;
isSharedPassword = true;
}
}
}
connect(context, id,
connect(context, peer.id,
password: password,
isSharedPassword: isSharedPassword,
isFileTransfer: isFileTransfer,
isTcpTunneling: isTcpTunneling,
isRDP: isRDP);

View File

@@ -13,8 +13,10 @@ import 'package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart'
as mod_menu;
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/models/ab_model.dart';
import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
@@ -36,7 +38,7 @@ class _TabEntry {
}
EdgeInsets? _menuPadding() {
return isDesktop ? kDesktopMenuPadding : null;
return (isDesktop || isWebDesktop) ? kDesktopMenuPadding : null;
}
class _PeerTabPageState extends State<PeerTabPage>
@@ -61,7 +63,9 @@ class _PeerTabPageState extends State<PeerTabPage>
AddressBook(
menuPadding: _menuPadding(),
),
({dynamic hint}) => gFFI.abModel.pullAb(force: hint == null)),
({dynamic hint}) => gFFI.abModel.pullAb(
force: hint == null ? ForcePullAb.listAndCurrent : null,
quiet: false)),
_TabEntry(
MyGroup(
menuPadding: _menuPadding(),
@@ -71,9 +75,14 @@ class _PeerTabPageState extends State<PeerTabPage>
];
RelativeRect? mobileTabContextMenuPos;
@override
void initState() {
final uiType = bind.getLocalFlutterOption(k: 'peer-card-ui-type');
final isOptVisiableFixed = isOptionFixed(kOptionPeerTabVisible);
_PeerTabPageState() {
_loadLocalOptions();
}
void _loadLocalOptions() {
final uiType = bind.getLocalFlutterOption(k: kOptionPeerCardUiType);
if (uiType != '') {
peerCardUiType.value = int.parse(uiType) == 0
? PeerUiType.grid
@@ -82,8 +91,7 @@ class _PeerTabPageState extends State<PeerTabPage>
: PeerUiType.list;
}
hideAbTagsPanel.value =
bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty;
super.initState();
bind.mainGetLocalOption(key: kOptionHideAbTagsPanel) == 'Y';
}
Future<void> handleTabSelection(int tabIndex) async {
@@ -100,31 +108,33 @@ class _PeerTabPageState extends State<PeerTabPage>
Widget build(BuildContext context) {
final model = Provider.of<PeerTabModel>(context);
Widget selectionWrap(Widget widget) {
return model.multiSelectionMode ? createMultiSelectionBar() : widget;
return model.multiSelectionMode ? createMultiSelectionBar(model) : widget;
}
return Column(
textBaseline: TextBaseline.ideographic,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 32,
child: Container(
padding: isDesktop ? null : EdgeInsets.symmetric(horizontal: 2),
child: selectionWrap(Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child:
visibleContextMenuListener(_createSwitchBar(context))),
if (isMobile)
..._mobileRightActions(context)
else
..._desktopRightActions(context)
],
)),
),
).paddingOnly(right: isDesktop ? 12 : 0),
Obx(() => SizedBox(
height: 32,
child: Container(
padding: stateGlobal.isPortrait.isTrue
? EdgeInsets.symmetric(horizontal: 2)
: null,
child: selectionWrap(Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: visibleContextMenuListener(
_createSwitchBar(context))),
if (stateGlobal.isPortrait.isTrue)
..._portraitRightActions(context)
else
..._landscapeRightActions(context)
],
)),
),
).paddingOnly(right: stateGlobal.isPortrait.isTrue ? 0 : 12)),
_createPeersView(),
],
);
@@ -132,11 +142,13 @@ class _PeerTabPageState extends State<PeerTabPage>
Widget _createSwitchBar(BuildContext context) {
final model = Provider.of<PeerTabModel>(context);
return ListView(
var counter = -1;
return ReorderableListView(
buildDefaultDragHandles: false,
onReorder: model.reorder,
scrollDirection: Axis.horizontal,
physics: NeverScrollableScrollPhysics(),
children: model.visibleIndexs.map((t) {
children: model.visibleEnabledOrderedIndexs.map((t) {
final selected = model.currentTab == t;
final color = selected
? MyTheme.tabbar(context).selectedTextColor
@@ -150,48 +162,56 @@ class _PeerTabPageState extends State<PeerTabPage>
border: Border(
bottom: BorderSide(width: 2, color: color!),
));
return Obx(() => InkWell(
child: Container(
decoration: (hover.value
? (selected ? decoBorder : deco)
: (selected ? decoBorder : null)),
child: Tooltip(
counter += 1;
return ReorderableDragStartListener(
key: ValueKey(t),
index: counter,
child: Obx(() => Tooltip(
preferBelow: false,
message: model.tabTooltip(t),
onTriggered: isMobile ? mobileShowTabVisibilityMenu : null,
child: Icon(model.tabIcon(t), color: color),
).paddingSymmetric(horizontal: 4),
).paddingSymmetric(horizontal: 4),
onTap: () async {
await handleTabSelection(t);
await bind.setLocalFlutterOption(
k: 'peer-tab-index', v: t.toString());
},
onHover: (value) => hover.value = value,
));
child: InkWell(
child: Container(
decoration: (hover.value
? (selected ? decoBorder : deco)
: (selected ? decoBorder : null)),
child: Icon(model.tabIcon(t), color: color)
.paddingSymmetric(horizontal: 4),
).paddingSymmetric(horizontal: 4),
onTap: isOptionFixed(kOptionPeerTabIndex)
? null
: () async {
await handleTabSelection(t);
await bind.setLocalFlutterOption(
k: kOptionPeerTabIndex, v: t.toString());
},
onHover: (value) => hover.value = value,
),
)));
}).toList());
}
Widget _createPeersView() {
final model = Provider.of<PeerTabModel>(context);
Widget child;
if (model.visibleIndexs.isEmpty) {
if (model.visibleEnabledOrderedIndexs.isEmpty) {
child = visibleContextMenuListener(Row(
children: [Expanded(child: InkWell())],
));
} else {
if (model.visibleIndexs.contains(model.currentTab)) {
if (model.visibleEnabledOrderedIndexs.contains(model.currentTab)) {
child = entries[model.currentTab].widget;
} else {
debugPrint("should not happen! currentTab not in visibleIndexs");
Future.delayed(Duration.zero, () {
model.setCurrentTab(model.indexs[0]);
model.setCurrentTab(model.visibleEnabledOrderedIndexs[0]);
});
child = entries[0].widget;
}
}
return Expanded(
child: child.marginSymmetric(vertical: isDesktop ? 12.0 : 6.0));
child: child.marginSymmetric(
vertical: (isDesktop || isWebDesktop) ? 12.0 : 6.0));
}
Widget _createRefresh(
@@ -200,22 +220,23 @@ class _PeerTabPageState extends State<PeerTabPage>
final textColor = Theme.of(context).textTheme.titleLarge?.color;
return Offstage(
offstage: model.currentTab != index.index,
child: RefreshWidget(
onPressed: () {
if (gFFI.peerTabModel.currentTab < entries.length) {
entries[gFFI.peerTabModel.currentTab].load();
}
},
spinning: loading,
child: RotatedBox(
quarterTurns: 2,
child: Tooltip(
message: translate('Refresh'),
child: Icon(
Icons.refresh,
size: 18,
color: textColor,
)))),
child: Tooltip(
message: translate('Refresh'),
child: RefreshWidget(
onPressed: () {
if (gFFI.peerTabModel.currentTab < entries.length) {
entries[gFFI.peerTabModel.currentTab].load();
}
},
spinning: loading,
child: RotatedBox(
quarterTurns: 2,
child: Icon(
Icons.refresh,
size: 18,
color: textColor,
))),
),
);
}
@@ -227,6 +248,7 @@ class _PeerTabPageState extends State<PeerTabPage>
final textColor = Theme.of(context).textTheme.titleLarge?.color;
final model = Provider.of<PeerTabModel>(context);
return _hoverAction(
toolTip: translate('Select'),
context: context,
onTap: () {
model.setMultiSelectionMode(true);
@@ -234,34 +256,38 @@ class _PeerTabPageState extends State<PeerTabPage>
Navigator.pop(context);
}
},
child: Tooltip(
message: translate('Select'),
child: SvgPicture.asset(
"assets/checkbox-outline.svg",
width: 18,
height: 18,
colorFilter: svgColor(textColor),
)),
child: SvgPicture.asset(
"assets/checkbox-outline.svg",
width: 18,
height: 18,
colorFilter: svgColor(textColor),
),
);
}
void mobileShowTabVisibilityMenu() {
final model = gFFI.peerTabModel;
final items = List<PopupMenuItem>.empty(growable: true);
for (int i = 0; i < model.tabNames.length; i++) {
for (int i = 0; i < PeerTabModel.maxTabCount; i++) {
if (!model.isEnabled[i]) continue;
items.add(PopupMenuItem(
height: kMinInteractiveDimension * 0.8,
onTap: () => model.setTabVisible(i, !model.isVisible[i]),
onTap: isOptVisiableFixed
? null
: () => model.setTabVisible(i, !model.isVisibleEnabled[i]),
enabled: !isOptVisiableFixed,
child: Row(
children: [
Checkbox(
value: model.isVisible[i],
onChanged: (_) {
model.setTabVisible(i, !model.isVisible[i]);
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
}),
value: model.isVisibleEnabled[i],
onChanged: isOptVisiableFixed
? null
: (_) {
model.setTabVisible(i, !model.isVisibleEnabled[i]);
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
}),
Expanded(child: Text(model.tabTooltip(i))),
],
),
@@ -274,7 +300,7 @@ class _PeerTabPageState extends State<PeerTabPage>
}
Widget visibleContextMenuListener(Widget child) {
if (isMobile) {
if (!(isDesktop || isWebDesktop)) {
return GestureDetector(
onLongPressDown: (e) {
final x = e.globalPosition.dx;
@@ -307,18 +333,21 @@ class _PeerTabPageState extends State<PeerTabPage>
Widget visibleContextMenu(CancelFunc cancelFunc) {
final model = Provider.of<PeerTabModel>(context);
final menu = List<MenuEntrySwitch>.empty(growable: true);
for (int i = 0; i < model.tabNames.length; i++) {
menu.add(MenuEntrySwitch(
final menu = List<MenuEntrySwitchSync>.empty(growable: true);
for (int i = 0; i < model.orders.length; i++) {
int tabIndex = model.orders[i];
if (tabIndex < 0 || tabIndex >= PeerTabModel.maxTabCount) continue;
if (!model.isEnabled[tabIndex]) continue;
menu.add(MenuEntrySwitchSync(
switchType: SwitchType.scheckbox,
text: model.tabTooltip(i),
getter: () async {
return model.isVisible[i];
},
text: model.tabTooltip(tabIndex),
currentValue: model.isVisibleEnabled[tabIndex],
setter: (show) async {
model.setTabVisible(i, show);
cancelFunc();
}));
model.setTabVisible(tabIndex, show);
// Do not hide the current menu (checkbox)
// cancelFunc();
},
enabled: (!isOptVisiableFixed).obs));
}
return mod_menu.PopupMenu(
items: menu
@@ -333,8 +362,7 @@ class _PeerTabPageState extends State<PeerTabPage>
.toList());
}
Widget createMultiSelectionBar() {
final model = Provider.of<PeerTabModel>(context);
Widget createMultiSelectionBar(PeerTabModel model) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -352,7 +380,7 @@ class _PeerTabPageState extends State<PeerTabPage>
Row(
children: [
selectionCount(model.selectedPeers.length),
selectAll(),
selectAll(model),
closeSelection(),
],
)
@@ -367,14 +395,15 @@ class _PeerTabPageState extends State<PeerTabPage>
}
return _hoverAction(
context: context,
toolTip: translate('Delete'),
onTap: () {
onSubmit() async {
final peers = model.selectedPeers;
switch (model.currentTab) {
case 0:
peers.map((p) async {
for (var p in peers) {
await bind.mainRemovePeer(id: p.id);
}).toList();
}
await bind.mainLoadRecentPeers();
break;
case 1:
@@ -386,27 +415,13 @@ class _PeerTabPageState extends State<PeerTabPage>
await bind.mainLoadFavPeers();
break;
case 2:
peers.map((p) async {
for (var p in peers) {
await bind.mainRemoveDiscovered(id: p.id);
}).toList();
}
await bind.mainLoadLanPeers();
break;
case 3:
{
bool hasSynced = false;
if (shouldSyncAb()) {
for (var p in peers) {
if (await bind.mainPeerExists(id: p.id)) {
hasSynced = true;
}
}
}
gFFI.abModel.deletePeers(peers.map((p) => p.id).toList());
final future = gFFI.abModel.pushAb();
if (hasSynced) {
gFFI.abModel.reSyncToast(future);
}
}
await gFFI.abModel.deletePeers(peers.map((p) => p.id).toList());
break;
default:
break;
@@ -415,11 +430,9 @@ class _PeerTabPageState extends State<PeerTabPage>
if (model.currentTab != 3) showToast(translate('Successful'));
}
deletePeerConfirmDialog(onSubmit, translate('Delete'));
deleteConfirmDialog(onSubmit, translate('Delete'));
},
child: Tooltip(
message: translate('Delete'),
child: Icon(Icons.delete, color: Colors.red)));
child: Icon(Icons.delete, color: Colors.red));
}
Widget addSelectionToFav() {
@@ -429,6 +442,7 @@ class _PeerTabPageState extends State<PeerTabPage>
model.currentTab != PeerTabIndex.recent.index, // show based on recent
child: _hoverAction(
context: context,
toolTip: translate('Add to Favorites'),
onTap: () async {
final peers = model.selectedPeers;
final favs = (await bind.mainGetFav()).toList();
@@ -441,38 +455,29 @@ class _PeerTabPageState extends State<PeerTabPage>
model.setMultiSelectionMode(false);
showToast(translate('Successful'));
},
child: Tooltip(
message: translate('Add to Favorites'),
child: Icon(model.icons[PeerTabIndex.fav.index])),
).marginOnly(left: isMobile ? 11 : 6),
child: Icon(PeerTabModel.icons[PeerTabIndex.fav.index]),
).marginOnly(left: !(isDesktop || isWebDesktop) ? 11 : 6),
);
}
Widget addSelectionToAb() {
final model = Provider.of<PeerTabModel>(context);
final addressbooks = gFFI.abModel.addressBooksCanWrite();
if (model.currentTab == PeerTabIndex.ab.index) {
addressbooks.remove(gFFI.abModel.currentName.value);
}
return Offstage(
offstage:
!gFFI.userModel.isLogin || model.currentTab == PeerTabIndex.ab.index,
offstage: !gFFI.userModel.isLogin || addressbooks.isEmpty,
child: _hoverAction(
context: context,
toolTip: translate('Add to address book'),
onTap: () {
if (gFFI.abModel.isFull(true)) {
return;
}
final peers = model.selectedPeers;
gFFI.abModel.addPeers(peers);
final future = gFFI.abModel.pushAb();
final peers = model.selectedPeers.map((e) => Peer.copy(e)).toList();
addPeersToAbDialog(peers);
model.setMultiSelectionMode(false);
Future.delayed(Duration.zero, () async {
await future;
await Future.delayed(Duration(seconds: 2)); // toast
gFFI.abModel.isFull(true);
});
},
child: Tooltip(
message: translate('Add to address book'),
child: Icon(model.icons[PeerTabIndex.ab.index])),
).marginOnly(left: isMobile ? 11 : 6),
child: Icon(PeerTabModel.icons[PeerTabIndex.ab.index]),
).marginOnly(left: !(isDesktop || isWebDesktop) ? 11 : 6),
);
}
@@ -481,22 +486,21 @@ class _PeerTabPageState extends State<PeerTabPage>
return Offstage(
offstage: !gFFI.userModel.isLogin ||
model.currentTab != PeerTabIndex.ab.index ||
gFFI.abModel.tags.isEmpty,
gFFI.abModel.currentAbTags.isEmpty,
child: _hoverAction(
context: context,
toolTip: translate('Edit Tag'),
onTap: () {
editAbTagDialog(List.empty(), (selectedTags) async {
final peers = model.selectedPeers;
gFFI.abModel.changeTagForPeers(
await gFFI.abModel.changeTagForPeers(
peers.map((p) => p.id).toList(), selectedTags);
gFFI.abModel.pushAb();
model.setMultiSelectionMode(false);
showToast(translate('Successful'));
});
},
child: Tooltip(
message: translate('Edit Tag'), child: Icon(Icons.tag)))
.marginOnly(left: isMobile ? 11 : 6),
child: Icon(Icons.tag))
.marginOnly(left: !(isDesktop || isWebDesktop) ? 11 : 6),
);
}
@@ -507,18 +511,17 @@ class _PeerTabPageState extends State<PeerTabPage>
);
}
Widget selectAll() {
final model = Provider.of<PeerTabModel>(context);
Widget selectAll(PeerTabModel model) {
return Offstage(
offstage:
model.selectedPeers.length >= model.currentTabCachedPeers.length,
child: _hoverAction(
context: context,
toolTip: translate('Select All'),
onTap: () {
model.selectAll();
},
child: Tooltip(
message: translate('Select All'), child: Icon(Icons.select_all)),
child: Icon(Icons.select_all),
).marginOnly(left: 6),
);
}
@@ -527,36 +530,37 @@ class _PeerTabPageState extends State<PeerTabPage>
final model = Provider.of<PeerTabModel>(context);
return _hoverAction(
context: context,
toolTip: translate('Close'),
onTap: () {
model.setMultiSelectionMode(false);
},
child:
Tooltip(message: translate('Close'), child: Icon(Icons.clear)))
child: Icon(Icons.clear))
.marginOnly(left: 6);
}
Widget _toggleTags() {
return _hoverAction(
context: context,
toolTip: translate('Toggle Tags'),
hoverableWhenfalse: hideAbTagsPanel,
child: Tooltip(
message: translate('Toggle Tags'),
child: Icon(
Icons.tag_rounded,
size: 18,
)),
child: Icon(
Icons.tag_rounded,
size: 18,
),
onTap: () async {
await bind.mainSetLocalOption(
key: "hideAbTagsPanel", value: hideAbTagsPanel.value ? "" : "Y");
key: kOptionHideAbTagsPanel,
value: hideAbTagsPanel.value ? defaultOptionNo : "Y");
hideAbTagsPanel.value = !hideAbTagsPanel.value;
});
}
List<Widget> _desktopRightActions(BuildContext context) {
List<Widget> _landscapeRightActions(BuildContext context) {
final model = Provider.of<PeerTabModel>(context);
return [
const PeerSearchBar().marginOnly(right: isMobile ? 0 : 13),
_createRefresh(index: PeerTabIndex.ab, loading: gFFI.abModel.abLoading),
const PeerSearchBar().marginOnly(right: 13),
_createRefresh(
index: PeerTabIndex.ab, loading: gFFI.abModel.currentAbLoading),
_createRefresh(
index: PeerTabIndex.group, loading: gFFI.groupModel.groupLoading),
Offstage(
@@ -575,12 +579,12 @@ class _PeerTabPageState extends State<PeerTabPage>
];
}
List<Widget> _mobileRightActions(BuildContext context) {
List<Widget> _portraitRightActions(BuildContext context) {
final model = Provider.of<PeerTabModel>(context);
final screenWidth = MediaQuery.of(context).size.width;
final leftIconSize = Theme.of(context).iconTheme.size ?? 24;
final leftActionsSize =
(leftIconSize + (4 + 4) * 2) * model.visibleIndexs.length;
(leftIconSize + (4 + 4) * 2) * model.visibleEnabledOrderedIndexs.length;
final availableWidth = screenWidth - 10 * 2 - leftActionsSize - 2 * 2;
final searchWidth = 120;
final otherActionWidth = 18 + 10;
@@ -593,14 +597,13 @@ class _PeerTabPageState extends State<PeerTabPage>
(BuildContext context, Future<void> Function() showMenu) {
return _hoverAction(
context: context,
child: Tooltip(
message: translate('More'),
child: SvgPicture.asset(
"assets/chevron_up_chevron_down.svg",
width: 18,
height: 18,
colorFilter: svgColor(textColor),
)),
toolTip: translate('More'),
child: SvgPicture.asset(
"assets/chevron_up_chevron_down.svg",
width: 18,
height: 18,
colorFilter: svgColor(textColor),
),
onTap: showMenu,
);
},
@@ -624,7 +627,8 @@ class _PeerTabPageState extends State<PeerTabPage>
List<Widget> actions = [
const PeerSearchBar(),
if (model.currentTab == PeerTabIndex.ab.index)
_createRefresh(index: PeerTabIndex.ab, loading: gFFI.abModel.abLoading),
_createRefresh(
index: PeerTabIndex.ab, loading: gFFI.abModel.currentAbLoading),
if (model.currentTab == PeerTabIndex.group.index)
_createRefresh(
index: PeerTabIndex.group, loading: gFFI.groupModel.groupLoading),
@@ -674,18 +678,17 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
? _buildSearchBar()
: _hoverAction(
context: context,
toolTip: translate('Search'),
padding: const EdgeInsets.only(right: 2),
onTap: () {
setState(() {
drawer = true;
});
},
child: Tooltip(
message: translate('Search'),
child: Icon(
Icons.search_rounded,
color: Theme.of(context).hintColor,
)));
child: Icon(
Icons.search_rounded,
color: Theme.of(context).hintColor,
));
}
Widget _buildSearchBar() {
@@ -697,13 +700,13 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
baseOffset: 0,
extentOffset: peerSearchTextController.value.text.length);
});
return Container(
width: isMobile ? 120 : 140,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(6),
),
child: Obx(() => Row(
return Obx(() => Container(
width: stateGlobal.isPortrait.isTrue ? 120 : 140,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(6),
),
child: Row(
children: [
Expanded(
child: Row(
@@ -764,8 +767,8 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
),
)
],
)),
);
),
));
}
}
@@ -800,25 +803,38 @@ class _PeerViewDropdownState extends State<PeerViewDropdown> {
child: SizedBox(
height: 36,
child: getRadio<PeerUiType>(
Text(
translate(types.indexOf(e) == 0
Tooltip(
message: translate(types.indexOf(e) == 0
? 'Big tiles'
: types.indexOf(e) == 1
? 'Small tiles'
: 'List'),
style: style),
child: Icon(
e == PeerUiType.grid
? Icons.grid_view_rounded
: e == PeerUiType.list
? Icons.view_list_rounded
: Icons.view_agenda_rounded,
size: 18,
)),
e,
peerCardUiType.value,
dense: true, (PeerUiType? v) async {
if (v != null) {
peerCardUiType.value = v;
setState(() {});
await bind.setLocalFlutterOption(
k: "peer-card-ui-type",
v: peerCardUiType.value.index.toString(),
);
}
}),
dense: true,
isOptionFixed(kOptionPeerCardUiType)
? null
: (PeerUiType? v) async {
if (v != null) {
peerCardUiType.value = v;
setState(() {});
await bind.setLocalFlutterOption(
k: kOptionPeerCardUiType,
v: peerCardUiType.value.index.toString(),
);
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
}
}),
),
))));
}
@@ -826,16 +842,15 @@ class _PeerViewDropdownState extends State<PeerViewDropdown> {
var menuPos = RelativeRect.fromLTRB(0, 0, 0, 0);
return _hoverAction(
context: context,
child: Tooltip(
message: translate('Change view'),
child: Icon(
peerCardUiType.value == PeerUiType.grid
? Icons.grid_view_rounded
: peerCardUiType.value == PeerUiType.tile
? Icons.view_list_rounded
: Icons.view_agenda_rounded,
size: 18,
)),
toolTip: translate('Change view'),
child: Icon(
peerCardUiType.value == PeerUiType.grid
? Icons.grid_view_rounded
: peerCardUiType.value == PeerUiType.list
? Icons.view_list_rounded
: Icons.view_agenda_rounded,
size: 18,
),
onTapDown: (details) {
final x = details.globalPosition.dx;
final y = details.globalPosition.dy;
@@ -858,16 +873,18 @@ class PeerSortDropdown extends StatefulWidget {
}
class _PeerSortDropdownState extends State<PeerSortDropdown> {
@override
void initState() {
_PeerSortDropdownState() {
if (!PeerSortType.values.contains(peerSort.value)) {
peerSort.value = PeerSortType.remoteId;
bind.setLocalFlutterOption(
k: "peer-sorting",
v: peerSort.value,
);
_loadLocalOptions();
}
super.initState();
}
void _loadLocalOptions() {
peerSort.value = PeerSortType.remoteId;
bind.setLocalFlutterOption(
k: kOptionPeerSorting,
v: peerSort.value,
);
}
@override
@@ -893,7 +910,7 @@ class _PeerSortDropdownState extends State<PeerSortDropdown> {
if (v != null) {
peerSort.value = v;
await bind.setLocalFlutterOption(
k: "peer-sorting",
k: kOptionPeerSorting,
v: peerSort.value,
);
}
@@ -905,12 +922,11 @@ class _PeerSortDropdownState extends State<PeerSortDropdown> {
var menuPos = RelativeRect.fromLTRB(0, 0, 0, 0);
return _hoverAction(
context: context,
child: Tooltip(
message: translate('Sort by'),
child: Icon(
Icons.sort_rounded,
size: 18,
)),
toolTip: translate('Sort by'),
child: Icon(
Icons.sort_rounded,
size: 18,
),
onTapDown: (details) {
final x = details.globalPosition.dx;
final y = details.globalPosition.dy;
@@ -992,6 +1008,7 @@ Widget _hoverAction(
{required BuildContext context,
required Widget child,
required Function() onTap,
required String toolTip,
GestureTapDownCallback? onTapDown,
RxBool? hoverableWhenfalse,
EdgeInsetsGeometry padding = const EdgeInsets.all(4.0)}) {
@@ -1000,16 +1017,19 @@ Widget _hoverAction(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(6),
);
return Obx(
() => Container(
margin: EdgeInsets.symmetric(horizontal: 1),
decoration:
(hover.value || hoverableWhenfalse?.value == false) ? deco : null,
child: InkWell(
onHover: (value) => hover.value = value,
onTap: onTap,
onTapDown: onTapDown,
child: Container(padding: padding, child: child))),
return Tooltip(
message: toolTip,
child: Obx(
() => Container(
margin: EdgeInsets.symmetric(horizontal: 1),
decoration:
(hover.value || hoverableWhenfalse?.value == false) ? deco : null,
child: InkWell(
onHover: (value) => hover.value = value,
onTap: onTap,
onTapDown: onTapDown,
child: Container(padding: padding, child: child))),
),
);
}

View File

@@ -4,12 +4,14 @@ import 'dart:collection';
import 'package:dynamic_layouts/dynamic_layouts.dart';
import 'package:flutter/foundation.dart';
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/peer_tab_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:visibility_detector/visibility_detector.dart';
import 'package:window_manager/window_manager.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart';
import '../../common.dart';
import '../../models/peer_model.dart';
@@ -41,14 +43,26 @@ class LoadEvent {
static const String group = 'load_group_peers';
}
class PeersModelName {
static const String recent = 'recent peer';
static const String favorite = 'fav peer';
static const String lan = 'discovered peer';
static const String addressBook = 'address book peer';
static const String group = 'group peer';
}
/// for peer search text, global obs value
final peerSearchText = "".obs;
/// for peer sort, global obs value
final peerSort = bind.getLocalFlutterOption(k: 'peer-sorting').obs;
RxString? _peerSort;
RxString get peerSort {
_peerSort ??= bind.getLocalFlutterOption(k: kOptionPeerSorting).obs;
return _peerSort!;
}
// list for listener
final obslist = [peerSearchText, peerSort].obs;
RxList<RxString> get obslist => [peerSearchText, peerSort].obs;
final peerSearchTextController =
TextEditingController(text: peerSearchText.value);
@@ -70,7 +84,8 @@ class _PeersView extends StatefulWidget {
}
/// State for the peer widget.
class _PeersViewState extends State<_PeersView> with WindowListener {
class _PeersViewState extends State<_PeersView>
with WindowListener, WidgetsBindingObserver {
static const int _maxQueryCount = 3;
final HashMap<String, String> _emptyMessages = HashMap.from({
LoadEvent.recent: 'empty_recent_tip',
@@ -78,24 +93,15 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
LoadEvent.lan: 'empty_lan_tip',
LoadEvent.addressBook: 'empty_address_book_tip',
});
final space = isDesktop ? 12.0 : 8.0;
final space = (isDesktop || isWebDesktop) ? 12.0 : 8.0;
final _curPeers = <String>{};
var _lastChangeTime = DateTime.now();
var _lastQueryPeers = <String>{};
var _lastQueryTime = DateTime.now().add(const Duration(seconds: 30));
var _lastQueryTime = DateTime.now();
var _lastWindowRestoreTime = DateTime.now();
var _queryCount = 0;
var _exit = false;
late final mobileWidth = () {
const minWidth = 320.0;
final windowWidth = MediaQuery.of(context).size.width;
var width = windowWidth - 2 * space;
if (windowWidth > minWidth + 2 * space) {
final n = (windowWidth / (minWidth + 2 * space)).floor();
width = windowWidth / n - 2 * space;
}
return width;
}();
bool _isActive = true;
final _scrollController = ScrollController();
@@ -106,12 +112,14 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
@override
void initState() {
windowManager.addListener(this);
WidgetsBinding.instance.addObserver(this);
super.initState();
}
@override
void dispose() {
windowManager.removeListener(this);
WidgetsBinding.instance.removeObserver(this);
_exit = true;
super.dispose();
}
@@ -119,17 +127,61 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
@override
void onWindowFocus() {
_queryCount = 0;
_isActive = true;
}
@override
void onWindowBlur() {
// We need this comparison because window restore (on Windows) also triggers `onWindowBlur()`.
// Maybe it's a bug of the window manager, but the source code seems to be correct.
//
// Although `onWindowRestore()` is called after `onWindowBlur()` in my test,
// we need the following comparison to ensure that `_isActive` is true in the end.
if (isWindows &&
DateTime.now().difference(_lastWindowRestoreTime) <
const Duration(milliseconds: 300)) {
return;
}
_queryCount = _maxQueryCount;
_isActive = false;
}
@override
void onWindowRestore() {
// Window restore (on MacOS and Linux) also triggers `onWindowFocus()`.
// But on Windows, it triggers `onWindowBlur()`, mybe it's a bug of the window manager.
if (!isWindows) return;
_queryCount = 0;
_isActive = true;
_lastWindowRestoreTime = DateTime.now();
}
@override
void onWindowMinimize() {
_queryCount = _maxQueryCount;
// Window minimize also triggers `onWindowBlur()`.
}
// This function is required for mobile.
// `onWindowFocus` works fine for desktop.
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (isDesktop || isWebDesktop) return;
if (state == AppLifecycleState.resumed) {
_isActive = true;
_queryCount = 0;
} else if (state == AppLifecycleState.inactive) {
_isActive = false;
}
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Peers>(
create: (context) => widget.peers,
// We should avoid too many rebuilds. MacOS(m1, 14.6.1) on Flutter 3.19.6.
// Continious rebuilds of `ChangeNotifierProvider` will cause memory leak.
// Simple demo can reproduce this issue.
return ChangeNotifierProvider<Peers>.value(
value: widget.peers,
child: Consumer<Peers>(builder: (context, peers, child) {
if (peers.peers.isEmpty) {
gFFI.peerTabModel.setCurrentTabCachedPeers([]);
@@ -183,61 +235,68 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
var peers = snapshot.data!;
if (peers.length > 1000) peers = peers.sublist(0, 1000);
gFFI.peerTabModel.setCurrentTabCachedPeers(peers);
buildOnePeer(Peer peer) {
buildOnePeer(Peer peer, bool isPortrait) {
final visibilityChild = VisibilityDetector(
key: ValueKey(_cardId(peer.id)),
onVisibilityChanged: onVisibilityChanged,
child: widget.peerCardBuilder(peer),
);
final windowWidth = MediaQuery.of(context).size.width;
// `Provider.of<PeerTabModel>(context)` will causes infinete loop.
// Because `gFFI.peerTabModel.setCurrentTabCachedPeers(peers)` will trigger `notifyListeners()`.
//
// No need to listen the currentTab change event.
// Because the currentTab change event will trigger the peers change event,
// and the peers change event will trigger _buildPeersView().
final currentTab = Provider.of<PeerTabModel>(context, listen: false).currentTab;
final hideAbTagsPanel = bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty;
return isDesktop
? Obx(
() => SizedBox(
width: peerCardUiType.value != PeerUiType.list
? 220
: currentTab == PeerTabIndex.group.index || (currentTab == PeerTabIndex.ab.index && !hideAbTagsPanel)
? windowWidth - 390 :
windowWidth - 227,
height:
peerCardUiType.value == PeerUiType.grid ? 140 : peerCardUiType.value != PeerUiType.list ? 42 : 45,
child: visibilityChild,
),
)
: SizedBox(width: mobileWidth, child: visibilityChild);
return !isPortrait
? Obx(() => peerCardUiType.value == PeerUiType.list
? Container(height: 45, child: visibilityChild)
: peerCardUiType.value == PeerUiType.grid
? SizedBox(
width: 220, height: 140, child: visibilityChild)
: SizedBox(
width: 220, height: 42, child: visibilityChild))
: Container(child: visibilityChild);
}
final Widget child;
if (isMobile) {
child = DynamicGridView.builder(
gridDelegate: SliverGridDelegateWithWrapping(
mainAxisSpacing: space / 2, crossAxisSpacing: space),
itemCount: peers.length,
itemBuilder: (BuildContext context, int index) {
return buildOnePeer(peers[index]);
},
);
} else {
child = DesktopScrollWrapper(
scrollController: _scrollController,
child: DynamicGridView.builder(
controller: _scrollController,
physics: DraggableNeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithWrapping(
mainAxisSpacing: space / 2, crossAxisSpacing: space),
// We should avoid too many rebuilds. Win10(Some machines) on Flutter 3.19.6.
// Continious rebuilds of `ListView.builder` will cause memory leak.
// Simple demo can reproduce this issue.
final Widget child = Obx(() => stateGlobal.isPortrait.isTrue
? ListView.builder(
itemCount: peers.length,
itemBuilder: (BuildContext context, int index) {
return buildOnePeer(peers[index]);
}),
);
}
return buildOnePeer(peers[index], true).marginOnly(
top: index == 0 ? 0 : space / 2, bottom: space / 2);
},
)
: peerCardUiType.value == PeerUiType.list
? DesktopScrollWrapper(
scrollController: _scrollController,
child: ListView.builder(
controller: _scrollController,
physics: DraggableNeverScrollableScrollPhysics(),
itemCount: peers.length,
itemBuilder: (BuildContext context, int index) {
return buildOnePeer(peers[index], false)
.marginOnly(
right: space,
top: index == 0 ? 0 : space / 2,
bottom: space / 2);
}),
)
: DesktopScrollWrapper(
scrollController: _scrollController,
child: DynamicGridView.builder(
controller: _scrollController,
physics: DraggableNeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithWrapping(
mainAxisSpacing: space / 2,
crossAxisSpacing: space),
itemCount: peers.length,
itemBuilder: (BuildContext context, int index) {
return buildOnePeer(peers[index], false);
}),
));
if (updateEvent == UpdateEvent.load) {
_curPeers.clear();
@@ -258,10 +317,14 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
return body;
}
final _queryInterval = const Duration(seconds: 20);
var _queryInterval = const Duration(seconds: 20);
void _startCheckOnlines() {
() async {
final p = await bind.mainIsUsingPublicServer();
if (!p) {
_queryInterval = const Duration(seconds: 6);
}
while (!_exit) {
final now = DateTime.now();
if (!setEquals(_curPeers, _lastQueryPeers)) {
@@ -269,7 +332,12 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
_queryOnlines(false);
}
} else {
if (_queryCount < _maxQueryCount) {
final skipIfIsWeb =
isWeb && !(stateGlobal.isWebVisible && stateGlobal.isInMainPage);
final skipIfMobile =
(isAndroid || isIOS) && !stateGlobal.isInMainPage;
final skipIfNotActive = skipIfIsWeb || skipIfMobile || !_isActive;
if (!skipIfNotActive && (_queryCount < _maxQueryCount || !p)) {
if (now.difference(_lastQueryTime) >= _queryInterval) {
if (_curPeers.isNotEmpty) {
bind.queryOnlines(ids: _curPeers.toList(growable: false));
@@ -287,14 +355,14 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
_queryOnlines(bool isLoadEvent) {
if (_curPeers.isNotEmpty) {
bind.queryOnlines(ids: _curPeers.toList(growable: false));
_lastQueryPeers = {..._curPeers};
if (isLoadEvent) {
_lastChangeTime = DateTime.now();
} else {
_lastQueryTime = DateTime.now().subtract(_queryInterval);
}
_queryCount = 0;
}
_lastQueryPeers = {..._curPeers};
if (isLoadEvent) {
_lastChangeTime = DateTime.now();
} else {
_lastQueryTime = DateTime.now().subtract(_queryInterval);
}
}
Future<List<Peer>>? matchPeers(
@@ -307,7 +375,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
if (!PeerSortType.values.contains(sortedBy)) {
sortedBy = PeerSortType.remoteId;
bind.setLocalFlutterOption(
k: "peer-sorting",
k: kOptionPeerSorting,
v: sortedBy,
);
}
@@ -350,27 +418,39 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
}
abstract class BasePeersView extends StatelessWidget {
final String name;
final String loadEvent;
final PeerTabIndex peerTabIndex;
final PeerFilter? peerFilter;
final PeerCardBuilder peerCardBuilder;
final RxList<Peer>? initPeers;
const BasePeersView({
Key? key,
required this.name,
required this.loadEvent,
required this.peerTabIndex,
this.peerFilter,
required this.peerCardBuilder,
required this.initPeers,
}) : super(key: key);
@override
Widget build(BuildContext context) {
Peers peers;
switch (peerTabIndex) {
case PeerTabIndex.recent:
peers = gFFI.recentPeersModel;
break;
case PeerTabIndex.fav:
peers = gFFI.favoritePeersModel;
break;
case PeerTabIndex.lan:
peers = gFFI.lanPeersModel;
break;
case PeerTabIndex.ab:
peers = gFFI.abModel.peersModel;
break;
case PeerTabIndex.group:
peers = gFFI.groupModel.peersModel;
break;
}
return _PeersView(
peers: Peers(name: name, loadEvent: loadEvent, initPeers: initPeers),
peerFilter: peerFilter,
peerCardBuilder: peerCardBuilder);
peers: peers, peerFilter: peerFilter, peerCardBuilder: peerCardBuilder);
}
}
@@ -379,13 +459,11 @@ class RecentPeersView extends BasePeersView {
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
: super(
key: key,
name: 'recent peer',
loadEvent: LoadEvent.recent,
peerTabIndex: PeerTabIndex.recent,
peerCardBuilder: (Peer peer) => RecentPeerCard(
peer: peer,
menuPadding: menuPadding,
),
initPeers: null,
);
@override
@@ -401,13 +479,11 @@ class FavoritePeersView extends BasePeersView {
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
: super(
key: key,
name: 'favorite peer',
loadEvent: LoadEvent.favorite,
peerTabIndex: PeerTabIndex.fav,
peerCardBuilder: (Peer peer) => FavoritePeerCard(
peer: peer,
menuPadding: menuPadding,
),
initPeers: null,
);
@override
@@ -423,13 +499,11 @@ class DiscoveredPeersView extends BasePeersView {
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
: super(
key: key,
name: 'discovered peer',
loadEvent: LoadEvent.lan,
peerTabIndex: PeerTabIndex.lan,
peerCardBuilder: (Peer peer) => DiscoveredPeerCard(
peer: peer,
menuPadding: menuPadding,
),
initPeers: null,
);
@override
@@ -442,21 +516,16 @@ class DiscoveredPeersView extends BasePeersView {
class AddressBookPeersView extends BasePeersView {
AddressBookPeersView(
{Key? key,
EdgeInsets? menuPadding,
ScrollController? scrollController,
required RxList<Peer> initPeers})
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
: super(
key: key,
name: 'address book peer',
loadEvent: LoadEvent.addressBook,
peerTabIndex: PeerTabIndex.ab,
peerFilter: (Peer peer) =>
_hitTag(gFFI.abModel.selectedTags, peer.tags),
peerCardBuilder: (Peer peer) => AddressBookPeerCard(
peer: peer,
menuPadding: menuPadding,
),
initPeers: initPeers,
);
static bool _hitTag(List<dynamic> selectedTags, List<dynamic> idents) {
@@ -483,20 +552,15 @@ class AddressBookPeersView extends BasePeersView {
class MyGroupPeerView extends BasePeersView {
MyGroupPeerView(
{Key? key,
EdgeInsets? menuPadding,
ScrollController? scrollController,
required RxList<Peer> initPeers})
{Key? key, EdgeInsets? menuPadding, ScrollController? scrollController})
: super(
key: key,
name: 'group peer',
loadEvent: LoadEvent.group,
peerTabIndex: PeerTabIndex.group,
peerFilter: filter,
peerCardBuilder: (Peer peer) => MyGroupPeerCard(
peer: peer,
menuPadding: menuPadding,
),
initPeers: initPeers,
);
static bool filter(Peer peer) {

View File

@@ -27,6 +27,10 @@ class RawKeyFocusScope extends StatelessWidget {
@override
Widget build(BuildContext context) {
// https://github.com/flutter/flutter/issues/154053
final useRawKeyEvents = isLinux && !isWeb;
// FIXME: On Windows, `AltGr` will generate `Alt` and `Control` key events,
// while `Alt` and `Control` are seperated key events for en-US input method.
return FocusScope(
autofocus: true,
child: Focus(
@@ -34,8 +38,14 @@ class RawKeyFocusScope extends StatelessWidget {
canRequestFocus: true,
focusNode: focusNode,
onFocusChange: onFocusChange,
onKey: (FocusNode data, RawKeyEvent e) =>
inputModel.handleRawKeyEvent(e),
onKey: useRawKeyEvents
? (FocusNode data, RawKeyEvent event) =>
inputModel.handleRawKeyEvent(event)
: null,
onKeyEvent: useRawKeyEvents
? null
: (FocusNode node, KeyEvent event) =>
inputModel.handleKeyEvent(event),
child: child));
}
}
@@ -69,6 +79,8 @@ class RawTouchGestureDetectorRegion extends StatefulWidget {
class _RawTouchGestureDetectorRegionState
extends State<RawTouchGestureDetectorRegion> {
Offset _cacheLongPressPosition = Offset(0, 0);
// Timestamp of the last long press event.
int _cacheLongPressPositionTs = 0;
double _mouseScrollIntegral = 0; // mouse scroll speed controller
double _scale = 1;
@@ -77,7 +89,7 @@ class _RawTouchGestureDetectorRegionState
FFI get ffi => widget.ffi;
FfiModel get ffiModel => widget.ffiModel;
InputModel get inputModel => widget.inputModel;
bool get handleTouch => isDesktop || ffiModel.touchMode;
bool get handleTouch => (isDesktop || isWebDesktop) || ffiModel.touchMode;
SessionID get sessionId => ffi.sessionId;
@override
@@ -95,8 +107,9 @@ class _RawTouchGestureDetectorRegionState
}
if (handleTouch) {
// Desktop or mobile "Touch mode"
ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
inputModel.tapDown(MouseButtons.left);
if (ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy)) {
inputModel.tapDown(MouseButtons.left);
}
}
}
@@ -105,8 +118,9 @@ class _RawTouchGestureDetectorRegionState
return;
}
if (handleTouch) {
ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
inputModel.tapUp(MouseButtons.left);
if (ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy)) {
inputModel.tapUp(MouseButtons.left);
}
}
}
@@ -134,6 +148,9 @@ class _RawTouchGestureDetectorRegionState
if (lastDeviceKind != PointerDeviceKind.touch) {
return;
}
if (ffiModel.touchMode && ffi.cursorModel.lastIsBlocked) {
return;
}
inputModel.tap(MouseButtons.left);
inputModel.tap(MouseButtons.left);
}
@@ -146,6 +163,7 @@ class _RawTouchGestureDetectorRegionState
if (handleTouch) {
ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
_cacheLongPressPosition = d.localPosition;
_cacheLongPressPositionTs = DateTime.now().millisecondsSinceEpoch;
}
}
@@ -183,7 +201,7 @@ class _RawTouchGestureDetectorRegionState
if (lastDeviceKind != PointerDeviceKind.touch) {
return;
}
if (isDesktop || !ffiModel.touchMode) {
if ((isDesktop || isWebDesktop) || !ffiModel.touchMode) {
inputModel.tap(MouseButtons.right);
}
}
@@ -203,7 +221,7 @@ class _RawTouchGestureDetectorRegionState
return;
}
if (!handleTouch) {
ffi.cursorModel.updatePan(d.delta.dx, d.delta.dy, handleTouch);
ffi.cursorModel.updatePan(d.delta, d.localPosition, handleTouch);
}
}
@@ -222,6 +240,22 @@ class _RawTouchGestureDetectorRegionState
return;
}
if (handleTouch) {
if (ffi.cursorModel.shouldBlock(d.localPosition.dx, d.localPosition.dy)) {
return;
}
if (isDesktop || isWebDesktop) {
ffi.cursorModel.trySetRemoteWindowCoords();
}
// Workaround for the issue that the first pan event is sent a long time after the start event.
// If the time interval between the start event and the first pan event is less than 500ms,
// we consider to use the long press position as the start position.
//
// TODO: We should find a better way to send the first pan event as soon as possible.
if (DateTime.now().millisecondsSinceEpoch - _cacheLongPressPositionTs <
500) {
ffi.cursorModel
.move(_cacheLongPressPosition.dx, _cacheLongPressPosition.dy);
}
inputModel.sendMouse('down', MouseButtons.left);
ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
} else {
@@ -241,13 +275,19 @@ class _RawTouchGestureDetectorRegionState
if (lastDeviceKind != PointerDeviceKind.touch) {
return;
}
ffi.cursorModel.updatePan(d.delta.dx, d.delta.dy, handleTouch);
if (ffi.cursorModel.shouldBlock(d.localPosition.dx, d.localPosition.dy)) {
return;
}
ffi.cursorModel.updatePan(d.delta, d.localPosition, handleTouch);
}
onOneFingerPanEnd(DragEndDetails d) {
if (lastDeviceKind != PointerDeviceKind.touch) {
return;
}
if (isDesktop || isWebDesktop) {
ffi.cursorModel.clearRemoteWindowCoords();
}
inputModel.sendMouse('up', MouseButtons.left);
}
@@ -262,7 +302,7 @@ class _RawTouchGestureDetectorRegionState
if (lastDeviceKind != PointerDeviceKind.touch) {
return;
}
if (isDesktop) {
if ((isDesktop || isWebDesktop)) {
final scale = ((d.scale - _scale) * 1000).toInt();
_scale = d.scale;
@@ -275,7 +315,7 @@ class _RawTouchGestureDetectorRegionState
}
} else {
// mobile
ffi.canvasModel.updateScale(d.scale / _scale);
ffi.canvasModel.updateScale(d.scale / _scale, d.focalPoint);
_scale = d.scale;
ffi.canvasModel.panX(d.focalPointDelta.dx);
ffi.canvasModel.panY(d.focalPointDelta.dy);
@@ -286,7 +326,7 @@ class _RawTouchGestureDetectorRegionState
if (lastDeviceKind != PointerDeviceKind.touch) {
return;
}
if (isDesktop) {
if ((isDesktop || isWebDesktop)) {
bind.sessionSendPointer(
sessionId: sessionId,
msg: json.encode(
@@ -409,7 +449,9 @@ class RawPointerMouseRegion extends StatelessWidget {
onPointerPanZoomUpdate: inputModel.onPointerPanZoomUpdate,
onPointerPanZoomEnd: inputModel.onPointerPanZoomEnd,
child: MouseRegion(
cursor: cursor ?? MouseCursor.defer,
cursor: inputModel.isViewOnly
? MouseCursor.defer
: (cursor ?? MouseCursor.defer),
onEnter: onEnter,
onExit: onExit,
child: child,

View File

@@ -3,15 +3,14 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/desktop_render_texture.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart';
customImageQualityWidget(
{required double initQuality,
required double initFps,
required Function(double) setQuality,
required Function(double) setFps,
required Function(double)? setQuality,
required Function(double)? setFps,
required bool showFps,
required bool showMoreQuality}) {
if (initQuality < kMinQuality ||
@@ -27,16 +26,12 @@ customImageQualityWidget(
final RxBool moreQualityChecked = RxBool(qualityValue.value > kMaxQuality);
final debouncerQuality = Debouncer<double>(
Duration(milliseconds: 1000),
onChanged: (double v) {
setQuality(v);
},
onChanged: setQuality,
initialValue: qualityValue.value,
);
final debouncerFps = Debouncer<double>(
Duration(milliseconds: 1000),
onChanged: (double v) {
setFps(v);
},
onChanged: setFps,
initialValue: fpsValue.value,
);
@@ -62,10 +57,12 @@ customImageQualityWidget(
divisions: moreQualityChecked.value
? ((kMaxMoreQuality - kMinQuality) / 10).round()
: ((kMaxQuality - kMinQuality) / 5).round(),
onChanged: (double value) async {
qualityValue.value = value;
debouncerQuality.value = value;
},
onChanged: setQuality == null
? null
: (double value) async {
qualityValue.value = value;
debouncerQuality.value = value;
},
),
),
Expanded(
@@ -124,10 +121,12 @@ customImageQualityWidget(
min: kMinFps,
max: kMaxFps,
divisions: ((kMaxFps - kMinFps) / 5).round(),
onChanged: (double value) async {
fpsValue.value = value;
debouncerFps.value = value;
},
onChanged: setFps == null
? null
: (double value) async {
fpsValue.value = value;
debouncerFps.value = value;
},
),
),
Expanded(
@@ -152,21 +151,29 @@ customImageQualitySetting() {
final qualityKey = 'custom_image_quality';
final fpsKey = 'custom-fps';
var initQuality =
final initQuality =
(double.tryParse(bind.mainGetUserDefaultOption(key: qualityKey)) ??
kDefaultQuality);
var initFps = (double.tryParse(bind.mainGetUserDefaultOption(key: fpsKey)) ??
kDefaultFps);
final isQuanlityFixed = isOptionFixed(qualityKey);
final initFps =
(double.tryParse(bind.mainGetUserDefaultOption(key: fpsKey)) ??
kDefaultFps);
final isFpsFixed = isOptionFixed(fpsKey);
return customImageQualityWidget(
initQuality: initQuality,
initFps: initFps,
setQuality: (v) {
bind.mainSetUserDefaultOption(key: qualityKey, value: v.toString());
},
setFps: (v) {
bind.mainSetUserDefaultOption(key: fpsKey, value: v.toString());
},
setQuality: isQuanlityFixed
? null
: (v) {
bind.mainSetUserDefaultOption(
key: qualityKey, value: v.toString());
},
setFps: isFpsFixed
? null
: (v) {
bind.mainSetUserDefaultOption(key: fpsKey, value: v.toString());
},
showFps: true,
showMoreQuality: true);
}
@@ -208,27 +215,31 @@ List<Widget> ServerConfigImportExportWidgets(
List<(String, String)> otherDefaultSettings() {
List<(String, String)> v = [
('View Mode', 'view_only'),
if (isDesktop) ('show_monitors_tip', kKeyShowMonitorsToolbar),
if (isDesktop) ('Collapse toolbar', 'collapse_toolbar'),
('Show remote cursor', 'show_remote_cursor'),
if (isDesktop) ('Zoom cursor', 'zoom-cursor'),
('Show quality monitor', 'show_quality_monitor'),
('Mute', 'disable_audio'),
if (isDesktop) ('Enable file copy and paste', 'enable_file_transfer'),
('Disable clipboard', 'disable_clipboard'),
('Lock after session end', 'lock_after_session_end'),
('Privacy mode', 'privacy_mode'),
if (isMobile) ('Touch mode', 'touch-mode'),
('True color (4:4:4)', 'i444'),
('Reverse mouse wheel', 'reverse_mouse_wheel'),
('swap-left-right-mouse', 'swap-left-right-mouse'),
if (isDesktop && useTextureRender)
('View Mode', kOptionViewOnly),
if ((isDesktop || isWebDesktop))
('show_monitors_tip', kKeyShowMonitorsToolbar),
if ((isDesktop || isWebDesktop))
('Collapse toolbar', kOptionCollapseToolbar),
('Show remote cursor', kOptionShowRemoteCursor),
('Follow remote cursor', kOptionFollowRemoteCursor),
('Follow remote window focus', kOptionFollowRemoteWindow),
if ((isDesktop || isWebDesktop)) ('Zoom cursor', kOptionZoomCursor),
('Show quality monitor', kOptionShowQualityMonitor),
('Mute', kOptionDisableAudio),
if (isDesktop) ('Enable file copy and paste', kOptionEnableFileCopyPaste),
('Disable clipboard', kOptionDisableClipboard),
('Lock after session end', kOptionLockAfterSessionEnd),
('Privacy mode', kOptionPrivacyMode),
if (isMobile) ('Touch mode', kOptionTouchMode),
('True color (4:4:4)', kOptionI444),
('Reverse mouse wheel', kKeyReverseMouseWheel),
('swap-left-right-mouse', kOptionSwapLeftRightMouse),
if (isDesktop)
(
'Show displays as individual windows',
kKeyShowDisplaysAsIndividualWindows
),
if (isDesktop && useTextureRender)
if (isDesktop)
(
'Use all my displays for the remote session',
kKeyUseAllMyDisplaysForTheRemoteSession

View File

@@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -7,9 +6,9 @@ import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/common/shared_state.dart';
import 'package:flutter_hbb/common/widgets/dialog.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
import 'package:flutter_hbb/models/model.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/desktop_render_texture.dart';
import 'package:get/get.dart';
bool isEditOsPassword = false;
@@ -24,6 +23,20 @@ class TTextMenu {
required this.onPressed,
this.trailingIcon,
this.divider = false});
Widget getChild() {
if (trailingIcon != null) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
child,
trailingIcon!,
],
);
} else {
return child;
}
}
}
class TRadioMenu<T> {
@@ -95,7 +108,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
Text(translate(pi.isHeadless ? 'OS Account' : 'OS Password')),
]),
trailingIcon: Transform.scale(
scale: isDesktop ? 0.8 : 1,
scale: (isDesktop || isWebDesktop) ? 0.8 : 1,
child: IconButton(
onPressed: () {
if (isMobile && Navigator.canPop(context)) {
@@ -117,9 +130,9 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
);
}
// paste
if (isMobile && perms['keyboard'] != false && perms['clipboard'] != false) {
if (pi.platform != kPeerPlatformAndroid && perms['keyboard'] != false) {
v.add(TTextMenu(
child: Text(translate('Paste')),
child: Text(translate('Send clipboard keystrokes')),
onPressed: () async {
ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
if (data != null && data.text != null) {
@@ -134,12 +147,23 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
child: Text(translate('Reset canvas')),
onPressed: () => ffi.cursorModel.reset()));
}
connectWithToken(
{required bool isFileTransfer, required bool isTcpTunneling}) {
final connToken = bind.sessionGetConnToken(sessionId: ffi.sessionId);
connect(context, id,
isFileTransfer: isFileTransfer,
isTcpTunneling: isTcpTunneling,
connToken: connToken);
}
// transferFile
if (isDesktop) {
v.add(
TTextMenu(
child: Text(translate('Transfer file')),
onPressed: () => connect(context, id, isFileTransfer: true)),
onPressed: () =>
connectWithToken(isFileTransfer: true, isTcpTunneling: false)),
);
}
// tcpTunneling
@@ -147,7 +171,8 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
v.add(
TTextMenu(
child: Text(translate('TCP tunneling')),
onPressed: () => connect(context, id, isTcpTunneling: true)),
onPressed: () =>
connectWithToken(isFileTransfer: false, isTcpTunneling: true)),
);
}
// note
@@ -161,7 +186,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
);
}
// divider
if (isDesktop) {
if (isDesktop || isWebDesktop) {
v.add(TTextMenu(child: Offstage(), onPressed: () {}, divider: true));
}
// ctrlAltDel
@@ -170,7 +195,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
(pi.platform == kPeerPlatformLinux || pi.sasEnabled)) {
v.add(
TTextMenu(
child: Text('${translate("Insert")} Ctrl + Alt + Del'),
child: Text('${translate("Insert Ctrl + Alt + Del")}'),
onPressed: () => bind.sessionCtrlAltDel(sessionId: sessionId)),
);
}
@@ -230,7 +255,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
));
}
// record
if (!isDesktop &&
if (!(isDesktop || isWeb) &&
(ffi.recordingModel.start || (perms["recording"] != false))) {
v.add(TTextMenu(
child: Row(
@@ -251,7 +276,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
onPressed: () => ffi.recordingModel.toggle()));
}
// fingerprint
if (!isDesktop) {
if (!(isDesktop || isWebDesktop)) {
v.add(TTextMenu(
child: Text(translate('Copy Fingerprint')),
onPressed: () => onCopyFingerprint(FingerprintState.find(id).value),
@@ -328,7 +353,7 @@ Future<List<TRadioMenu<String>>> toolbarCodec(
final alternativeCodecs =
await bind.sessionAlternativeCodecs(sessionId: sessionId);
final groupValue = await bind.sessionGetOption(
sessionId: sessionId, arg: 'codec-preference') ??
sessionId: sessionId, arg: kOptionCodecPreference) ??
'';
final List<bool> codecs = [];
try {
@@ -350,20 +375,25 @@ Future<List<TRadioMenu<String>>> toolbarCodec(
onChanged(String? value) async {
if (value == null) return;
await bind.sessionPeerOption(
sessionId: sessionId, name: 'codec-preference', value: value);
sessionId: sessionId, name: kOptionCodecPreference, value: value);
bind.sessionChangePreferCodec(sessionId: sessionId);
}
TRadioMenu<String> radio(String label, String value, bool enabled) {
return TRadioMenu<String>(
child: Text(translate(label)),
child: Text(label),
value: value,
groupValue: groupValue,
onChanged: enabled ? onChanged : null);
}
var autoLabel = translate('Auto');
if (groupValue == 'auto' &&
ffi.qualityMonitorModel.data.codecFormat != null) {
autoLabel = '$autoLabel (${ffi.qualityMonitorModel.data.codecFormat})';
}
return [
radio('Auto', 'auto', true),
radio(autoLabel, 'auto', true),
if (codecs[0]) radio('VP8', 'vp8', codecs[0]),
radio('VP9', 'vp9', true),
if (codecs[1]) radio('AV1', 'av1', codecs[1]),
@@ -372,12 +402,11 @@ Future<List<TRadioMenu<String>>> toolbarCodec(
];
}
Future<List<TToggleMenu>> toolbarDisplayToggle(
Future<List<TToggleMenu>> toolbarCursor(
BuildContext context, String id, FFI ffi) async {
List<TToggleMenu> v = [];
final ffiModel = ffi.ffiModel;
final pi = ffiModel.pi;
final perms = ffiModel.permissions;
final sessionId = ffi.sessionId;
// show remote cursor
@@ -385,12 +414,17 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
!ffi.canvasModel.cursorEmbedded &&
!pi.isWayland) {
final state = ShowRemoteCursorState.find(id);
final lockState = ShowRemoteCursorLockState.find(id);
final enabled = !ffiModel.viewOnly;
final option = 'show-remote-cursor';
if (pi.currentDisplay == kAllDisplayValue ||
bind.sessionIsMultiUiSession(sessionId: sessionId)) {
lockState.value = false;
}
v.add(TToggleMenu(
child: Text(translate('Show remote cursor')),
value: state.value,
onChanged: enabled
onChanged: enabled && !lockState.value
? (value) async {
if (value == null) return;
await bind.sessionToggleOption(
@@ -400,6 +434,67 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
}
: null));
}
// follow remote cursor
if (pi.platform != kPeerPlatformAndroid &&
!ffi.canvasModel.cursorEmbedded &&
!pi.isWayland &&
versionCmp(pi.version, "1.2.4") >= 0 &&
pi.displays.length > 1 &&
pi.currentDisplay != kAllDisplayValue &&
!bind.sessionIsMultiUiSession(sessionId: sessionId)) {
final option = 'follow-remote-cursor';
final value =
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
final showCursorOption = 'show-remote-cursor';
final showCursorState = ShowRemoteCursorState.find(id);
final showCursorLockState = ShowRemoteCursorLockState.find(id);
final showCursorEnabled = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: showCursorOption);
showCursorLockState.value = value;
if (value && !showCursorEnabled) {
await bind.sessionToggleOption(
sessionId: sessionId, value: showCursorOption);
showCursorState.value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: showCursorOption);
}
v.add(TToggleMenu(
child: Text(translate('Follow remote cursor')),
value: value,
onChanged: (value) async {
if (value == null) return;
await bind.sessionToggleOption(sessionId: sessionId, value: option);
value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: option);
showCursorLockState.value = value;
if (!showCursorEnabled) {
await bind.sessionToggleOption(
sessionId: sessionId, value: showCursorOption);
showCursorState.value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: showCursorOption);
}
}));
}
// follow remote window focus
if (pi.platform != kPeerPlatformAndroid &&
!ffi.canvasModel.cursorEmbedded &&
!pi.isWayland &&
versionCmp(pi.version, "1.2.4") >= 0 &&
pi.displays.length > 1 &&
pi.currentDisplay != kAllDisplayValue &&
!bind.sessionIsMultiUiSession(sessionId: sessionId)) {
final option = 'follow-remote-window';
final value =
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
v.add(TToggleMenu(
child: Text(translate('Follow remote window focus')),
value: value,
onChanged: (value) async {
if (value == null) return;
await bind.sessionToggleOption(sessionId: sessionId, value: option);
value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: option);
}));
}
// zoom cursor
final viewStyle = await bind.sessionGetViewStyle(sessionId: sessionId) ?? '';
if (!isMobile &&
@@ -418,6 +513,17 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
},
));
}
return v;
}
Future<List<TToggleMenu>> toolbarDisplayToggle(
BuildContext context, String id, FFI ffi) async {
List<TToggleMenu> v = [];
final ffiModel = ffi.ffiModel;
final pi = ffiModel.pi;
final perms = ffiModel.permissions;
final sessionId = ffi.sessionId;
// show quality monitor
final option = 'show-quality-monitor';
v.add(TToggleMenu(
@@ -442,20 +548,27 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
child: Text(translate('Mute'))));
}
// file copy and paste
// If the version is less than 1.2.4, file copy and paste is supported on Windows only.
final isSupportIfPeer_1_2_3 = versionCmp(pi.version, '1.2.4') < 0 &&
isWindows &&
pi.platform == kPeerPlatformWindows;
// If the version is 1.2.4 or later, file copy and paste is supported when kPlatformAdditionsHasFileClipboard is set.
final isSupportIfPeer_1_2_4 = versionCmp(pi.version, '1.2.4') >= 0 &&
bind.mainHasFileClipboard() &&
pi.platformAdditions.containsKey(kPlatformAdditionsHasFileClipboard);
if (ffiModel.keyboard &&
perms['file'] != false &&
bind.mainHasFileClipboard() &&
pi.platformAdditions.containsKey(kPlatformAdditionsHasFileClipboard)) {
(isSupportIfPeer_1_2_3 || isSupportIfPeer_1_2_4)) {
final enabled = !ffiModel.viewOnly;
final option = 'enable-file-transfer';
final value =
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
final value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: kOptionEnableFileCopyPaste);
v.add(TToggleMenu(
value: value,
onChanged: enabled
? (value) {
if (value == null) return;
bind.sessionToggleOption(sessionId: sessionId, value: option);
bind.sessionToggleOption(
sessionId: sessionId, value: kOptionEnableFileCopyPaste);
}
: null,
child: Text(translate('Enable file copy and paste'))));
@@ -478,7 +591,7 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
child: Text(translate('Disable clipboard'))));
}
// lock after session end
if (ffiModel.keyboard) {
if (ffiModel.keyboard && !ffiModel.isPeerAndroid) {
final enabled = !ffiModel.viewOnly;
final option = 'lock-after-session-end';
final value =
@@ -494,8 +607,7 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
child: Text(translate('Lock after session end'))));
}
if (useTextureRender &&
pi.isSupportMultiDisplay &&
if (pi.isSupportMultiDisplay &&
PrivacyModeState.find(id).isEmpty &&
pi.displaysCount.value > 1 &&
bind.mainGetUserDefaultOption(key: kKeyShowMonitorsToolbar) == 'Y') {
@@ -507,13 +619,13 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
onChanged: (value) {
if (value == null) return;
bind.sessionSetDisplaysAsIndividualWindows(
sessionId: sessionId, value: value ? 'Y' : '');
sessionId: sessionId, value: value ? 'Y' : 'N');
},
child: Text(translate('Show displays as individual windows'))));
}
final screenList = await getScreenRectList();
if (useTextureRender && pi.isSupportMultiDisplay && screenList.length > 1) {
final isMultiScreens = !isWeb && (await getScreenRectList()).length > 1;
if (pi.isSupportMultiDisplay && isMultiScreens) {
final value = bind.sessionGetUseAllMyDisplaysForTheRemoteSession(
sessionId: ffi.sessionId) ==
'Y';
@@ -522,7 +634,7 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
onChanged: (value) {
if (value == null) return;
bind.sessionSetUseAllMyDisplaysForTheRemoteSession(
sessionId: sessionId, value: value ? 'Y' : '');
sessionId: sessionId, value: value ? 'Y' : 'N');
},
child: Text(translate('Use all my displays for the remote session'))));
}
@@ -548,6 +660,18 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
v.addAll(toolbarKeyboardToggles(ffi));
}
// view mode (mobile only, desktop is in keyboard menu)
if (isMobile && versionCmp(pi.version, '1.2.0') >= 0) {
v.add(TToggleMenu(
value: ffiModel.viewOnly,
onChanged: (value) async {
if (value == null) return;
await bind.sessionToggleOption(
sessionId: ffi.sessionId, value: kOptionToggleViewOnly);
ffiModel.setViewOnly(id, value);
},
child: Text(translate('View Mode'))));
}
return v;
}
@@ -633,8 +757,8 @@ List<TToggleMenu> toolbarKeyboardToggles(FFI ffi) {
// swap key
if (ffiModel.keyboard &&
((Platform.isMacOS && pi.platform != kPeerPlatformMacOS) ||
(!Platform.isMacOS && pi.platform == kPeerPlatformMacOS))) {
((isMacOS && pi.platform != kPeerPlatformMacOS) ||
(!isMacOS && pi.platform == kPeerPlatformMacOS))) {
final option = 'allow_swap_key';
final value =
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
@@ -655,7 +779,7 @@ List<TToggleMenu> toolbarKeyboardToggles(FFI ffi) {
var optionValue =
bind.sessionGetReverseMouseWheelSync(sessionId: sessionId) ?? '';
if (optionValue == '') {
optionValue = bind.mainGetUserDefaultOption(key: 'reverse_mouse_wheel');
optionValue = bind.mainGetUserDefaultOption(key: kKeyReverseMouseWheel);
}
onChanged(bool? value) async {
if (value == null) return;
@@ -688,3 +812,106 @@ List<TToggleMenu> toolbarKeyboardToggles(FFI ffi) {
}
return v;
}
bool showVirtualDisplayMenu(FFI ffi) {
if (ffi.ffiModel.pi.platform != kPeerPlatformWindows) {
return false;
}
if (!ffi.ffiModel.pi.isInstalled) {
return false;
}
if (ffi.ffiModel.pi.isRustDeskIdd || ffi.ffiModel.pi.isAmyuniIdd) {
return true;
}
return false;
}
List<Widget> getVirtualDisplayMenuChildren(
FFI ffi, String id, VoidCallback? clickCallBack) {
if (!showVirtualDisplayMenu(ffi)) {
return [];
}
final pi = ffi.ffiModel.pi;
final privacyModeState = PrivacyModeState.find(id);
if (pi.isRustDeskIdd) {
final virtualDisplays = ffi.ffiModel.pi.RustDeskVirtualDisplays;
final children = <Widget>[];
for (var i = 0; i < kMaxVirtualDisplayCount; i++) {
children.add(Obx(() => CkbMenuButton(
value: virtualDisplays.contains(i + 1),
onChanged: privacyModeState.isNotEmpty
? null
: (bool? value) async {
if (value != null) {
bind.sessionToggleVirtualDisplay(
sessionId: ffi.sessionId, index: i + 1, on: value);
clickCallBack?.call();
}
},
child: Text('${translate('Virtual display')} ${i + 1}'),
ffi: ffi,
)));
}
children.add(Divider());
children.add(Obx(() => MenuButton(
onPressed: privacyModeState.isNotEmpty
? null
: () {
bind.sessionToggleVirtualDisplay(
sessionId: ffi.sessionId,
index: kAllVirtualDisplay,
on: false);
clickCallBack?.call();
},
ffi: ffi,
child: Text(translate('Plug out all')),
)));
return children;
}
if (pi.isAmyuniIdd) {
final count = ffi.ffiModel.pi.amyuniVirtualDisplayCount;
final children = <Widget>[
Obx(() => Row(
children: [
TextButton(
onPressed: privacyModeState.isNotEmpty || count == 0
? null
: () {
bind.sessionToggleVirtualDisplay(
sessionId: ffi.sessionId, index: 0, on: false);
clickCallBack?.call();
},
child: Icon(Icons.remove),
),
Text(count.toString()),
TextButton(
onPressed: privacyModeState.isNotEmpty || count == 4
? null
: () {
bind.sessionToggleVirtualDisplay(
sessionId: ffi.sessionId, index: 0, on: true);
clickCallBack?.call();
},
child: Icon(Icons.add),
),
],
)),
Divider(),
Obx(() => MenuButton(
onPressed: privacyModeState.isNotEmpty || count == 0
? null
: () {
bind.sessionToggleVirtualDisplay(
sessionId: ffi.sessionId,
index: kAllVirtualDisplay,
on: false);
clickCallBack?.call();
},
ffi: ffi,
child: Text(translate('Plug out all')),
)),
];
return children;
}
return [];
}

View File

@@ -1,5 +1,3 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/state_model.dart';
@@ -21,14 +19,20 @@ const kKeyTranslateMode = 'translate';
const String kPlatformAdditionsIsWayland = "is_wayland";
const String kPlatformAdditionsHeadless = "headless";
const String kPlatformAdditionsIsInstalled = "is_installed";
const String kPlatformAdditionsVirtualDisplays = "virtual_displays";
const String kPlatformAdditionsIddImpl = "idd_impl";
const String kPlatformAdditionsRustDeskVirtualDisplays =
"rustdesk_virtual_displays";
const String kPlatformAdditionsAmyuniVirtualDisplays =
"amyuni_virtual_displays";
const String kPlatformAdditionsHasFileClipboard = "has_file_clipboard";
const String kPlatformAdditionsSupportedPrivacyModeImpl = "supported_privacy_mode_impl";
const String kPlatformAdditionsSupportedPrivacyModeImpl =
"supported_privacy_mode_impl";
const String kPeerPlatformWindows = "Windows";
const String kPeerPlatformLinux = "Linux";
const String kPeerPlatformMacOS = "Mac OS";
const String kPeerPlatformAndroid = "Android";
const String kPeerPlatformWebDesktop = "WebDesktop";
const double kScrollbarThickness = 12.0;
@@ -59,17 +63,101 @@ const String kWindowEventActiveSession = "active_session";
const String kWindowEventActiveDisplaySession = "active_display_session";
const String kWindowEventGetRemoteList = "get_remote_list";
const String kWindowEventGetSessionIdList = "get_session_id_list";
const String kWindowEventRemoteWindowCoords = "remote_window_coords";
const String kWindowEventSetFullscreen = "set_fullscreen";
const String kWindowEventMoveTabToNewWindow = "move_tab_to_new_window";
const String kWindowEventGetCachedSessionData = "get_cached_session_data";
const String kWindowEventOpenMonitorSession = "open_monitor_session";
const String kOptionViewStyle = "view_style";
const String kOptionScrollStyle = "scroll_style";
const String kOptionImageQuality = "image_quality";
const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
const String kOptionTextureRender = "use-texture-render";
const String kOptionOpenInTabs = "allow-open-in-tabs";
const String kOptionOpenInWindows = "allow-open-in-windows";
const String kOptionForceAlwaysRelay = "force-always-relay";
const String kOptionViewOnly = "view_only";
const String kOptionEnableLanDiscovery = "enable-lan-discovery";
const String kOptionWhitelist = "whitelist";
const String kOptionEnableAbr = "enable-abr";
const String kOptionEnableRecordSession = "enable-record-session";
const String kOptionDirectServer = "direct-server";
const String kOptionDirectAccessPort = "direct-access-port";
const String kOptionAllowAutoDisconnect = "allow-auto-disconnect";
const String kOptionAutoDisconnectTimeout = "auto-disconnect-timeout";
const String kOptionEnableHwcodec = "enable-hwcodec";
const String kOptionAllowAutoRecordIncoming = "allow-auto-record-incoming";
const String kOptionAllowAutoRecordOutgoing = "allow-auto-record-outgoing";
const String kOptionVideoSaveDirectory = "video-save-directory";
const String kOptionAccessMode = "access-mode";
const String kOptionEnableKeyboard = "enable-keyboard";
// "Settings -> Security -> Permissions"
const String kOptionEnableClipboard = "enable-clipboard";
const String kOptionEnableFileTransfer = "enable-file-transfer";
const String kOptionEnableAudio = "enable-audio";
const String kOptionEnableTunnel = "enable-tunnel";
const String kOptionEnableRemoteRestart = "enable-remote-restart";
const String kOptionEnableBlockInput = "enable-block-input";
const String kOptionAllowRemoteConfigModification =
"allow-remote-config-modification";
const String kOptionVerificationMethod = "verification-method";
const String kOptionApproveMode = "approve-mode";
const String kOptionCollapseToolbar = "collapse_toolbar";
const String kOptionShowRemoteCursor = "show_remote_cursor";
const String kOptionFollowRemoteCursor = "follow_remote_cursor";
const String kOptionFollowRemoteWindow = "follow_remote_window";
const String kOptionZoomCursor = "zoom-cursor";
const String kOptionShowQualityMonitor = "show_quality_monitor";
const String kOptionDisableAudio = "disable_audio";
const String kOptionEnableFileCopyPaste = "enable-file-copy-paste";
// "Settings -> Display -> Other default options"
const String kOptionDisableClipboard = "disable_clipboard";
const String kOptionLockAfterSessionEnd = "lock_after_session_end";
const String kOptionPrivacyMode = "privacy_mode";
const String kOptionTouchMode = "touch-mode";
const String kOptionI444 = "i444";
const String kOptionSwapLeftRightMouse = "swap-left-right-mouse";
const String kOptionCodecPreference = "codec-preference";
const String kOptionRemoteMenubarDragLeft = "remote-menubar-drag-left";
const String kOptionRemoteMenubarDragRight = "remote-menubar-drag-right";
const String kOptionHideAbTagsPanel = "hideAbTagsPanel";
const String kOptionRemoteMenubarState = "remoteMenubarState";
const String kOptionPeerSorting = "peer-sorting";
const String kOptionPeerTabIndex = "peer-tab-index";
const String kOptionPeerTabOrder = "peer-tab-order";
const String kOptionPeerTabVisible = "peer-tab-visible";
const String kOptionPeerCardUiType = "peer-card-ui-type";
const String kOptionCurrentAbName = "current-ab-name";
const String kOptionEnableConfirmClosingTabs = "enable-confirm-closing-tabs";
const String kOptionAllowAlwaysSoftwareRender = "allow-always-software-render";
const String kOptionEnableCheckUpdate = "enable-check-update";
const String kOptionAllowLinuxHeadless = "allow-linux-headless";
const String kOptionAllowRemoveWallpaper = "allow-remove-wallpaper";
const String kOptionStopService = "stop-service";
const String kOptionDirectxCapture = "enable-directx-capture";
const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification";
const String kOptionEnableTrustedDevices = "enable-trusted-devices";
// buildin opitons
const String kOptionHideServerSetting = "hide-server-settings";
const String kOptionHideProxySetting = "hide-proxy-settings";
const String kOptionHideSecuritySetting = "hide-security-settings";
const String kOptionHideNetworkSetting = "hide-network-settings";
const String kOptionRemovePresetPasswordWarning =
"remove-preset-password-warning";
const kHideUsernameOnCard = "hide-username-on-card";
const String kOptionHideHelpCards = "hide-help-cards";
const String kOptionToggleViewOnly = "view-only";
const String kOptionDisableFloatingWindow = "disable-floating-window";
const String kOptionKeepScreenOn = "keep-screen-on";
const String kOptionShowMobileAction = "showMobileActions";
const String kUniLinksPrefix = "rustdesk://";
const String kUrlActionClose = "close";
const String kTabLabelHomePage = "Home";
@@ -81,16 +169,22 @@ const int kWindowMainId = 0;
const String kPointerEventKindTouch = "touch";
const String kPointerEventKindMouse = "mouse";
const String kKeyFlutterKey = "flutter_key";
const String kKeyShowDisplaysAsIndividualWindows =
'displays_as_individual_windows';
const String kKeyUseAllMyDisplaysForTheRemoteSession =
'use_all_my_displays_for_the_remote_session';
const String kKeyShowMonitorsToolbar = 'show_monitors_toolbar';
const String kKeyReverseMouseWheel = "reverse_mouse_wheel";
const String kMsgboxTextWaitingForImage = 'Connected, waiting for image...';
// the executable name of the portable version
const String kEnvPortableExecutable = "RUSTDESK_APPNAME";
const Color kColorWarn = Color.fromARGB(255, 245, 133, 59);
const Color kColorCanvas = Colors.black;
const int kMobileDefaultDisplayWidth = 720;
const int kMobileDefaultDisplayHeight = 1280;
@@ -108,25 +202,24 @@ const double kMinFps = 5;
const double kDefaultFps = 30;
const double kMaxFps = 120;
const double kMinQuality = 10;
const double kMinQuality = 5;
const double kDefaultQuality = 50;
const double kMaxQuality = 100;
const double kMaxMoreQuality = 2000;
double kNewWindowOffset = Platform.isWindows
double kNewWindowOffset = isWindows
? 56.0
: Platform.isLinux
: isLinux
? 50.0
: Platform.isMacOS
: isMacOS
? 30.0
: 50.0;
EdgeInsets get kDragToResizeAreaPadding =>
!kUseCompatibleUiMode && Platform.isLinux
? stateGlobal.fullscreen.isTrue || stateGlobal.isMaximized.value
? EdgeInsets.zero
: EdgeInsets.all(5.0)
: EdgeInsets.zero;
EdgeInsets get kDragToResizeAreaPadding => !kUseCompatibleUiMode && isLinux
? stateGlobal.fullscreen.isTrue || stateGlobal.isMaximized.value
? EdgeInsets.zero
: EdgeInsets.all(5.0)
: EdgeInsets.zero;
// https://en.wikipedia.org/wiki/Non-breaking_space
const int $nbsp = 0x00A0;
@@ -150,9 +243,15 @@ const kDefaultScrollDuration = Duration(milliseconds: 50);
const kDefaultMouseWheelThrottleDuration = Duration(milliseconds: 50);
const kFullScreenEdgeSize = 0.0;
const kMaximizeEdgeSize = 0.0;
var kWindowEdgeSize = Platform.isWindows ? 1.0 : 5.0;
// Do not use kWindowResizeEdgeSize directly. Use `windowResizeEdgeSize` in `common.dart` instead.
const kWindowResizeEdgeSize = 5.0;
const kWindowBorderWidth = 1.0;
const kDesktopMenuPadding = EdgeInsets.only(left: 12.0, right: 3.0);
const kFrameBorderRadius = 12.0;
const kFrameClipRRectBorderRadius = 12.0;
const kFrameBoxShadowBlurRadius = 32.0;
const kFrameBoxShadowOffsetFocused = 4.0;
const kFrameBoxShadowOffsetUnfocused = 2.0;
const kInvalidValueStr = 'InvalidValueStr';
@@ -198,12 +297,6 @@ const kRemoteImageQualityLow = 'low';
/// [kRemoteImageQualityCustom] Custom image quality.
const kRemoteImageQualityCustom = 'custom';
/// [kRemoteAudioGuestToHost] Guest to host audio mode(default).
const kRemoteAudioGuestToHost = 'guest-to-host';
/// [kRemoteAudioDualWay] dual-way audio mode(default).
const kRemoteAudioDualWay = 'dual-way';
const kIgnoreDpi = true;
// ================================ mobile ================================
@@ -222,6 +315,7 @@ const kManageExternalStorage = "android.permission.MANAGE_EXTERNAL_STORAGE";
const kRequestIgnoreBatteryOptimizations =
"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
const kSystemAlertWindow = "android.permission.SYSTEM_ALERT_WINDOW";
const kAndroid13Notification = "android.permission.POST_NOTIFICATIONS";
/// Android channel invoke type key
class AndroidChannel {
@@ -477,3 +571,5 @@ enum WindowsTarget {
extension WindowsTargetExt on int {
WindowsTarget get windowsVersion => getWindowsTarget(this);
}
const kCheckSoftwareUpdateFinish = 'check_software_update_finish';

View File

@@ -2,10 +2,9 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/connection_page_title.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
@@ -20,6 +19,169 @@ import '../../common/widgets/autocomplete.dart';
import '../../models/platform_model.dart';
import '../widgets/button.dart';
class OnlineStatusWidget extends StatefulWidget {
const OnlineStatusWidget({Key? key, this.onSvcStatusChanged})
: super(key: key);
final VoidCallback? onSvcStatusChanged;
@override
State<OnlineStatusWidget> createState() => _OnlineStatusWidgetState();
}
/// State for the connection page.
class _OnlineStatusWidgetState extends State<OnlineStatusWidget> {
final _svcStopped = Get.find<RxBool>(tag: 'stop-service');
final _svcIsUsingPublicServer = true.obs;
Timer? _updateTimer;
double get em => 14.0;
double? get height => bind.isIncomingOnly() ? null : em * 3;
void onUsePublicServerGuide() {
const url = "https://rustdesk.com/pricing.html";
canLaunchUrlString(url).then((can) {
if (can) {
launchUrlString(url);
}
});
}
@override
void initState() {
super.initState();
_updateTimer = periodic_immediate(Duration(seconds: 1), () async {
updateStatus();
});
}
@override
void dispose() {
_updateTimer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
final isIncomingOnly = bind.isIncomingOnly();
startServiceWidget() => Offstage(
offstage: !_svcStopped.value,
child: InkWell(
onTap: () async {
await start_service(true);
},
child: Text(translate("Start service"),
style: TextStyle(
decoration: TextDecoration.underline, fontSize: em)))
.marginOnly(left: em),
);
setupServerWidget() => Flexible(
child: Offstage(
offstage: !(!_svcStopped.value &&
stateGlobal.svcStatus.value == SvcStatus.ready &&
_svcIsUsingPublicServer.value),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(', ', style: TextStyle(fontSize: em)),
Flexible(
child: InkWell(
onTap: onUsePublicServerGuide,
child: Row(
children: [
Flexible(
child: Text(
translate('setup_server_tip'),
style: TextStyle(
decoration: TextDecoration.underline,
fontSize: em),
),
),
],
),
),
)
],
),
),
);
basicWidget() => Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 8,
width: 8,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: _svcStopped.value ||
stateGlobal.svcStatus.value == SvcStatus.connecting
? kColorWarn
: (stateGlobal.svcStatus.value == SvcStatus.ready
? Color.fromARGB(255, 50, 190, 166)
: Color.fromARGB(255, 224, 79, 95)),
),
).marginSymmetric(horizontal: em),
Container(
width: isIncomingOnly ? 226 : null,
child: _buildConnStatusMsg(),
),
// stop
if (!isIncomingOnly) startServiceWidget(),
// ready && public
// No need to show the guide if is custom client.
if (!isIncomingOnly) setupServerWidget(),
],
);
return Container(
height: height,
child: Obx(() => isIncomingOnly
? Column(
children: [
basicWidget(),
Align(
child: startServiceWidget(),
alignment: Alignment.centerLeft)
.marginOnly(top: 2.0, left: 22.0),
],
)
: basicWidget()),
).paddingOnly(right: isIncomingOnly ? 8 : 0);
}
_buildConnStatusMsg() {
widget.onSvcStatusChanged?.call();
return Text(
_svcStopped.value
? translate("Service is not running")
: stateGlobal.svcStatus.value == SvcStatus.connecting
? translate("connecting_status")
: stateGlobal.svcStatus.value == SvcStatus.notReady
? translate("not_ready_status")
: translate('Ready'),
style: TextStyle(fontSize: em),
);
}
updateStatus() async {
final status =
jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>;
final statusNum = status['status_num'] as int;
if (statusNum == 0) {
stateGlobal.svcStatus.value = SvcStatus.connecting;
} else if (statusNum == -1) {
stateGlobal.svcStatus.value = SvcStatus.notReady;
} else if (statusNum == 1) {
stateGlobal.svcStatus.value = SvcStatus.ready;
} else {
stateGlobal.svcStatus.value = SvcStatus.notReady;
}
_svcIsUsingPublicServer.value = await bind.mainIsUsingPublicServer();
}
}
/// Connection page for connecting to a remote peer.
class ConnectionPage extends StatefulWidget {
const ConnectionPage({Key? key}) : super(key: key);
@@ -34,13 +196,8 @@ class _ConnectionPageState extends State<ConnectionPage>
/// Controller for the id input bar.
final _idController = IDTextEditingController();
Timer? _updateTimer;
final RxBool _idInputFocused = false.obs;
var svcStopped = Get.find<RxBool>(tag: 'stop-service');
var svcIsUsingPublicServer = true.obs;
bool isWindowMinimized = false;
List<Peer> peers = [];
@@ -51,18 +208,15 @@ class _ConnectionPageState extends State<ConnectionPage>
void initState() {
super.initState();
if (_idController.text.isEmpty) {
() async {
WidgetsBinding.instance.addPostFrameCallback((_) async {
final lastRemoteId = await bind.mainGetLastRemoteId();
if (lastRemoteId != _idController.id) {
setState(() {
_idController.id = lastRemoteId;
});
}
}();
});
}
_updateTimer = periodic_immediate(Duration(seconds: 1), () async {
updateStatus();
});
Get.put<IDTextEditingController>(_idController);
windowManager.addListener(this);
}
@@ -70,7 +224,6 @@ class _ConnectionPageState extends State<ConnectionPage>
@override
void dispose() {
_idController.dispose();
_updateTimer?.cancel();
windowManager.removeListener(this);
if (Get.isRegistered<IDTextEditingController>()) {
Get.delete<IDTextEditingController>();
@@ -87,7 +240,7 @@ class _ConnectionPageState extends State<ConnectionPage>
if (eventName == 'minimize') {
isWindowMinimized = true;
} else if (eventName == 'maximize' || eventName == 'restore') {
if (isWindowMinimized && Platform.isWindows) {
if (isWindowMinimized && isWindows) {
// windows can't update when minimized.
Get.forceAppUpdate();
}
@@ -104,8 +257,9 @@ class _ConnectionPageState extends State<ConnectionPage>
@override
void onWindowLeaveFullScreen() {
// Restore edge border to default edge size.
stateGlobal.resizeEdgeSize.value =
stateGlobal.isMaximized.isTrue ? kMaximizeEdgeSize : kWindowEdgeSize;
stateGlobal.resizeEdgeSize.value = stateGlobal.isMaximized.isTrue
? kMaximizeEdgeSize
: windowResizeEdgeSize;
}
@override
@@ -116,6 +270,7 @@ class _ConnectionPageState extends State<ConnectionPage>
@override
Widget build(BuildContext context) {
final isOutgoingOnly = bind.isOutgoingOnly();
return Column(
children: [
Expanded(
@@ -131,8 +286,8 @@ class _ConnectionPageState extends State<ConnectionPage>
Expanded(child: PeerTabPage()),
],
).paddingOnly(left: 12.0)),
const Divider(height: 1),
buildStatus()
if (!isOutgoingOnly) const Divider(height: 1),
if (!isOutgoingOnly) OnlineStatusWidget()
],
);
}
@@ -168,36 +323,7 @@ class _ConnectionPageState extends State<ConnectionPage>
child: Ink(
child: Column(
children: [
Row(
children: [
Expanded(
child: Row(
children: [
AutoSizeText(
translate('Control Remote Desktop'),
maxLines: 1,
style: Theme.of(context)
.textTheme
.titleLarge
?.merge(TextStyle(height: 1)),
).marginOnly(right: 4),
Tooltip(
waitDuration: Duration(milliseconds: 0),
message: translate("id_input_tip"),
child: Icon(
Icons.help_outline_outlined,
size: 16,
color: Theme.of(context)
.textTheme
.titleLarge
?.color
?.withOpacity(0.5),
),
),
],
)),
],
).marginOnly(bottom: 15),
getConnectionPageTitle(context, false).marginOnly(bottom: 15),
Row(
children: [
Expanded(
@@ -214,6 +340,7 @@ class _ConnectionPageState extends State<ConnectionPage>
platform: '',
tags: [],
hash: '',
password: '',
forceAlwaysRelay: false,
rdpPort: '',
rdpUsername: '',
@@ -289,6 +416,9 @@ class _ConnectionPageState extends State<ConnectionPage>
onChanged: (v) {
_idController.id = v;
},
onSubmitted: (_) {
onConnect();
},
));
},
onSelected: (option) {
@@ -383,111 +513,4 @@ class _ConnectionPageState extends State<ConnectionPage>
return Container(
constraints: const BoxConstraints(maxWidth: 600), child: w);
}
Widget buildStatus() {
final em = 14.0;
return Container(
height: 3 * em,
child: Obx(() => Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 8,
width: 8,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: svcStopped.value ||
stateGlobal.svcStatus.value == SvcStatus.connecting
? kColorWarn
: (stateGlobal.svcStatus.value == SvcStatus.ready
? Color.fromARGB(255, 50, 190, 166)
: Color.fromARGB(255, 224, 79, 95)),
),
).marginSymmetric(horizontal: em),
Text(
svcStopped.value
? translate("Service is not running")
: stateGlobal.svcStatus.value == SvcStatus.connecting
? translate("connecting_status")
: stateGlobal.svcStatus.value == SvcStatus.notReady
? translate("not_ready_status")
: translate('Ready'),
style: TextStyle(fontSize: em)),
// stop
Offstage(
offstage: !svcStopped.value,
child: InkWell(
onTap: () async {
await start_service(true);
},
child: Text(translate("Start service"),
style: TextStyle(
decoration: TextDecoration.underline,
fontSize: em)))
.marginOnly(left: em),
),
// ready && public
Flexible(
child: Offstage(
offstage: !(!svcStopped.value &&
stateGlobal.svcStatus.value == SvcStatus.ready &&
svcIsUsingPublicServer.value),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(', ', style: TextStyle(fontSize: em)),
Flexible(
child: InkWell(
onTap: onUsePublicServerGuide,
child: Row(
children: [
Flexible(
child: Text(
translate('setup_server_tip'),
style: TextStyle(
decoration: TextDecoration.underline,
fontSize: em),
),
),
],
),
),
)
],
),
),
)
],
)),
);
}
void onUsePublicServerGuide() {
const url = "https://rustdesk.com/pricing.html";
canLaunchUrlString(url).then((can) {
if (can) {
launchUrlString(url);
}
});
}
updateStatus() async {
final status =
jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>;
final statusNum = status['status_num'] as int;
final preStatus = stateGlobal.svcStatus.value;
if (statusNum == 0) {
stateGlobal.svcStatus.value = SvcStatus.connecting;
} else if (statusNum == -1) {
stateGlobal.svcStatus.value = SvcStatus.notReady;
} else if (statusNum == 1) {
stateGlobal.svcStatus.value = SvcStatus.ready;
if (preStatus != SvcStatus.ready) {
gFFI.userModel.refreshCurrentUser();
}
} else {
stateGlobal.svcStatus.value = SvcStatus.notReady;
}
svcIsUsingPublicServer.value = await bind.mainIsUsingPublicServer();
}
}

View File

@@ -20,6 +20,7 @@ import 'package:flutter_hbb/utils/multi_window_manager.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:window_manager/window_manager.dart';
import 'package:window_size/window_size.dart' as window_size;
import '../widgets/button.dart';
@@ -50,50 +51,120 @@ class _DesktopHomePageState extends State<DesktopHomePage>
Timer? _updateTimer;
bool isCardClosed = false;
final RxBool _editHover = false.obs;
final GlobalKey _childKey = GlobalKey();
@override
Widget build(BuildContext context) {
super.build(context);
final isIncomingOnly = bind.isIncomingOnly();
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildLeftPane(context),
const VerticalDivider(width: 1),
Expanded(
child: buildRightPane(context),
),
if (!isIncomingOnly) const VerticalDivider(width: 1),
if (!isIncomingOnly) Expanded(child: buildRightPane(context)),
],
);
}
Widget buildLeftPane(BuildContext context) {
final isIncomingOnly = bind.isIncomingOnly();
final isOutgoingOnly = bind.isOutgoingOnly();
final children = <Widget>[
if (!isOutgoingOnly) buildPresetPasswordWarning(),
if (bind.isCustomClient())
Align(
alignment: Alignment.center,
child: loadPowered(context),
),
Align(
alignment: Alignment.center,
child: loadLogo(),
),
buildTip(context),
if (!isOutgoingOnly) buildIDBoard(context),
if (!isOutgoingOnly) buildPasswordBoard(context),
FutureBuilder<Widget>(
future: buildHelpCards(),
builder: (_, data) {
if (data.hasData) {
if (isIncomingOnly) {
if (isInHomePage()) {
Future.delayed(Duration(milliseconds: 300), () {
_updateWindowSize();
});
}
}
return data.data!;
} else {
return const Offstage();
}
},
),
buildPluginEntry(),
];
if (isIncomingOnly) {
children.addAll([
Divider(),
OnlineStatusWidget(
onSvcStatusChanged: () {
if (isInHomePage()) {
Future.delayed(Duration(milliseconds: 300), () {
_updateWindowSize();
});
}
},
).marginOnly(bottom: 6, right: 6)
]);
}
final textColor = Theme.of(context).textTheme.titleLarge?.color;
return ChangeNotifierProvider.value(
value: gFFI.serverModel,
child: Container(
width: 200,
width: isIncomingOnly ? 280.0 : 200.0,
color: Theme.of(context).colorScheme.background,
child: DesktopScrollWrapper(
scrollController: _leftPaneScrollController,
child: SingleChildScrollView(
controller: _leftPaneScrollController,
physics: DraggableNeverScrollableScrollPhysics(),
child: Column(
children: [
buildTip(context),
buildIDBoard(context),
buildPasswordBoard(context),
FutureBuilder<Widget>(
future: buildHelpCards(),
builder: (_, data) {
if (data.hasData) {
return data.data!;
} else {
return const Offstage();
}
},
child: Stack(
children: [
SingleChildScrollView(
controller: _leftPaneScrollController,
physics: DraggableNeverScrollableScrollPhysics(),
child: Column(
key: _childKey,
children: children,
),
buildPluginEntry()
],
),
),
if (isOutgoingOnly)
Positioned(
bottom: 6,
left: 12,
child: Align(
alignment: Alignment.centerLeft,
child: InkWell(
child: Obx(
() => Icon(
Icons.settings,
color: _editHover.value
? textColor
: Colors.grey.withOpacity(0.5),
size: 22,
),
),
onTap: () => {
if (DesktopSettingPage.tabKeys.isNotEmpty)
{
DesktopSettingPage.switch2page(
DesktopSettingPage.tabKeys[0])
}
},
onHover: (value) => _editHover.value = value,
),
),
)
],
),
),
),
@@ -180,19 +251,20 @@ class _DesktopHomePageState extends State<DesktopHomePage>
RxBool hover = false.obs;
return InkWell(
onTap: DesktopTabPage.onAddSetting,
child: Obx(
() => CircleAvatar(
radius: 15,
backgroundColor: hover.value
? Theme.of(context).scaffoldBackgroundColor
: Theme.of(context).colorScheme.background,
child: Tooltip(
message: translate('Settings'),
child: Icon(
Icons.more_vert_outlined,
size: 20,
color: hover.value ? textColor : textColor?.withOpacity(0.5),
)),
child: Tooltip(
message: translate('Settings'),
child: Obx(
() => CircleAvatar(
radius: 15,
backgroundColor: hover.value
? Theme.of(context).scaffoldBackgroundColor
: Theme.of(context).colorScheme.background,
child: Icon(
Icons.more_vert_outlined,
size: 20,
color: hover.value ? textColor : textColor?.withOpacity(0.5),
),
),
),
),
onHover: (value) => hover.value = value,
@@ -253,34 +325,38 @@ class _DesktopHomePageState extends State<DesktopHomePage>
),
AnimatedRotationWidget(
onPressed: () => bind.mainUpdateTemporaryPassword(),
child: Obx(() => RotatedBox(
quarterTurns: 2,
child: Tooltip(
message: translate('Refresh Password'),
child: Icon(
Icons.refresh,
color: refreshHover.value
? textColor
: Color(0xFFDDDDDD),
size: 22,
)))),
child: Tooltip(
message: translate('Refresh Password'),
child: Obx(() => RotatedBox(
quarterTurns: 2,
child: Icon(
Icons.refresh,
color: refreshHover.value
? textColor
: Color(0xFFDDDDDD),
size: 22,
))),
),
onHover: (value) => refreshHover.value = value,
).marginOnly(right: 8, top: 4),
InkWell(
child: Obx(
() => Tooltip(
message: translate('Change Password'),
child: Icon(
if (!bind.isDisableSettings())
InkWell(
child: Tooltip(
message: translate('Change Password'),
child: Obx(
() => Icon(
Icons.edit,
color: editHover.value
? textColor
: Color(0xFFDDDDDD),
size: 22,
)).marginOnly(right: 8, top: 4),
).marginOnly(right: 8, top: 4),
),
),
onTap: () => DesktopSettingPage.switch2page(
SettingsTabKey.safety),
onHover: (value) => editHover.value = value,
),
onTap: () => DesktopSettingPage.switch2page(1),
onHover: (value) => editHover.value = value,
),
],
),
],
@@ -293,6 +369,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
buildTip(BuildContext context) {
final isOutgoingOnly = bind.isOutgoingOnly();
return Padding(
padding:
const EdgeInsets.only(left: 20.0, right: 16, top: 16.0, bottom: 5),
@@ -300,29 +377,43 @@ class _DesktopHomePageState extends State<DesktopHomePage>
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
translate("Your Desktop"),
style: Theme.of(context).textTheme.titleLarge,
// style: TextStyle(
// // color: MyTheme.color(context).text,
// fontWeight: FontWeight.normal,
// fontSize: 19),
Column(
children: [
if (!isOutgoingOnly)
Align(
alignment: Alignment.centerLeft,
child: Text(
translate("Your Desktop"),
style: Theme.of(context).textTheme.titleLarge,
),
),
],
),
SizedBox(
height: 10.0,
),
Text(
translate("desk_tip"),
overflow: TextOverflow.clip,
style: Theme.of(context).textTheme.bodySmall,
)
if (!isOutgoingOnly)
Text(
translate("desk_tip"),
overflow: TextOverflow.clip,
style: Theme.of(context).textTheme.bodySmall,
),
if (isOutgoingOnly)
Text(
translate("outgoing_only_desk_tip"),
overflow: TextOverflow.clip,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
);
}
Future<Widget> buildHelpCards() async {
if (updateUrl.isNotEmpty && !isCardClosed) {
if (!bind.isCustomClient() &&
updateUrl.isNotEmpty &&
!isCardClosed &&
bind.mainUriPrefixSync().contains('rustdesk')) {
return buildInstallCard(
"Status",
"There is a newer version of ${bind.mainGetAppNameSync()} ${bind.mainGetNewVersion()} available.",
@@ -334,9 +425,12 @@ class _DesktopHomePageState extends State<DesktopHomePage>
if (systemError.isNotEmpty) {
return buildInstallCard("", systemError, "", () {});
}
if (Platform.isWindows) {
if (isWindows && !bind.isDisableInstallation()) {
if (!bind.mainIsInstalled()) {
return buildInstallCard("", "install_tip", "Install", () async {
return buildInstallCard(
"", bind.isOutgoingOnly() ? "" : "install_tip", "Install",
() async {
await rustDeskWinManager.closeAllSubWindows();
bind.mainGotoInstall();
});
@@ -348,14 +442,15 @@ class _DesktopHomePageState extends State<DesktopHomePage>
bind.mainUpdateMe();
});
}
} else if (Platform.isMacOS) {
if (!bind.mainIsCanScreenRecording(prompt: false)) {
} else if (isMacOS) {
final isOutgoingOnly = bind.isOutgoingOnly();
if (!(isOutgoingOnly || bind.mainIsCanScreenRecording(prompt: false))) {
return buildInstallCard("Permissions", "config_screen", "Configure",
() async {
bind.mainIsCanScreenRecording(prompt: true);
watchIsCanScreenRecording = true;
}, help: 'Help', link: translate("doc_mac_permission"));
} else if (!bind.mainIsProcessTrusted(prompt: false)) {
} else if (!isOutgoingOnly && !bind.mainIsProcessTrusted(prompt: false)) {
return buildInstallCard("Permissions", "config_acc", "Configure",
() async {
bind.mainIsProcessTrusted(prompt: true);
@@ -367,7 +462,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
bind.mainIsCanInputMonitoring(prompt: true);
watchIsInputMonitoring = true;
}, help: 'Help', link: translate("doc_mac_permission"));
} else if (!svcStopped.value &&
} else if (!isOutgoingOnly &&
!svcStopped.value &&
bind.mainIsInstalled() &&
!bind.mainIsInstalledDaemon(prompt: false)) {
return buildInstallCard("", "install_daemon_tip", "Install", () async {
@@ -383,7 +479,10 @@ class _DesktopHomePageState extends State<DesktopHomePage>
// watchIsCanRecordAudio = true;
// });
// }
} else if (Platform.isLinux) {
} else if (isLinux) {
if (bind.isOutgoingOnly()) {
return Container();
}
final LinuxCards = <Widget>[];
if (bind.isSelinuxEnforcing()) {
// Check is SELinux enforcing, but show user a tip of is SELinux enabled for simple.
@@ -422,6 +521,21 @@ class _DesktopHomePageState extends State<DesktopHomePage>
);
}
}
if (bind.isIncomingOnly()) {
return Align(
alignment: Alignment.centerRight,
child: OutlinedButton(
onPressed: () {
SystemNavigator.pop(); // Close the application
// https://github.com/flutter/flutter/issues/66631
if (isWindows) {
exit(0);
}
},
child: Text(translate('Quit')),
),
).marginAll(14);
}
return Container();
}
@@ -432,6 +546,10 @@ class _DesktopHomePageState extends State<DesktopHomePage>
String? link,
bool? closeButton,
String? closeOption}) {
if (bind.mainGetBuildinOption(key: kOptionHideHelpCards) == 'Y' &&
content != 'install_daemon_tip') {
return const SizedBox();
}
void closeCard() async {
if (closeOption != null) {
await bind.mainSetLocalOption(key: closeOption, value: 'N');
@@ -450,7 +568,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
return Stack(
children: [
Container(
margin: EdgeInsets.only(top: marginTop),
margin: EdgeInsets.fromLTRB(
0, marginTop, 0, bind.isIncomingOnly() ? marginTop : 0),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
@@ -478,14 +597,15 @@ class _DesktopHomePageState extends State<DesktopHomePage>
]
: <Widget>[]) +
<Widget>[
Text(
translate(content),
style: TextStyle(
height: 1.5,
color: Colors.white,
fontWeight: FontWeight.normal,
fontSize: 13),
).marginOnly(bottom: 20)
if (content.isNotEmpty)
Text(
translate(content),
style: TextStyle(
height: 1.5,
color: Colors.white,
fontWeight: FontWeight.normal,
fontSize: 13),
).marginOnly(bottom: 20)
] +
(btnText.isNotEmpty
? <Widget>[
@@ -543,10 +663,20 @@ class _DesktopHomePageState extends State<DesktopHomePage>
@override
void initState() {
super.initState();
Timer(const Duration(seconds: 1), () async {
updateUrl = await bind.mainGetSoftwareUpdateUrl();
if (updateUrl.isNotEmpty) setState(() {});
});
if (!bind.isCustomClient()) {
platformFFI.registerEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
(Map<String, dynamic> evt) async {
if (evt['url'] is String) {
setState(() {
updateUrl = evt['url'];
});
}
});
Timer(const Duration(seconds: 1), () async {
bind.mainGetSoftwareUpdateUrl();
});
}
_updateTimer = periodic_immediate(const Duration(seconds: 1), () async {
await gFFI.serverModel.fetchID();
final error = await bind.mainGetError();
@@ -554,7 +684,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
systemError = error;
setState(() {});
}
final v = await bind.mainGetOption(key: "stop-service") == "Y";
final v = await mainGetBoolOption(kOptionStopService);
if (v != svcStopped.value) {
svcStopped.value = v;
setState(() {});
@@ -582,7 +712,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
}
if (watchIsCanRecordAudio) {
if (Platform.isMacOS) {
if (isMacOS) {
Future.microtask(() async {
if ((await osxCanRecordAudio() ==
PermissionAuthorizeType.authorized)) {
@@ -642,7 +772,9 @@ class _DesktopHomePageState extends State<DesktopHomePage>
isFileTransfer: call.arguments['isFileTransfer'],
isTcpTunneling: call.arguments['isTcpTunneling'],
isRDP: call.arguments['isRDP'],
password: call.arguments['password'],
forceRelay: call.arguments['forceRelay'],
connToken: call.arguments['connToken'],
);
} else if (call.method == kWindowEventMoveTabToNewWindow) {
final args = call.arguments.split(',');
@@ -665,9 +797,35 @@ class _DesktopHomePageState extends State<DesktopHomePage>
final screenRect = parseParamScreenRect(args);
await rustDeskWinManager.openMonitorSession(
windowId, peerId, display, displayCount, screenRect);
} else if (call.method == kWindowEventRemoteWindowCoords) {
final windowId = int.tryParse(call.arguments);
if (windowId != null) {
return jsonEncode(
await rustDeskWinManager.getOtherRemoteWindowCoords(windowId));
}
}
});
_uniLinksSubscription = listenUniLinks();
if (bind.isIncomingOnly()) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateWindowSize();
});
}
}
_updateWindowSize() {
RenderObject? renderObject = _childKey.currentContext?.findRenderObject();
if (renderObject == null) {
return;
}
if (renderObject is RenderBox) {
final size = renderObject.size;
if (size != imcomingOnlyHomeSize) {
imcomingOnlyHomeSize = size;
windowManager.setSize(getIncomingOnlyHomeSize());
}
}
}
@override
@@ -675,6 +833,10 @@ class _DesktopHomePageState extends State<DesktopHomePage>
_uniLinksSubscription?.cancel();
Get.delete<RxBool>(tag: 'stop-service');
_updateTimer?.cancel();
if (!bind.isCustomClient()) {
platformFFI.unregisterEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish);
}
super.dispose();
}
@@ -694,7 +856,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
}
void setPasswordDialog() async {
void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
final pw = await bind.mainGetPermanentPassword();
final p0 = TextEditingController(text: pw);
final p1 = TextEditingController(text: pw);
@@ -708,6 +870,7 @@ void setPasswordDialog() async {
// SpecialCharacterValidationRule(),
MinCharactersValidationRule(8),
];
final maxLength = bind.mainMaxEncryptLen();
gFFI.dialogManager.show((setState, close, context) {
submit() {
@@ -734,6 +897,9 @@ void setPasswordDialog() async {
return;
}
bind.mainSetPermanentPassword(password: pass);
if (pass.isNotEmpty) {
notEmptyCallback?.call();
}
close();
}
@@ -763,6 +929,7 @@ void setPasswordDialog() async {
errMsg0 = '';
});
},
maxLength: maxLength,
),
),
],
@@ -789,6 +956,7 @@ void setPasswordDialog() async {
errMsg1 = '';
});
},
maxLength: maxLength,
),
),
],

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
import 'package:window_manager/window_manager.dart';
// import 'package:flutter/services.dart';
import '../../common/shared_state.dart';
@@ -18,9 +18,10 @@ class DesktopTabPage extends StatefulWidget {
@override
State<DesktopTabPage> createState() => _DesktopTabPageState();
static void onAddSetting({int initialPage = 0}) {
static void onAddSetting(
{SettingsTabKey initialPage = SettingsTabKey.general}) {
try {
DesktopTabController tabController = Get.find();
DesktopTabController tabController = Get.find<DesktopTabController>();
tabController.add(TabInfo(
key: kTabLabelSettingPage,
label: kTabLabelSettingPage,
@@ -28,7 +29,7 @@ class DesktopTabPage extends StatefulWidget {
unselectedIcon: Icons.build_outlined,
page: DesktopSettingPage(
key: const ValueKey(kTabLabelSettingPage),
initialPage: initialPage,
initialTabkey: initialPage,
)));
} catch (e) {
debugPrintStack(label: '$e');
@@ -36,14 +37,16 @@ class DesktopTabPage extends StatefulWidget {
}
}
class _DesktopTabPageState extends State<DesktopTabPage> {
class _DesktopTabPageState extends State<DesktopTabPage>
with WidgetsBindingObserver {
final tabController = DesktopTabController(tabType: DesktopTabType.main);
@override
void initState() {
super.initState();
Get.put<DesktopTabController>(tabController);
final RxBool _block = false.obs;
// bool mouseIn = false;
_DesktopTabPageState() {
RemoteCountState.init();
Get.put<DesktopTabController>(tabController);
tabController.add(TabInfo(
key: kTabLabelHomePage,
label: kTabLabelHomePage,
@@ -53,12 +56,51 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
page: DesktopHomePage(
key: const ValueKey(kTabLabelHomePage),
)));
if (bind.isIncomingOnly()) {
tabController.onSelected = (key) {
if (key == kTabLabelHomePage) {
windowManager.setSize(getIncomingOnlyHomeSize());
setResizable(false);
} else {
windowManager.setSize(getIncomingOnlySettingsSize());
setResizable(true);
}
};
}
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed) {
shouldBeBlocked(_block, canBeBlocked);
} else if (state == AppLifecycleState.inactive) {}
}
@override
void initState() {
super.initState();
// HardwareKeyboard.instance.addHandler(_handleKeyEvent);
WidgetsBinding.instance.addObserver(this);
}
/*
bool _handleKeyEvent(KeyEvent event) {
if (!mouseIn && event is KeyDownEvent) {
print('key down: ${event.logicalKey}');
shouldBeBlocked(_block, canBeBlocked);
}
return false; // allow it to propagate
}
*/
@override
void dispose() {
super.dispose();
// HardwareKeyboard.instance.removeHandler(_handleKeyEvent);
WidgetsBinding.instance.removeObserver(this);
Get.delete<DesktopTabController>();
super.dispose();
}
@override
@@ -68,18 +110,23 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
backgroundColor: Theme.of(context).colorScheme.background,
body: DesktopTab(
controller: tabController,
tail: ActionIcon(
message: 'Settings',
icon: IconFont.menu,
onTap: DesktopTabPage.onAddSetting,
isClose: false,
tail: Offstage(
offstage: bind.isIncomingOnly() || bind.isDisableSettings(),
child: ActionIcon(
message: 'Settings',
icon: IconFont.menu,
onTap: DesktopTabPage.onAddSetting,
isClose: false,
),
),
blockTab: _block,
)));
return Platform.isMacOS || kUseCompatibleUiMode
return isMacOS || kUseCompatibleUiMode
? tabWidget
: Obx(
() => DragToResizeArea(
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
enableResizeEdges: windowManagerEnableResizeEdges,
child: tabWidget,
),
);

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:extended_text/extended_text.dart';
import 'package:flutter_hbb/desktop/widgets/dragable_divider.dart';
import 'package:percent_indicator/percent_indicator.dart';
import 'package:desktop_drop/desktop_drop.dart';
@@ -16,6 +17,8 @@ import 'package:flutter_hbb/models/file_model.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:flutter_hbb/web/dummy.dart'
if (dart.library.html) 'package:flutter_hbb/web/web_unique.dart';
import '../../consts.dart';
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
@@ -53,20 +56,24 @@ class FileManagerPage extends StatefulWidget {
{Key? key,
required this.id,
required this.password,
required this.tabController,
required this.isSharedPassword,
this.tabController,
this.connToken,
this.forceRelay})
: super(key: key);
final String id;
final String? password;
final bool? isSharedPassword;
final bool? forceRelay;
final DesktopTabController tabController;
final String? connToken;
final DesktopTabController? tabController;
@override
State<StatefulWidget> createState() => _FileManagerPageState();
}
class _FileManagerPageState extends State<FileManagerPage>
with AutomaticKeepAliveClientMixin {
with AutomaticKeepAliveClientMixin, WidgetsBindingObserver {
final _mouseFocusScope = Rx<MouseFocusScope>(MouseFocusScope.none);
final _dropMaskVisible = false.obs; // TODO impl drop mask
@@ -84,18 +91,27 @@ class _FileManagerPageState extends State<FileManagerPage>
_ffi.start(widget.id,
isFileTransfer: true,
password: widget.password,
isSharedPassword: widget.isSharedPassword,
connToken: widget.connToken,
forceRelay: widget.forceRelay);
WidgetsBinding.instance.addPostFrameCallback((_) {
_ffi.dialogManager
.showLoading(translate('Connecting...'), onCancel: closeConnection);
});
Get.put(_ffi, tag: 'ft_${widget.id}');
if (!Platform.isLinux) {
Get.put<FFI>(_ffi, tag: 'ft_${widget.id}');
if (!isLinux) {
WakelockPlus.enable();
}
if (isWeb) {
_ffi.ffiModel.updateEventListener(_ffi.sessionId, widget.id);
}
debugPrint("File manager page init success with id ${widget.id}");
_ffi.dialogManager.setOverlayState(_overlayKeyState);
widget.tabController.onSelected?.call(widget.id);
// Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.tabController?.onSelected?.call(widget.id);
});
WidgetsBinding.instance.addObserver(this);
}
@override
@@ -103,17 +119,26 @@ class _FileManagerPageState extends State<FileManagerPage>
model.close().whenComplete(() {
_ffi.close();
_ffi.dialogManager.dismissAll();
if (!Platform.isLinux) {
if (!isLinux) {
WakelockPlus.disable();
}
Get.delete<FFI>(tag: 'ft_${widget.id}');
});
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
bool get wantKeepAlive => true;
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed) {
jobController.jobTable.refresh();
}
}
@override
Widget build(BuildContext context) {
super.build(context);
@@ -123,10 +148,11 @@ class _FileManagerPageState extends State<FileManagerPage>
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: Row(
children: [
Flexible(
flex: 3,
child: dropArea(FileManagerView(
model.localController, _ffi, _mouseFocusScope))),
if (!isWeb)
Flexible(
flex: 3,
child: dropArea(FileManagerView(
model.localController, _ffi, _mouseFocusScope))),
Flexible(
flex: 3,
child: dropArea(FileManagerView(
@@ -167,10 +193,31 @@ class _FileManagerPageState extends State<FileManagerPage>
/// transfer status list
/// watch transfer status
Widget statusList() {
Widget getIcon(JobProgress job) {
final color = Theme.of(context).tabBarTheme.labelColor;
switch (job.type) {
case JobType.deleteDir:
case JobType.deleteFile:
return Icon(Icons.delete_outline, color: color);
default:
return Transform.rotate(
angle: isWeb
? job.isRemoteToLocal
? pi / 2
: pi / 2 * 3
: job.isRemoteToLocal
? pi
: 0,
child: Icon(Icons.arrow_forward_ios, color: color),
);
}
}
statusListView(List<JobProgress> jobs) => ListView.builder(
controller: ScrollController(),
itemBuilder: (BuildContext context, int index) {
final item = jobs[index];
final status = item.getStatus();
return Padding(
padding: const EdgeInsets.only(bottom: 5),
child: generateCard(
@@ -180,15 +227,8 @@ class _FileManagerPageState extends State<FileManagerPage>
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Transform.rotate(
angle: item.isRemoteToLocal ? pi : 0,
child: SvgPicture.asset("assets/arrow.svg",
colorFilter: svgColor(
Theme.of(context).tabBarTheme.labelColor)),
).paddingOnly(left: 15),
const SizedBox(
width: 16.0,
),
getIcon(item)
.marginSymmetric(horizontal: 10, vertical: 12),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -197,45 +237,28 @@ class _FileManagerPageState extends State<FileManagerPage>
Tooltip(
waitDuration: Duration(milliseconds: 500),
message: item.jobName,
child: Text(
item.fileName,
child: ExtendedText(
item.jobName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
).paddingSymmetric(vertical: 10),
),
Text(
'${translate("Total")} ${readableFileSize(item.totalSize.toDouble())}',
style: TextStyle(
fontSize: 12,
color: MyTheme.darkGray,
overflowWidget: TextOverflowWidget(
child: Text("..."),
position: TextOverflowPosition.start),
),
),
Offstage(
offstage: item.state != JobState.inProgress,
child: Text(
'${translate("Speed")} ${readableFileSize(item.speed)}/s',
style: TextStyle(
fontSize: 12,
color: MyTheme.darkGray,
),
),
Tooltip(
waitDuration: Duration(milliseconds: 500),
message: status,
child: Text(status,
style: TextStyle(
fontSize: 12,
color: MyTheme.darkGray,
)).marginOnly(top: 6),
),
Offstage(
offstage: item.state == JobState.inProgress,
child: Text(
translate(
item.display(),
),
style: TextStyle(
fontSize: 12,
color: MyTheme.darkGray,
),
),
),
Offstage(
offstage: item.state != JobState.inProgress,
offstage: item.type != JobType.transfer ||
item.state != JobState.inProgress,
child: LinearPercentIndicator(
padding: EdgeInsets.only(right: 15),
animateFromLastPercent: true,
center: Text(
'${(item.finishedSize / item.totalSize * 100).toStringAsFixed(0)}%',
@@ -245,7 +268,7 @@ class _FileManagerPageState extends State<FileManagerPage>
progressColor: MyTheme.accent,
backgroundColor: Theme.of(context).hoverColor,
lineHeight: kDesktopFileTransferRowHeight,
).paddingSymmetric(vertical: 15),
).paddingSymmetric(vertical: 8),
),
],
),
@@ -256,6 +279,7 @@ class _FileManagerPageState extends State<FileManagerPage>
Offstage(
offstage: item.state != JobState.paused,
child: MenuButton(
tooltip: translate("Resume"),
onPressed: () {
jobController.resumeJob(item.id);
},
@@ -268,7 +292,7 @@ class _FileManagerPageState extends State<FileManagerPage>
),
),
MenuButton(
padding: EdgeInsets.only(right: 15),
tooltip: translate("Delete"),
child: SvgPicture.asset(
"assets/close.svg",
colorFilter: svgColor(Colors.white),
@@ -281,11 +305,11 @@ class _FileManagerPageState extends State<FileManagerPage>
hoverColor: MyTheme.accent80,
),
],
),
).marginAll(12),
],
),
],
).paddingSymmetric(vertical: 10),
),
),
);
},
@@ -469,6 +493,9 @@ class _FileManagerViewState extends State<FileManagerView> {
}
Widget headTools() {
var uploadButtonTapPosition = RelativeRect.fill;
RxBool isUploadFolder =
(bind.mainGetLocalOption(key: 'upload-folder-button') == 'Y').obs;
return Container(
child: Column(
children: [
@@ -515,6 +542,7 @@ class _FileManagerViewState extends State<FileManagerView> {
Row(
children: [
MenuButton(
tooltip: translate('Back'),
padding: EdgeInsets.only(
right: 3,
),
@@ -534,6 +562,7 @@ class _FileManagerViewState extends State<FileManagerView> {
},
),
MenuButton(
tooltip: translate('Parent directory'),
child: RotatedBox(
quarterTurns: 3,
child: SvgPicture.asset(
@@ -598,6 +627,7 @@ class _FileManagerViewState extends State<FileManagerView> {
switch (_locationStatus.value) {
case LocationStatus.bread:
return MenuButton(
tooltip: translate('Search'),
onPressed: () {
_locationStatus.value = LocationStatus.fileSearchBar;
Future.delayed(
@@ -624,6 +654,7 @@ class _FileManagerViewState extends State<FileManagerView> {
);
case LocationStatus.fileSearchBar:
return MenuButton(
tooltip: translate('Clear'),
onPressed: () {
onSearchText("", isLocal);
_locationStatus.value = LocationStatus.bread;
@@ -639,6 +670,7 @@ class _FileManagerViewState extends State<FileManagerView> {
}
}),
MenuButton(
tooltip: translate('Refresh File'),
padding: EdgeInsets.only(
left: 3,
),
@@ -664,6 +696,7 @@ class _FileManagerViewState extends State<FileManagerView> {
isLocal ? MainAxisAlignment.start : MainAxisAlignment.end,
children: [
MenuButton(
tooltip: translate('Home'),
padding: EdgeInsets.only(
right: 3,
),
@@ -679,11 +712,27 @@ class _FileManagerViewState extends State<FileManagerView> {
hoverColor: Theme.of(context).hoverColor,
),
MenuButton(
tooltip: translate('Create Folder'),
onPressed: () {
final name = TextEditingController();
String? errorText;
_ffi.dialogManager.show((setState, close, context) {
name.addListener(() {
if (errorText != null) {
setState(() {
errorText = null;
});
}
});
submit() {
if (name.value.text.isNotEmpty) {
if (!PathUtil.validName(name.value.text,
controller.options.value.isWindows)) {
setState(() {
errorText = translate("Invalid folder name");
});
return;
}
controller.createDir(PathUtil.join(
controller.directory.value.path,
name.value.text,
@@ -715,6 +764,7 @@ class _FileManagerViewState extends State<FileManagerView> {
labelText: translate(
"Please enter the folder name",
),
errorText: errorText,
),
controller: name,
autofocus: true,
@@ -748,6 +798,7 @@ class _FileManagerViewState extends State<FileManagerView> {
hoverColor: Theme.of(context).hoverColor,
),
Obx(() => MenuButton(
tooltip: translate('Delete'),
onPressed: SelectedItems.valid(selectedItems.items)
? () async {
await (controller
@@ -767,6 +818,66 @@ class _FileManagerViewState extends State<FileManagerView> {
],
),
),
if (isWeb)
Obx(() => ElevatedButton.icon(
style: ButtonStyle(
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
isLocal
? EdgeInsets.only(left: 10)
: EdgeInsets.only(right: 10)),
backgroundColor: MaterialStateProperty.all(
selectedItems.items.isEmpty
? MyTheme.accent80
: MyTheme.accent,
),
),
onPressed: () =>
{webselectFiles(is_folder: isUploadFolder.value)},
label: InkWell(
hoverColor: Colors.transparent,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
focusColor: Colors.transparent,
onTapDown: (e) {
final x = e.globalPosition.dx;
final y = e.globalPosition.dy;
uploadButtonTapPosition =
RelativeRect.fromLTRB(x, y, x, y);
},
onTap: () async {
final value = await showMenu<bool>(
context: context,
position: uploadButtonTapPosition,
items: [
PopupMenuItem<bool>(
value: false,
child: Text(translate('Upload files')),
),
PopupMenuItem<bool>(
value: true,
child: Text(translate('Upload folder')),
),
]);
if (value != null) {
isUploadFolder.value = value;
bind.mainSetLocalOption(
key: 'upload-folder-button',
value: value ? 'Y' : '');
webselectFiles(is_folder: value);
}
},
child: Icon(Icons.arrow_drop_down),
),
icon: Text(
translate(isUploadFolder.isTrue
? 'Upload folder'
: 'Upload files'),
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.white,
),
).marginOnly(left: 8),
)).marginOnly(left: 16),
Obx(() => ElevatedButton.icon(
style: ButtonStyle(
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
@@ -800,19 +911,22 @@ class _FileManagerViewState extends State<FileManagerView> {
: Colors.white,
),
)
: RotatedBox(
quarterTurns: 2,
child: SvgPicture.asset(
"assets/arrow.svg",
colorFilter: svgColor(selectedItems.items.isEmpty
? Theme.of(context).brightness ==
Brightness.light
? MyTheme.grayBg
: MyTheme.darkGray
: Colors.white),
alignment: Alignment.bottomRight,
),
),
: isWeb
? Offstage()
: RotatedBox(
quarterTurns: 2,
child: SvgPicture.asset(
"assets/arrow.svg",
colorFilter: svgColor(
selectedItems.items.isEmpty
? Theme.of(context).brightness ==
Brightness.light
? MyTheme.grayBg
: MyTheme.darkGray
: Colors.white),
alignment: Alignment.bottomRight,
),
),
label: isLocal
? SvgPicture.asset(
"assets/arrow.svg",
@@ -824,7 +938,7 @@ class _FileManagerViewState extends State<FileManagerView> {
: Colors.white),
)
: Text(
translate('Receive'),
translate(isWeb ? 'Download' : 'Receive'),
style: TextStyle(
color: selectedItems.items.isEmpty
? Theme.of(context).brightness ==
@@ -879,6 +993,7 @@ class _FileManagerViewState extends State<FileManagerView> {
menuPos = RelativeRect.fromLTRB(x, y, x, y);
},
child: MenuButton(
tooltip: translate('More'),
onPressed: () => mod_menu.showMenu(
context: context,
position: menuPos,
@@ -910,6 +1025,7 @@ class _FileManagerViewState extends State<FileManagerView> {
BuildContext context, ScrollController scrollController) {
final fd = controller.directory.value;
final entries = fd.entries;
Rx<Entry?> rightClickEntry = Rx(null);
return ListSearchActionListener(
node: _keyboardNode,
@@ -968,16 +1084,70 @@ class _FileManagerViewState extends State<FileManagerView> {
final lastModifiedStr = entry.isDrive
? " "
: "${entry.lastModified().toString().replaceAll(".000", "")} ";
var secondaryPosition = RelativeRect.fromLTRB(0, 0, 0, 0);
onTap() {
final items = selectedItems;
// handle double click
if (_checkDoubleClick(entry)) {
controller.openDirectory(entry.path);
items.clear();
return;
}
_onSelectedChanged(items, filteredEntries, entry, isLocal);
}
onSecondaryTap() {
final items = [
if (!entry.isDrive &&
versionCmp(_ffi.ffiModel.pi.version, "1.3.0") >= 0)
mod_menu.PopupMenuItem(
child: Text(translate("Rename")),
height: CustomPopupMenuTheme.height,
onTap: () {
controller.renameAction(entry, isLocal);
},
)
];
if (items.isNotEmpty) {
rightClickEntry.value = entry;
final future = mod_menu.showMenu(
context: context,
position: secondaryPosition,
items: items,
);
future.then((value) {
rightClickEntry.value = null;
});
future.onError((error, stackTrace) {
rightClickEntry.value = null;
});
}
}
onSecondaryTapDown(details) {
secondaryPosition = RelativeRect.fromLTRB(
details.globalPosition.dx,
details.globalPosition.dy,
details.globalPosition.dx,
details.globalPosition.dy);
}
return Padding(
padding: EdgeInsets.symmetric(vertical: 1),
child: Obx(() => Container(
decoration: BoxDecoration(
color: selectedItems.items.contains(entry)
? Theme.of(context).hoverColor
? MyTheme.button
: Theme.of(context).cardColor,
borderRadius: BorderRadius.all(
Radius.circular(5.0),
),
border: rightClickEntry.value == entry
? Border.all(
color: MyTheme.button,
width: 1.0,
)
: null,
),
key: ValueKey(entry.name),
height: kDesktopFileTransferRowHeight,
@@ -1016,22 +1186,19 @@ class _FileManagerViewState extends State<FileManagerView> {
),
Expanded(
child: Text(entry.name.nonBreaking,
style: TextStyle(
color: selectedItems.items
.contains(entry)
? Colors.white
: null),
overflow:
TextOverflow.ellipsis))
]),
)),
),
onTap: () {
final items = selectedItems;
// handle double click
if (_checkDoubleClick(entry)) {
controller.openDirectory(entry.path);
items.clear();
return;
}
_onSelectedChanged(
items, filteredEntries, entry, isLocal);
},
onTap: onTap,
onSecondaryTap: onSecondaryTap,
onSecondaryTapDown: onSecondaryTapDown,
),
SizedBox(
width: 2.0,
@@ -1048,11 +1215,17 @@ class _FileManagerViewState extends State<FileManagerView> {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12,
color: MyTheme.darkGray,
color: selectedItems.items
.contains(entry)
? Colors.white70
: MyTheme.darkGray,
),
)),
),
),
onTap: onTap,
onSecondaryTap: onSecondaryTap,
onSecondaryTapDown: onSecondaryTapDown,
),
// Divider from header.
SizedBox(
@@ -1068,9 +1241,16 @@ class _FileManagerViewState extends State<FileManagerView> {
sizeStr,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 10, color: MyTheme.darkGray),
fontSize: 10,
color:
selectedItems.items.contains(entry)
? Colors.white70
: MyTheme.darkGray),
),
),
onTap: onTap,
onSecondaryTap: onSecondaryTap,
onSecondaryTapDown: onSecondaryTapDown,
),
),
],
@@ -1295,7 +1475,7 @@ class _FileManagerViewState extends State<FileManagerView> {
onPointerSignal: (e) {
if (e is PointerScrollEvent) {
final sc = _breadCrumbScroller;
final scale = Platform.isWindows ? 2 : 4;
final scale = isWindows ? 2 : 4;
sc.jumpTo(sc.offset + e.scrollDelta.dy / scale);
}
},

View File

@@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:io';
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/material.dart';
@@ -35,6 +34,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
WindowController.fromWindowId(windowId())
.setTitle(getWindowNameWithId(id));
};
tabController.onRemoved = (_, id) => onRemoveId(id);
tabController.add(TabInfo(
key: params['id'],
label: params['id'],
@@ -45,8 +45,10 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
key: ValueKey(params['id']),
id: params['id'],
password: params['password'],
isSharedPassword: params['isSharedPassword'],
tabController: tabController,
forceRelay: params['forceRelay'],
connToken: params['connToken'],
)));
}
@@ -54,10 +56,8 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
void initState() {
super.initState();
tabController.onRemoved = (_, id) => onRemoveId(id);
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
print(
debugPrint(
"[FileTransfer] call ${call.method} with args ${call.arguments} from window $fromWindowId to ${windowId()}");
// for simplify, just replace connectionId
if (call.method == kWindowEventNewFileTransfer) {
@@ -74,8 +74,10 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
key: ValueKey(id),
id: id,
password: args['password'],
isSharedPassword: args['isSharedPassword'],
tabController: tabController,
forceRelay: args['forceRelay'],
connToken: args['connToken'],
)));
} else if (call.method == "onDestroy") {
tabController.clear();
@@ -90,23 +92,28 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
@override
Widget build(BuildContext context) {
final tabWidget = Container(
decoration: BoxDecoration(
border: Border.all(color: MyTheme.color(context).border!)),
child: Scaffold(
backgroundColor: Theme.of(context).cardColor,
body: DesktopTab(
controller: tabController,
onWindowCloseButton: handleWindowCloseButton,
tail: const AddButton().paddingOnly(left: 10),
labelGetter: DesktopTab.tablabelGetter,
)),
);
return Platform.isMacOS || kUseCompatibleUiMode
final child = Scaffold(
backgroundColor: Theme.of(context).cardColor,
body: DesktopTab(
controller: tabController,
onWindowCloseButton: handleWindowCloseButton,
tail: const AddButton(),
selectedBorderColor: MyTheme.accent,
labelGetter: DesktopTab.tablabelGetter,
));
final tabWidget = isLinux
? buildVirtualWindowFrame(context, child)
: Container(
decoration: BoxDecoration(
border: Border.all(color: MyTheme.color(context).border!)),
child: child,
);
return isMacOS || kUseCompatibleUiMode
? tabWidget
: SubWindowDragToResizeArea(
child: tabWidget,
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
enableResizeEdges: subWindowManagerEnableResizeEdges,
windowId: stateGlobal.windowId,
);
}
@@ -127,9 +134,9 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
tabController.clear();
return true;
} else {
final opt = "enable-confirm-closing-tabs";
final bool res;
if (!option2bool(opt, bind.mainGetLocalOption(key: opt))) {
if (!option2bool(kOptionEnableConfirmClosingTabs,
bind.mainGetLocalOption(key: kOptionEnableConfirmClosingTabs))) {
res = true;
} else {
res = await closeConfirmDialog();

View File

@@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
@@ -19,9 +21,7 @@ class InstallPage extends StatefulWidget {
class _InstallPageState extends State<InstallPage> {
final tabController = DesktopTabController(tabType: DesktopTabType.main);
@override
void initState() {
super.initState();
_InstallPageState() {
Get.put<DesktopTabController>(tabController);
const label = "install";
tabController.add(TabInfo(
@@ -43,6 +43,7 @@ class _InstallPageState extends State<InstallPage> {
Widget build(BuildContext context) {
return DragToResizeArea(
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
enableResizeEdges: windowManagerEnableResizeEdges,
child: Container(
child: Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
@@ -73,10 +74,16 @@ class _InstallPageBodyState extends State<_InstallPageBody>
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 12),
);
_InstallPageBodyState() {
controller = TextEditingController(text: bind.installInstallPath());
final installOptions = jsonDecode(bind.installInstallOptions());
startmenu.value = installOptions['STARTMENUSHORTCUTS'] != '0';
desktopicon.value = installOptions['DESKTOPSHORTCUTS'] != '0';
}
@override
void initState() {
windowManager.addListener(this);
controller = TextEditingController(text: bind.installInstallPath());
super.initState();
}
@@ -248,6 +255,7 @@ class _InstallPageBodyState extends State<_InstallPageBody>
if (desktopicon.value) args += ' desktopicon';
bind.installInstallMe(options: args, path: controller.text);
}
do_install();
}

View File

@@ -25,19 +25,23 @@ class _PortForward {
}
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);
const PortForwardPage({
Key? key,
required this.id,
required this.password,
required this.tabController,
required this.isRDP,
required this.isSharedPassword,
this.forceRelay,
this.connToken,
}) : super(key: key);
final String id;
final String? password;
final DesktopTabController tabController;
final bool isRDP;
final bool? forceRelay;
final bool? isSharedPassword;
final String? connToken;
@override
State<PortForwardPage> createState() => _PortForwardPageState();
@@ -58,11 +62,16 @@ class _PortForwardPageState extends State<PortForwardPage>
_ffi.start(widget.id,
isPortForward: true,
password: widget.password,
isSharedPassword: widget.isSharedPassword,
forceRelay: widget.forceRelay,
connToken: widget.connToken,
isRdp: widget.isRDP);
Get.put(_ffi, tag: 'pf_${widget.id}');
Get.put<FFI>(_ffi, tag: 'pf_${widget.id}');
debugPrint("Port forward page init success with id ${widget.id}");
widget.tabController.onSelected?.call(widget.id);
// Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.tabController.onSelected?.call(widget.id);
});
}
@override
@@ -138,8 +147,9 @@ class _PortForwardPageState extends State<PortForwardPage>
child: Text(translate(label)).marginOnly(left: _kTextLeftMargin));
return Theme(
data: Theme.of(context)
.copyWith(backgroundColor: Theme.of(context).colorScheme.background),
data: Theme.of(context).copyWith(
colorScheme: Theme.of(context).colorScheme,
),
child: Obx(() => ListView.builder(
controller: ScrollController(),
itemCount: pfs.length + 2,
@@ -286,7 +296,7 @@ class _PortForwardPageState extends State<PortForwardPage>
).marginOnly(left: _kTextLeftMargin));
return Theme(
data: Theme.of(context)
.copyWith(backgroundColor: Theme.of(context).colorScheme.background),
.copyWith(colorScheme: Theme.of(context).colorScheme),
child: ListView.builder(
controller: ScrollController(),
itemCount: 2,

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