Compare commits

..

142 Commits
1.2.7 ... 1.3.0

Author SHA1 Message Date
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
202 changed files with 7961 additions and 2738 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

@@ -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:
@@ -112,6 +112,8 @@ jobs:
libgstreamer-plugins-base1.0-dev \
libgtk-3-dev \
libpulse-dev \
libva-dev \
libvdpau-dev \
libxcb-randr0-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \

View File

@@ -19,7 +19,12 @@ jobs:
steps:
- name: Generate RustDesk version file
run: |
UPSTREAM_VERNAME="$(curl https://api.github.com/repos/rustdesk/rustdesk/releases/latest | jq -r .tag_name | sed 's/^v//')"
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

View File

@@ -18,9 +18,11 @@ on:
# in this file!
env:
WIN_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
SCITER_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
CARGO_NDK_VERSION: "3.1.2"
SCITER_ARMV7_CMAKE_VERSION: "3.29.7"
SCITER_NASM_DEBVERSION: "2.14-1"
LLVM_VERSION: "15.0.6"
FLUTTER_VERSION: "3.19.6"
ANDROID_FLUTTER_VERSION: "3.13.9" # >= 3.16 is very slow on my android phone, but work well on most of others. We may switch to new flutter after changing to texture rendering (I believe it can solve my problem).
@@ -29,10 +31,10 @@ env:
FLUTTER_ELINUX_VERSION: "3.16.9"
TAG_NAME: "${{ inputs.upload-tag }}"
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2024.06.15
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
VERSION: "1.2.7"
NDK_VERSION: "r26d"
# vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "1de2026f28ead93ff1773e6e680387643e914ea1"
VERSION: "1.3.0"
NDK_VERSION: "r27"
#signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
@@ -56,7 +58,7 @@ jobs:
fail-fast: false
build-for-windows-flutter:
name: ${{ matrix.job.target }}
name: ${{ matrix.job.target }}
needs: [build-RustDeskTempTopMostWindow]
runs-on: ${{ matrix.job.os }}
strategy:
@@ -65,7 +67,12 @@ jobs:
job:
# - { target: i686-pc-windows-msvc , os: windows-2022 }
# - { target: x86_64-pc-windows-gnu , os: windows-2022 }
- { target: x86_64-pc-windows-msvc, os: windows-2022, arch: x86_64, vcpkg-triplet: x64-windows-static }
- {
target: x86_64-pc-windows-msvc,
os: windows-2022,
arch: x86_64,
vcpkg-triplet: x64-windows-static,
}
# - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 }
steps:
- name: Export GitHub Actions cache environment variables
@@ -93,7 +100,7 @@ jobs:
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ env.WIN_RUST_VERSION }}
toolchain: ${{ env.SCITER_RUST_VERSION }}
targets: ${{ matrix.job.target }}
components: "rustfmt"
@@ -113,10 +120,25 @@ jobs:
with:
vcpkgDirectory: C:\vcpkg
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
env:
VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }}
run: |
$VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"
if ! $VCPKG_ROOT/vcpkg \
install \
--triplet ${{ matrix.job.vcpkg-triplet }} \
--x-install-root="$VCPKG_ROOT/installed"; then
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
echo "$_1:"
echo "======"
cat "$_1"
echo "======"
echo ""
done
exit 1
fi
shell: bash
- name: Build rustdesk
@@ -156,7 +178,7 @@ jobs:
if: env.UPLOAD_ARTIFACT == 'true'
uses: actions/upload-artifact@master
with:
name: rustdesk-unsigned-windows-${{ matrix.job.arch }}
name: rustdesk-unsigned-windows-${{ matrix.job.arch }}
path: rustdesk
- name: Sign rustdesk files
@@ -219,7 +241,12 @@ jobs:
job:
# - { target: i686-pc-windows-msvc , os: windows-2022 }
# - { target: x86_64-pc-windows-gnu , os: windows-2022 }
- { target: i686-pc-windows-msvc, os: windows-2022, arch: x86, vcpkg-triplet: x86-windows-static }
- {
target: i686-pc-windows-msvc,
os: windows-2022,
arch: x86,
vcpkg-triplet: x86-windows-static,
}
# - { target: aarch64-pc-windows-msvc, os: windows-2022 }
steps:
- name: Export GitHub Actions cache environment variables
@@ -230,7 +257,7 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install LLVM and Clang
uses: rustdesk-org/install-llvm-action-32bit@master
@@ -253,10 +280,25 @@ jobs:
with:
vcpkgDirectory: C:\vcpkg
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
env:
VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }}
run: |
$VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"
if ! $VCPKG_ROOT/vcpkg \
install \
--triplet ${{ matrix.job.vcpkg-triplet }} \
--x-install-root="$VCPKG_ROOT/installed"; then
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
echo "$_1:"
echo "======"
cat "$_1"
echo "======"
echo ""
done
exit 1
fi
shell: bash
- name: Build rustdesk
@@ -276,7 +318,7 @@ jobs:
# Do not remove x64 files, because the user may run the 32bit version on a 64bit system.
# Do not remove ./usbmmidd_v2/deviceinstaller64.exe, as x86 exe cannot install and uninstall drivers when running on x64,
# we need the x64 exe to install and uninstall the driver.
rm -rf ./usbmmidd_v2/deviceinstaller.exe ./usbmmidd_v2/usbmmidd.bat
rm -rf ./usbmmidd_v2/deviceinstaller.exe ./usbmmidd_v2/usbmmidd.bat
mv ./usbmmidd_v2 ./Release || true
- name: find Runner.res
@@ -341,7 +383,7 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install flutter rust bridge deps
shell: bash
@@ -424,9 +466,9 @@ jobs:
- name: Install dependencies
run: |
brew install nasm
brew install nasm yasm
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install flutter
uses: subosito/flutter-action@v2
with:
@@ -437,10 +479,23 @@ jobs:
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
run: |
$VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"
if ! $VCPKG_ROOT/vcpkg \
install \
--triplet ${{ matrix.job.vcpkg-triplet }} \
--x-install-root="$VCPKG_ROOT/installed"; then
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
echo "$_1:"
echo "======"
cat "$_1"
echo "======"
echo ""
done
exit 1
fi
shell: bash
- name: Install Rust toolchain
@@ -506,7 +561,7 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
# $VCPKG_ROOT/vcpkg install --triplet arm64-ios --x-install-root="$VCPKG_ROOT/installed"
@@ -576,7 +631,7 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Import the codesign cert
if: env.MACOS_P12_BASE64 != null
@@ -651,10 +706,22 @@ jobs:
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
run: |
$VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed"
if ! $VCPKG_ROOT/vcpkg \
install \
--x-install-root="$VCPKG_ROOT/installed"; then
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
echo "$_1:"
echo "======"
cat "$_1"
echo "======"
echo ""
done
exit 1
fi
- name: Show version information (Rust, cargo, Clang)
shell: bash
@@ -770,15 +837,35 @@ jobs:
arch: aarch64,
target: aarch64-linux-android,
os: ubuntu-20.04,
openssl-arch: android-arm64,
reltype: release,
suffix: "",
}
- {
arch: armv7,
target: armv7-linux-androideabi,
os: ubuntu-20.04,
openssl-arch: android-arm,
reltype: release,
suffix: "",
}
- {
arch: x86_64,
target: x86_64-linux-android,
os: ubuntu-20.04,
reltype: release,
suffix: "",
}
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: false
dotnet: true
haskell: true
large-packages: false
docker-images: true
swap-storage: false
- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6
with:
@@ -822,7 +909,7 @@ jobs:
wget
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install flutter
uses: subosito/flutter-action@v2
with:
@@ -839,17 +926,34 @@ jobs:
with:
vcpkgDirectory: /opt/artifacts/vcpkg
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
run: |
case ${{ matrix.job.target }} in
aarch64-linux-android)
./flutter/build_android_deps.sh arm64-v8a
ANDROID_TARGET=arm64-v8a
;;
armv7-linux-androideabi)
./flutter/build_android_deps.sh armeabi-v7a
ANDROID_TARGET=armeabi-v7a
;;
x86_64-linux-android)
ANDROID_TARGET=x86_64
;;
i686-linux-android)
ANDROID_TARGET=x86
;;
esac
if ! ./flutter/build_android_deps.sh "${ANDROID_TARGET}"; then
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
echo "$_1:"
echo "======"
cat "$_1"
echo "======"
echo ""
done
exit 1
fi
shell: bash
- name: Restore bridge files
@@ -894,8 +998,24 @@ jobs:
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
;;
x86_64-linux-android)
./flutter/ndk_x64.sh
mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so
;;
i686-linux-android)
./flutter/ndk_x86.sh
mkdir -p ./flutter/android/app/src/main/jniLibs/x86
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so
;;
esac
- name: Upload Rustdesk library to Artifacts
uses: actions/upload-artifact@master
with:
name: librustdesk.so.${{ matrix.job.target }}
path: ./target/${{ matrix.job.target }}/release/liblibrustdesk.so
- name: Build rustdesk
shell: bash
env:
@@ -911,8 +1031,8 @@ jobs:
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
flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-arm64 --split-per-abi
mv build/app/outputs/flutter-apk/app-arm64-v8a-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
;;
armv7-linux-androideabi)
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
@@ -920,13 +1040,31 @@ jobs:
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
flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-arm --split-per-abi
mv build/app/outputs/flutter-apk/app-armeabi-v7a-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
;;
x86_64-linux-android)
mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so
# build flutter
pushd flutter
flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-x64 --split-per-abi
mv build/app/outputs/flutter-apk/app-x86_64-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
;;
i686-linux-android)
mkdir -p ./flutter/android/app/src/main/jniLibs/x86
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so
# build flutter
pushd flutter
flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-x86 --split-per-abi
mv build/app/outputs/flutter-apk/app-x86-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
;;
esac
popd
mkdir -p signed-apk; pushd signed-apk
mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk .
mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk .
- uses: r0adkll/sign-android-release@v1
name: Sign app APK
@@ -967,9 +1105,182 @@ jobs:
files: |
signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
build-rustdesk-android-universal:
needs: [build-rustdesk-android]
name: build rustdesk android universal apk
if: ${{ inputs.upload-artifact }}
runs-on: ubuntu-20.04
env:
reltype: release
x86_target: "" # can be ",android-x86"
suffix: ""
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: false
dotnet: true
haskell: true
large-packages: false
docker-images: true
swap-storage: false
- 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: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
clang \
cmake \
curl \
gcc-multilib \
git \
g++ \
g++-multilib \
libappindicator3-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 \
ninja-build \
openjdk-11-jdk-headless \
pkg-config \
tree \
wget
- name: Checkout source code
uses: actions/checkout@v4
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.ANDROID_FLUTTER_VERSION }}
- name: Restore bridge files
uses: actions/download-artifact@master
with:
name: bridge-artifact
path: ./
- name: Download Rustdesk library from Artifacts
uses: actions/download-artifact@master
with:
name: librustdesk.so.aarch64-linux-android
path: ./flutter/android/app/src/main/jniLibs/arm64-v8a
- name: Download Rustdesk library from Artifacts
uses: actions/download-artifact@master
with:
name: librustdesk.so.armv7-linux-androideabi
path: ./flutter/android/app/src/main/jniLibs/armeabi-v7a
- name: Download Rustdesk library from Artifacts
uses: actions/download-artifact@master
with:
name: librustdesk.so.x86_64-linux-android
path: ./flutter/android/app/src/main/jniLibs/x86_64
- name: Download Rustdesk library from Artifacts
if: ${{ env.reltype == 'debug' }}
uses: actions/download-artifact@master
with:
name: librustdesk.so.i686-linux-android
path: ./flutter/android/app/src/main/jniLibs/x86
- name: fix android for flutter 3.13
if: $${{ env.ANDROID_FLUTTER_VERSION == '3.13.9' }}
run: |
sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml
cd flutter/lib
find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g'
- 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
mv ./flutter/android/app/src/main/jniLibs/arm64-v8a/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
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/
mv ./flutter/android/app/src/main/jniLibs/armeabi-v7a/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
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/
mv ./flutter/android/app/src/main/jniLibs/x86_64/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/
if [ "${{ env.reltype }}" = "debug" ]; then
mv ./flutter/android/app/src/main/jniLibs/x86/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/
fi
# build flutter
pushd flutter
flutter build apk "--${{ env.reltype }}" --target-platform android-arm64,android-arm,android-x64${{ env.x86_target }}
popd
mkdir -p signed-apk
mv ./flutter/build/app/outputs/flutter-apk/app-${{ env.reltype }}.apk signed-apk/rustdesk-${{ env.VERSION }}-universal${{ env.suffix }}.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: Upload Artifacts
if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
uses: actions/upload-artifact@master
with:
name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}}
- name: Publish signed apk package
if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
${{steps.sign-rustdesk.outputs.signedReleaseFile}}
- name: Publish unsigned apk package
if: env.ANDROID_SIGNING_KEY == null && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
signed-apk/rustdesk-${{ env.VERSION }}-universal${{ env.suffix }}.apk
build-rustdesk-linux:
needs: [generate-bridge-linux]
name: build rustdesk linux ${{ matrix.job.target }}
name: build rustdesk linux ${{ matrix.job.target }}
runs-on: ${{ matrix.job.on }}
strategy:
fail-fast: false
@@ -1010,7 +1321,7 @@ jobs:
sudo apt-get install -y nasm qemu-user-static
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set Swap Space
if: ${{ matrix.job.arch == 'x86_64' }}
@@ -1054,11 +1365,24 @@ jobs:
with:
vcpkgDirectory: /opt/artifacts/vcpkg
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
run: |
$VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"
if ! $VCPKG_ROOT/vcpkg \
install \
--triplet ${{ matrix.job.vcpkg-triplet }} \
--x-install-root="$VCPKG_ROOT/installed"; then
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
echo "$_1:"
echo "======"
cat "$_1"
echo "======"
echo ""
done
exit 1
fi
shell: bash
- name: Restore bridge files
@@ -1237,16 +1561,16 @@ jobs:
rustdesk-*.deb
rustdesk-*.rpm
- name: Upload deb
- name: Upload deb
uses: actions/upload-artifact@master
if: env.UPLOAD_ARTIFACT == 'true'
with:
name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb
path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb
# only x86_64 for arch since we can not find newest arm64 docker image to build
# old arch image does not make sense for arch since it is "arch" which always update to date
# and failed to makepkg arm64 on x86_64
# and failed to makepkg arm64 on x86_64
- name: Patch archlinux PKGBUILD
if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true'
run: |
@@ -1290,6 +1614,7 @@ jobs:
deb_arch: amd64,
sciter_arch: x64,
vcpkg-triplet: x64-linux,
extra_features: ",hwcodec",
}
- {
arch: armv7,
@@ -1299,6 +1624,7 @@ jobs:
deb_arch: armhf,
sciter_arch: arm32,
vcpkg-triplet: arm-linux,
extra_features: "",
}
steps:
- name: Export GitHub Actions cache environment variables
@@ -1308,25 +1634,8 @@ jobs:
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
cmake \
crossbuild-essential-armhf \
curl \
g++ \
gcc \
git \
nasm \
ninja-build \
pkg-config \
unzip \
xz-utils \
zip
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Free Space
run: |
@@ -1336,7 +1645,7 @@ jobs:
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
toolchain: ${{ env.SCITER_RUST_VERSION }}
targets: ${{ matrix.job.target }}
components: "rustfmt"
@@ -1345,18 +1654,6 @@ jobs:
RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}')
echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV
- 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: |
cp $PWD/res/vcpkg/linux.cmake $VCPKG_ROOT/scripts/toolchains/linux.cmake
$VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"
shell: bash
- uses: rustdesk-org/run-on-arch-action@amd64-support
name: Build rustdesk sciter binary for ${{ matrix.job.arch }}
id: vcpkg
@@ -1368,14 +1665,12 @@ jobs:
ls -l "${PWD}"
dockerRunArgs: |
--volume "${PWD}:/workspace"
--volume "/opt/artifacts:/opt/artifacts"
shell: /bin/bash
install: |
apt-get update -y
apt-get update
apt-get install -y \
build-essential \
clang \
cmake \
curl \
gcc \
git \
@@ -1398,39 +1693,93 @@ jobs:
libxcb-xfixes0-dev \
libxdo-dev \
libxfixes-dev \
nasm \
ninja-build \
pkg-config \
python3 \
python3.7 \
rpm \
unzip \
wget \
xz-utils
run: |
# disable git safe.directory
git config --global --add safe.directory "*"
xz-utils \
zip
# arm-linux needs CMake and vcokg built from source as there
# are no prebuilts available from Kitware and Microsoft
if [ "${{ matrix.job.vcpkg-triplet }}" = "arm-linux" ]; then
# install gcc/g++ 8 for vcpkg and OpenSSL headers for CMake
apt-get install -y gcc-8 g++-8 libssl-dev
# bootstrap CMake amd add it to PATH
git clone --depth 1 https://github.com/kitware/cmake -b "v${{ env.SCITER_ARMV7_CMAKE_VERSION }}" /tmp/cmake
pushd /tmp/cmake
./bootstrap --generator='Unix Makefiles' "--prefix=/opt/cmake-${{ env.SCITER_ARMV7_CMAKE_VERSION }}-linux-armhf"
make -j1 install
popd
rm -rf /tmp/cmake
export PATH="/opt/cmake-${{ env.SCITER_ARMV7_CMAKE_VERSION }}-linux-armhf/bin:$PATH"
fi
# bootstrap vcpkg and set VCPKG_ROOT
export VCPKG_ROOT=/opt/artifacts/vcpkg
mkdir -p /opt/artifacts
pushd /opt/artifacts
rm -rf vcpkg
git clone https://github.com/microsoft/vcpkg
pushd vcpkg
git reset --hard ${{ env.VCPKG_COMMIT_ID }}
# build vcpkg helper executable with gcc-8 for arm-linux but use prebuilt one on x64-linux
if [ "${{ matrix.job.vcpkg-triplet }}" = "arm-linux" ]; then
CC=/usr/bin/gcc-8 CXX=/usr/bin/g++-8 sh bootstrap-vcpkg.sh -disableMetrics
else
sh bootstrap-vcpkg.sh -disableMetrics
fi
popd
popd
# rust
pushd /opt
# do not use rustup, because memory overflow in qemu
wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}.tar.gz
wget --output-document rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}.tar.gz
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
cd rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }} && ./install.sh
pushd rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
./install.sh
popd
rm -rf rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
# edit config
popd
# install newer nasm for aom
wget --output-document nasm.deb "http://ftp.us.debian.org/debian/pool/main/n/nasm/nasm_${{ env.SCITER_NASM_DEBVERSION }}_${{ matrix.job.deb_arch }}.deb"
dpkg -i nasm.deb
rm -f nasm.deb
run: |
# disable git safe.directory
git config --global --add safe.directory "*"
# set python3.7 as default python3
update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1
# add built CMake to PATH and set VCPKG_FORCE_SYSTEM_BINARIES Afor arm-linux
if [ "${{ matrix.job.vcpkg-triplet }}" = "arm-linux" ]; then
export PATH="/opt/cmake-${{ env.SCITER_ARMV7_CMAKE_VERSION }}-linux-armhf/bin:$PATH"
export VCPKG_FORCE_SYSTEM_BINARIES=1
fi
# edit cargo config
mkdir -p ~/.cargo/
echo """
[source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index'
""" > ~/.cargo/config
cat ~/.cargo/config
# build
pushd /workspace
python3 ./res/inline-sciter.py
# install dependencies from vcpkg
export VCPKG_ROOT=/opt/artifacts/vcpkg
if ! $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"; then
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
echo "$_1:"
echo "======"
cat "$_1"
echo "======"
echo ""
done
exit 1
fi
# build rustdesk
python3 ./res/inline-sciter.py
export CARGO_INCREMENTAL=0
cargo build --features inline --release --bins --jobs 1
# package
cargo build --features inline${{ matrix.job.extra_features }} --release --bins --jobs 1
# make debian package
mkdir -p ./Release
mv ./target/release/rustdesk ./Release/rustdesk
wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/${{ matrix.job.sciter_arch }}/libsciter-gtk.so
@@ -1454,7 +1803,7 @@ jobs:
files: |
rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb
- name: Upload deb
- name: Upload deb
uses: actions/upload-artifact@master
if: env.UPLOAD_ARTIFACT == 'true'
with:
@@ -1470,17 +1819,11 @@ jobs:
fail-fast: false
matrix:
job:
- {
target: x86_64-unknown-linux-gnu,
arch: x86_64,
}
- {
target: aarch64-unknown-linux-gnu,
arch: aarch64,
}
- { target: x86_64-unknown-linux-gnu, arch: x86_64 }
- { target: aarch64-unknown-linux-gnu, arch: aarch64 }
steps:
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Download Binary
uses: actions/download-artifact@master
@@ -1519,7 +1862,7 @@ jobs:
build-flatpak:
name: Build flatpak ${{ matrix.job.target }}${{ matrix.job.suffix }}
needs:
needs:
- build-rustdesk-linux
- build-rustdesk-linux-sciter
runs-on: ${{ matrix.job.on }}
@@ -1552,7 +1895,7 @@ jobs:
}
steps:
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Download Binary
uses: actions/download-artifact@master
@@ -1587,7 +1930,7 @@ jobs:
# disable git safe.directory
git config --global --add safe.directory "*"
pushd /workspace
# install
# install
apt-get update -y
apt-get install -y \
cmake \
@@ -1628,7 +1971,7 @@ jobs:
RELEASE_NAME: web-basic
steps:
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Prepare env
run: |

View File

@@ -18,7 +18,7 @@ env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2024.06.15
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
VERSION: "1.2.7"
VERSION: "1.3.0"
NDK_VERSION: "r26d"
#signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"

363
Cargo.lock generated
View File

@@ -123,7 +123,7 @@ checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android-wakelock"
version = "0.1.0"
source = "git+https://github.com/21pages/android-wakelock#d0292e5a367e627c4fa6f1ca6bdfad005dca7d90"
source = "git+https://github.com/rustdesk-org/android-wakelock#d0292e5a367e627c4fa6f1ca6bdfad005dca7d90"
dependencies = [
"jni 0.21.1",
"log",
@@ -224,7 +224,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "arboard"
version = "3.4.0"
source = "git+https://github.com/rustdesk-org/arboard#27b4e503caa70ec6306e5270461429f2cf907ad6"
source = "git+https://github.com/rustdesk-org/arboard#a04bdb1b368a99691822c33bf0f7ed497d6a7a35"
dependencies = [
"clipboard-win",
"core-graphics 0.23.2",
@@ -234,24 +234,13 @@ dependencies = [
"objc2-app-kit",
"objc2-foundation",
"parking_lot",
"resvg",
"serde 1.0.203",
"serde_derive",
"windows-sys 0.48.0",
"wl-clipboard-rs",
"x11rb 0.13.1",
]
[[package]]
name = "arrayref"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "async-broadcast"
version = "0.5.1"
@@ -987,11 +976,14 @@ dependencies = [
[[package]]
name = "clipboard-master"
version = "4.0.0-beta.6"
source = "git+https://github.com/rustdesk-org/clipboard-master#5268c7b3d7728699566ad863da0911f249706f8c"
source = "git+https://github.com/rustdesk-org/clipboard-master#4fb62e5b62fb6350d82b571ec7ba94b3cd466695"
dependencies = [
"objc",
"objc-foundation",
"objc_id",
"wayland-client",
"wayland-protocols",
"wayland-protocols-wlr",
"windows-win",
"wl-clipboard-rs",
"x11-clipboard 0.9.2",
@@ -1000,9 +992,9 @@ dependencies = [
[[package]]
name = "clipboard-win"
version = "5.3.1"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad"
checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"
dependencies = [
"error-code",
]
@@ -1526,12 +1518,6 @@ dependencies = [
"dasp_sample",
]
[[package]]
name = "data-url"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]]
name = "dbus"
version = "0.9.7"
@@ -1691,7 +1677,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [
"libloading 0.7.4",
"libloading 0.8.4",
]
[[package]]
@@ -2081,12 +2067,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]]
name = "flume"
version = "0.11.0"
@@ -2143,29 +2123,6 @@ dependencies = [
"libm",
]
[[package]]
name = "fontconfig-parser"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d"
dependencies = [
"roxmltree 0.19.0",
]
[[package]]
name = "fontdb"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770"
dependencies = [
"fontconfig-parser",
"log",
"memmap2",
"slotmap",
"tinyvec",
"ttf-parser",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
@@ -3087,8 +3044,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hwcodec"
version = "0.6.0"
source = "git+https://github.com/21pages/hwcodec#89879f2f02c6f74e88a4a43744a1153aec5b7e7f"
version = "0.7.0"
source = "git+https://github.com/rustdesk-org/hwcodec#6abd1898f3a03481ed0c038507b5218d6ea94267"
dependencies = [
"bindgen 0.59.2",
"cc",
@@ -3213,16 +3170,10 @@ dependencies = [
"tiff",
]
[[package]]
name = "imagesize"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]]
name = "impersonate_system"
version = "0.1.0"
source = "git+https://github.com/21pages/impersonate-system#2f429010a5a10b1fe5eceb553c6672fd53d20167"
source = "git+https://github.com/rustdesk-org/impersonate-system#2f429010a5a10b1fe5eceb553c6672fd53d20167"
dependencies = [
"cc",
]
@@ -3462,16 +3413,6 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "kurbo"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c"
dependencies = [
"arrayvec",
"smallvec",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -3558,7 +3499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
dependencies = [
"cfg-if 1.0.0",
"windows-targets 0.48.5",
"windows-targets 0.52.5",
]
[[package]]
@@ -3733,7 +3674,7 @@ dependencies = [
[[package]]
name = "machine-uid"
version = "0.3.0"
source = "git+https://github.com/21pages/machine-uid#381ff579c1dc3a6c54db9dfec47c44bcb0246542"
source = "git+https://github.com/rustdesk-org/machine-uid#381ff579c1dc3a6c54db9dfec47c44bcb0246542"
dependencies = [
"bindgen 0.59.2",
"cc",
@@ -3777,15 +3718,6 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "memmap2"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.6.5"
@@ -3847,14 +3779,6 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "mouce"
version = "0.2.1"
source = "git+https://github.com/rustdesk-org/mouce.git#177625a395cd8fa73964714d0039535cb9b47893"
dependencies = [
"glob",
]
[[package]]
name = "muda"
version = "0.13.5"
@@ -4732,15 +4656,9 @@ version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
dependencies = [
"siphasher 0.2.3",
"siphasher",
]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "pin-project"
version = "1.1.5"
@@ -5065,6 +4983,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "quick-xml"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f24d770aeca0eacb81ac29dfbc55ebcc09312fdd1f8bbecdc7e4a84e000e3b4"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "0.6.13"
@@ -5414,31 +5341,6 @@ dependencies = [
"winreg 0.50.0",
]
[[package]]
name = "resvg"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051"
dependencies = [
"gif",
"jpeg-decoder",
"log",
"pico-args",
"rgb",
"svgtypes",
"tiny-skia",
"usvg",
]
[[package]]
name = "rgb"
version = "0.8.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741"
dependencies = [
"bytemuck",
]
[[package]]
name = "ring"
version = "0.17.8"
@@ -5463,18 +5365,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "roxmltree"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
[[package]]
name = "roxmltree"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]]
name = "rpassword"
version = "2.1.0"
@@ -5572,7 +5462,7 @@ dependencies = [
[[package]]
name = "rustdesk"
version = "1.2.7"
version = "1.3.0"
dependencies = [
"android-wakelock",
"android_logger",
@@ -5618,7 +5508,6 @@ dependencies = [
"libpulse-simple-binding",
"mac_address",
"magnum-opus",
"mouce",
"num_cpus",
"objc",
"objc_id",
@@ -5670,7 +5559,7 @@ dependencies = [
[[package]]
name = "rustdesk-portable-packer"
version = "1.2.7"
version = "1.3.0"
dependencies = [
"brotli",
"dirs 5.0.1",
@@ -5853,22 +5742,6 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "rustybuzz"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c"
dependencies = [
"bitflags 2.6.0",
"bytemuck",
"smallvec",
"ttf-parser",
"unicode-bidi-mirroring",
"unicode-ccc",
"unicode-properties",
"unicode-script",
]
[[package]]
name = "ryu"
version = "1.0.18"
@@ -5905,7 +5778,7 @@ dependencies = [
[[package]]
name = "sciter-rs"
version = "0.5.57"
source = "git+https://github.com/open-trade/rust-sciter?branch=dyn#fab913b7c2e779b05c249b0c5de5a08759b2c15d"
source = "git+https://github.com/open-trade/rust-sciter?branch=dyn#5322f3a755a0e6bf999fbc60d1efc35246c0f821"
dependencies = [
"lazy_static",
"libc",
@@ -6159,27 +6032,12 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simplecss"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
dependencies = [
"log",
]
[[package]]
name = "siphasher"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
[[package]]
name = "siphasher"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "slab"
version = "0.4.9"
@@ -6189,15 +6047,6 @@ dependencies = [
"autocfg 1.3.0",
]
[[package]]
name = "slotmap"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
dependencies = [
"version_check",
]
[[package]]
name = "smallvec"
version = "1.13.2"
@@ -6268,15 +6117,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
[[package]]
name = "strict-num"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]]
name = "strsim"
version = "0.8.0"
@@ -6338,16 +6178,6 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "svgtypes"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c"
dependencies = [
"kurbo",
"siphasher 1.0.1",
]
[[package]]
name = "syn"
version = "0.15.44"
@@ -6399,7 +6229,7 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.29.10"
source = "git+https://github.com/rustdesk-org/sysinfo#f45dcc6510d48c3a1401c5a33eedccc8899f67b2"
source = "git+https://github.com/rustdesk-org/sysinfo?branch=rlim_max#90b1705d909a4902dbbbdea37ee64db17841077d"
dependencies = [
"cfg-if 1.0.0",
"core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -6593,11 +6423,11 @@ dependencies = [
[[package]]
name = "tfc"
version = "0.6.1"
source = "git+https://github.com/rustdesk-org/The-Fat-Controller#9dd86151525fd010dc93f6bc9b6aedd1a75cc342"
version = "0.7.0"
source = "git+https://github.com/rustdesk-org/The-Fat-Controller?branch=history/rebase_upstream_20240722#de9c8ba480f166a9fc90aaa47bb0e84b443ea9c6"
dependencies = [
"anyhow",
"core-graphics 0.22.3",
"core-graphics 0.23.2",
"unicode-segmentation",
"winapi 0.3.9",
"x11 2.19.0",
@@ -6687,32 +6517,6 @@ dependencies = [
"time-core",
]
[[package]]
name = "tiny-skia"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
dependencies = [
"arrayref",
"arrayvec",
"bytemuck",
"cfg-if 1.0.0",
"log",
"png",
"tiny-skia-path",
]
[[package]]
name = "tiny-skia-path"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
dependencies = [
"arrayref",
"bytemuck",
"strict-num",
]
[[package]]
name = "tinyvec"
version = "1.6.1"
@@ -7004,12 +6808,6 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "ttf-parser"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
[[package]]
name = "typenum"
version = "1.17.0"
@@ -7082,18 +6880,6 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-bidi-mirroring"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86"
[[package]]
name = "unicode-ccc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
[[package]]
name = "unicode-ident"
version = "1.0.12"
@@ -7109,30 +6895,12 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-properties"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291"
[[package]]
name = "unicode-script"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd"
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-vo"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
[[package]]
name = "unicode-width"
version = "0.1.13"
@@ -7195,33 +6963,6 @@ dependencies = [
"log",
]
[[package]]
name = "usvg"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032"
dependencies = [
"base64 0.22.1",
"data-url",
"flate2",
"fontdb",
"imagesize",
"kurbo",
"log",
"pico-args",
"roxmltree 0.20.0",
"rustybuzz",
"simplecss",
"siphasher 1.0.1",
"strict-num",
"svgtypes",
"tiny-skia-path",
"unicode-bidi",
"unicode-script",
"unicode-vo",
"xmlwriter",
]
[[package]]
name = "utf16string"
version = "0.2.0"
@@ -7309,7 +7050,7 @@ dependencies = [
[[package]]
name = "wallpaper"
version = "3.2.0"
source = "git+https://github.com/21pages/wallpaper.rs#ce4a0cd3f58327c7cc44d15a63706fb0c022bacf"
source = "git+https://github.com/rustdesk-org/wallpaper.rs#ce4a0cd3f58327c7cc44d15a63706fb0c022bacf"
dependencies = [
"dirs 5.0.1",
"enquote",
@@ -7414,9 +7155,9 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "wayland-backend"
version = "0.3.4"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34e9e6b6d4a2bb4e7e69433e0b35c7923b95d4dc8503a84d25ec917a4bbfdf07"
checksum = "f90e11ce2ca99c97b940ee83edbae9da2d56a08f9ea8158550fd77fa31722993"
dependencies = [
"cc",
"downcast-rs",
@@ -7428,9 +7169,9 @@ dependencies = [
[[package]]
name = "wayland-client"
version = "0.31.3"
version = "0.31.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e63801c85358a431f986cffa74ba9599ff571fc5774ac113ed3b490c19a1133"
checksum = "7e321577a0a165911bdcfb39cf029302479d7527b517ee58ab0f6ad09edf0943"
dependencies = [
"bitflags 2.6.0",
"rustix 0.38.34",
@@ -7440,9 +7181,9 @@ dependencies = [
[[package]]
name = "wayland-protocols"
version = "0.32.1"
version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83d0f1056570486e26a3773ec633885124d79ae03827de05ba6c85f79904026c"
checksum = "62989625a776e827cc0f15d41444a3cea5205b963c3a25be48ae1b52d6b4daaa"
dependencies = [
"bitflags 2.6.0",
"wayland-backend",
@@ -7452,9 +7193,9 @@ dependencies = [
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.1"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7dab47671043d9f5397035975fe1cac639e5bca5cc0b3c32d09f01612e34d24"
checksum = "fd993de54a40a40fbe5601d9f1fbcaef0aebcc5fda447d7dc8f6dcbaae4f8953"
dependencies = [
"bitflags 2.6.0",
"wayland-backend",
@@ -7465,20 +7206,20 @@ dependencies = [
[[package]]
name = "wayland-scanner"
version = "0.31.2"
version = "0.31.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67da50b9f80159dec0ea4c11c13e24ef9e7574bd6ce24b01860a175010cea565"
checksum = "d7b56f89937f1cf2ee1f1259cf2936a17a1f45d8f0aa1019fae6d470d304cfa6"
dependencies = [
"proc-macro2 1.0.86",
"quick-xml 0.31.0",
"quick-xml 0.34.0",
"quote 1.0.36",
]
[[package]]
name = "wayland-sys"
version = "0.31.2"
version = "0.31.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "105b1842da6554f91526c14a2a2172897b7f745a805d62af4ce698706be79c12"
checksum = "43676fe2daf68754ecf1d72026e4e6c15483198b5d24e888b74d3f22f887a148"
dependencies = [
"dlib",
"log",
@@ -7498,7 +7239,7 @@ dependencies = [
[[package]]
name = "webm"
version = "1.1.0"
source = "git+https://github.com/21pages/rust-webm#d2c4d3ac133c7b0e4c0f656da710b48391981e64"
source = "git+https://github.com/rustdesk-org/rust-webm#d2c4d3ac133c7b0e4c0f656da710b48391981e64"
dependencies = [
"webm-sys",
]
@@ -7506,7 +7247,7 @@ dependencies = [
[[package]]
name = "webm-sys"
version = "1.0.4"
source = "git+https://github.com/21pages/rust-webm#d2c4d3ac133c7b0e4c0f656da710b48391981e64"
source = "git+https://github.com/rustdesk-org/rust-webm#d2c4d3ac133c7b0e4c0f656da710b48391981e64"
dependencies = [
"cc",
]
@@ -8220,12 +7961,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "zbus"
version = "3.15.2"

View File

@@ -1,6 +1,6 @@
[package]
name = "rustdesk"
version = "1.2.7"
version = "1.3.0"
authors = ["rustdesk <info@rustdesk.com>"]
edition = "2021"
build= "build.rs"
@@ -90,7 +90,7 @@ enigo = { path = "libs/enigo", features = [ "with_serde" ] }
clipboard = { path = "libs/clipboard" }
ctrlc = "3.2"
# arboard = { version = "3.4.0", features = ["wayland-data-control"] }
arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control", "image-data"] }
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"
@@ -114,7 +114,7 @@ winapi = { version = "0.3", features = [
winreg = "0.11"
windows-service = "0.6"
virtual_display = { path = "libs/virtual_display" }
impersonate_system = { git = "https://github.com/21pages/impersonate-system" }
impersonate_system = { git = "https://github.com/rustdesk-org/impersonate-system" }
shared_memory = "0.12"
tauri-winrt-notification = "0.1.2"
runas = "1.2"
@@ -138,7 +138,7 @@ 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
@@ -152,7 +152,6 @@ 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/rustdesk-org/mouce.git" }
evdev = { git="https://github.com/rustdesk-org/evdev" }
dbus = "0.9"
dbus-crossroads = "0.5"
@@ -166,7 +165,7 @@ once_cell = {version = "1.18", optional = true}
[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"]

View File

@@ -59,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)

View File

@@ -18,7 +18,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.2.7
version: 1.3.0
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:

View File

@@ -18,7 +18,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.2.7
version: 1.3.0
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:
@@ -37,6 +37,9 @@ AppDir:
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-security main restricted
universe multiverse
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

View File

@@ -31,6 +31,11 @@ def get_deb_arch() -> str:
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):
exit_code = os.system(cmd)
@@ -282,10 +287,10 @@ 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, libappindicator3-1, gstreamer1.0-pipewire%s
Description: A remote control software.
""" % (version, get_deb_arch())
""" % (version, get_deb_arch(), get_deb_extra_depends())
file = open(control_file_path, "w")
file.write(content)
file.close()

View File

@@ -43,15 +43,13 @@ arm64-v8a)
FLUTTER_TARGET=android-arm64
NDK_TARGET=aarch64-linux-android
RUST_TARGET=aarch64-linux-android
# RUSTDESK_FEATURES='flutter,hwcodec'
RUSTDESK_FEATURES='flutter'
RUSTDESK_FEATURES='flutter,hwcodec'
;;
armeabi-v7a)
FLUTTER_TARGET=android-arm
NDK_TARGET=arm-linux-androideabi
RUST_TARGET=armv7-linux-androideabi
# RUSTDESK_FEATURES='flutter,hwcodec'
RUSTDESK_FEATURES='flutter'
RUSTDESK_FEATURES='flutter,hwcodec'
;;
x86_64)
FLUTTER_TARGET=android-x64

View File

@@ -31,7 +31,6 @@ import 'mobile/pages/file_manager_page.dart';
import 'mobile/pages/remote_page.dart';
import 'desktop/pages/remote_page.dart' as desktop_remote;
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
import 'models/input_model.dart';
import 'models/model.dart';
import 'models/platform_model.dart';
@@ -630,10 +629,30 @@ List<Locale> supportedLocales = const [
Locale('da'),
Locale('eo'),
Locale('tr'),
Locale('vi'),
Locale('pl'),
Locale('kz'),
Locale('es'),
Locale('nl'),
Locale('nb'),
Locale('et'),
Locale('eu'),
Locale('bg'),
Locale('be'),
Locale('vn'),
Locale('uk'),
Locale('fa'),
Locale('ca'),
Locale('el'),
Locale('sv'),
Locale('sq'),
Locale('sr'),
Locale('th'),
Locale('sl'),
Locale('ro'),
Locale('lt'),
Locale('lv'),
Locale('ar'),
Locale('he'),
Locale('hr'),
];
String formatDurationToTime(Duration duration) {
@@ -647,8 +666,12 @@ String formatDurationToTime(Duration duration) {
closeConnection({String? id}) {
if (isAndroid || isIOS) {
gFFI.chatModel.hideChatOverlay();
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
() async {
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
gFFI.chatModel.hideChatOverlay();
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
}();
} else {
if (isWeb) {
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
@@ -1054,6 +1077,49 @@ class CustomAlertDialog extends StatelessWidget {
}
}
Widget createDialogContent(String text) {
final RegExp linkRegExp = RegExp(r'(https?://[^\s]+)');
final List<TextSpan> spans = [];
int start = 0;
bool hasLink = false;
linkRegExp.allMatches(text).forEach((match) {
hasLink = true;
if (match.start > start) {
spans.add(TextSpan(text: text.substring(start, match.start)));
}
spans.add(TextSpan(
text: match.group(0) ?? '',
style: TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () {
String linkText = match.group(0) ?? '';
linkText = linkText.replaceAll(RegExp(r'[.,;!?]+$'), '');
launchUrl(Uri.parse(linkText));
},
));
start = match.end;
});
if (start < text.length) {
spans.add(TextSpan(text: text.substring(start)));
}
if (!hasLink) {
return SelectableText(text, style: const TextStyle(fontSize: 15));
}
return SelectableText.rich(
TextSpan(
style: TextStyle(color: Colors.black, fontSize: 15),
children: spans,
),
);
}
void msgBox(SessionID sessionId, String type, String title, String text,
String link, OverlayDialogManager dialogManager,
{bool? hasCancel, ReconnectHandle? reconnect, int? reconnectTimeout}) {
@@ -1211,7 +1277,7 @@ Widget msgboxContent(String type, String title, String text) {
translate(title),
style: TextStyle(fontSize: 21),
).marginOnly(bottom: 10),
Text(translateText(text), style: const TextStyle(fontSize: 15)),
createDialogContent(translateText(text)),
],
),
),
@@ -1406,14 +1472,10 @@ class AndroidPermissionManager {
}
}
// TODO move this to mobile/widgets.
// Used only for mobile, pages remote, settings, dialog
// TODO remove argument contentPadding, its not used, getToggle() has not
RadioListTile<T> getRadio<T>(
Widget title, T toValue, T curValue, ValueChanged<T?>? onChange,
{EdgeInsetsGeometry? contentPadding, bool? dense}) {
{bool? dense}) {
return RadioListTile<T>(
contentPadding: contentPadding ?? EdgeInsets.zero,
visualDensity: VisualDensity.compact,
controlAffinity: ListTileControlAffinity.trailing,
title: title,
@@ -2814,7 +2876,7 @@ Widget buildErrorBanner(BuildContext context,
alignment: Alignment.centerLeft,
child: Tooltip(
message: translate(err.value),
child: Text(
child: SelectableText(
translate(err.value),
),
)).marginSymmetric(vertical: 2),
@@ -2935,6 +2997,16 @@ openMonitorInTheSameTab(int i, FFI ffi, PeerInfo pi,
final displays = i == kAllDisplayValue
? List.generate(pi.displays.length, (index) => index)
: [i];
// Try clear image model before switching from all displays
// 1. The remote side has multiple displays.
// 2. Do not use texture render.
// 3. Connect to Display 1.
// 4. Switch to multi-displays `kAllDisplayValue`
// 5. Switch to Display 2.
// Then the remote page will display last picture of Display 1 at the beginning.
if (pi.forceTextureRender && i != kAllDisplayValue) {
ffi.imageModel.clearImage();
}
bind.sessionSwitchDisplay(
isDesktop: isDesktop,
sessionId: ffi.sessionId,
@@ -2968,11 +3040,15 @@ openMonitorInNewTabOrWindow(int i, String peerId, PeerInfo pi,
kMainWindowId, kWindowEventOpenMonitorSession, jsonEncode(args));
}
setNewConnectWindowFrame(
int windowId, String peerId, int? display, Rect? screenRect) async {
setNewConnectWindowFrame(int windowId, String peerId, int preSessionCount,
int? display, Rect? screenRect) async {
if (screenRect == null) {
await restoreWindowPosition(WindowType.RemoteDesktop,
windowId: windowId, display: display, peerId: peerId);
// Do not restore window position to new connection if there's a pre-session.
// https://github.com/rustdesk/rustdesk/discussions/8825
if (preSessionCount == 0) {
await restoreWindowPosition(WindowType.RemoteDesktop,
windowId: windowId, display: display, peerId: peerId);
}
} else {
await tryMoveToScreenAndSetFullscreen(screenRect);
}
@@ -3316,6 +3392,42 @@ bool isInHomePage() {
return controller.state.value.selected == 0;
}
Widget _buildPresetPasswordWarning() {
if (bind.mainGetBuildinOption(key: kOptionRemovePresetPasswordWarning) !=
'N') {
return SizedBox.shrink();
}
return Container(
color: Colors.yellow,
child: Column(
children: [
Align(
child: Text(
translate("Security Alert"),
style: TextStyle(
color: Colors.red,
fontSize:
18, // https://github.com/rustdesk/rustdesk-server-pro/issues/261
fontWeight: FontWeight.bold,
),
)).paddingOnly(bottom: 8),
Text(
translate("preset_password_warning"),
style: TextStyle(color: Colors.red),
)
],
).paddingAll(8),
); // Show a warning message if the Future completed with true
}
Widget buildPresetPasswordWarningMobile() {
if (bind.isPresetPasswordMobileOnly()) {
return _buildPresetPasswordWarning();
} else {
return SizedBox.shrink();
}
}
Widget buildPresetPasswordWarning() {
return FutureBuilder<bool>(
future: bind.isPresetPassword(),
@@ -3326,32 +3438,7 @@ Widget buildPresetPasswordWarning() {
return Text(
'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error
} else if (snapshot.hasData && snapshot.data == true) {
if (bind.mainGetBuildinOption(
key: kOptionRemovePresetPasswordWarning) !=
'N') {
return SizedBox.shrink();
}
return Container(
color: Colors.yellow,
child: Column(
children: [
Align(
child: Text(
translate("Security Alert"),
style: TextStyle(
color: Colors.red,
fontSize:
18, // https://github.com/rustdesk/rustdesk-server-pro/issues/261
fontWeight: FontWeight.bold,
),
)).paddingOnly(bottom: 8),
Text(
translate("preset_password_warning"),
style: TextStyle(color: Colors.red),
)
],
).paddingAll(8),
); // Show a warning message if the Future completed with true
return _buildPresetPasswordWarning();
} else {
return SizedBox
.shrink(); // Show nothing if the Future completed with false or null
@@ -3405,7 +3492,8 @@ Widget buildVirtualWindowFrame(BuildContext context, Widget child) {
);
}
get windowEdgeSize => isLinux && !_linuxWindowResizable ? 0.0 : kWindowEdgeSize;
get windowResizeEdgeSize =>
isLinux && !_linuxWindowResizable ? 0.0 : kWindowResizeEdgeSize;
// `windowManager.setResizable(false)` will reset the window size to the default size on Linux and then set unresizable.
// See _linuxWindowResizable for more details.
@@ -3423,7 +3511,12 @@ setResizable(bool resizable) {
isOptionFixed(String key) => bind.mainIsOptionFixed(key: key);
final isCustomClient = bind.isCustomClient();
bool? _isCustomClient;
bool get isCustomClient {
_isCustomClient ??= bind.isCustomClient();
return _isCustomClient!;
}
get defaultOptionLang => isCustomClient ? 'default' : '';
get defaultOptionTheme => isCustomClient ? 'system' : '';
get defaultOptionYes => isCustomClient ? 'Y' : '';
@@ -3461,3 +3554,36 @@ disableWindowMovable(int? windowId) {
WindowController.fromWindowId(windowId).setMovable(false);
}
}
Widget netWorkErrorWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(translate("network_error_tip")),
ElevatedButton(
onPressed: gFFI.userModel.refreshCurrentUser,
child: Text(translate("Retry")))
.marginSymmetric(vertical: 16),
SelectableText(gFFI.userModel.networkError.value,
style: TextStyle(fontSize: 11, color: Colors.red)),
],
));
}
List<ResizeEdge>? get windowManagerEnableResizeEdges => isWindows
? [
ResizeEdge.topLeft,
ResizeEdge.top,
ResizeEdge.topRight,
]
: null;
List<SubWindowResizeEdge>? get subWindowManagerEnableResizeEdges => isWindows
? [
SubWindowResizeEdge.topLeft,
SubWindowResizeEdge.top,
SubWindowResizeEdge.topRight,
]
: null;

View File

@@ -35,17 +35,14 @@ 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 {
return Column(
children: [
@@ -425,7 +422,8 @@ class _AddressBookState extends State<AddressBook> {
if (canWrite) getEntry(translate("Add ID"), addIdToCurrentAb),
if (canWrite) getEntry(translate("Add Tag"), abAddTag),
getEntry(translate("Unselect all tags"), gFFI.abModel.unsetSelectedTags),
sortMenuItem(),
if (gFFI.abModel.legacyMode.value)
sortMenuItem(), // It's already sorted after pulling down
if (canWrite) syncMenuItem(),
filterMenuItem(),
if (!gFFI.abModel.legacyMode.value && canWrite)

View File

@@ -679,6 +679,7 @@ class PasswordWidget extends StatefulWidget {
this.reRequestFocus = false,
this.hintText,
this.errorText,
this.title,
}) : super(key: key);
final TextEditingController controller;
@@ -686,6 +687,7 @@ class PasswordWidget extends StatefulWidget {
final bool reRequestFocus;
final String? hintText;
final String? errorText;
final String? title;
@override
State<PasswordWidget> createState() => _PasswordWidgetState();
@@ -729,7 +731,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,
@@ -1829,6 +1831,7 @@ void changeBot({Function()? callback}) async {
void change2fa({Function()? callback}) async {
if (bind.mainHasValid2FaSync()) {
await bind.mainSetOption(key: "2fa", value: "");
await bind.mainClearTrustedDevices();
callback?.call();
return;
}
@@ -1896,6 +1899,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) {
@@ -1905,7 +1909,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);
@@ -1919,9 +1923,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,
@@ -2216,3 +2238,252 @@ void CommonConfirmDialog(OverlayDialogManager dialogManager, String content,
);
});
}
void changeUnlockPinDialog(String oldPin, Function() callback) {
final pinController = TextEditingController(text: oldPin);
final confirmController = TextEditingController(text: oldPin);
String? pinErrorText;
String? confirmationErrorText;
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,
),
DialogTextField(
title: translate('Confirmation'),
controller: confirmController,
obscureText: true,
errorText: confirmationErrorText,
)
],
).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

@@ -142,11 +142,6 @@ class _WidgetOPState extends State<WidgetOP> {
String _failedMsg = '';
String _url = '';
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();

View File

@@ -23,11 +23,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 +30,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(),

View File

@@ -353,7 +353,7 @@ class Draggable extends StatefulWidget {
final Widget Function(BuildContext, GestureDragUpdateCallback) builder;
@override
State<StatefulWidget> createState() => _DraggableState();
State<StatefulWidget> createState() => _DraggableState(chatModel);
}
class _DraggableState extends State<Draggable> {
@@ -362,10 +362,8 @@ class _DraggableState extends State<Draggable> {
double _saveHeight = 0;
double _lastBottomHeight = 0;
@override
void initState() {
super.initState();
_chatModel = widget.chatModel;
_DraggableState(ChatModel? chatModel) {
_chatModel = chatModel;
}
get position => widget.position.pos;
@@ -467,7 +465,8 @@ class IOSDraggable extends StatefulWidget {
final Widget Function(BuildContext) builder;
@override
IOSDraggableState createState() => IOSDraggableState();
IOSDraggableState createState() =>
IOSDraggableState(chatModel, width, height);
}
class IOSDraggableState extends State<IOSDraggable> {
@@ -478,12 +477,10 @@ class IOSDraggableState extends State<IOSDraggable> {
double _saveHeight = 0;
double _lastBottomHeight = 0;
@override
void initState() {
super.initState();
_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;

View File

@@ -636,8 +636,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

View File

@@ -76,8 +76,11 @@ class _PeerTabPageState extends State<PeerTabPage>
final isOptVisiableFixed = isOptionFixed(kOptionPeerTabVisible);
@override
void initState() {
_PeerTabPageState() {
_loadLocalOptions();
}
void _loadLocalOptions() {
final uiType = bind.getLocalFlutterOption(k: kOptionPeerCardUiType);
if (uiType != '') {
peerCardUiType.value = int.parse(uiType) == 0
@@ -87,8 +90,7 @@ class _PeerTabPageState extends State<PeerTabPage>
: PeerUiType.list;
}
hideAbTagsPanel.value =
bind.mainGetLocalOption(key: kOptionHideAbTagsPanel).isNotEmpty;
super.initState();
bind.mainGetLocalOption(key: kOptionHideAbTagsPanel) == 'Y';
}
Future<void> handleTabSelection(int tabIndex) async {
@@ -872,16 +874,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: kOptionPeerSorting,
v: peerSort.value,
);
_loadLocalOptions();
}
super.initState();
}
void _loadLocalOptions() {
peerSort.value = PeerSortType.remoteId;
bind.setLocalFlutterOption(
k: kOptionPeerSorting,
v: peerSort.value,
);
}
@override

View File

@@ -45,10 +45,14 @@ class LoadEvent {
final peerSearchText = "".obs;
/// for peer sort, global obs value
final peerSort = bind.getLocalFlutterOption(k: kOptionPeerSorting).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 +74,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',
@@ -85,6 +90,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
var _lastQueryTime = DateTime.now();
var _queryCount = 0;
var _exit = false;
bool _isActive = true;
final _scrollController = ScrollController();
@@ -95,12 +101,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();
}
@@ -115,6 +123,20 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
_queryCount = _maxQueryCount;
}
// This function is required for mobile.
// `onWindowFocus` works fine for desktop.
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (isDesktop) return;
if (state == AppLifecycleState.resumed) {
_isActive = true;
_queryCount = 0;
} else if (state == AppLifecycleState.inactive) {
_isActive = false;
}
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Peers>(
@@ -268,7 +290,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
_queryOnlines(false);
}
} else {
if (_queryCount < _maxQueryCount || !p) {
if (_isActive && (_queryCount < _maxQueryCount || !p)) {
if (now.difference(_lastQueryTime) >= _queryInterval) {
if (_curPeers.isNotEmpty) {
bind.queryOnlines(ids: _curPeers.toList(growable: false));
@@ -286,14 +308,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(

View File

@@ -130,12 +130,9 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
);
}
// paste
if (isMobile &&
pi.platform != kPeerPlatformAndroid &&
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) {

View File

@@ -136,6 +136,7 @@ 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";
@@ -240,9 +241,9 @@ const kDefaultScrollDuration = Duration(milliseconds: 50);
const kDefaultMouseWheelThrottleDuration = Duration(milliseconds: 50);
const kFullScreenEdgeSize = 0.0;
const kMaximizeEdgeSize = 0.0;
// Do not use kWindowEdgeSize directly. Use `windowEdgeSize` in `common.dart` instead.
final kWindowEdgeSize = isWindows ? 1.0 : 5.0;
final kWindowBorderWidth = 1.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;

View File

@@ -169,16 +169,12 @@ class _OnlineStatusWidgetState extends State<OnlineStatusWidget> {
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;
}
@@ -212,14 +208,14 @@ 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;
});
}
}();
});
}
Get.put<IDTextEditingController>(_idController);
windowManager.addListener(this);
@@ -261,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 : windowEdgeSize;
stateGlobal.resizeEdgeSize.value = stateGlobal.isMaximized.isTrue
? kMaximizeEdgeSize
: windowResizeEdgeSize;
}
@override

View File

@@ -78,7 +78,8 @@ class DesktopSettingPage extends StatefulWidget {
DesktopSettingPage({Key? key, required this.initialTabkey}) : super(key: key);
@override
State<DesktopSettingPage> createState() => _DesktopSettingPageState();
State<DesktopSettingPage> createState() =>
_DesktopSettingPageState(initialTabkey);
static void switch2page(SettingsTabKey page) {
try {
@@ -111,10 +112,8 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
var initialIndex = DesktopSettingPage.tabKeys.indexOf(widget.initialTabkey);
_DesktopSettingPageState(SettingsTabKey initialTabkey) {
var initialIndex = DesktopSettingPage.tabKeys.indexOf(initialTabkey);
if (initialIndex == -1) {
initialIndex = 0;
}
@@ -516,16 +515,16 @@ class _GeneralState extends State<_General> {
}
builder(devices, currentDevice, setDevice) {
return _Card(title: 'Audio Input Device', children: [
...devices.map((device) => _Radio<String>(context,
value: device,
groupValue: currentDevice,
autoNewLine: false,
label: device, onChanged: (value) {
setDevice(value);
setState(() {});
}))
]);
final child = ComboBox(
keys: devices,
values: devices,
initialKey: currentDevice,
onChanged: (key) async {
setDevice(key);
setState(() {});
},
).marginOnly(left: _kContentHMargin);
return _Card(title: 'Audio Input Device', children: [child]);
}
return AudioInput(builder: builder, isCm: false, isVoiceCall: false);
@@ -784,8 +783,33 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
onChangedBot(!hasBot.value);
},
).marginOnly(left: _kCheckBoxLeftMargin + 30);
final trust = Row(
children: [
Flexible(
child: Tooltip(
waitDuration: Duration(milliseconds: 300),
message: translate("enable-trusted-devices-tip"),
child: _OptionCheckBox(context, "Enable trusted devices",
kOptionEnableTrustedDevices,
enabled: !locked, update: (v) {
setState(() {});
}),
),
),
if (mainGetBoolOptionSync(kOptionEnableTrustedDevices))
ElevatedButton(
onPressed: locked
? null
: () {
manageTrustedDeviceDialog();
},
child: Text(translate('Manage trusted devices')))
],
).marginOnly(left: 30);
return Column(
children: [tfa, bot],
children: [tfa, bot, trust],
);
}
@@ -1019,6 +1043,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
_OptionCheckBox(context, 'allow-only-conn-window-open-tip',
'allow-only-conn-window-open',
reverse: false, enabled: enabled),
if (bind.mainIsInstalled()) unlockPin()
]);
}
@@ -1266,6 +1291,40 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
}(),
];
}
Widget unlockPin() {
bool enabled = !locked;
RxString unlockPin = bind.mainGetUnlockPin().obs;
update() async {
unlockPin.value = bind.mainGetUnlockPin();
}
onChanged(bool? checked) async {
changeUnlockPinDialog(unlockPin.value, update);
}
final isOptFixed = isOptionFixed(kOptionWhitelist);
return GestureDetector(
child: Obx(() => Row(
children: [
Checkbox(
value: unlockPin.isNotEmpty,
onChanged: enabled && !isOptFixed ? onChanged : null)
.marginOnly(right: 5),
Expanded(
child: Text(
translate('Unlock with PIN'),
style: TextStyle(color: disabledTextColor(context, enabled)),
))
],
)),
onTap: enabled
? () {
onChanged(!unlockPin.isNotEmpty);
}
: null,
).marginOnly(left: _kCheckBoxLeftMargin);
}
}
class _Network extends StatefulWidget {
@@ -2161,9 +2220,14 @@ Widget _lock(
Text(translate(label)).marginOnly(left: 5),
]).marginSymmetric(vertical: 2)),
onPressed: () async {
bool checked = await callMainCheckSuperUserPermission();
if (checked) {
onUnlock();
final unlockPin = bind.mainGetUnlockPin();
if (unlockPin.isEmpty) {
bool checked = await callMainCheckSuperUserPermission();
if (checked) {
onUnlock();
}
} else {
checkUnlockPinDialog(unlockPin, onUnlock);
}
},
).marginSymmetric(horizontal: 2, vertical: 4),

View File

@@ -44,21 +44,9 @@ class _DesktopTabPageState extends State<DesktopTabPage>
final RxBool _block = false.obs;
// bool mouseIn = false;
@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);
Get.put<DesktopTabController>(tabController);
_DesktopTabPageState() {
RemoteCountState.init();
Get.put<DesktopTabController>(tabController);
tabController.add(TabInfo(
key: kTabLabelHomePage,
label: kTabLabelHomePage,
@@ -81,6 +69,21 @@ class _DesktopTabPageState extends State<DesktopTabPage>
}
}
@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) {
@@ -123,6 +126,7 @@ class _DesktopTabPageState extends State<DesktopTabPage>
: Obx(
() => DragToResizeArea(
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
enableResizeEdges: windowManagerEnableResizeEdges,
child: tabWidget,
),
);

View File

@@ -98,7 +98,10 @@ class _FileManagerPageState extends State<FileManagerPage>
}
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);
});
}
@override
@@ -259,6 +262,7 @@ class _FileManagerPageState extends State<FileManagerPage>
Offstage(
offstage: item.state != JobState.paused,
child: MenuButton(
tooltip: translate("Resume"),
onPressed: () {
jobController.resumeJob(item.id);
},
@@ -271,6 +275,7 @@ class _FileManagerPageState extends State<FileManagerPage>
),
),
MenuButton(
tooltip: translate("Delete"),
padding: EdgeInsets.only(right: 15),
child: SvgPicture.asset(
"assets/close.svg",
@@ -518,6 +523,7 @@ class _FileManagerViewState extends State<FileManagerView> {
Row(
children: [
MenuButton(
tooltip: translate('Back'),
padding: EdgeInsets.only(
right: 3,
),
@@ -537,6 +543,7 @@ class _FileManagerViewState extends State<FileManagerView> {
},
),
MenuButton(
tooltip: translate('Parent directory'),
child: RotatedBox(
quarterTurns: 3,
child: SvgPicture.asset(
@@ -601,6 +608,7 @@ class _FileManagerViewState extends State<FileManagerView> {
switch (_locationStatus.value) {
case LocationStatus.bread:
return MenuButton(
tooltip: translate('Search'),
onPressed: () {
_locationStatus.value = LocationStatus.fileSearchBar;
Future.delayed(
@@ -627,6 +635,7 @@ class _FileManagerViewState extends State<FileManagerView> {
);
case LocationStatus.fileSearchBar:
return MenuButton(
tooltip: translate('Clear'),
onPressed: () {
onSearchText("", isLocal);
_locationStatus.value = LocationStatus.bread;
@@ -642,6 +651,7 @@ class _FileManagerViewState extends State<FileManagerView> {
}
}),
MenuButton(
tooltip: translate('Refresh File'),
padding: EdgeInsets.only(
left: 3,
),
@@ -667,6 +677,7 @@ class _FileManagerViewState extends State<FileManagerView> {
isLocal ? MainAxisAlignment.start : MainAxisAlignment.end,
children: [
MenuButton(
tooltip: translate('Home'),
padding: EdgeInsets.only(
right: 3,
),
@@ -682,11 +693,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,
@@ -718,6 +745,7 @@ class _FileManagerViewState extends State<FileManagerView> {
labelText: translate(
"Please enter the folder name",
),
errorText: errorText,
),
controller: name,
autofocus: true,
@@ -751,6 +779,7 @@ class _FileManagerViewState extends State<FileManagerView> {
hoverColor: Theme.of(context).hoverColor,
),
Obx(() => MenuButton(
tooltip: translate('Delete'),
onPressed: SelectedItems.valid(selectedItems.items)
? () async {
await (controller
@@ -882,6 +911,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,
@@ -971,6 +1001,7 @@ class _FileManagerViewState extends State<FileManagerView> {
final lastModifiedStr = entry.isDrive
? " "
: "${entry.lastModified().toString().replaceAll(".000", "")} ";
var secondaryPosition = RelativeRect.fromLTRB(0, 0, 0, 0);
return Padding(
padding: EdgeInsets.symmetric(vertical: 1),
child: Obx(() => Container(
@@ -1035,6 +1066,35 @@ class _FileManagerViewState extends State<FileManagerView> {
_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("Rename"),
height: CustomPopupMenuTheme.height,
onTap: () {
controller.renameAction(entry, isLocal);
},
)
];
if (items.isNotEmpty) {
mod_menu.showMenu(
context: context,
position: secondaryPosition,
items: items,
);
}
},
onSecondaryTapDown: (details) {
secondaryPosition = RelativeRect.fromLTRB(
details.globalPosition.dx,
details.globalPosition.dy,
details.globalPosition.dx,
details.globalPosition.dy);
},
),
SizedBox(
width: 2.0,

View File

@@ -34,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'],
@@ -54,8 +55,6 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
void initState() {
super.initState();
tabController.onRemoved = (_, id) => onRemoveId(id);
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
print(
"[FileTransfer] call ${call.method} with args ${call.arguments} from window $fromWindowId to ${windowId()}");
@@ -97,6 +96,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
controller: tabController,
onWindowCloseButton: handleWindowCloseButton,
tail: const AddButton(),
selectedBorderColor: MyTheme.accent,
labelGetter: DesktopTab.tablabelGetter,
));
final tabWidget = isLinux
@@ -111,6 +111,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
: SubWindowDragToResizeArea(
child: tabWidget,
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
enableResizeEdges: subWindowManagerEnableResizeEdges,
windowId: stateGlobal.windowId,
);
}

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

@@ -65,7 +65,10 @@ class _PortForwardPageState extends State<PortForwardPage>
isRdp: widget.isRDP);
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

View File

@@ -34,6 +34,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
WindowController.fromWindowId(windowId())
.setTitle(getWindowNameWithId(id));
};
tabController.onRemoved = (_, id) => onRemoveId(id);
tabController.add(TabInfo(
key: params['id'],
label: params['id'],
@@ -54,8 +55,6 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
void initState() {
super.initState();
tabController.onRemoved = (_, id) => onRemoveId(id);
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
debugPrint(
"[Port Forward] call ${call.method} with args ${call.arguments} from window $fromWindowId");
@@ -106,6 +105,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
return true;
},
tail: AddButton(),
selectedBorderColor: MyTheme.accent,
labelGetter: DesktopTab.tablabelGetter,
),
);
@@ -127,6 +127,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
() => SubWindowDragToResizeArea(
child: tabWidget,
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
enableResizeEdges: subWindowManagerEnableResizeEdges,
windowId: stateGlobal.windowId,
),
);

View File

@@ -45,7 +45,9 @@ class RemotePage extends StatefulWidget {
this.switchUuid,
this.forceRelay,
this.isSharedPassword,
}) : super(key: key);
}) : super(key: key) {
initSharedStates(id);
}
final String id;
final SessionID? sessionId;
@@ -64,7 +66,7 @@ class RemotePage extends StatefulWidget {
@override
State<RemotePage> createState() {
final state = _RemotePageState();
final state = _RemotePageState(id);
_lastState.value = state;
return state;
}
@@ -94,8 +96,11 @@ class _RemotePageState extends State<RemotePage>
SessionID get sessionId => _ffi.sessionId;
_RemotePageState(String id) {
_initStates(id);
}
void _initStates(String id) {
initSharedStates(id);
_zoomCursor = PeerBoolOption.find(id, kOptionZoomCursor);
_showRemoteCursor = ShowRemoteCursorState.find(id);
_keyboardEnabled = KeyboardEnabledState.find(id);
@@ -105,7 +110,6 @@ class _RemotePageState extends State<RemotePage>
@override
void initState() {
super.initState();
_initStates(widget.id);
_ffi = FFI(widget.sessionId);
Get.put<FFI>(_ffi, tag: widget.id);
_ffi.imageModel.addCallbackOnFirstImage((String peerId) {
@@ -135,11 +139,13 @@ class _RemotePageState extends State<RemotePage>
if (!isWeb) bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote);
_ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId);
_ffi.dialogManager.loadMobileActionsOverlayVisible();
// Session option should be set after models.dart/FFI.start
_showRemoteCursor.value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: 'show-remote-cursor');
_zoomCursor.value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: kOptionZoomCursor);
WidgetsBinding.instance.addPostFrameCallback((_) {
// Session option should be set after models.dart/FFI.start
_showRemoteCursor.value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: 'show-remote-cursor');
_zoomCursor.value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: kOptionZoomCursor);
});
DesktopMultiWindow.addListener(this);
// if (!_isCustomCursorInited) {
// customCursorController.registerNeedUpdateCursorCallback(
@@ -154,7 +160,10 @@ class _RemotePageState extends State<RemotePage>
// }
_blockableOverlayState.applyFfi(_ffi);
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
@@ -565,11 +574,6 @@ class _ImagePaintState extends State<ImagePaint> {
RxBool get remoteCursorMoved => widget.remoteCursorMoved;
Widget Function(Widget)? get listenerBuilder => widget.listenerBuilder;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final m = Provider.of<ImageModel>(context);

View File

@@ -71,7 +71,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
final ffi = remotePage.ffi;
bind.setCurSessionId(sessionId: ffi.sessionId);
}
WindowController.fromWindowId(windowId())
WindowController.fromWindowId(params['windowId'])
.setTitle(getWindowNameWithId(id));
UnreadChatCountState.find(id).value = 0;
};
@@ -98,15 +98,14 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
));
_update_remote_count();
}
tabController.onRemoved = (_, id) => onRemoveId(id);
rustDeskWinManager.setMethodHandler(_remoteMethodHandler);
}
@override
void initState() {
super.initState();
tabController.onRemoved = (_, id) => onRemoveId(id);
rustDeskWinManager.setMethodHandler(_remoteMethodHandler);
if (!_isScreenRectSet) {
Future.delayed(Duration.zero, () {
restoreWindowPosition(
@@ -121,11 +120,6 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
}
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
final child = Scaffold(
@@ -134,6 +128,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
controller: tabController,
onWindowCloseButton: handleWindowCloseButton,
tail: const AddButton(),
selectedBorderColor: MyTheme.accent,
pageViewBuilder: (pageView) => pageView,
labelGetter: DesktopTab.tablabelGetter,
tabBuilder: (key, icon, label, themeConf) => Obx(() {
@@ -233,6 +228,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
// Specially configured for a better resize area and remote control.
childPadding: kDragToResizeAreaPadding,
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
enableResizeEdges: subWindowManagerEnableResizeEdges,
windowId: stateGlobal.windowId,
));
}
@@ -413,12 +409,14 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
final display = args['display'];
final displays = args['displays'];
final screenRect = parseParamScreenRect(args);
final prePeerCount = tabController.length;
Future.delayed(Duration.zero, () async {
if (stateGlobal.fullscreen.isTrue) {
await WindowController.fromWindowId(windowId()).setFullscreen(false);
stateGlobal.setFullscreen(false, procWnd: false);
}
await setNewConnectWindowFrame(windowId(), id!, display, screenRect);
await setNewConnectWindowFrame(
windowId(), id!, prePeerCount, display, screenRect);
Future.delayed(Duration(milliseconds: isWindows ? 100 : 0), () async {
await windowOnTop(windowId());
});

View File

@@ -32,14 +32,18 @@ class DesktopServerPage extends StatefulWidget {
class _DesktopServerPageState extends State<DesktopServerPage>
with WindowListener, AutomaticKeepAliveClientMixin {
final tabController = gFFI.serverModel.tabController;
@override
void initState() {
_DesktopServerPageState() {
gFFI.ffiModel.updateEventListener(gFFI.sessionId, "");
windowManager.addListener(this);
Get.put<DesktopTabController>(tabController);
tabController.onRemoved = (_, id) {
onRemoveId(id);
};
}
@override
void initState() {
windowManager.addListener(this);
super.initState();
}
@@ -79,7 +83,7 @@ class _DesktopServerPageState extends State<DesktopServerPage>
child: Consumer<ServerModel>(
builder: (context, serverModel, child) {
final body = Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
backgroundColor: Theme.of(context).colorScheme.background,
body: ConnectionManager(),
);
return isLinux
@@ -108,6 +112,28 @@ class ConnectionManagerState extends State<ConnectionManager>
with WidgetsBindingObserver {
final RxBool _block = false.obs;
ConnectionManagerState() {
gFFI.serverModel.tabController.onSelected = (client_id_str) {
final client_id = int.tryParse(client_id_str);
if (client_id != null) {
final client =
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == client_id);
if (client != null) {
gFFI.chatModel.changeCurrentKey(MessageKey(client.peerId, client.id));
if (client.unreadChatMessageCount.value > 0) {
WidgetsBinding.instance.addPostFrameCallback((_) {
client.unreadChatMessageCount.value = 0;
gFFI.chatModel.showChatPage(MessageKey(client.peerId, client.id));
});
}
windowManager.setTitle(getWindowNameWithId(client.peerId));
gFFI.cmFileModel.updateCurrentClientId(client.id);
}
}
};
gFFI.chatModel.isConnManager = true;
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
@@ -121,25 +147,6 @@ class ConnectionManagerState extends State<ConnectionManager>
@override
void initState() {
gFFI.serverModel.updateClientState();
gFFI.serverModel.tabController.onSelected = (client_id_str) {
final client_id = int.tryParse(client_id_str);
if (client_id != null) {
final client =
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == client_id);
if (client != null) {
gFFI.chatModel.changeCurrentKey(MessageKey(client.peerId, client.id));
if (client.unreadChatMessageCount.value > 0) {
Future.delayed(Duration.zero, () {
client.unreadChatMessageCount.value = 0;
gFFI.chatModel.showChatPage(MessageKey(client.peerId, client.id));
});
}
windowManager.setTitle(getWindowNameWithId(client.peerId));
gFFI.cmFileModel.updateCurrentClientId(client.id);
}
}
};
gFFI.chatModel.isConnManager = true;
WidgetsBinding.instance.addObserver(this);
super.initState();
}
@@ -186,8 +193,6 @@ class ConnectionManagerState extends State<ConnectionManager>
maxLabelWidth: 100,
tail: null, //buildScrollJumper(),
blockTab: allowRemoteCMModification() ? null : _block,
selectedTabBackgroundColor:
Theme.of(context).hintColor.withOpacity(0),
tabBuilder: (key, icon, label, themeConf) {
final client = serverModel.clients
.firstWhereOrNull((client) => client.id.toString() == key);
@@ -222,7 +227,7 @@ class ConnectionManagerState extends State<ConnectionManager>
borderWidth;
final realChatPageWidth =
constrains.maxWidth - realClosedWidth;
return Row(children: [
final row = Row(children: [
if (constrains.maxWidth >
kConnectionManagerWindowSizeClosedChat.width)
Consumer<ChatModel>(
@@ -240,6 +245,10 @@ class ConnectionManagerState extends State<ConnectionManager>
child:
SizedBox(width: realClosedWidth, child: pageView)),
]);
return Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: row,
);
},
),
),
@@ -399,7 +408,10 @@ class _CmHeaderState extends State<_CmHeader>
_time.value = _time.value + 1;
}
});
gFFI.serverModel.tabController.onSelected?.call(client.id.toString());
// Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
WidgetsBinding.instance.addPostFrameCallback((_) {
gFFI.serverModel.tabController.onSelected?.call(client.id.toString());
});
}
@override
@@ -732,7 +744,8 @@ class _CmControlPanel extends StatelessWidget {
child: buildButton(context,
color: MyTheme.accent,
onClick: null, onTapDown: (details) async {
final devicesInfo = await AudioInput.getDevicesInfo(true, true);
final devicesInfo =
await AudioInput.getDevicesInfo(true, true);
List<String> devices = devicesInfo['devices'] as List<String>;
if (devices.isEmpty) {
msgBox(
@@ -764,7 +777,8 @@ class _CmControlPanel extends StatelessWidget {
value: d,
groupValue: currentDevice,
onChanged: (v) {
if (v != null) AudioInput.setDevice(v, true, true);
if (v != null)
AudioInput.setDevice(v, true, true);
},
child: Container(
child: Text(
@@ -1143,6 +1157,16 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
Text(translate('Create Folder'))
],
);
case CmFileAction.rename:
return Column(
children: [
Icon(
Icons.drive_file_move_outlined,
color: Theme.of(context).tabBarTheme.labelColor,
),
Text(translate('Rename'))
],
);
}
}

View File

@@ -34,6 +34,7 @@ class _MenuButtonState extends State<MenuButton> {
return Padding(
padding: widget.padding,
child: Tooltip(
waitDuration: Duration(milliseconds: 300),
message: widget.tooltip,
child: Material(
type: MaterialType.transparency,

View File

@@ -38,18 +38,16 @@ class PopupMenuChildrenItem<T> extends mod_menu.PopupMenuEntry<T> {
@override
MyPopupMenuItemState<T, PopupMenuChildrenItem<T>> createState() =>
MyPopupMenuItemState<T, PopupMenuChildrenItem<T>>();
MyPopupMenuItemState<T, PopupMenuChildrenItem<T>>(enabled?.value);
}
class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>>
extends State<W> {
RxBool enabled = true.obs;
@override
void initState() {
super.initState();
if (widget.enabled != null) {
enabled.value = widget.enabled!.value;
MyPopupMenuItemState(bool? e) {
if (e != null) {
enabled.value = e;
}
}

View File

@@ -372,7 +372,7 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
initState() {
super.initState();
Future.delayed(Duration.zero, () async {
WidgetsBinding.instance.addPostFrameCallback((_) async {
_fractionX.value = double.tryParse(await bind.sessionGetOption(
sessionId: widget.ffi.sessionId,
arg: 'remote-menubar-drag-x') ??
@@ -1032,11 +1032,6 @@ class _DisplayMenuState extends State<_DisplayMenu> {
FFI get ffi => widget.ffi;
String get id => widget.id;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
_screenAdjustor.updateScreen();
@@ -1278,7 +1273,9 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> {
@override
void initState() {
super.initState();
_getLocalResolutionWayland();
WidgetsBinding.instance.addPostFrameCallback((_) {
_getLocalResolutionWayland();
});
}
Rect? scaledRect() {

View File

@@ -227,8 +227,7 @@ typedef TabMenuBuilder = Widget Function(String key);
typedef LabelGetter = Rx<String> Function(String key);
/// [_lastClickTime], help to handle double click
int _lastClickTime =
DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000;
int _lastClickTime = 0;
class DesktopTab extends StatefulWidget {
final bool showLogo;
@@ -506,17 +505,20 @@ class _DesktopTabState extends State<DesktopTab>
Obx(() {
if (stateGlobal.showTabBar.isTrue &&
!(kUseCompatibleUiMode && isHideSingleItem())) {
final showBottomDivider = _showTabBarBottomDivider(tabType);
return SizedBox(
height: _kTabBarHeight,
child: Column(
children: [
SizedBox(
height: _kTabBarHeight - 1,
height:
showBottomDivider ? _kTabBarHeight - 1 : _kTabBarHeight,
child: _buildBar(),
),
const Divider(
height: 1,
),
if (showBottomDivider)
const Divider(
height: 1,
),
],
),
);
@@ -727,16 +729,6 @@ class WindowActionPanel extends StatefulWidget {
}
class WindowActionPanelState extends State<WindowActionPanel> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
bool showTabDowndown() {
return widget.tabController.state.value.tabs.length > 1 &&
(widget.tabController.tabType == DesktopTabType.remoteScreen ||
@@ -1172,7 +1164,10 @@ class _TabState extends State<_Tab> with RestorationMixin {
child: Row(
children: [
SizedBox(
height: _kTabBarHeight,
// _kTabBarHeight also displays normally
height: _showTabBarBottomDivider(widget.tabType)
? _kTabBarHeight - 1
: _kTabBarHeight,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@@ -1271,13 +1266,7 @@ class ActionIcon extends StatefulWidget {
}
class _ActionIconState extends State<ActionIcon> {
var hover = false.obs;
@override
void initState() {
super.initState();
hover.value = false;
}
final hover = false.obs;
@override
Widget build(BuildContext context) {
@@ -1431,6 +1420,10 @@ class _TabDropDownButtonState extends State<_TabDropDownButton> {
}
}
bool _showTabBarBottomDivider(DesktopTabType tabType) {
return tabType == DesktopTabType.main || tabType == DesktopTabType.install;
}
class TabbarTheme extends ThemeExtension<TabbarTheme> {
final Color? selectedTabIconColor;
final Color? unSelectedTabIconColor;

View File

@@ -260,7 +260,7 @@ showCmWindow({bool isStartup = false}) async {
WindowOptions windowOptions = getHiddenTitleBarWindowOptions(
size: kConnectionManagerWindowSizeClosedChat, alwaysOnTop: true);
await windowManager.waitUntilReadyToShow(windowOptions, null);
bind.mainHideDocker();
bind.mainHideDock();
await Future.wait([
windowManager.show(),
windowManager.focus(),
@@ -288,14 +288,14 @@ hideCmWindow({bool isStartup = false}) async {
size: kConnectionManagerWindowSizeClosedChat);
windowManager.setOpacity(0);
await windowManager.waitUntilReadyToShow(windowOptions, null);
bind.mainHideDocker();
bind.mainHideDock();
await windowManager.minimize();
await windowManager.hide();
_isCmReadyToShow = true;
} else if (_isCmReadyToShow) {
if (await windowManager.getOpacity() != 0) {
await windowManager.setOpacity(0);
bind.mainHideDocker();
bind.mainHideDock();
await windowManager.minimize();
await windowManager.hide();
}

View File

@@ -50,19 +50,26 @@ class _ConnectionPageState extends State<ConnectionPage> {
bool isPeersLoaded = false;
StreamSubscription? _uniLinksSubscription;
_ConnectionPageState() {
if (!isWeb) _uniLinksSubscription = listenUniLinks();
_idController.addListener(() {
_idEmpty.value = _idController.text.isEmpty;
});
Get.put<IDTextEditingController>(_idController);
}
@override
void initState() {
super.initState();
if (!isWeb) _uniLinksSubscription = listenUniLinks();
if (_idController.text.isEmpty) {
() async {
WidgetsBinding.instance.addPostFrameCallback((_) async {
final lastRemoteId = await bind.mainGetLastRemoteId();
if (lastRemoteId != _idController.id) {
setState(() {
_idController.id = lastRemoteId;
});
}
}();
});
}
if (isAndroid) {
if (!bind.isCustomClient()) {
@@ -72,11 +79,6 @@ class _ConnectionPageState extends State<ConnectionPage> {
});
}
}
_idController.addListener(() {
_idEmpty.value = _idController.text.isEmpty;
});
Get.put<IDTextEditingController>(_idController);
}
@override

View File

@@ -204,36 +204,54 @@ class _FileManagerPageState extends State<FileManagerPage> {
setState(() {});
} else if (v == "folder") {
final name = TextEditingController();
gFFI.dialogManager
.show((setState, close, context) => CustomAlertDialog(
title: Text(translate("Create Folder")),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
decoration: InputDecoration(
labelText: translate(
"Please enter the folder name"),
),
controller: name,
),
],
String? errorText;
gFFI.dialogManager.show((setState, close, context) {
name.addListener(() {
if (errorText != null) {
setState(() {
errorText = null;
});
}
});
return CustomAlertDialog(
title: Text(translate("Create Folder")),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
decoration: InputDecoration(
labelText:
translate("Please enter the folder name"),
errorText: errorText,
),
actions: [
dialogButton("Cancel",
onPressed: () => close(false),
isOutline: true),
dialogButton("OK", onPressed: () {
if (name.value.text.isNotEmpty) {
currentFileController.createDir(
PathUtil.join(
currentDir.path,
name.value.text,
currentOptions.isWindows));
close();
}
})
]));
controller: name,
),
],
),
actions: [
dialogButton("Cancel",
onPressed: () => close(false), isOutline: true),
dialogButton("OK", onPressed: () {
if (name.value.text.isNotEmpty) {
if (!PathUtil.validName(
name.value.text,
currentFileController
.options.value.isWindows)) {
setState(() {
errorText =
translate("Invalid folder name");
});
return;
}
currentFileController.createDir(PathUtil.join(
currentDir.path,
name.value.text,
currentOptions.isWindows));
close();
}
})
]);
});
} else if (v == "hidden") {
currentFileController.toggleShowHidden();
}
@@ -497,7 +515,15 @@ class _FileManagerViewState extends State<FileManagerView> {
child: Text(translate("Properties")),
value: "properties",
enabled: false,
)
),
if (!entries[index].isDrive &&
versionCmp(gFFI.ffiModel.pi.version,
"1.3.0") >=
0)
PopupMenuItem(
child: Text(translate("Rename")),
value: "rename",
)
];
},
onSelected: (v) {
@@ -509,6 +535,9 @@ class _FileManagerViewState extends State<FileManagerView> {
_selectedItems.clear();
widget.selectMode.toggle(isLocal);
setState(() {});
} else if (v == "rename") {
controller.renameAction(
entries[index], isLocal);
}
}),
onTap: () {

View File

@@ -34,7 +34,7 @@ class RemotePage extends StatefulWidget {
final bool? isSharedPassword;
@override
State<RemotePage> createState() => _RemotePageState();
State<RemotePage> createState() => _RemotePageState(id);
}
class _RemotePageState extends State<RemotePage> {
@@ -58,6 +58,12 @@ class _RemotePageState extends State<RemotePage> {
final TextEditingController _textController =
TextEditingController(text: initText);
_RemotePageState(String id) {
initSharedStates(id);
gFFI.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted;
gFFI.dialogManager.loadMobileActionsOverlayVisible();
}
@override
void initState() {
super.initState();
@@ -80,12 +86,9 @@ class _RemotePageState extends State<RemotePage> {
gFFI.qualityMonitorModel.checkShowQualityMonitor(sessionId);
keyboardSubscription =
keyboardVisibilityController.onChange.listen(onSoftKeyboardChanged);
initSharedStates(widget.id);
gFFI.chatModel
.changeCurrentKey(MessageKey(widget.id, ChatModel.clientModeID));
gFFI.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted;
_blockableOverlayState.applyFfi(gFFI);
gFFI.dialogManager.loadMobileActionsOverlayVisible();
}
@override
@@ -776,11 +779,6 @@ class _KeyHelpToolsState extends State<KeyHelpTools> {
onPressed: onPressed);
}
@override
void initState() {
super.initState();
}
_updateRect() {
RenderObject? renderObject = _key.currentContext?.findRenderObject();
if (renderObject == null) {

View File

@@ -187,7 +187,7 @@ class _ServerPageState extends State<ServerPage> {
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
buildPresetPasswordWarning(),
buildPresetPasswordWarningMobile(),
gFFI.serverModel.isStart
? ServerInfo()
: ServiceNotRunningNotification(),

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
@@ -87,12 +88,9 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
var _hideServer = false;
var _hideProxy = false;
var _hideNetwork = false;
var _enableTrustedDevices = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_SettingsState() {
_enableAbr = option2bool(
kOptionEnableAbr, bind.mainGetOptionSync(key: kOptionEnableAbr));
_denyLANDiscovery = !option2bool(kOptionEnableLanDiscovery,
@@ -117,8 +115,15 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
_hideProxy = bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y';
_hideNetwork =
bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) == 'Y';
_enableTrustedDevices = mainGetBoolOptionSync(kOptionEnableTrustedDevices);
}
() async {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
WidgetsBinding.instance.addPostFrameCallback((_) async {
var update = false;
if (_hasIgnoreBattery) {
@@ -177,7 +182,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
if (update) {
setState(() {});
}
}();
});
}
@override
@@ -241,18 +246,76 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
],
));
final List<AbstractSettingsTile> enhancementsTiles = [];
final List<AbstractSettingsTile> shareScreenTiles = [
final enable2fa = bind.mainHasValid2FaSync();
final List<AbstractSettingsTile> tfaTiles = [
SettingsTile.switchTile(
title: Text(translate('enable-2fa-title')),
initialValue: bind.mainHasValid2FaSync(),
onToggle: (_) async {
initialValue: enable2fa,
onToggle: (v) async {
update() async {
setState(() {});
}
change2fa(callback: update);
if (v == false) {
CommonConfirmDialog(
gFFI.dialogManager, translate('cancel-2fa-confirm-tip'), () {
change2fa(callback: update);
});
} else {
change2fa(callback: update);
}
},
),
if (enable2fa)
SettingsTile.switchTile(
title: Text(translate('Telegram bot')),
initialValue: bind.mainHasValidBotSync(),
onToggle: (v) async {
update() async {
setState(() {});
}
if (v == false) {
CommonConfirmDialog(
gFFI.dialogManager, translate('cancel-bot-confirm-tip'), () {
changeBot(callback: update);
});
} else {
changeBot(callback: update);
}
},
),
if (enable2fa)
SettingsTile.switchTile(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(translate('Enable trusted devices')),
Text('* ${translate('enable-trusted-devices-tip')}',
style: Theme.of(context).textTheme.bodySmall),
],
),
initialValue: _enableTrustedDevices,
onToggle: isOptionFixed(kOptionEnableTrustedDevices)
? null
: (v) async {
mainSetBoolOption(kOptionEnableTrustedDevices, v);
setState(() {
_enableTrustedDevices = v;
});
},
),
if (enable2fa && _enableTrustedDevices)
SettingsTile(
title: Text(translate('Manage trusted devices')),
trailing: Icon(Icons.arrow_forward_ios),
onPressed: (context) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return _ManageTrustedDevices();
}));
})
];
final List<AbstractSettingsTile> shareScreenTiles = [
SettingsTile.switchTile(
title: Text(translate('Deny LAN discovery')),
initialValue: _denyLANDiscovery,
@@ -640,6 +703,11 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
),
],
),
if (isAndroid &&
!disabledSettings &&
!outgoingOnly &&
!hideSecuritySettings)
SettingsSection(title: Text('2FA'), tiles: tfaTiles),
if (isAndroid &&
!disabledSettings &&
!outgoingOnly &&
@@ -961,6 +1029,51 @@ class __DisplayPageState extends State<_DisplayPage> {
}
}
class _ManageTrustedDevices extends StatefulWidget {
const _ManageTrustedDevices();
@override
State<_ManageTrustedDevices> createState() => __ManageTrustedDevicesState();
}
class __ManageTrustedDevicesState extends State<_ManageTrustedDevices> {
RxList<TrustedDevice> trustedDevices = RxList.empty(growable: true);
RxList<Uint8List> selectedDevices = RxList.empty();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(translate('Manage trusted devices')),
centerTitle: true,
actions: [
Obx(() => IconButton(
icon: Icon(Icons.delete, color: Colors.white),
onPressed: selectedDevices.isEmpty
? null
: () {
confrimDeleteTrustedDevicesDialog(
trustedDevices, selectedDevices);
}))
],
),
body: FutureBuilder(
future: TrustedDevice.get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
}
final devices = snapshot.data as List<TrustedDevice>;
trustedDevices = devices.obs;
return trustedDevicesTable(trustedDevices, selectedDevices);
}),
);
}
}
class _RadioEntry {
final String label;
final String value;

View File

@@ -41,18 +41,16 @@ class GestureHelp extends StatefulWidget {
final OnTouchModeChange onTouchModeChange;
@override
State<StatefulWidget> createState() => _GestureHelpState();
State<StatefulWidget> createState() => _GestureHelpState(touchMode);
}
class _GestureHelpState extends State<GestureHelp> {
var _selectedIndex;
var _touchMode;
late int _selectedIndex;
late bool _touchMode;
@override
void initState() {
_touchMode = widget.touchMode;
_GestureHelpState(bool touchMode) {
_touchMode = touchMode;
_selectedIndex = _touchMode ? 1 : 0;
super.initState();
}
@override

View File

@@ -17,17 +17,17 @@ import '../common.dart';
final syncAbOption = 'sync-ab-with-recent-sessions';
bool shouldSyncAb() {
return bind.mainGetLocalOption(key: syncAbOption).isNotEmpty;
return bind.mainGetLocalOption(key: syncAbOption) == 'Y';
}
final sortAbTagsOption = 'sync-ab-tags';
bool shouldSortTags() {
return bind.mainGetLocalOption(key: sortAbTagsOption).isNotEmpty;
return bind.mainGetLocalOption(key: sortAbTagsOption) == 'Y';
}
final filterAbTagOption = 'filter-ab-by-intersection';
bool filterAbTagByIntersection() {
return bind.mainGetLocalOption(key: filterAbTagOption).isNotEmpty;
return bind.mainGetLocalOption(key: filterAbTagOption) == 'Y';
}
const _personalAddressBookName = "My address book";
@@ -111,9 +111,10 @@ class AbModel {
Future<void> _pullAb(
{required ForcePullAb? force, required bool quiet}) async {
if (bind.isDisableAb()) return;
debugPrint("pullAb, force: $force, quiet: $quiet");
if (!gFFI.userModel.isLogin) return;
if (gFFI.userModel.networkError.isNotEmpty) return;
if (force == null && listInitialized && current.initialized) return;
debugPrint("pullAb, force: $force, quiet: $quiet");
if (!listInitialized || force == ForcePullAb.listAndCurrent) {
try {
// Read personal guid every time to avoid upgrading the server without closing the main window
@@ -815,8 +816,6 @@ abstract class BaseAb {
}
class LegacyAb extends BaseAb {
final sortTags = shouldSortTags().obs;
final filterByIntersection = filterAbTagByIntersection().obs;
bool get emtpy => peers.isEmpty && tags.isEmpty;
// licensedDevices is obtained from personal ab, shared ab restrict it in server
var licensedDevices = 0;
@@ -1209,8 +1208,6 @@ class LegacyAb extends BaseAb {
class Ab extends BaseAb {
AbProfile profile;
late final bool personal;
final sortTags = shouldSortTags().obs;
final filterByIntersection = filterAbTagByIntersection().obs;
bool get emtpy => peers.isEmpty && tags.isEmpty;
Ab(this.profile, this.personal);

View File

@@ -33,6 +33,8 @@ class CmFileModel {
_onFileRemove(evt['remove']);
} else if (evt['create_dir'] != null) {
_onDirCreate(evt['create_dir']);
} else if (evt['rename'] != null) {
_onRename(evt['rename']);
}
}
@@ -59,8 +61,6 @@ class CmFileModel {
_dealOneJob(dynamic l, bool calcSpeed) {
final data = TransferJobSerdeData.fromJson(l);
Client? client =
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == data.connId);
var jobTable = _jobTables[data.connId];
if (jobTable == null) {
debugPrint("jobTable should not be null");
@@ -70,12 +70,7 @@ class CmFileModel {
if (job == null) {
job = CmFileLog();
jobTable.add(job);
final currentSelectedTab =
gFFI.serverModel.tabController.state.value.selectedTabInfo;
if (!(gFFI.chatModel.isShowCMSidePage &&
currentSelectedTab.key == data.connId.toString())) {
client?.unreadChatMessageCount.value += 1;
}
_addUnread(data.connId);
}
job.id = data.id;
job.action =
@@ -167,8 +162,6 @@ class CmFileModel {
try {
dynamic d = jsonDecode(log);
FileActionLog data = FileActionLog.fromJson(d);
Client? client =
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == data.connId);
var jobTable = _jobTables[data.connId];
if (jobTable == null) {
debugPrint("jobTable should not be null");
@@ -179,17 +172,45 @@ class CmFileModel {
..fileName = data.path
..action = CmFileAction.createDir
..state = JobState.done);
final currentSelectedTab =
gFFI.serverModel.tabController.state.value.selectedTabInfo;
if (!(gFFI.chatModel.isShowCMSidePage &&
currentSelectedTab.key == data.connId.toString())) {
client?.unreadChatMessageCount.value += 1;
}
_addUnread(data.connId);
jobTable.refresh();
} catch (e) {
debugPrint('$e');
}
}
_onRename(dynamic log) {
try {
dynamic d = jsonDecode(log);
FileRenamenLog data = FileRenamenLog.fromJson(d);
var jobTable = _jobTables[data.connId];
if (jobTable == null) {
debugPrint("jobTable should not be null");
return;
}
final fileName = '${data.path} -> ${data.newName}';
jobTable.add(CmFileLog()
..id = 0
..fileName = fileName
..action = CmFileAction.rename
..state = JobState.done);
_addUnread(data.connId);
jobTable.refresh();
} catch (e) {
debugPrint('$e');
}
}
_addUnread(int connId) {
Client? client =
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == connId);
final currentSelectedTab =
gFFI.serverModel.tabController.state.value.selectedTabInfo;
if (!(gFFI.chatModel.isShowCMSidePage &&
currentSelectedTab.key == connId.toString())) {
client?.unreadChatMessageCount.value += 1;
}
}
}
enum CmFileAction {
@@ -198,6 +219,7 @@ enum CmFileAction {
localToRemote,
remove,
createDir,
rename,
}
class CmFileLog {
@@ -285,3 +307,22 @@ class FileActionLog {
dir: d['dir'] ?? false,
);
}
class FileRenamenLog {
int connId = 0;
String path = '';
String newName = '';
FileRenamenLog({
required this.connId,
required this.path,
required this.newName,
});
FileRenamenLog.fromJson(dynamic d)
: this(
connId: d['connId'] ?? 0,
path: d['path'] ?? '',
newName: d['newName'] ?? '',
);
}

View File

@@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/common/widgets/dialog.dart';
import 'package:flutter_hbb/utils/event_loop.dart';
import 'package:get/get.dart';
import 'package:path/path.dart' as path;
@@ -642,6 +643,77 @@ class FileController {
path: path,
isRemote: !isLocal);
}
Future<void> renameAction(Entry item, bool isLocal) async {
final textEditingController = TextEditingController(text: item.name);
String? errorText;
dialogManager?.show((setState, close, context) {
textEditingController.addListener(() {
if (errorText != null) {
setState(() {
errorText = null;
});
}
});
submit() async {
final newName = textEditingController.text;
if (newName.isEmpty || newName == item.name) {
close();
return;
}
if (directory.value.entries.any((e) => e.name == newName)) {
setState(() {
errorText = translate("Already exists");
});
return;
}
if (!PathUtil.validName(newName, options.value.isWindows)) {
setState(() {
if (item.isDirectory) {
errorText = translate("Invalid folder name");
} else {
errorText = translate("Invalid file name");
}
});
return;
}
await bind.sessionRenameFile(
sessionId: sessionId,
actId: JobController.jobID.next(),
path: item.path,
newName: newName,
isRemote: !isLocal);
close();
}
return CustomAlertDialog(
content: Column(
children: [
DialogTextField(
title: '${translate('Rename')} ${item.name}',
controller: textEditingController,
errorText: errorText,
),
],
),
actions: [
dialogButton(
"Cancel",
icon: Icon(Icons.close_rounded),
onPressed: close,
isOutline: true,
),
dialogButton(
"OK",
icon: Icon(Icons.done_rounded),
onPressed: submit,
),
],
onSubmit: submit,
onCancel: close,
);
});
}
}
class JobController {
@@ -1083,6 +1155,13 @@ class PathUtil {
final pathUtil = isWindows ? windowsContext : posixContext;
return pathUtil.dirname(path);
}
static bool validName(String name, bool isWindows) {
final unixFileNamePattern = RegExp(r'^[^/\0]+$');
final windowsFileNamePattern = RegExp(r'^[^<>:"/\\|?*]+$');
final reg = isWindows ? windowsFileNamePattern : unixFileNamePattern;
return reg.hasMatch(name);
}
}
class DirectoryOptions {

View File

@@ -28,6 +28,7 @@ class GroupModel {
Future<void> pull({force = true, quiet = false}) async {
if (bind.isDisableGroupPanel()) return;
if (!gFFI.userModel.isLogin || groupLoading.value) return;
if (gFFI.userModel.networkError.isNotEmpty) return;
if (!force && initialized) return;
if (!quiet) {
groupLoading.value = true;

View File

@@ -384,7 +384,7 @@ class FfiModel with ChangeNotifier {
} else if (name == 'use_texture_render') {
_handleUseTextureRender(evt, sessionId, peerId);
} else {
debugPrint('Unknown event name: $name');
debugPrint('Event is not handled in the fixed branch: $name');
}
};
}
@@ -723,6 +723,8 @@ class FfiModel with ChangeNotifier {
/// Handle the peer info event based on [evt].
handlePeerInfo(Map<String, dynamic> evt, String peerId, bool isCache) async {
parent.target?.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted;
// This call is to ensuer the keyboard mode is updated depending on the peer version.
parent.target?.inputModel.updateKeyboardMode();
@@ -1001,14 +1003,15 @@ class FfiModel with ChangeNotifier {
// Notify to switch display
msgBox(sessionId, 'custom-nook-nocancel-hasclose-info', 'Prompt',
'display_is_plugged_out_msg', '', parent.target!.dialogManager);
final newDisplay = pi.primaryDisplay == kInvalidDisplayIndex
? 0
: pi.primaryDisplay;
final displays = newDisplay;
final isPeerPrimaryDisplayValid =
pi.primaryDisplay == kInvalidDisplayIndex ||
pi.primaryDisplay >= pi.displays.length;
final newDisplay =
isPeerPrimaryDisplayValid ? 0 : pi.primaryDisplay;
bind.sessionSwitchDisplay(
isDesktop: isDesktop,
sessionId: sessionId,
value: Int32List.fromList([displays]),
value: Int32List.fromList([newDisplay]),
);
if (_pi.isSupportMultiUiSession) {
@@ -1173,26 +1176,28 @@ class ImageModel with ChangeNotifier {
addCallbackOnFirstImage(Function(String) cb) => callbacksOnFirstImage.add(cb);
onRgba(int display, Uint8List rgba) {
clearImage() => _image = null;
onRgba(int display, Uint8List rgba) async {
try {
await decodeAndUpdate(display, rgba);
} catch (e) {
debugPrint('onRgba error: $e');
}
platformFFI.nextRgba(sessionId, display);
}
decodeAndUpdate(int display, Uint8List rgba) async {
final pid = parent.target?.id;
final rect = parent.target?.ffiModel.pi.getDisplayRect(display);
img.decodeImageFromPixels(
rgba,
rect?.width.toInt() ?? 0,
rect?.height.toInt() ?? 0,
isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888,
onPixelsCopied: () {
// Unlock the rgba memory from rust codes.
platformFFI.nextRgba(sessionId, display);
}).then((image) {
if (parent.target?.id != pid) return;
try {
// my throw exception, because the listener maybe already dispose
update(image);
} catch (e) {
debugPrint('update image: $e');
}
});
final image = await img.decodeImageFromPixels(
rgba,
rect?.width.toInt() ?? 0,
rect?.height.toInt() ?? 0,
isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888,
);
if (parent.target?.id != pid) return;
await update(image);
}
update(ui.Image? image) async {
@@ -2547,32 +2552,30 @@ class FFI {
}
} else if (message is EventToUI_Rgba) {
final display = message.field0;
if (imageModel.useTextureRender || ffiModel.pi.forceTextureRender) {
//debugPrint("EventToUI_Rgba display:$display");
textureModel.setTextureType(display: display, gpuTexture: false);
// Fetch the image buffer from rust codes.
final sz = platformFFI.getRgbaSize(sessionId, display);
if (sz == 0) {
platformFFI.nextRgba(sessionId, display);
return;
}
final rgba = platformFFI.getRgba(sessionId, display, sz);
if (rgba != null) {
onEvent2UIRgba();
await imageModel.onRgba(display, rgba);
} else {
// Fetch the image buffer from rust codes.
final sz = platformFFI.getRgbaSize(sessionId, display);
if (sz == 0) {
platformFFI.nextRgba(sessionId, display);
return;
}
final rgba = platformFFI.getRgba(sessionId, display, sz);
if (rgba != null) {
onEvent2UIRgba();
imageModel.onRgba(display, rgba);
} else {
platformFFI.nextRgba(sessionId, display);
}
platformFFI.nextRgba(sessionId, display);
}
} else if (message is EventToUI_Texture) {
final display = message.field0;
debugPrint("EventToUI_Texture display:$display");
if (hasGpuTextureRender) {
textureModel.setTextureType(display: display, gpuTexture: true);
onEvent2UIRgba();
final gpuTexture = message.field1;
debugPrint(
"EventToUI_Texture display:$display, gpuTexture:$gpuTexture");
if (gpuTexture && !hasGpuTextureRender) {
debugPrint('the gpuTexture is not supported.');
return;
}
textureModel.setTextureType(display: display, gpuTexture: gpuTexture);
onEvent2UIRgba();
}
}();
});
@@ -2608,8 +2611,9 @@ class FFI {
remember: remember);
}
void send2FA(SessionID sessionId, String code) {
bind.sessionSend2Fa(sessionId: sessionId, code: code);
void send2FA(SessionID sessionId, String code, bool trustThisDevice) {
bind.sessionSend2Fa(
sessionId: sessionId, code: code, trustThisDevice: trustThisDevice);
}
/// Close the remote session.
@@ -2626,7 +2630,7 @@ class FFI {
canvasModel.scale,
ffiModel.pi.currentDisplay);
}
imageModel.update(null);
await imageModel.update(null);
cursorModel.clear();
ffiModel.clear();
canvasModel.clear();

View File

@@ -196,7 +196,7 @@ class ServerModel with ChangeNotifier {
bind.mainSetOption(key: kOptionEnableAudio, value: "N");
} else {
final audioOption = await bind.mainGetOption(key: kOptionEnableAudio);
_audioOk = audioOption.isEmpty;
_audioOk = audioOption != 'N';
}
// file
@@ -206,7 +206,7 @@ class ServerModel with ChangeNotifier {
} else {
final fileOption =
await bind.mainGetOption(key: kOptionEnableFileTransfer);
_fileOk = fileOption.isEmpty;
_fileOk = fileOption != 'N';
}
notifyListeners();

View File

@@ -14,7 +14,7 @@ class StateGlobal {
bool _isMinimized = false;
final RxBool isMaximized = false.obs;
final RxBool _showTabBar = true.obs;
final RxDouble _resizeEdgeSize = RxDouble(windowEdgeSize);
final RxDouble _resizeEdgeSize = RxDouble(windowResizeEdgeSize);
final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth);
final RxBool showRemoteToolBar = false.obs;
final svcStatus = SvcStatus.notReady.obs;
@@ -93,7 +93,7 @@ class StateGlobal {
? kFullScreenEdgeSize
: isMaximized.isTrue
? kMaximizeEdgeSize
: windowEdgeSize;
: windowResizeEdgeSize;
String getInputSource({bool force = false}) {
if (force || _inputSource.isEmpty) {

View File

@@ -17,13 +17,23 @@ bool refreshingUser = false;
class UserModel {
final RxString userName = ''.obs;
final RxBool isAdmin = false.obs;
final RxString networkError = ''.obs;
bool get isLogin => userName.isNotEmpty;
WeakReference<FFI> parent;
UserModel(this.parent);
UserModel(this.parent) {
userName.listen((p0) {
// When user name becomes empty, show login button
// When user name becomes non-empty:
// For _updateLocalUserInfo, network error will be set later
// For login success, should clear network error
networkError.value = '';
});
}
void refreshCurrentUser() async {
if (bind.isDisableAccount()) return;
networkError.value = '';
final token = bind.mainGetLocalOption(key: 'access_token');
if (token == '') {
await updateOtherModels();
@@ -38,12 +48,18 @@ class UserModel {
if (refreshingUser) return;
try {
refreshingUser = true;
final response = await http.post(Uri.parse('$url/api/currentUser'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
},
body: json.encode(body));
final http.Response response;
try {
response = await http.post(Uri.parse('$url/api/currentUser'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
},
body: json.encode(body));
} catch (e) {
networkError.value = e.toString();
rethrow;
}
refreshingUser = false;
final status = response.statusCode;
if (status == 401 || status == 400) {

View File

@@ -13,14 +13,12 @@ Future<ui.Image?> decodeImageFromPixels(
int? rowBytes,
int? targetWidth,
int? targetHeight,
VoidCallback? onPixelsCopied, // must ensure onPixelsCopied is called no matter this function succeeds
bool allowUpscaling = true,
}) async {
if (targetWidth != null) {
assert(allowUpscaling || targetWidth <= width);
if (!(allowUpscaling || targetWidth <= width)) {
print("not allow upscaling but targetWidth > width");
onPixelsCopied?.call();
return null;
}
}
@@ -28,7 +26,6 @@ Future<ui.Image?> decodeImageFromPixels(
assert(allowUpscaling || targetHeight <= height);
if (!(allowUpscaling || targetHeight <= height)) {
print("not allow upscaling but targetHeight > height");
onPixelsCopied?.call();
return null;
}
}
@@ -36,9 +33,7 @@ Future<ui.Image?> decodeImageFromPixels(
final ui.ImmutableBuffer buffer;
try {
buffer = await ui.ImmutableBuffer.fromUint8List(pixels);
onPixelsCopied?.call();
} catch (e) {
onPixelsCopied?.call();
return null;
}

View File

@@ -142,7 +142,10 @@ class RustdeskImpl {
}
Future<void> sessionSend2Fa(
{required UuidValue sessionId, required String code, dynamic hint}) {
{required UuidValue sessionId,
required String code,
required bool trustThisDevice,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['send_2fa', code]));
}
@@ -1412,7 +1415,7 @@ class RustdeskImpl {
return false;
}
bool mainHideDocker({dynamic hint}) {
bool mainHideDock({dynamic hint}) {
throw UnimplementedError();
}
@@ -1622,5 +1625,30 @@ class RustdeskImpl {
throw UnimplementedError();
}
String mainGetUnlockPin({dynamic hint}) {
throw UnimplementedError();
}
String mainSetUnlockPin({required String pin, dynamic hint}) {
throw UnimplementedError();
}
bool sessionGetEnableTrustedDevices(
{required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError();
}
Future<String> mainGetTrustedDevices({dynamic hint}) {
throw UnimplementedError();
}
Future<void> mainRemoveTrustedDevices({required String json, dynamic hint}) {
throw UnimplementedError();
}
Future<void> mainClearTrustedDevices({dynamic hint}) {
throw UnimplementedError();
}
void dispose() {}
}

View File

@@ -45,5 +45,7 @@
<string>Record the sound from microphone for the purpose of the remote desktop.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>LSUIElement</key>
<string>1</string>
</dict>
</plist>

2
flutter/ndk_x86.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/usr/bin/env bash
cargo ndk --platform 21 --target i686-linux-android build --release --features flutter

View File

@@ -335,7 +335,7 @@ packages:
description:
path: "."
ref: HEAD
resolved-ref: "336308d86ec8b9640504a371b50ba500eb779363"
resolved-ref: "80b063b9d4e015f62e17f42a5aa0b3d20a365926"
url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window"
source: git
version: "0.1.0"
@@ -377,7 +377,7 @@ packages:
path: "."
ref: "24cb88413fa5181d949ddacbb30a65d5c459e7d9"
resolved-ref: "24cb88413fa5181d949ddacbb30a65d5c459e7d9"
url: "https://github.com/21pages/dynamic_layouts.git"
url: "https://github.com/rustdesk-org/dynamic_layouts.git"
source: git
version: "0.0.1+1"
external_path:
@@ -511,7 +511,7 @@ packages:
path: "."
ref: "38951317afe79d953ab25733667bd96e172a80d3"
resolved-ref: "38951317afe79d953ab25733667bd96e172a80d3"
url: "https://github.com/21pages/flutter_gpu_texture_renderer"
url: "https://github.com/rustdesk-org/flutter_gpu_texture_renderer"
source: git
version: "0.0.1"
flutter_improved_scrolling:

View File

@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers
version: 1.2.7+46
version: 1.3.0+46
environment:
sdk: '^3.1.0'
@@ -92,14 +92,14 @@ dependencies:
dropdown_button2: ^2.0.0
flutter_gpu_texture_renderer:
git:
url: https://github.com/21pages/flutter_gpu_texture_renderer
url: https://github.com/rustdesk-org/flutter_gpu_texture_renderer
ref: 38951317afe79d953ab25733667bd96e172a80d3
uuid: ^3.0.7
auto_size_text_field: ^2.2.1
flex_color_picker: ^3.3.0
dynamic_layouts:
git:
url: https://github.com/21pages/dynamic_layouts.git
url: https://github.com/rustdesk-org/dynamic_layouts.git
ref: 24cb88413fa5181d949ddacbb30a65d5c459e7d9
pull_down_button: ^0.9.3
device_info_plus: ^9.1.0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 481 KiB

View File

@@ -1,3 +1,4 @@
#[cfg(any(target_os = "linux", target_os = "macos"))]
use crate::{CliprdrError, CliprdrServiceContext};
#[cfg(target_os = "windows")]
@@ -63,8 +64,10 @@ pub fn create_cliprdr_context(
return Ok(Box::new(DummyCliprdrContext {}) as Box<_>);
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
struct DummyCliprdrContext {}
#[cfg(any(target_os = "linux", target_os = "macos"))]
impl CliprdrServiceContext for DummyCliprdrContext {
fn set_is_stopped(&mut self) -> Result<(), CliprdrError> {
Ok(())

View File

@@ -23,7 +23,7 @@ serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
log = "0.4"
rdev = { git = "https://github.com/rustdesk-org/rdev" }
tfc = { git = "https://github.com/rustdesk-org/The-Fat-Controller" }
tfc = { git = "https://github.com/rustdesk-org/The-Fat-Controller", branch = "history/rebase_upstream_20240722" }
hbb_common = { path = "../hbb_common" }
[features]

View File

@@ -37,8 +37,8 @@ libc = "0.2"
dlopen = "0.1"
toml = "0.7"
uuid = { version = "1.3", features = ["v4"] }
# crash, versions >= 0.29.1 are affected by #GuillaumeGomez/sysinfo/1052
sysinfo = { git = "https://github.com/rustdesk-org/sysinfo" }
# new sysinfo issue: https://github.com/rustdesk/rustdesk/pull/6330#issuecomment-2270871442
sysinfo = { git = "https://github.com/rustdesk-org/sysinfo", branch = "rlim_max" }
thiserror = "1.0"
httparse = "1.5"
base64 = "0.22"
@@ -46,7 +46,7 @@ url = "2.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
mac_address = "1.1"
machine-uid = { git = "https://github.com/21pages/machine-uid" }
machine-uid = { git = "https://github.com/rustdesk-org/machine-uid" }
[target.'cfg(not(any(target_os = "macos", target_os = "windows")))'.dependencies]
tokio-rustls = { version = "0.26", features = ["logging", "tls12", "ring"], default-features = false }
rustls-platform-verifier = "0.3.1"

View File

@@ -81,10 +81,13 @@ message LoginRequest {
uint64 session_id = 10;
string version = 11;
OSLogin os_login = 12;
string my_platform = 13;
bytes hwid = 14;
}
message Auth2FA {
string code = 1;
bytes hwid = 2;
}
message ChatMessage { string text = 1; }
@@ -136,6 +139,7 @@ message LoginResponse {
string error = 1;
PeerInfo peer_info = 2;
}
bool enable_trusted_devices = 3;
}
message TouchScaleUpdate {
@@ -315,13 +319,25 @@ message Hash {
string challenge = 2;
}
enum ClipboardFormat {
Text = 0;
Rtf = 1;
Html = 2;
ImageRgba = 21;
ImagePng = 22;
ImageSvg = 23;
}
message Clipboard {
bool compress = 1;
bytes content = 2;
int32 width = 3;
int32 height = 4;
ClipboardFormat format = 5;
}
message MultiClipboards { repeated Clipboard clipboards = 1; }
enum FileType {
Dir = 0;
DirLink = 2;
@@ -355,6 +371,12 @@ message ReadAllFiles {
bool include_hidden = 3;
}
message FileRename {
int32 id = 1;
string path = 2;
string new_name = 3;
}
message FileAction {
oneof union {
ReadDir read_dir = 1;
@@ -366,6 +388,7 @@ message FileAction {
ReadAllFiles all_files = 7;
FileTransferCancel cancel = 8;
FileTransferSendConfirmRequest send_confirm = 9;
FileRename rename = 10;
}
}
@@ -816,5 +839,6 @@ message Message {
PeerInfo peer_info = 25;
PointerDeviceEvent pointer_device_event = 26;
Auth2FA auth_2fa = 27;
MultiClipboards multi_clipboards = 28;
}
}

View File

@@ -10,6 +10,7 @@ use std::{
};
use anyhow::Result;
use bytes::Bytes;
use rand::Rng;
use regex::Regex;
use serde as de;
@@ -52,6 +53,7 @@ lazy_static::lazy_static! {
static ref CONFIG: RwLock<Config> = RwLock::new(Config::load());
static ref CONFIG2: RwLock<Config2> = RwLock::new(Config2::load());
static ref LOCAL_CONFIG: RwLock<LocalConfig> = RwLock::new(LocalConfig::load());
static ref TRUSTED_DEVICES: RwLock<(Vec<TrustedDevice>, bool)> = Default::default();
static ref ONLINE: Mutex<HashMap<String, i64>> = Default::default();
pub static ref PROD_RENDEZVOUS_SERVER: RwLock<String> = RwLock::new(match option_env!("RENDEZVOUS_SERVER") {
Some(key) if !key.is_empty() => key,
@@ -69,7 +71,7 @@ lazy_static::lazy_static! {
pub static ref DEFAULT_LOCAL_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
pub static ref OVERWRITE_LOCAL_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
pub static ref HARD_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
pub static ref BUILDIN_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
pub static ref BUILTIN_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
}
lazy_static::lazy_static! {
@@ -208,6 +210,10 @@ pub struct Config2 {
nat_type: i32,
#[serde(default, deserialize_with = "deserialize_i32")]
serial: i32,
#[serde(default, deserialize_with = "deserialize_string")]
unlock_pin: String,
#[serde(default, deserialize_with = "deserialize_string")]
trusted_devices: String,
#[serde(default)]
socks: Option<Socks5Server>,
@@ -427,14 +433,20 @@ fn patch(path: PathBuf) -> PathBuf {
impl Config2 {
fn load() -> Config2 {
let mut config = Config::load_::<Config2>("2");
let mut store = false;
if let Some(mut socks) = config.socks {
let (password, _, store) =
let (password, _, store2) =
decrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION);
socks.password = password;
config.socks = Some(socks);
if store {
config.store();
}
store |= store2;
}
let (unlock_pin, _, store2) =
decrypt_str_or_original(&config.unlock_pin, PASSWORD_ENC_VERSION);
config.unlock_pin = unlock_pin;
store |= store2;
if store {
config.store();
}
config
}
@@ -450,6 +462,8 @@ impl Config2 {
encrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN);
config.socks = Some(socks);
}
config.unlock_pin =
encrypt_str_or_original(&config.unlock_pin, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN);
Config::store_(&config, "2");
}
@@ -988,6 +1002,7 @@ impl Config {
}
config.password = password.into();
config.store();
Self::clear_trusted_devices();
}
pub fn get_permanent_password() -> String {
@@ -1081,6 +1096,77 @@ impl Config {
NetworkType::Direct
}
pub fn get_unlock_pin() -> String {
CONFIG2.read().unwrap().unlock_pin.clone()
}
pub fn set_unlock_pin(pin: &str) {
let mut config = CONFIG2.write().unwrap();
if pin == config.unlock_pin {
return;
}
config.unlock_pin = pin.to_string();
config.store();
}
pub fn get_trusted_devices_json() -> String {
serde_json::to_string(&Self::get_trusted_devices()).unwrap_or_default()
}
pub fn get_trusted_devices() -> Vec<TrustedDevice> {
let (devices, synced) = TRUSTED_DEVICES.read().unwrap().clone();
if synced {
return devices;
}
let devices = CONFIG2.read().unwrap().trusted_devices.clone();
let (devices, succ, store) = decrypt_str_or_original(&devices, PASSWORD_ENC_VERSION);
if succ {
let mut devices: Vec<TrustedDevice> =
serde_json::from_str(&devices).unwrap_or_default();
let len = devices.len();
devices.retain(|d| !d.outdate());
if store || devices.len() != len {
Self::set_trusted_devices(devices.clone());
}
*TRUSTED_DEVICES.write().unwrap() = (devices.clone(), true);
devices
} else {
Default::default()
}
}
fn set_trusted_devices(mut trusted_devices: Vec<TrustedDevice>) {
trusted_devices.retain(|d| !d.outdate());
let devices = serde_json::to_string(&trusted_devices).unwrap_or_default();
let max_len = 1024 * 1024;
if devices.bytes().len() > max_len {
log::error!("Trusted devices too large: {}", devices.bytes().len());
return;
}
let devices = encrypt_str_or_original(&devices, PASSWORD_ENC_VERSION, max_len);
let mut config = CONFIG2.write().unwrap();
config.trusted_devices = devices;
config.store();
*TRUSTED_DEVICES.write().unwrap() = (trusted_devices, true);
}
pub fn add_trusted_device(device: TrustedDevice) {
let mut devices = Self::get_trusted_devices();
devices.retain(|d| d.hwid != device.hwid);
devices.push(device);
Self::set_trusted_devices(devices);
}
pub fn remove_trusted_devices(hwids: &Vec<Bytes>) {
let mut devices = Self::get_trusted_devices();
devices.retain(|d| !hwids.contains(&d.hwid));
Self::set_trusted_devices(devices);
}
pub fn clear_trusted_devices() {
Self::set_trusted_devices(Default::default());
}
pub fn get() -> Config {
return CONFIG.read().unwrap().clone();
}
@@ -1911,6 +1997,22 @@ impl Group {
}
}
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct TrustedDevice {
pub hwid: Bytes,
pub time: i64,
pub id: String,
pub name: String,
pub platform: String,
}
impl TrustedDevice {
pub fn outdate(&self) -> bool {
const DAYS_90: i64 = 90 * 24 * 60 * 60 * 1000;
self.time + DAYS_90 < crate::get_time()
}
}
deserialize_default!(deserialize_string, String);
deserialize_default!(deserialize_bool, bool);
deserialize_default!(deserialize_i32, i32);
@@ -2100,6 +2202,7 @@ pub mod keys {
pub const OPTION_ENABLE_DIRECTX_CAPTURE: &str = "enable-directx-capture";
pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str =
"enable-android-software-encoding-half-scale";
pub const OPTION_ENABLE_TRUSTED_DEVICES: &str = "enable-trusted-devices";
// buildin options
pub const OPTION_DISPLAY_NAME: &str = "display-name";
@@ -2114,6 +2217,7 @@ pub mod keys {
pub const OPTION_HIDE_USERNAME_ON_CARD: &str = "hide-username-on-card";
pub const OPTION_HIDE_HELP_CARDS: &str = "hide-help-cards";
pub const OPTION_DEFAULT_CONNECT_PASSWORD: &str = "default-connect-password";
pub const OPTION_HIDE_TRAY: &str = "hide-tray";
// flutter local options
pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState";
@@ -2240,6 +2344,7 @@ pub mod keys {
OPTION_PRESET_ADDRESS_BOOK_TAG,
OPTION_ENABLE_DIRECTX_CAPTURE,
OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE,
OPTION_ENABLE_TRUSTED_DEVICES,
];
// BUILDIN_SETTINGS
@@ -2256,6 +2361,7 @@ pub mod keys {
OPTION_HIDE_USERNAME_ON_CARD,
OPTION_HIDE_HELP_CARDS,
OPTION_DEFAULT_CONNECT_PASSWORD,
OPTION_HIDE_TRAY,
];
}

View File

@@ -838,6 +838,21 @@ pub fn create_dir(dir: &str) -> ResultType<()> {
Ok(())
}
#[inline]
pub fn rename_file(path: &str, new_name: &str) -> ResultType<()> {
let path = std::path::Path::new(&path);
if path.exists() {
let dir = path
.parent()
.ok_or(anyhow!("Parent directoy of {path:?} not exists"))?;
let new_path = dir.join(&new_name);
std::fs::rename(&path, &new_path)?;
Ok(())
} else {
bail!("{path:?} not exists");
}
}
#[inline]
pub fn transform_windows_path(entries: &mut Vec<FileEntry>) {
for entry in entries {

View File

@@ -1,6 +1,6 @@
[package]
name = "rustdesk-portable-packer"
version = "1.2.7"
version = "1.3.0"
edition = "2021"
description = "RustDesk Remote Desktop"

View File

@@ -21,7 +21,7 @@ cfg-if = "1.0"
num_cpus = "1.15"
lazy_static = "1.4"
hbb_common = { path = "../hbb_common" }
webm = { git = "https://github.com/21pages/rust-webm" }
webm = { git = "https://github.com/rustdesk-org/rust-webm" }
serde = {version="1.0", features=["derive"]}
[dependencies.winapi]
@@ -59,7 +59,7 @@ gstreamer-app = { version = "0.16", features = ["v1_10"], optional = true }
gstreamer-video = { version = "0.16", optional = true }
[dependencies.hwcodec]
git = "https://github.com/21pages/hwcodec"
git = "https://github.com/rustdesk-org/hwcodec"
optional = true

View File

@@ -188,6 +188,52 @@ fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &s
generate_bindings(&ffi_header, &includes, &ffi_rs, &exact_file, regex);
}
// If you have problems installing ffmpeg, you can download $VCPKG_ROOT/installed from ci
// Linux require link in hwcodec
/*
fn ffmpeg() {
// ffmpeg
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let static_libs = vec!["avcodec", "avutil", "avformat"];
static_libs.iter().for_each(|lib| {
find_package(lib);
});
if target_os == "windows" {
println!("cargo:rustc-link-lib=static=libmfx");
}
// os
let dyn_libs: Vec<&str> = if target_os == "windows" {
["User32", "bcrypt", "ole32", "advapi32"].to_vec()
} else if target_os == "linux" {
let mut v = ["va", "va-drm", "va-x11", "vdpau", "X11", "stdc++"].to_vec();
if target_arch == "x86_64" {
v.push("z");
}
v
} else if target_os == "macos" || target_os == "ios" {
["c++", "m"].to_vec()
} else if target_os == "android" {
["z", "m", "android", "atomic"].to_vec()
} else {
panic!("unsupported os");
};
dyn_libs
.iter()
.map(|lib| println!("cargo:rustc-link-lib={}", lib))
.count();
if target_os == "macos" || target_os == "ios" {
println!("cargo:rustc-link-lib=framework=CoreFoundation");
println!("cargo:rustc-link-lib=framework=CoreVideo");
println!("cargo:rustc-link-lib=framework=CoreMedia");
println!("cargo:rustc-link-lib=framework=VideoToolbox");
println!("cargo:rustc-link-lib=framework=AVFoundation");
}
}
*/
fn main() {
// note: all link symbol names in x86 (32-bit) are prefixed wth "_".
// run "rustup show" to show current default toolchain, if it is stable-x86-pc-windows-msvc,
@@ -204,6 +250,7 @@ fn main() {
gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*");
gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*");
gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*");
// ffmpeg();
// there is problem with cfg(target_os) in build.rs, so use our workaround
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();

View File

@@ -349,6 +349,10 @@ fn init_ndk_context() -> JniResult<()> {
jvm.get_java_vm_pointer() as _,
ctx.as_obj().as_raw() as _,
);
#[cfg(feature = "hwcodec")]
hwcodec::android::ffmpeg_set_java_vm(
jvm.get_java_vm_pointer() as _,
);
}
*lock = true;
return Ok(());

View File

@@ -593,7 +593,7 @@ pub fn request_remote_desktop() -> Result<
}
}
Err(Box::new(DBusError(
"Failed to obtain screen capture.".into(),
"Failed to obtain screen capture. You may need to upgrade the PipeWire library for better compatibility. Please check https://github.com/rustdesk/rustdesk/issues/8600#issuecomment-2254720954 for more details.".into()
)))
}

View File

@@ -1,5 +1,5 @@
pkgname=rustdesk
pkgver=1.2.7
pkgver=1.3.0
pkgrel=0
epoch=
pkgdesc=""

View File

@@ -46,7 +46,7 @@ def view(
devices.append(device)
continue
last_online = datetime.strptime(
device["last_online"], "%Y-%m-%dT%H:%M:%S"
device["last_online"].split(".")[0], "%Y-%m-%dT%H:%M:%S"
) # assuming date is in this format
if (datetime.utcnow() - last_online).days >= offline_days:
devices.append(device)
@@ -128,6 +128,8 @@ def main():
)
args = parser.parse_args()
while args.url.endswith("/"): args.url = args.url[:-1]
devices = view(
args.url,

View File

@@ -31,6 +31,11 @@ LExit:
return WcaFinalize(er);
}
// CAUTION: We can't simply remove the install folder here, because silent repair/upgrade will fail.
// `RemoveInstallFolder()` is a deferred custom action, it will be executed after the files are copied.
// `msiexec /i package.msi /qn`
//
// So we need to delete the files separately in install folder.
UINT __stdcall RemoveInstallFolder(
__in MSIHANDLE hInstall)
{
@@ -41,6 +46,7 @@ UINT __stdcall RemoveInstallFolder(
LPWSTR installFolder = NULL;
LPWSTR pwz = NULL;
LPWSTR pwzData = NULL;
WCHAR runtimeBroker[1024] = { 0, };
hr = WcaInitialize(hInstall, "RemoveInstallFolder");
ExitOnFailure(hr, "Failed to initialize");
@@ -52,21 +58,22 @@ UINT __stdcall RemoveInstallFolder(
hr = WcaReadStringFromCaData(&pwz, &installFolder);
ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz);
StringCchPrintfW(runtimeBroker, sizeof(runtimeBroker) / sizeof(runtimeBroker[0]), L"%ls\\RuntimeBroker_rustdesk.exe", installFolder);
SHFILEOPSTRUCTW fileOp;
ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT));
fileOp.wFunc = FO_DELETE;
fileOp.pFrom = installFolder;
fileOp.pFrom = runtimeBroker;
fileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT;
nResult = SHFileOperationW(&fileOp);
if (nResult == 0)
{
WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has been deleted.", installFolder);
WcaLog(LOGMSG_STANDARD, "The external file \"%ls\" has been deleted.", runtimeBroker);
}
else
{
WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has not been deleted, error code: 0x%02X. Please refer to https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa for the error codes.", installFolder, nResult);
WcaLog(LOGMSG_STANDARD, "The external file \"%ls\" has not been deleted, error code: 0x%02X. Please refer to https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa for the error codes.", runtimeBroker, nResult);
}
LExit:

View File

@@ -3,8 +3,26 @@
<?include ../Includes.wxi?>
<Fragment>
<!-- For compatibility with command line values from previous versions -->
<Property Id="INSTALLFOLDER" Secure="yes">
<RegistrySearch Id="InstallFolderSearch" Root="HKCR" Key="$(var.RegKeyRoot)" Name="INSTALLFOLDER" Type="raw" />
</Property>
<!-- If a property value has been passed via the command line (which includes when set from the bundle), the registry search will
overwrite the command line value, these actions temporarily store the command line value before the registry search
is performed so they can be restored after the registry search is complete -->
<SetProperty Id="SavedInstallFolderCmdLineValue" Value="[INSTALLFOLDER]" Before="AppSearch" Sequence="first" Condition="INSTALLFOLDER" />
<!-- If a command line value was stored, restore it after the registry search has been performed -->
<SetProperty Action="RestoreSavedInstallFolderValue" Id="INSTALLFOLDER" Value="[SavedInstallFolderCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedInstallFolderCmdLineValue" />
<!-- If a command line value or registry value was set, update the main properties with the value -->
<SetProperty Id="INSTALLFOLDER_INNER" Value="[INSTALLFOLDER]" After="RestoreSavedInstallFolderValue" Sequence="first" Condition="INSTALLFOLDER" />
<!-- INSTALLFOLDER_INNER is defined for compatibility with previous versions of the installer. -->
<!-- Because we need to use INSTALLFOLDER as the command line argument. -->
<StandardDirectory Id="ProgramFiles6432Folder">
<Directory Id="INSTALLFOLDER" Name="$(var.Product)" />
<Directory Id="INSTALLFOLDER_INNER" Name="$(var.Product)" />
</StandardDirectory>
<StandardDirectory Id="CommonAppDataFolder">

View File

@@ -5,16 +5,16 @@
<Fragment>
<!-- Regs for shortcuts are defined in "Fragments/ShortcutProperties.wxs" -->
<!-- Component that persists the property values to the registry so they are available during an upgrade/modify -->
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="Product.Registry.InstallDir" Guid="3196EDA7-9AEF-4705-A0C8-E3F3ECCCB153">
<DirectoryRef Id="INSTALLFOLDER_INNER">
<Component Id="Product.Registry.InstallFolder" Guid="3196EDA7-9AEF-4705-A0C8-E3F3ECCCB153">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
<RegistryValue Type="string" Name="INSTALLFOLDER" Value="[INSTALLFOLDER]" />
<RegistryValue Type="string" Name="INSTALLFOLDER" Value="[INSTALLFOLDER_INNER]" />
</RegistryKey>
</Component>
<Component Id="Product.Registry.DefaultIcon" Guid="6DBF2690-0955-4C6A-940F-634DDA503F49">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\DefaultIcon">
<RegistryValue Type="string" Value='"[INSTALLFOLDER]$(var.Product).exe",0' />
<RegistryValue Type="string" Value='"[INSTALLFOLDER_INNER]$(var.Product).exe",0' />
</RegistryKey>
</Component>
@@ -22,7 +22,7 @@
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell" />
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell\open" />
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell\open\command">
<RegistryValue Type="string" Value='"[INSTALLFOLDER]$(var.Product).exe" --play "%1"' />
<RegistryValue Type="string" Value='"[INSTALLFOLDER_INNER]$(var.Product).exe" --play "%1"' />
</RegistryKey>
</Component>
@@ -36,7 +36,7 @@
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell" />
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell\open" />
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell\open\command">
<RegistryValue Type="string" Value='"[INSTALLFOLDER]$(var.Product).exe" "%1"' />
<RegistryValue Type="string" Value='"[INSTALLFOLDER_INNER]$(var.Product).exe" "%1"' />
</RegistryKey>
</Component>

View File

@@ -4,7 +4,7 @@
<?include ../Includes.wxi?>
<DirectoryRef Id="INSTALLFOLDER" FileSource="$(var.BuildDir)">
<DirectoryRef Id="INSTALLFOLDER_INNER" FileSource="$(var.BuildDir)">
<Component Id="App.exe" Guid="620F0F69-4C17-4320-A619-495E329712A4">
<File Id="App.exe" Name="$(var.Product).exe" KeyPath="yes" Checksum="yes">
<!--<fire:FirewallException Id="AppEx" Name="$(var.Product) Service" Scope="any" IgnoreFailure="yes" />-->
@@ -12,12 +12,12 @@
</Component>
</DirectoryRef>
<CustomAction Id="RemoveInstallFolder.SetParam" Return="check" Property="RemoveInstallFolder" Value="[INSTALLFOLDER]" />
<CustomAction Id="AddFirewallRules.SetParam" Return="check" Property="AddFirewallRules" Value="1[INSTALLFOLDER]$(var.Product).exe" />
<CustomAction Id="RemoveFirewallRules.SetParam" Return="check" Property="RemoveFirewallRules" Value="0[INSTALLFOLDER]$(var.Product).exe" />
<CustomAction Id="CreateStartService.SetParam" Return="check" Property="CreateStartService" Value="$(var.Product);&quot;[INSTALLFOLDER]$(var.Product).exe&quot; --service" />
<CustomAction Id="RemoveInstallFolder.SetParam" Return="check" Property="RemoveInstallFolder" Value="[INSTALLFOLDER_INNER]" />
<CustomAction Id="AddFirewallRules.SetParam" Return="check" Property="AddFirewallRules" Value="1[INSTALLFOLDER_INNER]$(var.Product).exe" />
<CustomAction Id="RemoveFirewallRules.SetParam" Return="check" Property="RemoveFirewallRules" Value="0[INSTALLFOLDER_INNER]$(var.Product).exe" />
<CustomAction Id="CreateStartService.SetParam" Return="check" Property="CreateStartService" Value="$(var.Product);&quot;[INSTALLFOLDER_INNER]$(var.Product).exe&quot; --service" />
<CustomAction Id="TryStopDeleteService.SetParam" Return="check" Property="TryStopDeleteService" Value="$(var.Product)" />
<CustomAction Id="LaunchApp" ExeCommand="" Return="asyncNoWait" FileRef="App.exe" />
<CustomAction Id="LaunchAppTray" ExeCommand=" --tray" Return="asyncNoWait" FileRef="App.exe" />
<Property Id="TerminateProcesses" Value="AppTest.exe" />
@@ -29,7 +29,7 @@
<CustomAction Id="SetPropertyServiceStop.SetParam.ConfigKey" Return="check" Property="ConfigKey" Value="stop-service" />
<CustomAction Id="SetPropertyServiceStop.SetParam.PropertyName" Return="check" Property="PropertyName" Value="STOP_SERVICE" />
<CustomAction Id="TryDeleteStartupShortcut.SetParam" Return="check" Property="ShortcutName" Value="$(var.Product) Tray" />
<CustomAction Id="RemoveAmyuniIdd.SetParam" Return="check" Property="RemoveAmyuniIdd" Value="[INSTALLFOLDER]" />
<CustomAction Id="RemoveAmyuniIdd.SetParam" Return="check" Property="RemoveAmyuniIdd" Value="[INSTALLFOLDER_INNER]" />
<InstallExecuteSequence>
<Custom Action="SetPropertyIsServiceRunning" After="InstallInitialize" Condition="Installed" />
@@ -40,11 +40,11 @@
<Custom Action="SetPropertyServiceStop.SetParam.ConfigFile" Before="SetPropertyServiceStop" Condition="NOT Installed" />
<Custom Action="SetPropertyServiceStop.SetParam.ConfigKey" Before="SetPropertyServiceStop" Condition="NOT Installed" />
<Custom Action="SetPropertyServiceStop.SetParam.PropertyName" Before="SetPropertyServiceStop" Condition="NOT Installed" />
<!-- Do not call CreateStartService if is uninstalling. -->
<!-- (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE) means uninstalling. -->
<Custom Action="CreateStartService" Before="InstallFinalize" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE=&quot;&apos;Y&apos;&quot;)" />
<Custom Action="CreateStartService.SetParam" Before="CreateStartService" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE=&quot;&apos;Y&apos;&quot;)" />
<Custom Action="CreateStartService" Before="InstallFinalize" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE=&quot;&apos;Y&apos;&quot;) AND (NOT CC_CONNECTION_TYPE=&quot;outgoing&quot;)" />
<Custom Action="CreateStartService.SetParam" Before="CreateStartService" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE=&quot;&apos;Y&apos;&quot;) AND (NOT CC_CONNECTION_TYPE=&quot;outgoing&quot;)" />
<Custom Action="CustomActionHello" Before="InstallFinalize" />
@@ -54,14 +54,14 @@
<!-- Launch ClientLauncher if installing or already installed and not uninstalling -->
<Custom Action="LaunchApp" After="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
<Custom Action="LaunchAppTray" After="InstallFinalize" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE=&quot;&apos;Y&apos;&quot;)"/>
<Custom Action="LaunchAppTray" After="InstallFinalize" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE=&quot;&apos;Y&apos;&quot;) AND (NOT CC_CONNECTION_TYPE=&quot;outgoing&quot;)"/>
<!--Workaround of "fire:FirewallException". If Outbound="Yes" or Outbound="true", the following error occurs.-->
<!--ExecFirewallExceptions: Error 0x80070057: failed to add app to the authorized apps list-->
<Custom Action="AddFirewallRules" Before="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
<Custom Action="AddFirewallRules.SetParam" Before="AddFirewallRules" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
<Custom Action="AddRegSoftwareSASGeneration" Before="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
<Custom Action="AddRegSoftwareSASGeneration" Before="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE) AND (NOT CC_CONNECTION_TYPE=&quot;outgoing&quot;)"/>
<Custom Action="RemoveInstallFolder" Before="RemoveFiles"/>
<Custom Action="RemoveInstallFolder.SetParam" Before="RemoveInstallFolder"/>
@@ -88,8 +88,8 @@
</DirectoryRef>
<DirectoryRef Id="App.StartMenu">
<Component Id="App.StartMenu.Shortcut" Guid="43ABCAC7-E47D-42D8-A408-25EC70DBB993" Condition="STARTMENUSHORTCUTS = 1">
<Shortcut Id="App.StartMenu.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
<Component Id="App.StartMenu.Shortcut" Guid="43ABCAC7-E47D-42D8-A408-25EC70DBB993" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = &quot;Y&quot; OR STARTMENUSHORTCUTS = &quot;y&quot;">
<Shortcut Id="App.StartMenu.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
<!--
Fix ICE 38 by adding a dummy registry key that is the key for this shortcut.
https://learn.microsoft.com/en-us/windows/win32/msi/ice38
@@ -97,31 +97,31 @@
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.Shortcut" Type="string" Value="1" KeyPath="yes" />
</Component>
<Component Id="App.StartMenu.ShortcutUninstall" Guid="E100D7F8-D607-4513-28DA-2C95E5EA698E" Condition="STARTMENUSHORTCUTS = 1">
<Component Id="App.StartMenu.ShortcutUninstall" Guid="E100D7F8-D607-4513-28DA-2C95E5EA698E" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = &quot;Y&quot; OR STARTMENUSHORTCUTS = &quot;y&quot;">
<Shortcut Id="App.StartMenu.ShortcutUninstall" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" />
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.ShortcutUninstall" Type="string" Value="1" KeyPath="yes" />
</Component>
</DirectoryRef>
<StandardDirectory Id="DesktopFolder">
<Component Id="App.Desktop.Shortcut" Guid="CA8FB7AA-17F7-4E36-A58A-5A016A303709" Condition="DESKTOPSHORTCUTS = 1">
<Shortcut Id="App.Desktop.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
<Component Id="App.Desktop.Shortcut" Guid="CA8FB7AA-17F7-4E36-A58A-5A016A303709" Condition="DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = &quot;Y&quot; OR DESKTOPSHORTCUTS = &quot;y&quot;">
<Shortcut Id="App.Desktop.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.Desktop.Shortcut" Type="string" Value="1" KeyPath="yes" />
</Component>
</StandardDirectory>
<StandardDirectory Id="StartupFolder">
<Component Id="App.StartupFolder.ShortcutTray" Guid="B1D1E2BB-E53E-E159-DB7C-744D5C726A8C" Condition="STARTUPSHORTCUTS = 1">
<Shortcut Id="App.StartupFolder.ShortcutTray" Name="!(loc.SC_Client_Tray)" Description="!(loc.SC_Client_Tray_Desc)" Target="[!App.exe]" Arguments="--tray" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
<Component Id="App.StartupFolder.ShortcutTray" Guid="B1D1E2BB-E53E-E159-DB7C-744D5C726A8C" Condition="STARTUPSHORTCUTS = 1 AND (NOT CC_CONNECTION_TYPE=&quot;outgoing&quot;)">
<Shortcut Id="App.StartupFolder.ShortcutTray" Name="!(loc.SC_Client_Tray)" Description="!(loc.SC_Client_Tray_Desc)" Target="[!App.exe]" Arguments="--tray" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartupFolder.ShortcutTray" Type="string" Value="1" KeyPath="yes" />
</Component>
</StandardDirectory>
<!--<DirectoryRef Id="INSTALLFOLDER">
<!--<DirectoryRef Id="INSTALLFOLDER_INNER">
<Component Id="App.UninstallShortcut" Guid="FB0F2AC7-2AE5-4C54-B860-5E472620B6B1">
<Shortcut Id="App.UninstallShortcut" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" />
</Component>
</DirectoryRef>-->
<ComponentGroup Id="Components" Directory="INSTALLFOLDER">
<ComponentGroup Id="Components" Directory="INSTALLFOLDER_INNER">
<ComponentRef Id="App.exe" />
<ComponentRef Id="App.Desktop.Shortcut" />
<!--<ComponentRef Id="App.UninstallShortcut" />-->

View File

@@ -25,6 +25,9 @@
<!--$ArpStart$-->
<!--$ArpEnd$-->
<!--$CustomClientPropsStart$-->
<!--$CustomClientPropsEnd$-->
<Property Id="APP_WINDOWS_INSTALLER">
<RegistrySearch Id="AppWindowsInstallerFolderSearch" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\$(var.Product)" Name="WindowsInstaller" Type="raw" />
</Property>

View File

@@ -25,11 +25,25 @@
</Property>
<!-- Component that persists the property values to the registry so they are available during an upgrade/modify -->
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="Product.Registry.PersistedShortcutProperties" Guid="1BBAD054-6EC2-4362-BF1B-E8BDE988B597">
<DirectoryRef Id="INSTALLFOLDER_INNER">
<Component Id="Product.Registry.PersistedStartMenuShortcutProperties1" Guid="62F79BCF-3367-4ACF-950F-F8BCABACDDC0" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = &quot;Y&quot; OR STARTMENUSHORTCUTS = &quot;y&quot;">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
<RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="[STARTMENUSHORTCUTS]" KeyPath="yes" />
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="[DESKTOPSHORTCUTS]" />
<RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="1" KeyPath="yes" />
</RegistryKey>
</Component>
<Component Id="Product.Registry.PersistedStartMenuShortcutProperties0" Guid="8EA2D5A8-6E5D-4BDD-9019-2099297FF519" Condition="NOT (STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = &quot;Y&quot; OR STARTMENUSHORTCUTS = &quot;y&quot;)">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
<RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="0" KeyPath="yes" />
</RegistryKey>
</Component>
<Component Id="Product.Registry.PersistedDesktopShortcutProperties1" Guid="1BBAD054-6EC2-4362-BF1B-E8BDE988B597" Condition="DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = &quot;Y&quot; OR DESKTOPSHORTCUTS = &quot;y&quot;">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="1" />
</RegistryKey>
</Component>
<Component Id="Product.Registry.PersistedDesktopShortcutProperties0" Guid="FA992614-D2E1-4795-9696-D45A5EF1B9C8" Condition="NOT (DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = &quot;Y&quot; OR DESKTOPSHORTCUTS = &quot;y&quot;)">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="0" />
</RegistryKey>
</Component>
</DirectoryRef>
@@ -45,8 +59,8 @@
<SetProperty Action="RestoreSavedDesktopShortcutsValue" Id="CREATEDESKTOPSHORTCUTS" Value="[SavedDesktopShortcutsCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedDesktopShortcutsCmdLineValue" />
<!-- If a command line value or registry value was set, update the main properties with the value -->
<SetProperty Id="STARTMENUSHORTCUTS" Value="[CREATESTARTMENUSHORTCUTS]" After="RestoreSavedStartMenuShortcutsValue" Sequence="first" Condition="CREATESTARTMENUSHORTCUTS" />
<SetProperty Id="DESKTOPSHORTCUTS" Value="[CREATEDESKTOPSHORTCUTS]" After="RestoreSavedDesktopShortcutsValue" Sequence="first" Condition="CREATEDESKTOPSHORTCUTS" />
<SetProperty Id="STARTMENUSHORTCUTS" Value="" After="RestoreSavedStartMenuShortcutsValue" Sequence="first" Condition="CREATESTARTMENUSHORTCUTS AND NOT (CREATESTARTMENUSHORTCUTS = 1 OR CREATESTARTMENUSHORTCUTS = &quot;Y&quot; OR CREATESTARTMENUSHORTCUTS = &quot;y&quot;)" />
<SetProperty Id="DESKTOPSHORTCUTS" Value="" After="RestoreSavedDesktopShortcutsValue" Sequence="first" Condition="CREATEDESKTOPSHORTCUTS AND NOT (CREATEDESKTOPSHORTCUTS = 1 OR CREATEDESKTOPSHORTCUTS = &quot;Y&quot; OR CREATEDESKTOPSHORTCUTS = &quot;y&quot;)" />
</Fragment>
</Wix>

View File

@@ -49,4 +49,7 @@ This file contains the declaration of all the localizable strings.
<String Id="AnotherAppDialogTitle" Value="Cancel installation."/>
<String Id="AnotherAppDialogDescription" Value="The application is installed by self-installation method, please uninstall it first."/>
<String Id="MyInstallDirDlgDesktopShortcuts" Value="Create desktop icon" />
<String Id="MyInstallDirDlgStartMenuShortcuts" Value="Create start menu shortcuts" />
</WixLocalization>

View File

@@ -18,11 +18,11 @@
<!-- User Interface -->
<WixVariable Id="WixUILicenseRtf" Value="License.rtf" />
<ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLFOLDER" />
<ui:WixUI Id="UI_MyInstallDialog" InstallDirectory="INSTALLFOLDER_INNER" />
<UIRef Id="WixUI_ErrorProgressText" />
<InstallUISequence>
<Show Dialog="AnotherAppDialog" Before="WelcomeDlg" Condition="Not installed AND APP_WINDOWS_INSTALLER=&quot;#0&quot;"/>
<Show Dialog="UI_AnotherAppDialog" Before="WelcomeDlg" Condition="Not installed AND APP_WINDOWS_INSTALLER=&quot;#0&quot;"/>
</InstallUISequence>
<InstallExecuteSequence>
@@ -40,14 +40,17 @@
<Feature Id="App" Level="1" AllowAdvertise="no" Display="expand" Title="!(loc.F_App)" Description="!(loc.F_App_Desc)" AllowAbsent="no">
<ComponentGroupRef Id="Components" />
<ComponentRef Id="Product.Registry.InstallDir" />
<ComponentRef Id="Product.Registry.InstallFolder" />
<ComponentRef Id="Product.Registry.DefaultIcon" />
<ComponentRef Id="Product.Registry.CommandPlay" />
<ComponentRef Id="Product.Registry.URLProtocol" />
<ComponentRef Id="Product.Registry.Command" />
<ComponentRef Id="Product.Registry.UninstallApp" />
<ComponentRef Id="App.StartMenu" />
<ComponentRef Id="Product.Registry.PersistedShortcutProperties" />
<ComponentRef Id="Product.Registry.PersistedStartMenuShortcutProperties1" />
<ComponentRef Id="Product.Registry.PersistedStartMenuShortcutProperties0" />
<ComponentRef Id="Product.Registry.PersistedDesktopShortcutProperties1" />
<ComponentRef Id="Product.Registry.PersistedDesktopShortcutProperties0" />
</Feature>
<!--https://wixtoolset.org/docs/tools/wixext/wixui/#customizing-a-dialog-set-->

View File

@@ -1,7 +1,7 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<UI>
<Dialog Id="AnotherAppDialog" Width="370" Height="270" Title="!(loc.ExitDialog_Title)">
<Dialog Id="UI_AnotherAppDialog" Width="370" Height="270" Title="!(loc.ExitDialog_Title)">
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="EndDialog" Value="ErrorAbort" />
</Control>

View File

@@ -0,0 +1,31 @@
<!-- https://github.com/wixtoolset/wix/blob/ce73352b1fa1d4f9cded10a0ee410f2e786bd326/src/ext/UI/wixlib/InstallDirDlg.wxs -->
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<UI>
<Dialog Id="MyInstallDirDlg" Width="370" Height="270" Title="!(loc.InstallDirDlg_Title)">
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)" />
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg" />
</Control>
<Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgDescription)" />
<Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgTitle)" />
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.InstallDirDlgBannerBitmap)" />
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="FolderLabel" Type="Text" X="20" Y="60" Width="290" Height="18" NoPrefix="yes" Text="!(loc.InstallDirDlgFolderLabel)" />
<Control Id="Folder" Type="PathEdit" X="20" Y="80" Width="320" Height="18" Property="WIXUI_INSTALLDIR" Indirect="yes" />
<Control Id="ChangeFolder" Type="PushButton" X="20" Y="100" Width="56" Height="17" Text="!(loc.InstallDirDlgChange)" />
<Control Id="ChkBoxStartMenuShortcuts" Type="CheckBox" X="20" Y="140" Width="290" Height="17" Property="STARTMENUSHORTCUTS" CheckBoxValue="1" Text="!(loc.MyInstallDirDlgStartMenuShortcuts)" />
<Control Id="ChkBoxDesktopShortcuts" Type="CheckBox" X="20" Y="160" Width="290" Height="17" Property="DESKTOPSHORTCUTS" CheckBoxValue="1" Text="!(loc.MyInstallDirDlgDesktopShortcuts)" />
</Dialog>
</UI>
</Fragment>
</Wix>

View File

@@ -0,0 +1,87 @@
<!-- From https://github.com/wixtoolset/wix/blob/ce73352b1fa1d4f9cded10a0ee410f2e786bd326/src/ext/UI/wixlib/WixUI_InstallDir.wxs -->
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
<!--
First-time install dialog sequence:
- WixUI_WelcomeDlg
- WixUI_LicenseAgreementDlg
- WixUI_InstallDirDlg
- WixUI_VerifyReadyDlg
- WixUI_DiskCostDlg
Maintenance dialog sequence:
- WixUI_MaintenanceWelcomeDlg
- WixUI_MaintenanceTypeDlg
- WixUI_InstallDirDlg
- WixUI_VerifyReadyDlg
Patch dialog sequence:
- WixUI_WelcomeDlg
- WixUI_VerifyReadyDlg
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<?foreach WIXUIARCH in X86;X64;A64 ?>
<Fragment>
<UI Id="UI_MyInstallDialog_$(WIXUIARCH)">
<Publish Dialog="LicenseAgreementDlg" Control="Print" Event="DoAction" Value="WixUIPrintEula_$(WIXUIARCH)" />
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH" />
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="2" Condition="NOT WIXUI_DONTVALIDATEPATH" />
</UI>
<UIRef Id="UI_MyInstallDialog" />
</Fragment>
<?endforeach?>
<Fragment>
<UI Id="file UI_MyInstallDialog">
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
<DialogRef Id="BrowseDlg" />
<DialogRef Id="DiskCostDlg" />
<DialogRef Id="ErrorDlg" />
<DialogRef Id="FatalError" />
<DialogRef Id="FilesInUse" />
<DialogRef Id="MsiRMFilesInUse" />
<DialogRef Id="PrepareDlg" />
<DialogRef Id="ProgressDlg" />
<DialogRef Id="ResumeDlg" />
<DialogRef Id="UserExit" />
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID&lt;&gt;&quot;1&quot;" />
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" />
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg" Condition="NOT Installed" />
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="Installed AND PATCH" />
<Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" />
<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="MyInstallDirDlg" Condition="LicenseAccepted = &quot;1&quot;" />
<Publish Dialog="MyInstallDirDlg" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg" />
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" />
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID&lt;&gt;&quot;1&quot;" />
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4" Condition="WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID=&quot;1&quot;" />
<Publish Dialog="MyInstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" />
<Publish Dialog="MyInstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MyInstallDirDlg" Order="1" Condition="NOT Installed" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2" Condition="Installed AND NOT PATCH" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" Condition="Installed AND PATCH" />
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" />
<Property Id="ARPNOMODIFY" Value="1" />
</UI>
<UIRef Id="WixUI_Common" />
</Fragment>
</Wix>

View File

@@ -64,6 +64,12 @@ def make_parser():
parser.add_argument(
"-c", "--custom", action="store_true", help="Is custom client", default=False
)
parser.add_argument(
"--conn-type",
type=str,
default="",
help='Connection type, e.g. "incoming", "outgoing". Default is empty, means incoming-outgoing',
)
parser.add_argument(
"--app-name", type=str, default="RustDesk", help="The app name."
)
@@ -84,7 +90,7 @@ def make_parser():
def read_lines_and_start_index(file_path, tag_start, tag_end):
with open(file_path, "r") as f:
with open(file_path, "r", encoding="utf-8") as f:
lines = f.readlines()
index_start = -1
index_end = -1
@@ -174,11 +180,11 @@ def gen_pre_vars(args, dist_dir):
def replace_app_name_in_langs(app_name):
langs_dir = Path(sys.argv[0]).parent.joinpath("Package/Language")
for file_path in langs_dir.glob("*.wxl"):
with open(file_path, "r") as f:
with open(file_path, "r", encoding="utf-8") as f:
lines = f.readlines()
for i, line in enumerate(lines):
lines[i] = line.replace("RustDesk", app_name)
with open(file_path, "w") as f:
with open(file_path, "w", encoding="utf-8") as f:
f.writelines(lines)
@@ -295,7 +301,7 @@ def gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir):
f'{indent}<RegistryValue Type="string" Name="DisplayName" Value="{args.app_name}" />\n'
)
lines_new.append(
f'{indent}<RegistryValue Type="string" Name="DisplayIcon" Value="[INSTALLFOLDER]{args.app_name}.exe" />\n'
f'{indent}<RegistryValue Type="string" Name="DisplayIcon" Value="[INSTALLFOLDER_INNER]{args.app_name}.exe" />\n'
)
lines_new.append(
f'{indent}<RegistryValue Type="string" Name="DisplayVersion" Value="{g_version}" />\n'
@@ -308,7 +314,7 @@ def gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir):
f'{indent}<RegistryValue Type="string" Name="InstallDate" Value="{installDate}" />\n'
)
lines_new.append(
f'{indent}<RegistryValue Type="string" Name="InstallLocation" Value="[INSTALLFOLDER]" />\n'
f'{indent}<RegistryValue Type="string" Name="InstallLocation" Value="[INSTALLFOLDER_INNER]" />\n'
)
lines_new.append(
f'{indent}<RegistryValue Type="string" Name="InstallSource" Value="[InstallSource]" />\n'
@@ -385,6 +391,26 @@ def gen_custom_ARPSYSTEMCOMPONENT(args, dist_dir):
else:
return gen_custom_ARPSYSTEMCOMPONENT_False(args)
def gen_conn_type(args):
def func(lines, index_start):
indent = g_indent_unit * 3
lines_new = []
if args.conn_type != "":
lines_new.append(
f"""{indent}<Property Id="CC_CONNECTION_TYPE" Value="{args.conn_type}" />\n"""
)
for i, line in enumerate(lines_new):
lines.insert(index_start + i + 1, line)
return lines
return gen_content_between_tags(
"Package/Fragments/AddRemoveProperties.wxs",
"<!--$CustomClientPropsStart$-->",
"<!--$CustomClientPropsEnd$-->",
func,
)
def gen_content_between_tags(filename, tag_start, tag_end, func):
target_file = Path(sys.argv[0]).parent.joinpath(filename)
@@ -394,7 +420,7 @@ def gen_content_between_tags(filename, tag_start, tag_end, func):
func(lines, index_start)
with open(target_file, "w") as f:
with open(target_file, "w", encoding="utf-8") as f:
f.writelines(lines)
return True
@@ -454,19 +480,19 @@ def update_license_file(app_name):
if app_name == "RustDesk":
return
license_file = Path(sys.argv[0]).parent.joinpath("Package/License.rtf")
with open(license_file, "r") as f:
with open(license_file, "r", encoding="utf-8") as f:
license_content = f.read()
license_content = license_content.replace("website rustdesk.com and other ", "")
license_content = license_content.replace("RustDesk", app_name)
license_content = re.sub("Purslane Ltd", app_name, license_content, flags=re.IGNORECASE)
with open(license_file, "w") as f:
with open(license_file, "w", encoding="utf-8") as f:
f.write(license_content)
def replace_component_guids_in_wxs():
langs_dir = Path(sys.argv[0]).parent.joinpath("Package")
for file_path in langs_dir.glob("**/*.wxs"):
with open(file_path, "r") as f:
with open(file_path, "r", encoding="utf-8") as f:
lines = f.readlines()
# <Component Id="Product.Registry.DefaultIcon" Guid="6DBF2690-0955-4C6A-940F-634DDA503F49">
@@ -475,7 +501,7 @@ def replace_component_guids_in_wxs():
if match:
lines[i] = re.sub(r'Guid="[^"]+"', f'Guid="{uuid.uuid4()}"', line)
with open(file_path, "w") as f:
with open(file_path, "w", encoding="utf-8") as f:
f.writelines(lines)
@@ -506,6 +532,9 @@ if __name__ == "__main__":
if not gen_custom_ARPSYSTEMCOMPONENT(args, dist_dir):
sys.exit(-1)
if not gen_conn_type(args):
sys.exit(-1)
if not gen_auto_component(app_name, dist_dir):
sys.exit(-1)

View File

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

View File

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

View File

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

123
res/users.py Executable file
View File

@@ -0,0 +1,123 @@
#!/usr/bin/env python3
import requests
import argparse
from datetime import datetime, timedelta
def view(
url,
token,
name=None,
group_name=None,
):
headers = {"Authorization": f"Bearer {token}"}
pageSize = 30
params = {
"name": name,
"group_name": group_name,
}
params = {
k: "%" + v + "%" if (v != "-" and "%" not in v) else v
for k, v in params.items()
if v is not None
}
params["pageSize"] = pageSize
users = []
current = 1
while True:
params["current"] = current
response = requests.get(f"{url}/api/users", headers=headers, params=params)
response_json = response.json()
data = response_json.get("data", [])
users.extend(data)
total = response_json.get("total", 0)
current += pageSize
if len(data) < pageSize or current > total:
break
return users
def check(response):
if response.status_code == 200:
try:
response_json = response.json()
return response_json
except ValueError:
return response.text or "Success"
else:
return "Failed", response.status_code, response.text
def disable(url, token, guid, name):
print("Disable", name)
headers = {"Authorization": f"Bearer {token}"}
response = requests.post(f"{url}/api/users/{guid}/disable", headers=headers)
return check(response)
def enable(url, token, guid, name):
print("Enable", name)
headers = {"Authorization": f"Bearer {token}"}
response = requests.post(f"{url}/api/users/{guid}/enable", headers=headers)
return check(response)
def delete(url, token, guid, name):
print("Delete", name)
headers = {"Authorization": f"Bearer {token}"}
response = requests.delete(f"{url}/api/users/{guid}", headers=headers)
return check(response)
def main():
parser = argparse.ArgumentParser(description="User manager")
parser.add_argument(
"command",
choices=["view", "disable", "enable", "delete"],
help="Command to execute",
)
parser.add_argument("--url", required=True, help="URL of the API")
parser.add_argument(
"--token", required=True, help="Bearer token for authentication"
)
parser.add_argument("--name", help="User name")
parser.add_argument("--group_name", help="Group name")
args = parser.parse_args()
while args.url.endswith("/"): args.url = args.url[:-1]
users = view(
args.url,
args.token,
args.name,
args.group_name,
)
if args.command == "view":
for user in users:
print(user)
elif args.command == "disable":
for user in users:
response = disable(args.url, args.token, user["guid"], user["name"])
print(response)
elif args.command == "enable":
for user in users:
response = enable(args.url, args.token, user["guid"], user["name"])
print(response)
elif args.command == "delete":
for user in users:
response = delete(args.url, args.token, user["guid"], user["name"])
print(response)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,60 @@
diff --git a/build/cmake/cpu.cmake b/build/cmake/cpu.cmake
index acebe20..8c67d89 100644
--- a/build/cmake/cpu.cmake
+++ b/build/cmake/cpu.cmake
@@ -120,6 +120,19 @@ elseif("${AOM_TARGET_CPU}" MATCHES "^x86")
set(RTCD_ARCH_X86_64 "yes")
endif()
+ # AVX2 requires __m256i definition starting v3.9.0
+
+ if(ENABLE_AVX2)
+ aom_check_source_compiles("x86_64_avx2_m256i_available" "
+#include <emmintrin.h>
+#ifndef __m256i
+#error 1
+#endif" HAVE_AVX2_M256I)
+ if(HAVE_AVX2_M256I EQUAL 0)
+ set(ENABLE_AVX2 0)
+ endif()
+ endif()
+
set(X86_FLAVORS "MMX;SSE;SSE2;SSE3;SSSE3;SSE4_1;SSE4_2;AVX;AVX2")
foreach(flavor ${X86_FLAVORS})
if(ENABLE_${flavor} AND NOT disable_remaining_flavors)
diff --git a/aom_dsp/x86/synonyms.h b/aom_dsp/x86/synonyms.h
index 0d51cdf..6744ec5 100644
--- a/aom_dsp/x86/synonyms.h
+++ b/aom_dsp/x86/synonyms.h
@@ -46,13 +46,6 @@ static INLINE __m128i xx_loadu_128(const void *a) {
return _mm_loadu_si128((const __m128i *)a);
}
-// Load 64 bits from each of hi and low, and pack into an SSE register
-// Since directly loading as `int64_t`s and using _mm_set_epi64 may violate
-// the strict aliasing rule, this takes a different approach
-static INLINE __m128i xx_loadu_2x64(const void *hi, const void *lo) {
- return _mm_unpacklo_epi64(_mm_loadu_si64(lo), _mm_loadu_si64(hi));
-}
-
static INLINE void xx_storel_32(void *const a, const __m128i v) {
const int val = _mm_cvtsi128_si32(v);
memcpy(a, &val, sizeof(val));
diff --git a/aom_dsp/x86/synonyms_avx2.h b/aom_dsp/x86/synonyms_avx2.h
index d4e8f69..45be17e 100644
--- a/aom_dsp/x86/synonyms_avx2.h
+++ b/aom_dsp/x86/synonyms_avx2.h
@@ -25,6 +25,13 @@
* Intrinsics prefixed with yy_ operate on or return 256bit YMM registers.
*/
+// Load 64 bits from each of hi and low, and pack into an SSE register
+// Since directly loading as `int64_t`s and using _mm_set_epi64 may violate
+// the strict aliasing rule, this takes a different approach
+static INLINE __m128i xx_loadu_2x64(const void *hi, const void *lo) {
+ return _mm_unpacklo_epi64(_mm_loadu_si64(lo), _mm_loadu_si64(hi));
+}
+
// Loads and stores to do away with the tedium of casting the address
// to the right type.
static INLINE __m256i yy_load_256(const void *a) {

View File

@@ -14,6 +14,7 @@ vcpkg_from_git(
REF 8ad484f8a18ed1853c094e7d3a4e023b2a92df28 # 3.9.1
PATCHES
aom-uninitialized-pointer.diff
aom-avx2.diff
# Can be dropped when https://bugs.chromium.org/p/aomedia/issues/detail?id=3029 is merged into the upstream
aom-install.diff
)

View File

@@ -0,0 +1,11 @@
diff --git a/configure b/configure
--- a/configure
+++ b/configure
@@ -6162,6 +6162,7 @@ EOF
test -n "$extern_prefix" && append X86ASMFLAGS "-DPREFIX"
case "$objformat" in
elf*) enabled debug && append X86ASMFLAGS $x86asm_debug ;;
+ win*) enabled debug && append X86ASMFLAGS "-g" ;;
esac
enabled avx512 && check_x86asm avx512_external "vmovdqa32 [eax]{k1}{z}, zmm0"

View File

@@ -0,0 +1,13 @@
diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c
--- a/fftools/cmdutils.c
+++ b/fftools/cmdutils.c
@@ -51,6 +51,8 @@
#include "fopen_utf8.h"
#include "opt_common.h"
#ifdef _WIN32
+#define _WIN32_WINNT 0x0502
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "compat/w32dlfcn.h"
#endif

View File

@@ -0,0 +1,55 @@
diff --git a/libavcodec/x86/Makefile b/libavcodec/x86/Makefile
--- a/libavcodec/x86/Makefile
+++ b/libavcodec/x86/Makefile
@@ -158,6 +158,8 @@ X86ASM-OBJS-$(CONFIG_ALAC_DECODER) += x86/alacdsp.o
X86ASM-OBJS-$(CONFIG_APNG_DECODER) += x86/pngdsp.o
X86ASM-OBJS-$(CONFIG_CAVS_DECODER) += x86/cavsidct.o
+ifdef ARCH_X86_64
X86ASM-OBJS-$(CONFIG_CFHD_ENCODER) += x86/cfhdencdsp.o
+endif
X86ASM-OBJS-$(CONFIG_CFHD_DECODER) += x86/cfhddsp.o
X86ASM-OBJS-$(CONFIG_DCA_DECODER) += x86/dcadsp.o x86/synth_filter.o
X86ASM-OBJS-$(CONFIG_DIRAC_DECODER) += x86/diracdsp.o \
@@ -175,15 +177,21 @@ x86/hevc_sao_10bit.o
X86ASM-OBJS-$(CONFIG_JPEG2000_DECODER) += x86/jpeg2000dsp.o
X86ASM-OBJS-$(CONFIG_LSCR_DECODER) += x86/pngdsp.o
+ifdef ARCH_X86_64
X86ASM-OBJS-$(CONFIG_MLP_DECODER) += x86/mlpdsp.o
+endif
X86ASM-OBJS-$(CONFIG_MPEG4_DECODER) += x86/xvididct.o
X86ASM-OBJS-$(CONFIG_PNG_DECODER) += x86/pngdsp.o
+ifdef ARCH_X86_64
X86ASM-OBJS-$(CONFIG_PRORES_DECODER) += x86/proresdsp.o
X86ASM-OBJS-$(CONFIG_PRORES_LGPL_DECODER) += x86/proresdsp.o
+endif
X86ASM-OBJS-$(CONFIG_RV40_DECODER) += x86/rv40dsp.o
X86ASM-OBJS-$(CONFIG_SBC_ENCODER) += x86/sbcdsp.o
X86ASM-OBJS-$(CONFIG_SVQ1_ENCODER) += x86/svq1enc.o
X86ASM-OBJS-$(CONFIG_TAK_DECODER) += x86/takdsp.o
+ifdef ARCH_X86_64
X86ASM-OBJS-$(CONFIG_TRUEHD_DECODER) += x86/mlpdsp.o
+endif
X86ASM-OBJS-$(CONFIG_TTA_DECODER) += x86/ttadsp.o
X86ASM-OBJS-$(CONFIG_TTA_ENCODER) += x86/ttaencdsp.o
X86ASM-OBJS-$(CONFIG_UTVIDEO_DECODER) += x86/utvideodsp.o
diff --git a/libavfilter/x86/Makefile b/libavfilter/x86/Makefile
--- a/libavfilter/x86/Makefile
+++ b/libavfilter/x86/Makefile
@@ -44,6 +44,8 @@
X86ASM-OBJS-$(CONFIG_AFIR_FILTER) += x86/af_afir.o
X86ASM-OBJS-$(CONFIG_ANLMDN_FILTER) += x86/af_anlmdn.o
+ifdef ARCH_X86_64
X86ASM-OBJS-$(CONFIG_ATADENOISE_FILTER) += x86/vf_atadenoise.o
+endif
X86ASM-OBJS-$(CONFIG_BLEND_FILTER) += x86/vf_blend.o
X86ASM-OBJS-$(CONFIG_BWDIF_FILTER) += x86/vf_bwdif.o
X86ASM-OBJS-$(CONFIG_COLORSPACE_FILTER) += x86/colorspacedsp.o
@@ -62,6 +62,8 @@ X86ASM-OBJS-$(CONFIG_LUT3D_FILTER) += x86/vf_lut3d.o
X86ASM-OBJS-$(CONFIG_MASKEDCLAMP_FILTER) += x86/vf_maskedclamp.o
X86ASM-OBJS-$(CONFIG_MASKEDMERGE_FILTER) += x86/vf_maskedmerge.o
+ifdef ARCH_X86_64
X86ASM-OBJS-$(CONFIG_NLMEANS_FILTER) += x86/vf_nlmeans.o
+endif
X86ASM-OBJS-$(CONFIG_OVERLAY_FILTER) += x86/vf_overlay.o
X86ASM-OBJS-$(CONFIG_PP7_FILTER) += x86/vf_pp7.o
X86ASM-OBJS-$(CONFIG_PSNR_FILTER) += x86/vf_psnr.o

View File

@@ -0,0 +1,14 @@
diff --git a/configure b/configure
index 2be953f7e7..e075949ffc 100755
--- a/configure
+++ b/configure
@@ -6497,6 +6497,7 @@ enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0
{ enabled gplv3 || ! enabled gpl || enabled nonfree || die "ERROR: OpenSSL >=3.0.0 requires --enable-version3"; }; } ||
{ enabled gpl && ! enabled nonfree && die "ERROR: OpenSSL <3.0.0 is incompatible with the gpl"; } ||
check_pkg_config openssl openssl openssl/ssl.h OPENSSL_init_ssl ||
check_pkg_config openssl openssl openssl/ssl.h SSL_library_init ||
+ check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto $pthreads_extralibs -ldl ||
check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto ||
check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto ||
check_lib openssl openssl/ssl.h SSL_library_init -lssl32 -leay32 ||

View File

@@ -0,0 +1,15 @@
diff --color -Naur src_old/libavcodec/mf_utils.c src/libavcodec/mf_utils.c
--- src_old/libavcodec/mf_utils.c 2020-07-11 05:26:17.000000000 +0700
+++ src/libavcodec/mf_utils.c 2020-11-13 12:55:57.226976400 +0700
@@ -22,6 +22,11 @@
#define _WIN32_WINNT 0x0602
#endif
+#if !defined(WINVER) || WINVER < 0x0602
+#undef WINVER
+#define WINVER 0x0602
+#endif
+
#include "mf_utils.h"
#include "libavutil/pixdesc.h"

View File

@@ -0,0 +1,71 @@
From f0b694749b38b2cfd94df4eed10e667342c234e5 Mon Sep 17 00:00:00 2001
From: 21pages <pages21@163.com>
Date: Sat, 24 Feb 2024 15:33:24 +0800
Subject: [PATCH 1/2] avcodec/amfenc: add query_timeout option for h264/hevc
Signed-off-by: 21pages <pages21@163.com>
---
libavcodec/amfenc.h | 1 +
libavcodec/amfenc_h264.c | 4 ++++
libavcodec/amfenc_hevc.c | 4 ++++
3 files changed, 9 insertions(+)
diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
index 1ab98d2f78..e92120ea39 100644
--- a/libavcodec/amfenc.h
+++ b/libavcodec/amfenc.h
@@ -87,6 +87,7 @@ typedef struct AmfContext {
int quality;
int b_frame_delta_qp;
int ref_b_frame_delta_qp;
+ int64_t query_timeout;
// Dynamic options, can be set after Init() call
diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c
index efb04589f6..f55dbc80f0 100644
--- a/libavcodec/amfenc_h264.c
+++ b/libavcodec/amfenc_h264.c
@@ -121,6 +121,7 @@ static const AVOption options[] = {
{ "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
{ "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg) , AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
+ { "query_timeout", "Timeout for QueryOutput call in ms", OFFSET(query_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, 1000, VE },
{ NULL }
};
@@ -155,6 +156,9 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx)
AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_FRAMERATE, framerate);
+ if (ctx->query_timeout >= 0)
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QUERY_TIMEOUT, ctx->query_timeout);
+
switch (avctx->profile) {
case FF_PROFILE_H264_BASELINE:
profile = AMF_VIDEO_ENCODER_PROFILE_BASELINE;
diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c
index 8ab9330730..7a40bcad31 100644
--- a/libavcodec/amfenc_hevc.c
+++ b/libavcodec/amfenc_hevc.c
@@ -89,6 +89,7 @@ static const AVOption options[] = {
{ "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE },
{ "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE },
+ { "query_timeout", "Timeout for QueryOutput call in ms", OFFSET(query_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, 1000, VE },
{ NULL }
};
@@ -122,6 +123,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx)
AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_FRAMERATE, framerate);
+ if (ctx->query_timeout >= 0)
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, ctx->query_timeout);
+
switch (avctx->profile) {
case FF_PROFILE_HEVC_MAIN:
profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN;
--
2.43.0.windows.1

View File

@@ -0,0 +1,71 @@
From 4d0d20d96ad458cfec0444b9be0182ca6085ee0c Mon Sep 17 00:00:00 2001
From: 21pages <pages21@163.com>
Date: Sat, 24 Feb 2024 16:02:44 +0800
Subject: [PATCH 2/2] libavcodec/amfenc: reconfig when bitrate change
Signed-off-by: 21pages <pages21@163.com>
---
libavcodec/amfenc.c | 20 ++++++++++++++++++++
libavcodec/amfenc.h | 1 +
2 files changed, 21 insertions(+)
diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
index a033e1220e..3eab01a903 100644
--- a/libavcodec/amfenc.c
+++ b/libavcodec/amfenc.c
@@ -222,6 +222,7 @@ static int amf_init_context(AVCodecContext *avctx)
ctx->hwsurfaces_in_queue = 0;
ctx->hwsurfaces_in_queue_max = 16;
+ ctx->av_bitrate = avctx->bit_rate;
// configure AMF logger
// the return of these functions indicates old state and do not affect behaviour
@@ -575,6 +576,23 @@ static void amf_release_buffer_with_frame_ref(AMFBuffer *frame_ref_storage_buffe
frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
}
+static int reconfig_encoder(AVCodecContext *avctx)
+{
+ AmfContext *ctx = avctx->priv_data;
+ AMF_RESULT res = AMF_OK;
+
+ if (ctx->av_bitrate != avctx->bit_rate) {
+ av_log(ctx, AV_LOG_INFO, "change bitrate from %d to %d\n", ctx->av_bitrate, avctx->bit_rate);
+ ctx->av_bitrate = avctx->bit_rate;
+ if (avctx->codec->id == AV_CODEC_ID_H264) {
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_TARGET_BITRATE, avctx->bit_rate);
+ } else if (avctx->codec->id == AV_CODEC_ID_HEVC) {
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, avctx->bit_rate);
+ }
+ }
+ return 0;
+}
+
int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
{
AmfContext *ctx = avctx->priv_data;
@@ -586,6 +604,8 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
AVFrame *frame = ctx->delayed_frame;
int block_and_wait;
+ reconfig_encoder(avctx);
+
if (!ctx->encoder)
return AVERROR(EINVAL);
diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
index e92120ea39..31172645f2 100644
--- a/libavcodec/amfenc.h
+++ b/libavcodec/amfenc.h
@@ -107,6 +107,7 @@ typedef struct AmfContext {
int me_half_pel;
int me_quarter_pel;
int aud;
+ int64_t av_bitrate;
// HEVC - specific options
--
2.43.0.windows.1

View File

@@ -0,0 +1,95 @@
From afe89a70f6bc7ebd0a6a0a31101801b88cbd60ee Mon Sep 17 00:00:00 2001
From: 21pages <pages21@163.com>
Date: Sun, 5 May 2024 12:45:23 +0800
Subject: [PATCH] use release/7.0's update_bitrate
Signed-off-by: 21pages <pages21@163.com>
---
libavcodec/qsvenc.c | 39 +++++++++++++++++++++++++++++++++++++++
libavcodec/qsvenc.h | 6 ++++++
2 files changed, 45 insertions(+)
diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c
index 2382c2f5f7..9b34f37eb3 100644
--- a/libavcodec/qsvenc.c
+++ b/libavcodec/qsvenc.c
@@ -714,6 +714,11 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q)
brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes),
initial_delay_in_kilobytes) + 0x10000) / 0x10000;
+ q->old_rc_buffer_size = avctx->rc_buffer_size;
+ q->old_rc_initial_buffer_occupancy = avctx->rc_initial_buffer_occupancy;
+ q->old_bit_rate = avctx->bit_rate;
+ q->old_rc_max_rate = avctx->rc_max_rate;
+
switch (q->param.mfx.RateControlMethod) {
case MFX_RATECONTROL_CBR:
case MFX_RATECONTROL_VBR:
@@ -1657,6 +1662,39 @@ static int update_qp(AVCodecContext *avctx, QSVEncContext *q,
return updated;
}
+static int update_bitrate(AVCodecContext *avctx, QSVEncContext *q)
+{
+ int updated = 0;
+ int target_bitrate_kbps, max_bitrate_kbps, brc_param_multiplier;
+ int buffer_size_in_kilobytes, initial_delay_in_kilobytes;
+
+ UPDATE_PARAM(q->old_rc_buffer_size, avctx->rc_buffer_size);
+ UPDATE_PARAM(q->old_rc_initial_buffer_occupancy, avctx->rc_initial_buffer_occupancy);
+ UPDATE_PARAM(q->old_bit_rate, avctx->bit_rate);
+ UPDATE_PARAM(q->old_rc_max_rate, avctx->rc_max_rate);
+ if (!updated)
+ return 0;
+
+ buffer_size_in_kilobytes = avctx->rc_buffer_size / 8000;
+ initial_delay_in_kilobytes = avctx->rc_initial_buffer_occupancy / 8000;
+ target_bitrate_kbps = avctx->bit_rate / 1000;
+ max_bitrate_kbps = avctx->rc_max_rate / 1000;
+ brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes),
+ initial_delay_in_kilobytes) + 0x10000) / 0x10000;
+
+ q->param.mfx.BufferSizeInKB = buffer_size_in_kilobytes / brc_param_multiplier;
+ q->param.mfx.InitialDelayInKB = initial_delay_in_kilobytes / brc_param_multiplier;
+ q->param.mfx.TargetKbps = target_bitrate_kbps / brc_param_multiplier;
+ q->param.mfx.MaxKbps = max_bitrate_kbps / brc_param_multiplier;
+ q->param.mfx.BRCParamMultiplier = brc_param_multiplier;
+ av_log(avctx, AV_LOG_VERBOSE,
+ "Reset BufferSizeInKB: %d; InitialDelayInKB: %d; "
+ "TargetKbps: %d; MaxKbps: %d; BRCParamMultiplier: %d\n",
+ q->param.mfx.BufferSizeInKB, q->param.mfx.InitialDelayInKB,
+ q->param.mfx.TargetKbps, q->param.mfx.MaxKbps, q->param.mfx.BRCParamMultiplier);
+ return updated;
+}
+
static int update_parameters(AVCodecContext *avctx, QSVEncContext *q,
const AVFrame *frame)
{
@@ -1666,6 +1704,7 @@ static int update_parameters(AVCodecContext *avctx, QSVEncContext *q,
return 0;
needReset = update_qp(avctx, q, frame);
+ needReset |= update_bitrate(avctx, q);
if (!needReset)
return 0;
diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h
index b754ac4b56..5745533165 100644
--- a/libavcodec/qsvenc.h
+++ b/libavcodec/qsvenc.h
@@ -224,6 +224,12 @@ typedef struct QSVEncContext {
int min_qp_p;
int max_qp_b;
int min_qp_b;
+
+ // These are used for bitrate control reset
+ int old_bit_rate;
+ int old_rc_buffer_size;
+ int old_rc_initial_buffer_occupancy;
+ int old_rc_max_rate;
} QSVEncContext;
int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q);
--
2.43.0.windows.1

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