Compare commits

...

939 Commits

Author SHA1 Message Date
rustdesk
914da2b86f add tutorial-guide-for-safenet-authentication-client-for-code-signing
doc
2024-02-19 23:23:00 +08:00
fufesou
582db9d542 Refact. Remove idd driver (#7068)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-02-19 19:55:04 +08:00
21pages
9a1fd1aa4f windows specific session, fix sciter empty file directory or wrong home (#7189)
Signed-off-by: 21pages <pages21@163.com>
2024-02-19 17:08:15 +08:00
21pages
58ddac63d2 judge failure of get_current_process_session_id with None rather than 0 (#7188)
Signed-off-by: 21pages <pages21@163.com>
2024-02-19 13:06:06 +08:00
rustdesk
a8eff641b2 fix ios ci for flutter 3.16 2024-02-19 12:35:12 +08:00
21pages
d723c10a3b add space after ":" (#7187)
Signed-off-by: 21pages <pages21@163.com>
2024-02-19 11:06:01 +08:00
21pages
bf5abdb520 refactor windows specific session, file transfer and waiting for image (#7184)
* refactor windows specific session, file transfer and waiting for image

1. File transfer doesn't show directory until correct session id is ensured
2. Fix file transfer, caused by `pi.username = self.lc.read().unwrap().get_username(&pi);` in `handle_peer_info` override empty username and `get_active_username` doesn't return currect session username
* Fix home directory not change when session changed, or wrong home directory
* Fix show empty remote directory rather than error messagbox when current session is in login screen
3. Show `Connected, waiting for image` after user choose the same
   session id

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

* update translations

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

* Update connection.rs

---------

Signed-off-by: 21pages <pages21@163.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-02-19 10:32:13 +08:00
21pages
0f44de7dc3 refactor windows specific session (#7170)
1. Modify the process to have the control side lead the session switching: After the control side sends a `LoginRequest`, the controlled side will add all session information and the current session ID in the `LoginResponse`. Upon receiving the `LoginResponse`, the control side will check if the current session ID matches the ID in the `LoginConfigHandler`. If they match, the control side will send the current session ID. If they don't match, a session selection dialog will pop up, the selected session id will be sent. Upon receiving this message, the controlled side will restart if different or sub service if same .
2. Always show physical console session on the top
3. Show running session and distinguish sessions with the same name
4. Not sub service until correct session id is ensured
5. Fix switch sides not work for multisession session
6. Remove all session string join/split except get_available_sessions in
   windows.rs
7. Fix prelogin, when share rdp is enabled and there is a rdp session,
   the console is in login screen, get_active_username will be the rdp's
   username and prelogin will be false, cm can't be created an that
   causes disconnection in a loop
8. Rename all user session to windows session

Known issue:
1. Use current process session id for `run_as_user`, sahil says it can
   be wrong but I didn't reproduce.
2. Have not change tray process to current session
3. File transfer doesn't update home directory when session changed
4. When it's in login screen, remote file directory is empty, because cm
   have not start up

Signed-off-by: 21pages <pages21@163.com>
2024-02-18 22:08:25 +08:00
fufesou
4f1a4dc6a5 Refact. Better ThrottledInterval (#7174)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-18 21:54:07 +08:00
fufesou
8c108065eb Refact. Replace all tokio::time::interval() (#7173)
* Refact. Replace all `tokio::time::interval()`

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

* Refact Better min_interval for `ThrottledInterval`.

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

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-18 21:18:00 +08:00
FastAct
5fdcc748e1 Update nl.rs (#7163) 2024-02-18 14:25:50 +08:00
rustdesk
bf03156dd9 fix ci 2024-02-17 16:59:43 +08:00
rustdesk
964c2ed2b5 smaller timeout for android and self-hosting 2024-02-17 16:50:03 +08:00
fufesou
e942c80afb Fix. Potential dead lock, interval.tick() & named pipe (#7162)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-17 16:35:38 +08:00
Kristian Kraljic
d7dcb5feab Fix build on Windows (#7161)
`winapi::um::shellapi` is used in https://github.com/rustdesk/rustdesk/blob/master/src/platform/windows.rs but not declared in `Cargo.toml` resulting in a compilation error, when I tried building on Windows, this change adds shellapi as a dependency.
2024-02-17 11:14:44 +08:00
rustdesk
39d41486d6 fix ci 2024-02-17 00:24:58 +08:00
rustdesk
b85526ce54 add relay in --config https://github.com/rustdesk/rustdesk/discussions/7118 2024-02-17 00:09:03 +08:00
21pages
2a0c081380 fix windows ci (#7156)
Signed-off-by: 21pages <pages21@163.com>
2024-02-16 15:24:43 +08:00
Kleofass
1db4236f23 Update lv.rs (#7153) 2024-02-16 12:40:55 +08:00
Mr-Update
6749595afe Update de.rs (#7152) 2024-02-16 12:40:46 +08:00
jxdv
e65c43a292 update cs.rs (#7151) 2024-02-16 12:40:37 +08:00
jxdv
00b13bf918 update sk.rs (#7150)
* update sk.rs

* remove comma
2024-02-16 12:40:29 +08:00
rustdesk
f7f3bc8bee fix mac tray icon to use template icon, no need to know theme any more 2024-02-16 12:29:32 +08:00
rustdesk
51c603a3a6 improve for https://github.com/rustdesk/rustdesk/issues/4162 2024-02-16 00:43:08 +08:00
rustdesk
5fb026b8d5 fix https://github.com/rustdesk/rustdesk/discussions/7143 2024-02-15 21:12:24 +08:00
Andrzej Rudnik
f7530b16b8 Update pl.rs (#7138) 2024-02-15 16:55:31 +08:00
bovirus
db47209362 Update Italian language (#7136) 2024-02-15 16:55:20 +08:00
rustdesk
f375cbd871 fix bg 2024-02-15 16:54:52 +08:00
Nevaran
0474c8fb03 Update lang.rs (#7140)
added bg into the list
2024-02-15 16:52:32 +08:00
Nevaran
c00d23846a Added bg file (#7137)
Bulgarian translation
2024-02-15 16:52:24 +08:00
fufesou
a3d5ea8fb8 Fix. Set peers offline when the server is unreachable. (#7139)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-15 16:51:39 +08:00
solokot
7525a6ed6a Update ru.rs (#7134) 2024-02-15 12:51:34 +08:00
XLion
dd93416cf7 Update tw.rs (#7131)
455,541,550: Fix typo
540: delete space
Add translation
2024-02-15 12:45:31 +08:00
21pages
73d429d064 windows specific session, fix conn count (#7133)
Signed-off-by: 21pages <pages21@163.com>
2024-02-15 10:44:32 +08:00
Sahil Yeole
4bf3764b5d Feat: Windows connect to a specific user session (#6825)
* feat windows connect to specific user session

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

* fix import

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

* fix multiple user session fields

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

* fix build

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

* fix build

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

* fix file transfer

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

* fix text color on light theme

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

* feat windows connect to specific user session code changes and sciter support

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

* update texts

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

* fix sciter selected user session

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

* add translations

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

* Use Y,N options

* feat windows specific user code changes

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

* Update dialog.dart

* Update connection.rs

* Update connection.rs

* feat windows specific user code changes

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

* fix sciter

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

* use lr.union

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

* remove unused peer options

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

* select user only when authorised and no existing connection

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

* check for multiple users only once

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

* optimise and add check for client version

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

* use misc option message

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

* update rdp user session proto

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

* fix show cm on user session

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

* Update pl.rs

* update on_message

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

* fix cm

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

* remove user_session_id

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

* fix cm

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

* fix multiple connections

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

---------

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2024-02-14 23:59:17 +08:00
rustdesk
236687ae53 fix some broken link 2024-02-14 19:49:06 +08:00
grummbeer
0d708f64b9 Fix dead link to docs for own server setup (#7128) 2024-02-14 17:33:50 +08:00
mehdi-song
222c9de19f Update fa.rs (#7124) 2024-02-14 12:23:13 +08:00
rustdesk
72fc34cd40 upgrade to tokio 1.36 2024-02-14 12:19:18 +08:00
grummbeer
3f7244f23f Peers. Multiselect. Provide actions only if peers selected (#7122) 2024-02-13 22:10:32 +08:00
21pages
dc24868800 fix multiselect does not reset on address book and groups (#7121)
Signed-off-by: 21pages <pages21@163.com>
2024-02-13 21:06:42 +08:00
21pages
1f557888f5 update pubspec.lock, remove some deprecated (#7110)
* fix some warnings and some deprecated reported by `flutter analyze`

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

* pubspec.lock changes from flutter 3.16.9

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

* pubspec.lock changes from `flutter pub upgrade`

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

---------

Signed-off-by: 21pages <pages21@163.com>
2024-02-12 21:39:19 +08:00
rustdesk
16db977fd8 fix iOS ci 2024-02-11 10:21:42 +08:00
21pages
a19d4d6686 upgrade flutter to 3.16.9 (#7099)
Signed-off-by: 21pages <pages21@163.com>
2024-02-11 00:15:11 +08:00
Samuel FORESTIER
633076ddd4 Fixes typo for dark theme in French i18n (#7095) 2024-02-10 11:55:18 +08:00
21pages
5581248a1e fix startup crash on windows without adapter (#7093)
Signed-off-by: 21pages <pages21@163.com>
2024-02-09 16:53:15 +08:00
flusheDData
a4393b8f90 New terms (#7053)
* Update es.rs

New terms added

* Update es.rs

mistype correction

* Update es.rs

New term added

* Update es.rs

New terms added
2024-02-05 13:30:02 +08:00
XLion
406d8469d8 Update tw.rs (#7047)
* Update tw.rs

Add new translation.

* Optimize tw.rs
2024-02-05 13:29:43 +08:00
fufesou
0cf4711515 Refact, remove avoidable clone (#7040)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-02-03 00:09:39 +08:00
Andrzej Rudnik
a2ba50c4ff Updated Polish translation (#7021)
* Update pl.rs

* Update pl.rs

* Update pl.rs

* Update pl.rs

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-02-02 22:01:11 +08:00
wesley800
c97cc15c0e Warn on MIT-SHM not working on Linux X11 (#6856)
* Clarify video capture method

* fix improper level of pointer usage of xcb_generic_error_t

* add ffi of xcb_shm_query_version

* throw a warn about MIT-SHM not working

* add missing #[cfg]

* checks SHM validity on the fly, rather than cache on creation

---------

Co-authored-by: root <root@localhost>
Co-authored-by: rustdesk-fork <rustdesk@fork.com>
2024-01-31 17:49:09 +08:00
rustdesk
750f1a1884 smaller MAX_FAILS 2024-01-30 18:03:46 +08:00
rustdesk
2cf83b41cc refactor reg_timeout to reduce fail try 2024-01-30 15:38:30 +08:00
mehdi-song
1f16b5236b Update fa.rs (#6999) 2024-01-29 14:15:50 +08:00
rustdesk
dd4b5349cb revert https://github.com/rustdesk/rustdesk/pull/6980 2024-01-26 19:22:26 +08:00
rustdesk
46b4e21e8c less udp message if failure 2024-01-26 18:58:05 +08:00
rustdesk
3899e4e12e add round to avoid precision problem 2024-01-26 12:59:47 +08:00
rustdesk
f016281e30 fix ci 2024-01-26 12:54:36 +08:00
Sahil Yeole
1d755c705b Fix: Mac retina display video blur (#6980)
* fix mac retina display blur

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

* Update display.rs

---------

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-01-26 12:50:13 +08:00
FastAct
a0d79dd26d Update nl.rs (#6979) 2024-01-26 11:07:24 +08:00
rustdesk
e6734af64e upgrade reqwest 2024-01-25 16:21:39 +08:00
rustdesk
0d8e3dc24f save debug disk 2024-01-25 15:46:45 +08:00
rustdesk
0e44aa1ada rustls-tls-native-roots 2024-01-25 15:40:02 +08:00
bovirus
9f40a0b490 Update Italian language (#6973) 2024-01-24 11:40:54 +08:00
jxdv
23430bee97 update cs.rs (#6969) 2024-01-24 11:40:34 +08:00
jxdv
04691a3b6c update sk.rs (#6967) 2024-01-24 11:40:24 +08:00
Ikko Eltociear Ashimine
db0f65eedd Add Japanese Documents (#6963)
* Add DEVCONTAINER-JP.md

Signed-off-by: Ikko Eltociear Ashimine <eltociear@gmail.com>

* Add CONTRIBUTING-JP.md

Signed-off-by: Ikko Eltociear Ashimine <eltociear@gmail.com>

* Add SECURITY-JP.md

Signed-off-by: Ikko Eltociear Ashimine <eltociear@gmail.com>

* Add CODE_OF_CONDUCT-JP.md

Signed-off-by: Ikko Eltociear Ashimine <eltociear@gmail.com>

---------

Signed-off-by: Ikko Eltociear Ashimine <eltociear@gmail.com>
2024-01-23 15:35:32 +08:00
fufesou
2118b6dd7d Fix. sciter, switch display. Close unused display services after switching. (#6962)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-01-23 15:30:57 +08:00
21pages
71d7398ae7 video handler holds only one decoder of the current codec format (#6939)
1. For example: when receiving h264 video frames, only 1 decoder is created, vram > ram
2. For creation and decoding failed:
  * Remove real_supported_decodings, this will update real existing decoders, replace it with the "mark_unsupported" vector. After creating the decoder failure, marks the codec as unsupported and updates supported decoding to the controlled side
  *  Add `fail_counter` in the decoder. When decoding 10 consecutive frames failed, adding codec type to 'mark_unsupported' vector
  *  The controlled end always ignores the unavailability of VP9

Signed-off-by: 21pages <pages21@163.com>
2024-01-22 20:01:17 +08:00
21pages
2e16a2be56 fix port forward 2fa (#6956)
Signed-off-by: 21pages <pages21@163.com>
2024-01-22 19:57:23 +08:00
Mr-Update
ed24f432c3 Update de.rs (#6955) 2024-01-22 18:45:36 +08:00
21pages
669e8d5f8e fix last commit (#6952)
Signed-off-by: 21pages <pages21@163.com>
2024-01-22 16:40:23 +08:00
21pages
016f4abb32 sciter 2fa setting (#6951)
not add qr code text line as it's not selectable, and selectable input will steal the focus.

Signed-off-by: 21pages <pages21@163.com>
2024-01-22 16:29:08 +08:00
fufesou
b1a946ec20 Fix. Connection, 2FA dialog, esc & enter (#6946)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-01-22 13:44:10 +08:00
emmanuel
2576b46f34 Update README.md (#6944) 2024-01-22 11:15:00 +08:00
fufesou
a42df9a27b Refact, verification login with secret (#6943)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-01-22 11:14:28 +08:00
21pages
48102e9c53 opt 2fa (#6942)
1. switch sides not check 2fa
2. let self.authorized=true ahead of return, call try_start_cm when 2fa confirmed for updating authorized state
3. flutter 2fa dialog button color

port forward check and sciter will be later on

Signed-off-by: 21pages <pages21@163.com>
2024-01-21 21:53:29 +08:00
Integral
5770aeee26 Update cn.rs (#6935) 2024-01-21 15:22:32 +08:00
fufesou
cb102a5a61 Refact. Verification code, keyboardType (#6934)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-01-21 15:22:05 +08:00
FastAct
28f01784c1 Update nl.rs (#6932) 2024-01-21 15:21:33 +08:00
Kleofass
29eefbcc25 Update lv.rs (#6931) 2024-01-21 15:21:23 +08:00
Andrzej Rudnik
8679e55f6a Updated Polish translation (#6929)
* Update pl.rs

* Update pl.rs

* Update pl.rs

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-01-21 15:21:01 +08:00
solokot
846db4e689 Update ru.rs (#6926) 2024-01-21 15:19:57 +08:00
rustdesk
68afb89b99 a quick fix for 2fa 2024-01-21 15:19:18 +08:00
RustDesk
804f035a87 Revert "disable ffmpeg ram codec temporarily" (#6928) 2024-01-20 17:20:08 +08:00
21pages
267342e7e6 disable ffmpeg ram codec temporarily (#6927)
Signed-off-by: 21pages <pages21@163.com>
2024-01-20 17:19:30 +08:00
fufesou
9e3633f1e4 Fix. Avoid invalid verification code attempts. (#6925)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-01-20 16:03:07 +08:00
fufesou
06b3894249 Refact/verification code input check (#6924)
* Refact, login verification code, input check

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

* Refact, settings, enable 2fa, dialog input

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

* Refact. Connect, 2fa code, input check

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

* refact, 2Fa text field, input formatter, only digits

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

* refact, error message

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

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-01-20 15:36:32 +08:00
Mr-Update
d39129887b Update de.rs (#6923) 2024-01-20 12:42:49 +08:00
rustdesk
e5379bb073 send2fa in sciter 2024-01-19 19:29:04 +08:00
rustdesk
c8dded1108 use native-tls for macos/windows only for the time being 2024-01-19 16:04:31 +08:00
rustdesk
44e6b7dbb0 2fa for unattended access 2024-01-19 15:35:58 +08:00
rustdesk
80857c22c9 https://github.com/rustdesk/rustdesk-server-pro/issues/189, using native-tls for better tls support 2024-01-18 13:37:42 +08:00
21pages
6548f9f0ed Fix ab merge and sync from recent (#6910)
* fix missing platform when merge ab

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

* sync from recent after ab initialized

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

---------

Signed-off-by: 21pages <pages21@163.com>
2024-01-17 14:38:12 +08:00
zEqueue
58fd8780e5 Fix incorrect CONTABUTING.md in README-ZH.md file (#6887)
Signed-off-by: zEqueue <zequeue@163.com>
2024-01-15 15:59:33 +08:00
Theofanis Sarmidis
0cccbc438b Update el.rs (#6869) 2024-01-15 15:59:16 +08:00
Andrzej Rudnik
558e7cb5ac Updated Polish translation (#6858)
* Update pl.rs

* Update pl.rs

* Update pl.rs

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-01-15 15:58:57 +08:00
rustdesk
286181ca04 tcp rendezvous works now 2024-01-11 14:47:53 +08:00
Sahil Yeole
ce0fc14a8a scrollable scam warning dialog (#6850)
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2024-01-11 12:05:30 +08:00
rustdesk
ebfbc8ce61 Merge branch 'master' of github.com-rustdesk:rustdesk/rustdesk 2024-01-10 11:48:54 +08:00
rustdesk
fdb038c7c9 one byte for future use 2024-01-10 11:48:23 +08:00
Integral
467e6dfd16 Update cn.rs (#6844)
* Update cn.rs

* Update cn.rs

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-01-10 09:44:06 +08:00
Mr-Update
8c40c28fe0 Update de.rs (#6843) 2024-01-10 09:43:18 +08:00
jxdv
9e25049fe8 update cs.rs (#6842) 2024-01-10 09:43:08 +08:00
jxdv
e170153090 update sk.rs (#6841) 2024-01-10 09:42:58 +08:00
FastAct
99e8753629 Update nl.rs (#6839) 2024-01-10 09:42:45 +08:00
solokot
d5b0829065 Update ru.rs (#6836) 2024-01-10 09:42:29 +08:00
Moaz bin Mokhtar
2c8a60e0ea fix: update version of zbus to build system (#6845) 2024-01-10 09:27:36 +08:00
Sahil Yeole
352de75fdd fix height (#6846) 2024-01-10 09:26:01 +08:00
rustdesk
f7b35defc9 finish tcp rendezvous keep alive logic following mqtt, but defined by
server so that it can be easily to be controlled at server side.
2024-01-09 22:41:11 +08:00
Yevhen Popok
e471c01269 Update Ukrainian translation (#6831)
Co-authored-by: Yevhen Popok <xalt7x.service@gmail..com>
2024-01-09 10:30:42 +08:00
Kleofass
5dfaa10709 Update lv.rs (#6830) 2024-01-09 10:30:31 +08:00
bovirus
7506f94a53 Update Italian language (#6827)
* Update Italian language

* Update italian language
2024-01-09 10:30:19 +08:00
21pages
a2d08fa40d opt mac sciter dark theme (#6824)
Signed-off-by: 21pages <pages21@163.com>
2024-01-08 11:35:55 +08:00
fufesou
159c883bf3 verification code, auto submit (#6822)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-01-07 21:07:25 +08:00
rustdesk
396d7680d3 fix possible id mismatch bug because of register_pk udp packet lost 2024-01-07 20:23:00 +08:00
rustdesk
7e93a5d3cf refactor to prepare for tcp rendezvous 2024-01-07 19:01:35 +08:00
rustdesk
955c55b6cc fix code merge 2024-01-07 13:25:19 +08:00
rustdesk
b2b30b0cf5 https://github.com/rustdesk/rustdesk/pull/6805 2024-01-07 13:21:38 +08:00
solokot
6352b3a594 Update ru.rs (#6814) 2024-01-07 09:45:42 +08:00
mehdi-song
5be684d2e5 Update fa.rs (#6810) 2024-01-07 09:45:29 +08:00
FastAct
8dda17d546 Update nl.rs (#6807) 2024-01-07 09:45:14 +08:00
Kleofass
0f5aaac1f5 Update lv.rs (#6806) 2024-01-07 09:45:02 +08:00
Integral
35fa75f9c9 Add cn translation for 2FA code (#6805) 2024-01-07 09:44:44 +08:00
fufesou
19d43b3f62 login on 'Enter' (#6819)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-01-06 21:51:05 +08:00
21pages
2b4a72897e Specify cmd.exe explicitly to avoid the replacement of cmd commands (#6818)
Signed-off-by: 21pages <pages21@163.com>
2024-01-06 20:56:40 +08:00
21pages
cdc31b7fc7 Execute the .bat file directly on install to avoid the replacement of cmd (#6817)
Signed-off-by: 21pages <pages21@163.com>
2024-01-06 20:28:46 +08:00
FastAct
f4ef1455c4 Update nl.rs (#6803) 2024-01-06 11:12:22 +08:00
bovirus
fe94512dd1 Update Italian language (#6798) 2024-01-06 11:12:10 +08:00
Juraj Virgovič
ae25542ce9 update sk.rs (#6796) 2024-01-06 11:11:59 +08:00
Juraj Virgovič
6c030a5230 update cs.rs (#6795) 2024-01-06 11:11:47 +08:00
tschettervictor
a145d6ebcc Punctuation correction (#6808) 2024-01-06 11:11:35 +08:00
Juraj Virgovič
14676e9618 fix 2fa_tip (#6797) 2024-01-04 20:34:35 +08:00
rustdesk
72e8476ded typo 2024-01-04 11:54:21 +08:00
Mr-Update
5888b051d9 Update de.rs (#6788) 2024-01-04 11:53:18 +08:00
ckesc
215cf73072 Fix russian translation of Ready to make it more understandable (#6787) 2024-01-04 11:52:54 +08:00
rustdesk
7f7d4a77b6 println install failure 2024-01-03 22:46:53 +08:00
fufesou
7e3a0c4ded Better 2fa verification support (#6782)
* Better 2fa verification support

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

* 2FA login verification, add request filed 'tfa_code'

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

* 2fa dialog

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

* msgbox, title

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

* Feat, oidc login, 2fa

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

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-01-03 16:43:39 +08:00
21pages
89150317e1 add zero copy mode hareware codec for windows (#6778)
Signed-off-by: 21pages <pages21@163.com>
2024-01-02 16:58:10 +08:00
rustdesk
f47faa548b revert test delay timeout to 1 sec since many deps on it, including
quality monitor, also change test delay with instant rather than
systemtime, because instant is stable
2024-01-02 16:23:47 +08:00
rustdesk
36ed8f3f73 change REG_INTERVAL to 15 seconds to follow quic 2024-01-02 13:42:32 +08:00
rustdesk
ca1ca21cf8 change TEST_DELAY_TIMEOUT from 1 to 3 2024-01-01 17:53:20 +08:00
rustdesk
c7df539b31 require rust 1.75 2023-12-29 16:42:23 +08:00
21pages
e873188775 use official async trait (#6765)
Signed-off-by: 21pages <pages21@163.com>
2023-12-29 16:41:49 +08:00
21pages
6953efc2d8 fix last commit, snapshot for sending to new subscribers (#6762)
Signed-off-by: 21pages <pages21@163.com>
2023-12-29 13:41:33 +08:00
21pages
3e78aacc7e add snapshot before sending SwitchDisplay (#6760)
Signed-off-by: 21pages <pages21@163.com>
2023-12-28 21:34:54 +08:00
Sahil Yeole
20b4ce3213 Wayland: Skip remote desktop portal calls when uinput is available (#6758)
* skip rdp when uinput is available

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

* fix is_server_running

* remove clones

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

---------

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-12-28 10:40:58 +08:00
RustDesk
4b581c385c Revert "fix xdg portal verison check for persist_mode (#6742)" (#6755)
This reverts commit 57acadd52a.
2023-12-27 11:28:17 +08:00
Sahil Yeole
bdf6f5c3b9 Fix: android scam warning not showing on toggling screen capture (#6750)
* fix scam warning not showing on toggling screen capture

* fix scam warning not showing on toggling screen capture

* fix scam warning not showing on toggling screen capture

* fix scam warning not showing on toggling screen capture
2023-12-26 10:19:05 +08:00
fufesou
6d0953dca4 Disable file copy & paste in view mode (#6749)
* Disable file copy & paste in view mode

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

* hide 'Enable file copy & paste' when no keyboard perm

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

* Disable some functions in view mode

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

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-12-25 21:49:34 +08:00
fufesou
fc0fc5ea10 Fix. Do not send resolutions on file transfer conn (#6748)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-12-25 16:47:40 +08:00
Yevhen Popok
f7f2d84e1f Update Ukrainian UI translation (#6745) 2023-12-25 09:03:08 +08:00
linuxrider
57acadd52a fix xdg portal verison check for persist_mode (#6742) 2023-12-24 16:33:36 +08:00
21pages
26cebd2aeb request focus again when android account password obtain focus (#6744)
Signed-off-by: 21pages <pages21@163.com>
2023-12-24 16:16:47 +08:00
Péter B
9a652e789d Update hu.rs (#6729) 2023-12-21 17:13:54 +08:00
Madis Otenurm
f6509e3fd6 Estonian translation (#6702)
* Create et.rs

* Update lang.rs
2023-12-19 10:47:34 +08:00
Sahil Yeole
445fe6e714 Feat: Wayland flatpak input support | Remote desktop portal (#6675)
* autogen portal code

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

* use remote desktop portal

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

* remove clipboard portal in favour of #6586

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

* remove clipboard portal

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

* use select_devices for input capture

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

* remove embedded cursor code as not being used | return session path for input capture

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

* setup rdp input

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

* remove simulate example

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

* setup rdp input raw key events + mouse movements

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

* fix rdp raw key input

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

* refact rdp raw key inpuy & fix right meta key

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

* refact and support rdp layout mode key input

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

* support rdp mouse clicks

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

* support rdp mouse scroll

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

* support rdp key sequence input

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

* use rdp input only when uinput is not available

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

* combine rdp input and get_capturables into a single rdp request

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

* rdp fix build

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

* rdp fix build

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

* fix rdp caps lock

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

* format pipewire.rs

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

* format rdp_input.rs

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

* revert #6628 as rdp request state is now managed (better solution)

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

* fix rdp crash on arch kde

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

* rdp_input.rs improvements

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

* refact request_remote_desktop

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

* improve unwraps

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

* remove unwraps

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

* use session references instead of clones

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

---------

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-12-19 10:44:58 +08:00
FastAct
dc8a70bb26 Update nl.rs (#6708) 2023-12-19 10:39:36 +08:00
jimmyGALLAND
adf9b82d6b update lang fr (#6701) 2023-12-18 11:23:15 +08:00
Andrzej Rudnik
be212b5186 Updated Polish translation (#6699)
* Update pl.rs

* Update pl.rs

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2023-12-18 11:23:00 +08:00
solokot
59ba461d83 Update ru.rs (#6695) 2023-12-18 11:22:46 +08:00
fufesou
004c2e069c https://github.com/rustdesk/rustdesk/issues/6686 (#6694)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-12-17 12:43:47 +08:00
Sahil Yeole
c168b7e979 Autocomplete container UI changes (#6690)
* add left & right padding to autocomplete peer and format

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

* handle max height for autocomplete equal padding, add box shadow and format

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

---------

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-12-17 09:22:18 +08:00
fufesou
79411430a5 Fix. Windows snap-function (#6680)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-12-16 21:41:55 +08:00
flusheDData
853d655a92 New term added (#6673)
* Update es.rs

New terms added

* Update es.rs

mistype correction

* Update es.rs

New term added
2023-12-14 09:04:32 +08:00
Kleofass
2d5fa9ebbf Update lv.rs (#6672) 2023-12-14 09:04:18 +08:00
jxdv
b2deab08ab update checkout version (#6671) 2023-12-13 21:22:38 +08:00
Mr-Update
8452a17d79 Update de.rs (#6669) 2023-12-13 10:58:16 +08:00
jxdv
19851c8a47 update cs.rs (#6665) 2023-12-12 19:36:25 +08:00
jxdv
6b8ec6ae16 update sk.rs (#6664) 2023-12-12 19:31:55 +08:00
flusheDData
d648e67eaf Update es.rs (#6658)
* Update es.rs

New terms added

* Update es.rs

mistype correction
2023-12-12 15:12:45 +08:00
RustDesk
ebb14af488 Revert "use fullrange by default for yuv420p if supported (#6655)" (#6656)
This reverts commit 80afa98d66.
2023-12-11 22:46:32 +08:00
21pages
80afa98d66 use fullrange by default for yuv420p if supported (#6655)
1. Explicitly specify the color space as bt601
2. Use fullrange by default for yuv420p if supported
3. Use the pix_fmt space range format to identify codec capabilities, make i444 proto field deprecated, and cause the non-release version of 444 true color to fail.

Signed-off-by: 21pages <pages21@163.com>
2023-12-11 22:31:01 +08:00
bovirus
2797a28c0d Update Italian language (#6654) 2023-12-11 20:09:55 +08:00
21pages
03c8d94024 mobile/desktop use same 'other default settings' (#6652)
1. mobile add 'reverse mouse wheel' default setting
2. change related menu position

Signed-off-by: 21pages <pages21@163.com>
2023-12-11 15:32:13 +08:00
21pages
c061eddf2a move 'swap key' and 'swap left right mouse' to keyboard menu (#6650)
Signed-off-by: 21pages <pages21@163.com>
2023-12-11 12:56:26 +08:00
rustdesk
2e146190e1 swap left right mouse button 2023-12-11 11:22:27 +08:00
rustdesk
fc110c4988 Fix icon color on Sonoma 2023-12-09 10:18:13 +08:00
rustdesk
6003003228 Fix https://github.com/rustdesk/rustdesk-server-pro/issues/174 2023-12-08 20:52:33 +08:00
Andrzej Rudnik
70e5bee519 Update pl.rs (#6633) 2023-12-07 18:33:47 +08:00
Sahil Yeole
4062d1920c Fix: wayland repeated share screen prompts (#6628)
* fix wayland repeated screen share prompts

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

* fix is_x11 import

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

* fix supported resolutions import

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

* fix supported resolutions import

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

---------

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-12-06 23:25:37 +08:00
21pages
50b81c2356 fix windows wakelock, add set_display (#6623)
Signed-off-by: 21pages <pages21@163.com>
2023-12-05 21:32:44 +08:00
21pages
00fe3a76c8 add popupMenuTheme (#6617)
Signed-off-by: 21pages <pages21@163.com>
2023-12-05 11:34:54 +08:00
fufesou
d11a3b9683 Refact, check current view style and scale mode, then update scroll percent (#6613)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-12-04 23:03:22 +08:00
rustdesk
7934fa24a2 Merge branch 'master' of github.com-rustdesk:rustdesk/rustdesk 2023-12-04 22:51:28 +08:00
rustdesk
f0d9dee6ce makepkg on archlinux ci failed from 20231201, fall back to mine 2023-12-04 22:50:54 +08:00
fufesou
3a82bddcd3 Fix, cursor position, scall origin, scrollbar (#6612)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-12-04 22:35:14 +08:00
rustdesk
ebe044aee8 Merge branch 'master' of github.com-rustdesk:rustdesk/rustdesk 2023-12-04 20:58:42 +08:00
rustdesk
674305ce29 https://github.com/rustdesk/rustdesk/discussions/6453#discussioncomment-7752797 2023-12-04 20:58:29 +08:00
21pages
b9a5c6ff31 showToast theme (#6603)
Signed-off-by: 21pages <pages21@163.com>
2023-12-03 22:56:47 +08:00
FastAct
bcb0a7822a Update nl.rs (#6602) 2023-12-03 22:54:12 +08:00
rustdesk
93aac0bc99 fix #616: support QR for uri link 2023-12-03 22:33:52 +08:00
rustdesk
7fe58afa9c fix #616: make ?key and /r work in uri 2023-12-03 22:18:11 +08:00
rustdesk
9d0f8d9886 Fix #616: add rustdesk://password/<password> for android only 2023-12-03 20:51:53 +08:00
rustdesk
aa7d0471db Fix #616, add rustdesk://config/<config_string> 2023-12-03 20:31:48 +08:00
RustDesk
6cd9227e8d Merge pull request #6598 from crwusiz/master_kr
kor translation update
2023-12-03 14:12:30 +08:00
RustDesk
213ef58959 Merge pull request #6595 from jxdv/cs-tr
update cs.rs
2023-12-03 14:12:09 +08:00
RustDesk
2fa1124752 Merge pull request #6594 from jxdv/sk-tr
update sk.rs
2023-12-03 14:11:58 +08:00
crwusiz
d00a0d6c75 kor translation update
fix typo
2023-12-03 11:27:05 +09:00
jxdv
2e225eb84f update cs.rs 2023-12-02 19:11:33 +01:00
jxdv
3c57ee89c4 update sk.rs 2023-12-02 19:04:08 +01:00
RustDesk
b6f870ac5f Merge pull request #6591 from fufesou/fix/6453
Fix. Do not update cursor pos when switching display on toolbar when "Show monitors on toolbar"
2023-12-02 22:23:19 +08:00
fufesou
570ce7d28d Refactor, optional parameter 'updateCursorPos'
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-12-02 22:01:05 +08:00
fufesou
3958090e0f Fix, wrong argument of mobile updating cursor position
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-12-02 21:53:00 +08:00
fufesou
99ed1b729e Fix. Do not update cursor pos when switching display on toolbar when 'Show monitors on toolbar'
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-12-02 21:23:19 +08:00
RustDesk
d16db77b52 Merge pull request #6585 from crwusiz/master_kr
kor translation update
2023-12-02 11:26:26 +08:00
RustDesk
56c176add4 Merge pull request #6586 from sahilyeole/feat/wayland_clipboard
Feat: wayland clipboard support
2023-12-01 23:06:02 +08:00
crwusiz
42a856c24a kor translation update 2023-12-01 22:33:18 +09:00
Sahil Yeole
b662090d64 update Cargo.lock for wayland clipboard 2023-12-01 18:23:23 +05:30
RustDesk
29e50a5f91 Merge pull request #6582 from xalt7x/ukrainian-translation
Update Ukrainian UI translation
2023-12-01 13:04:38 +08:00
Yevhen Popok
7885e07eed Update Ukrainian UI translation 2023-12-01 07:01:10 +02:00
rustdesk
65fd682f2a reorder lang to avoid translate miss some lines 2023-12-01 11:38:25 +08:00
RustDesk
cdecbb0857 Merge pull request #6579 from Mr-Update/patch-5
Update de.rs
2023-12-01 11:36:52 +08:00
RustDesk
68ee781f26 Merge pull request #6578 from bovirus/master
Update Italian language
2023-12-01 11:36:24 +08:00
Sahil Yeole
0f307e7ca2 preserve sudo env; required for WAYLAND_DISPLAY
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-12-01 05:14:09 +05:30
Sahil Yeole
7ac49287df set WAYLAND_DISPLAY env
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-12-01 05:13:03 +05:30
Sahil Yeole
83faf6025b use wayland-data-control arboard feature
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-12-01 05:11:41 +05:30
Mr-Update
4faf5e6d3e Update de.rs 2023-11-30 18:10:01 +01:00
bovirus
d300596ac1 Update Italian language 2023-11-30 15:33:44 +01:00
RustDesk
a30dc5988e Merge pull request #6577 from Kleofass/patch-7
Update lv.rs
2023-11-30 21:21:15 +08:00
Kleofass
e43a0da9a6 Update lv.rs 2023-11-30 15:19:34 +02:00
RustDesk
83b74c0930 Merge pull request #6575 from fufesou/fix/android_switch_display
fix, android switch display. Remove old display video subscription.
2023-11-30 16:32:06 +08:00
RustDesk
0d2a39267f Merge pull request #6574 from FastAct/patch-24
Update nl.rs
2023-11-30 16:29:18 +08:00
RustDesk
f87f380bb0 Update nl.rs 2023-11-30 16:29:02 +08:00
fufesou
ab28acd709 fix, android switch display. Remove old display video subscription.
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-30 16:27:20 +08:00
FastAct
04b53f8e1e Update nl.rs 2023-11-30 09:26:09 +01:00
rustdesk
eb0e5c2de3 change location to make tranlator easier 2023-11-30 16:12:17 +08:00
RustDesk
8cb9b910a6 Merge pull request #6573 from FastAct/patch-23
Update nl.rs
2023-11-30 16:08:29 +08:00
FastAct
bdac79c91b Update nl.rs 2023-11-30 08:57:59 +01:00
RustDesk
88081fc3c4 Merge pull request #6571 from solokot/master
Update ru.rs
2023-11-30 13:48:37 +08:00
solokot
6be451e9f3 Update ru.rs 2023-11-30 08:45:22 +03:00
RustDesk
64ed25879c Merge pull request #6565 from fufesou/fix/elevated_switch_display
fix, elevated mode, switch display
2023-11-30 00:18:14 +08:00
RustDesk
d66e087d94 Update cn.rs 2023-11-30 00:17:58 +08:00
RustDesk
b7b0a44c52 Update en.rs 2023-11-30 00:16:13 +08:00
fufesou
44cba7adf7 fix, prompt msg
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 23:50:17 +08:00
fufesou
5a8a64d284 fix, elevated mode, switch display
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 23:44:28 +08:00
RustDesk
eac6835982 Merge pull request #6564 from crwusiz/master_kr
kor translation update / fix typo
2023-11-29 23:34:02 +08:00
crwusiz
7992ca4c85 kor translation update / fix typo 2023-11-30 00:31:56 +09:00
RustDesk
ead200ea5d Merge pull request #6563 from fufesou/fix/is_subed
fix, is_subed()
2023-11-29 23:25:34 +08:00
fufesou
e146c2606f fix, is_subed()
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 23:19:29 +08:00
RustDesk
db158e1ffe Merge pull request #6561 from fufesou/feat/input_source
Feat/input source
2023-11-29 23:00:12 +08:00
RustDesk
72ef3c3394 Merge pull request #6562 from 21pages/force_reboot
fix macos and linux vm reboot
2023-11-29 22:57:38 +08:00
21pages
e17378c6b3 fix macos and linux vm reboot
Signed-off-by: 21pages <pages21@163.com>
2023-11-29 22:41:31 +08:00
fufesou
0e05f5305c fix build, android
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 22:13:38 +08:00
fufesou
58f43da23e update rdev
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 21:50:17 +08:00
fufesou
4a9e7f29da fix, flutter, keyboard mode
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 21:31:27 +08:00
fufesou
1589209567 release keys for flutter input source
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 21:05:21 +08:00
fufesou
5c6017b0a9 fix build, mobile
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 20:58:40 +08:00
fufesou
4246fe5b92 update rdev
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 20:58:40 +08:00
fufesou
c441d2f03f fix build
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 20:58:40 +08:00
fufesou
6c21529594 update rdev
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 20:58:40 +08:00
fufesou
6fc4253d46 debug, linux
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 20:58:40 +08:00
fufesou
7a2590d1f9 fix, compilation
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 20:58:40 +08:00
fufesou
4cbbb5b64f feat, input source, win->win
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 20:58:40 +08:00
fufesou
f11104fcb5 tmp commit
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 20:58:39 +08:00
RustDesk
19e8ca6c06 Merge pull request #6557 from 21pages/force_reboot
replace reboot with force_reboot
2023-11-29 17:34:09 +08:00
21pages
13005e8242 replace reboot with force_reboot
Signed-off-by: 21pages <pages21@163.com>
2023-11-29 17:26:13 +08:00
RustDesk
d0dde6e572 Merge pull request #6554 from fufesou/fix/mobile_switch_display
fix, mobile, switch display
2023-11-29 12:34:36 +08:00
fufesou
5e103a5ccb fix, mobile, switch display
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-29 12:30:41 +08:00
RustDesk
0c4e6f0a8c Merge pull request #6525 from bovirus/master
Update Italian language
2023-11-26 17:46:51 +08:00
RustDesk
03a89a5cdd Update it.rs 2023-11-26 17:33:12 +08:00
bovirus
e22d13be1f Update Italian language 2023-11-26 10:24:24 +01:00
RustDesk
ef9c4d96b7 Merge pull request #6524 from fufesou/fix/install_cert_silent
Fix. Install cert silently
2023-11-26 14:59:56 +08:00
fufesou
bb7300a055 Remove cert file arg on installing cert
Signed-off-by: fufesou <shuanglongchen@yeah.ne>
2023-11-26 14:26:45 +08:00
fufesou
2d456fd1fc --install-idd with cert
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-26 12:30:14 +08:00
fufesou
97108e788f Fix. Install cert silent
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-26 12:17:05 +08:00
RustDesk
b8f7259fbd Merge pull request #6520 from 21pages/wakelock
wake lock for all connection type
2023-11-25 20:34:14 +08:00
21pages
2de1c62daf wake lock for all connection type
Signed-off-by: 21pages <pages21@163.com>
2023-11-25 17:43:07 +08:00
RustDesk
f5d2eff8d3 Merge pull request #6514 from deep-soft/rustdesk-rustdesk
fix bridge.yml (dtolnay/rust-toolchain)
2023-11-24 13:15:17 +08:00
RustDesk
dbcf1fdb6f Merge pull request #6515 from 21pages/android_service_wakelock
android video service wakelock
2023-11-24 13:14:06 +08:00
21pages
bd81e4d0fb android video service wakelock
Signed-off-by: 21pages <pages21@163.com>
2023-11-24 12:02:45 +08:00
deep-soft
80786f692c fix bridge.yml (dtolnay/rust-toolchain)
toolchain
uses: dtolnay/rust-toolchain
2023-11-23 21:11:45 +02:00
RustDesk
177edfea3e Merge pull request #6511 from deep-soft/rustdesk-rustdesk
fix CI.yml (CARGO_TEST_OPTIONS and dtolnay/rust-toolchain)
2023-11-23 23:05:51 +08:00
deep-soft
b4b08db778 dtolnay/rust-toolchain 2023-11-23 16:47:54 +02:00
deep-soft
8abf2f768c Merge branch 'rustdesk:master' into rustdesk-rustdesk 2023-11-23 16:40:08 +02:00
deep-soft
8b14cd5aea Update ci.yml 2023-11-23 16:39:53 +02:00
rustdesk
84a21d72b9 link /usr/bin/rustdesk even for init system 2023-11-23 21:56:36 +08:00
rustdesk
fa8e0ed27b brotli 3.4 2023-11-23 19:52:53 +08:00
rustdesk
b6e403a3fb zstd 0.13 2023-11-23 19:35:42 +08:00
RustDesk
3d368f9aca Merge pull request #6506 from 21pages/fix_os_password_show
don't show os password when don't have keyboard permission
2023-11-23 19:21:22 +08:00
21pages
414ac950e0 don't show os password when don't have keyboard permission
Signed-off-by: 21pages <pages21@163.com>
2023-11-23 19:17:19 +08:00
RustDesk
34fa82f72c Merge pull request #6501 from deep-soft/rustdesk-rustdesk
github actions (ci.yml and flutter-build.yml)
2023-11-23 11:45:50 +08:00
RustDesk
4d5fdc4725 Merge pull request #6503 from cacing69/master
add idd trans on indonesia
2023-11-23 09:55:40 +08:00
RustDesk
1c4f28e708 Merge pull request #6502 from sahilyeole/fix/wayland_distorted_stream_scaling
Fix: wayland cursor mismatch on fractional scaling
2023-11-23 09:54:04 +08:00
Ibnul Mutaki
7bf555ce4c add idd trans 2023-11-23 07:54:42 +07:00
deep-soft
bb388628a7 Update flutter-build.yml 2023-11-22 23:09:56 +02:00
deep-soft
a2920578a1 Update ci.yml 2023-11-22 22:38:41 +02:00
Sahil Yeole
b41a73f08d fix cursor mismatch on fractional scaling single screen
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-22 23:53:43 +05:30
RustDesk
c6ace470e3 Merge pull request #6498 from Mr-Update/patch-4
Update de.rs
2023-11-22 17:48:17 +08:00
Mr-Update
cdbe79d3c1 Update de.rs 2023-11-22 10:46:07 +01:00
RustDesk
493ec07ff2 Merge pull request #6497 from 21pages/fix_os_password_click
fix mobile os password edit action
2023-11-22 16:59:51 +08:00
21pages
ec15412755 fix mobile os password edit action
Signed-off-by: 21pages <pages21@163.com>
2023-11-22 16:46:17 +08:00
RustDesk
191b6d5b4e Merge pull request #6496 from necarnot/master
Update fr.rs typo
2023-11-22 16:44:06 +08:00
rustdesk
8718152dba no sign 2023-11-22 16:43:47 +08:00
necarnot
c585db7516 Update fr.rs typo
Lots of minor typos
2023-11-22 09:39:53 +01:00
RustDesk
515c91a407 Delete .github/ISSUE_TEMPLATE/task.yaml 2023-11-22 13:22:22 +08:00
RustDesk
b615029304 Merge pull request #6488 from Kleofass/patch-6
Update lv.rs
2023-11-21 22:28:02 +08:00
Kleofass
3b0f4b1f82 Update lv.rs 2023-11-21 16:21:08 +02:00
RustDesk
4a2bf7a0c3 Merge pull request #6487 from sahilyeole/fix/wayland_distorted_stream_scaling
Fix: wayland fractional scaling distortion
2023-11-21 21:46:32 +08:00
RustDesk
79212f8ef7 Merge pull request #6486 from crwusiz/master_kr
kor_translation update
2023-11-21 21:05:59 +08:00
crwusiz
c57a596156 kor_translation update 2023-11-21 22:04:08 +09:00
Sahil Yeole
948db1451f fix stream resolution mismatch/distortion on scaling
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-21 16:59:45 +05:30
RustDesk
06aa18bfab Merge pull request #6483 from solokot/master
update ru.rs
2023-11-21 18:47:40 +08:00
solokot
f076afb6e1 update ru.rs 2023-11-21 12:48:16 +03:00
RustDesk
582402f4d9 Merge pull request #6482 from jxdv/cs-tr
update cs.rs
2023-11-21 16:58:04 +08:00
jxdv
0e04ff506b update cs.rs 2023-11-21 09:55:22 +01:00
RustDesk
e9f757dccb Merge pull request #6481 from jxdv/sk-tr
update sk.rs
2023-11-21 16:53:58 +08:00
jxdv
f02d858646 fix typo 2023-11-21 09:51:28 +01:00
jxdv
3727cd401c update sk.rs 2023-11-21 09:50:42 +01:00
RustDesk
f0c9deca4b Merge pull request #6475 from fufesou/feat/win10_virtual_display_not_support_msg
Feat/win10 virtual display not support msg
2023-11-21 11:59:02 +08:00
fufesou
f41a8bc355 idd_not_support_tip to idd_not_support_under_win10_2004_tip
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-21 11:05:50 +08:00
fufesou
3100930136 translation & Rustdesk to RustDesk
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-21 10:55:46 +08:00
fufesou
9abafafcdd translation
Signed-off-by: fufesou <shuanglongchen@yeah.ne>
2023-11-20 21:57:53 +08:00
fufesou
0d127dff3b translation
Signed-off-by: fufesou <shuanglongchen@yeah.ne>
2023-11-20 21:55:09 +08:00
fufesou
83dbf88ffb feat, win10, virtual display, not support message
Signed-off-by: fufesou <shuanglongchen@yeah.ne>
2023-11-20 21:44:25 +08:00
RustDesk
3a6f56ebbc Merge pull request #6473 from fufesou/feat/format_idd_error_message
Feat/format idd error message
2023-11-20 18:56:39 +08:00
fufesou
3e8c5d3b79 trivial changes
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 17:42:34 +08:00
fufesou
2aaca0c54a Remove get_error() in windows.rs
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 17:38:53 +08:00
fufesou
c6d587f0c7 Refact, use std::io::Error::from_raw_os_error() to format message
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 17:22:14 +08:00
fufesou
c85682de8d Refact, replace GetLastError() by std::io::Error::last_os_error()
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 17:15:15 +08:00
fufesou
9c12c3ee4e remove useless \n
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 16:48:05 +08:00
fufesou
2c432dbf4d format idd error message
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 16:41:02 +08:00
fufesou
0c8643837f Modify the error message
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 16:11:59 +08:00
fufesou
3fd97f7e60 modify error message
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 15:54:29 +08:00
fufesou
5064696480 send msgbox when plugging in/out virtual displays
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 15:46:22 +08:00
fufesou
6f0fbd1088 feat, format idd error message
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 15:30:56 +08:00
RustDesk
b535722421 Merge pull request #6470 from fufesou/feat/topmost_window_exclude_from_capture
Feat/topmost window exclude from capture
2023-11-20 12:46:23 +08:00
fufesou
60e96b637d privacy mode, remove 'recommended'
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 11:50:53 +08:00
fufesou
7f29c7a601 add comment
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 11:07:18 +08:00
fufesou
060dfedb74 topmost_window_exclude_from_capture, change label text
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 10:57:01 +08:00
RustDesk
2907ed6029 Merge pull request #6468 from 0ofta/norwegian-bokmal-translation
Added Norwegian Bokmål Translation
2023-11-20 10:33:30 +08:00
fufesou
5649fcc9ca win, is version equal or greater, build number
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 10:28:57 +08:00
fufesou
85ddfc0739 remove redundent global variables
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 07:41:06 +08:00
fufesou
7c98bfd363 check if WDA_EXCLUDEFROMCAPTURE is supported
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 07:41:06 +08:00
fufesou
c23f377039 feat, topmost window, exclude from capture
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-20 07:41:04 +08:00
0ofta
af89fb52f1 Added Norwegian Bokmål Translation
Signed-off-by: 0ofta <kaare@jenssen.it>
2023-11-19 20:32:11 +01:00
RustDesk
98f56fd506 Merge pull request #6466 from xalt7x/fix/README.md
README.md: Fix typo and use correct name for macOS
2023-11-20 00:02:14 +08:00
Yevhen Popok
88ea1ea85b README.md: Fix typo and use correct name for macOS 2023-11-19 17:57:25 +02:00
RustDesk
2a1e33f95c Merge pull request #6465 from xalt7x/ukrainian-translation
Update Ukrainian translations
2023-11-19 23:54:00 +08:00
Yevhen Popok
2fe55e2b55 Update Ukrainian README 2023-11-19 17:52:19 +02:00
Yevhen Popok
d6f5887b1d Update Ukrainian UI translation 2023-11-19 14:46:33 +02:00
RustDesk
dce045461a Merge pull request #6462 from fufesou/feat/top_most_window_cloak
feat, top most window cloak.
2023-11-19 13:38:57 +08:00
fufesou
4ab0324bc9 feat, top most window cloak. Window preview will not break privacy mode.
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-19 12:34:30 +08:00
RustDesk
b2bb69fe42 Merge pull request #6460 from fufesou/feat/install_idd_when_installing_app
feat, install indirect display driver when installing the app
2023-11-19 11:44:26 +08:00
RustDesk
ad96f9aacd Merge pull request #6461 from 21pages/fix_max_codec_thread_num
fix exceeding the maximum number of threads allowed
2023-11-19 11:43:12 +08:00
21pages
9847fc5c77 fix exceeding max allowed thread count, thread count <= available memory / 2
Signed-off-by: 21pages <pages21@163.com>
2023-11-19 11:36:23 +08:00
fufesou
3f3bca753c feat, install indirect display driver when installing the app
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-18 12:14:21 +08:00
RustDesk
c223d6a87d Merge pull request #6457 from flusheDData/master
Update es.rs
2023-11-18 10:23:39 +08:00
RustDesk
14a428467c Merge pull request #6458 from cacing69/master
update id trans
2023-11-18 10:23:28 +08:00
RustDesk
efc84e53ec Merge pull request #6459 from 21pages/format_log
format log
2023-11-18 10:22:55 +08:00
21pages
f9ee0189f0 format log, add space after colon
Signed-off-by: 21pages <pages21@163.com>
2023-11-18 09:52:46 +08:00
Ibnul Mutaki
d90580943f update id trans 2023-11-18 08:26:44 +07:00
flusheDData
f6484b4e2b Update es.rs
New terms added
2023-11-18 00:11:25 +01:00
RustDesk
0b5f48b926 Merge pull request #6446 from fufesou/fix/do_not_get_displays_on_start
Fix. Do not get displays on server start.
2023-11-18 00:31:38 +08:00
fufesou
f2f649319d Remove redundant mutex
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-17 23:38:27 +08:00
RustDesk
35eaa94228 Merge pull request #6445 from fufesou/fix/mobile_soft_keyboard_focus
fix, mobile soft keyboard focus
2023-11-17 23:09:55 +08:00
fufesou
e09a050d41 Fix. Do not get displays on server start.
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-17 17:11:25 +08:00
fufesou
38182ba020 tmp fix, mobile soft keyboard focus
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-17 14:43:23 +08:00
RustDesk
7960b2ca10 Merge pull request #6436 from fufesou/fix/android_soft_keyboard_input
fix, android_soft_keyboard_input, maybe flutter 3.13.*
2023-11-16 23:40:51 +08:00
fufesou
722c789448 add comment
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-16 23:39:07 +08:00
fufesou
faa4c36956 fix, android_soft_keyboard_input, maybe flutter 3.13.*
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-16 23:08:09 +08:00
RustDesk
d3a33d786d Merge pull request #6429 from bovirus/master
Update Italian language
2023-11-16 17:36:41 +08:00
bovirus
1a2d60dbfc Update Italian language 2023-11-16 08:00:23 +01:00
RustDesk
2aa5e685b8 Merge pull request #6425 from dignow/fix/query_onlines_block_thread
Fix/query onlines block thread
2023-11-16 13:29:13 +08:00
RustDesk
512c7df37d Merge pull request #6416 from basilgello/vcpkg-deps
vcpkg deps
2023-11-16 13:28:56 +08:00
RustDesk
57fddf1c85 Merge pull request #6418 from Kleofass/patch-5
Update lv.rs
2023-11-16 13:28:12 +08:00
RustDesk
afe4206782 Merge pull request #6415 from BestiaPL/master
Update pl.rs
2023-11-16 13:27:43 +08:00
dignow
02bc5e3111 debug, query_onlines_block_thread
Signed-off-by: dignow <linlong1265@gmail.com>
2023-11-16 10:32:22 +08:00
dignow
5a51284550 fix, query onlines, block current ffi thread
Signed-off-by: dignow <linlong1265@gmail.com>
2023-11-16 10:32:22 +08:00
Vasyl Gello
2d995826ad Globally drop yasm - libvpx should not need it
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-15 16:03:40 +02:00
Kleofass
92f3bf2999 Update lv.rs 2023-11-15 16:00:46 +02:00
Vasyl Gello
bfe282c2db VCPKG port fixes
* Do not patch vcpkg - replace the cmake script fully
  * Actualize libvpx port

[skip ci]

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-15 13:22:31 +02:00
Andrzej Rudnik
2d0b013af3 Update pl.rs 2023-11-15 11:16:34 +01:00
RustDesk
3df938ed61 Merge pull request #6414 from solokot/master
Update Russian translation
2023-11-15 16:32:54 +08:00
solokot
d4b6cab742 Update Russian translation 2023-11-15 11:29:33 +03:00
RustDesk
8d327af0ac Merge pull request #6411 from fufesou/fix/displays_order
fix, windows displays' order mismatch the system settings display
2023-11-15 15:39:59 +08:00
RustDesk
f135fbee26 Merge pull request #6413 from crwusiz/master_kr
kor_translation update
2023-11-15 14:05:04 +08:00
RustDesk
d65977bd59 Merge pull request #6410 from fufesou/fix/build_ios
fix, build ios
2023-11-15 14:03:41 +08:00
crwusiz
1e0242ede9 kor_translation update 2023-11-15 10:30:29 +09:00
fufesou
839d3365f8 fix, build ios
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-14 22:29:12 +08:00
fufesou
182ee8f233 fix, order displays from dxgi
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-14 22:04:17 +08:00
RustDesk
24eb6f8c38 Merge pull request #6406 from fufesou/feat/virtual_display_privacy_mode
Feat/Windows - virtual display privacy mode
2023-11-14 21:54:14 +08:00
fufesou
13bade262f remove warns
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-14 21:01:31 +08:00
fufesou
d2ff675fbc virtual_display_privacy_mode, switch privacy, debug
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-14 20:46:06 +08:00
fufesou
1905a81f9a virtual_display_privacy_mode, switch privacy mode directly
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-14 19:36:51 +08:00
fufesou
e2382a1465 refact, mobile, privacy mode menu
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-14 17:35:16 +08:00
RustDesk
ec00cd2ae0 Merge pull request #6408 from basilgello/vcpkg-deps
vcpkg deps
2023-11-14 14:47:57 +08:00
RustDesk
e0fad4eaa5 Merge pull request #6407 from 21pages/sysinfo
opt sysinfo
2023-11-14 14:43:52 +08:00
Vasyl Gello
882b39b067 Ignore CRLF/LF conversion on sciter patch apply
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-14 08:09:07 +02:00
21pages
d15a641d88 fix mobile doesn't init ConnectionTypeState
Signed-off-by: 21pages <pages21@163.com>
2023-11-14 12:46:21 +08:00
fufesou
90ac8b7b0b feat/virtual_display_privacy_mode
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-14 12:11:38 +08:00
RustDesk
d64afdcff1 Merge pull request #6403 from 21pages/flexi_logger
update flexi_logger to 0.27.3
2023-11-14 11:12:52 +08:00
21pages
eb58a39f57 replace System::new_all with System::new
Signed-off-by: 21pages <pages21@163.com>
2023-11-14 09:40:15 +08:00
RustDesk
d12352d568 Merge pull request #6358 from basilgello/vcpkg-deps
vcpkg deps
2023-11-13 23:33:53 +08:00
RustDesk
217a9808b2 Merge pull request #6392 from 21pages/fps
not use max fps by default
2023-11-13 23:32:55 +08:00
21pages
1b08adb178 not use max fps by default
Signed-off-by: 21pages <pages21@163.com>
2023-11-13 21:33:51 +08:00
Vasyl Gello
f113fda6d4 [TEMP] Try disabling LLVM IR bitcode and upload rust target folder to cache
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-13 14:01:10 +02:00
RustDesk
5a6a773583 Merge pull request #6394 from rustdesk/revert-6379-fix/the_displays_order
Revert "Fix, windows display orders"
2023-11-13 17:32:10 +08:00
RustDesk
627d21a00a Revert "Fix, windows display orders" 2023-11-13 17:31:55 +08:00
RustDesk
44e13c84bc Merge pull request #6389 from xalt7x/ukrainian-translation
Update Ukrainian translation
2023-11-13 12:46:33 +08:00
Yevhen Popok
b96067cf2e Update Ukrainian translation 2023-11-13 00:11:20 +02:00
Yevhen Popok
b2fe5cce5e Update Ukrainian translation 2023-11-13 00:06:50 +02:00
Yevhen Popok
55775adba6 Update Ukrainian translation 2023-11-13 00:01:14 +02:00
RustDesk
0500f56163 Merge pull request #6385 from 21pages/only_windows_check_cm_exit
only windows no prelogin check cm exit
2023-11-12 22:18:55 +08:00
21pages
52e5d519ab only windows no prelogin check cm exit
Signed-off-by: 21pages <pages21@163.com>
2023-11-12 22:09:41 +08:00
RustDesk
bdfbb97bf8 Merge pull request #6384 from basilgello/fix-rpmbuild-flutter-3.13.9
rpm: Add missing libfile_selector_linux_plugin.so()(64bit) to provides
2023-11-12 21:22:55 +08:00
RustDesk
cff117cb40 Merge pull request #6383 from crwusiz/master_kr
kor_translation update
2023-11-12 21:21:46 +08:00
RustDesk
cf71adc7e1 Merge pull request #6379 from fufesou/fix/the_displays_order
Fix, windows display orders
2023-11-12 21:21:23 +08:00
Vasyl Gello
0582f2a168 rpm: Add missing libfile_selector_linux_plugin.so()(64bit) to provides
Fixes installation of rpms on fedora/suse.

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-12 06:59:20 +02:00
crwusiz
2721af3052 kor_translation update 2023-11-12 13:36:25 +09:00
fufesou
f9950639e8 remove warn
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-12 11:49:38 +08:00
fufesou
8f8c9ddc25 Fix, windows display orders
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-12 00:08:16 +08:00
RustDesk
58596ad1ab Merge pull request #6374 from jxdv/cs-tr
update cs.rs
2023-11-11 19:36:13 +08:00
RustDesk
b17ea991a3 Merge pull request #6373 from jxdv/sk-tr
update sk.rs
2023-11-11 19:35:58 +08:00
RustDesk
45d70f73bd Merge pull request #6354 from 21pages/relay_hint
Show relay hint regardless of whether data is received or not
2023-11-11 19:35:44 +08:00
RustDesk
7a6e483171 Merge pull request #6361 from fufesou/fix/check_update_broker_on_privacy_start
fix, run check_update_broker_process on privacy mode start
2023-11-11 19:29:58 +08:00
RustDesk
1792dd8d2c Merge pull request #6362 from KetaDotCC/master
fix: add missing winapi features on a fresh build
2023-11-11 19:28:56 +08:00
RustDesk
a4ea4b138f Merge pull request #6359 from cacing69/master
update ID Trans
2023-11-11 19:27:21 +08:00
jxdv
e2c6aedddd update cs.rs 2023-11-11 11:53:05 +01:00
jxdv
b91a03b602 update sk.rs 2023-11-11 11:45:53 +01:00
21pages
27716f077e update flexi_logger to 0.27.3, android check path exist
Signed-off-by: 21pages <pages21@163.com>
2023-11-11 18:19:01 +08:00
21pages
eaa054e599 Show relay hint regardless of whether data is received or not
Signed-off-by: 21pages <pages21@163.com>
2023-11-10 19:13:50 +08:00
Kingtous
e57d07f7d6 fix: add missing winapi features on a fresh build 2023-11-10 12:00:08 +08:00
fufesou
895831f46f fix, run check_update_broker_process on privacy mode start
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-10 09:56:25 +08:00
Ibnul Mutaki
6b16d7ee8a update ID Trans 2023-11-10 08:04:11 +07:00
rustdesk
45dbea57f1 allow /r in command line 2023-11-09 21:58:03 +08:00
RustDesk
6ea0dbc70f Merge pull request #6351 from basilgello/vcpkg-deps
vcpkg-deps fixes
2023-11-09 19:21:45 +08:00
Vasyl Gello
30a5041799 Dont mess CROSS with CC/CXX/etc
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-09 12:41:06 +02:00
Vasyl Gello
35083724f6 macos: Try to patch underlying file and not symlink
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-09 11:48:13 +02:00
RustDesk
959d5dd9c2 Merge pull request #6349 from 21pages/fix
fix windows build, add winapi features
2023-11-09 17:11:10 +08:00
rustdesk
a24a261c36 API_SERVER 2023-11-09 16:52:04 +08:00
21pages
75feb90b19 fix windows build, add winapi features
Signed-off-by: 21pages <pages21@163.com>
2023-11-09 16:30:22 +08:00
RustDesk
fd3cb1b0eb Merge pull request #6333 from 21pages/cm
close all connections if cm exit unexpected, and allow retry
2023-11-09 16:06:57 +08:00
21pages
1902134f03 cm get config
Signed-off-by: 21pages <pages21@163.com>
2023-11-09 15:57:47 +08:00
RustDesk
cce48c5030 Merge pull request #6330 from dploeger/issue-6157
fix: Fix sysinfo >=0.29.1 crashing the main view on macOS
2023-11-09 15:47:59 +08:00
RustDesk
0654b8e427 Update Cargo.toml 2023-11-09 15:46:42 +08:00
RustDesk
7df10b20a4 Merge pull request #6328 from sahilyeole/fix/mobile_wayland_keyboard_mismatch
Fix: mobile -> wayland virtual keyboard mapping mismatch
2023-11-09 15:44:55 +08:00
RustDesk
dbde5af545 Merge pull request #6340 from grummbeer/allow-to-enable
Fix. translation keys "Allow file copy and paste" > "Enable file copy and paste"
2023-11-09 15:37:59 +08:00
RustDesk
85b4f0638f Merge pull request #6347 from rustdesk/revert-6343-remove_manual_fps
Revert "Remove manual adjustment of fps, automatically adjusted to the maximum already"
2023-11-09 15:25:10 +08:00
RustDesk
94f2751902 Revert "Remove manual adjustment of fps, automatically adjusted to the maximum already" 2023-11-09 15:24:57 +08:00
RustDesk
ffdfef0adc Merge pull request #6343 from 21pages/remove_manual_fps
Remove manual adjustment of fps, automatically adjusted to the maximum already
2023-11-09 15:13:45 +08:00
RustDesk
de7652836b Merge pull request #6334 from solokot/master
Update ru.rs
2023-11-09 15:12:24 +08:00
RustDesk
59913bc6f3 Merge pull request #6327 from Mr-Update/patch-3
Update de.rs
2023-11-09 15:11:44 +08:00
RustDesk
d77a684b64 Merge pull request #6326 from flusheDData/master
Update es.rs
2023-11-09 15:11:04 +08:00
RustDesk
529c075fe1 Merge pull request #6325 from basilgello/vcpkg-deps
Fixes for vcpkg-deps
2023-11-09 15:10:44 +08:00
Vasyl Gello
5190589653 Publish rustdesk version file on tag
[skip ci]

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-09 07:20:20 +02:00
Vasyl Gello
cb9a1b4bef Fix wrong CMake compiler detection of arm-linux on aarch64
[skip ci]

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-09 07:20:14 +02:00
21pages
a0beba4e20 remove fps manual adjust, which can only limit fps
Signed-off-by: 21pages <pages21@163.com>
2023-11-09 10:20:31 +08:00
Mr-Update
d794425f2c Update de.rs 2023-11-08 21:50:09 +01:00
Vasyl Gello
1ba11fc3ef Try to give more time to apple XProt service to verify app image
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-08 21:26:07 +02:00
Vasyl Gello
0dfb1ae776 Add oboe-wrapper local vcpkg port
... because we switched to unmodified oboe.

The wrapper is built as separate local vcpkg port
that depends on oboe.

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-08 21:26:04 +02:00
grummbeer
c8a054f997 fix translation keys. allow > enable 2023-11-08 18:32:55 +01:00
grummbeer
30c4e9cf00 fix translation keys. allow > enable 2023-11-08 18:31:37 +01:00
solokot
c9fd2fc1c2 Update ru.rs 2023-11-08 13:44:30 +03:00
Dennis Ploeger
e9e3a436eb fix: Corrected Cargo.lock 2023-11-08 10:50:15 +01:00
Dennis Ploeger
b9bd79895f fix: Fix sysinfo >=0.29.1 crashing the main view on macOS 2023-11-08 08:59:27 +01:00
21pages
36b574a5f8 close all connections if cm exit unexpected, and allow retry
Signed-off-by: 21pages <pages21@163.com>
2023-11-08 13:01:09 +08:00
Mr-Update
fe90372b2f Update de.rs 2023-11-07 23:52:56 +01:00
Sahil Yeole
d47ddac94c add layout mappings for shift+key events
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-07 23:44:26 +05:30
flusheDData
854c374e2f Update es.rs
New terms added
2023-11-07 16:27:41 +01:00
RustDesk
7bafe142ca Merge pull request #6322 from BestiaPL/master
Update pl.rs
2023-11-07 20:23:47 +08:00
RustDesk
14ec8c4bea Merge pull request #6323 from basilgello/vcpkg-deps
vcpkg deps
2023-11-07 20:21:01 +08:00
Vasyl Gello
e9f2a77bf6 Remove useless step for ios and document elinux need better
[skip ci]

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-07 14:17:09 +02:00
Vasyl Gello
dcb4ce8d9a Revert "CI: Remove flutter-elinux"
This reverts commit c4b68280fd.

Official flutter does not run on arm64 and it is very vaguely
documented.
2023-11-07 13:59:22 +02:00
Vasyl Gello
2702466cdf Revert "Avoid double build of bridge fies"
This reverts commit f0f52d7244.

Looks like macos and ios can use the same cached bridge but not
linux one because of missing headers: Runner/bridge_generated.h
2023-11-07 13:57:13 +02:00
Andrzej Rudnik
2016000d37 Update pl.rs 2023-11-07 12:36:05 +01:00
RustDesk
be0516c06c Merge pull request #6320 from basilgello/vcpkg-deps
vcpkg deps
2023-11-07 18:19:00 +08:00
Vasyl Gello
332a3635de Add ninja to vcpkg deps
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-07 12:08:44 +02:00
RustDesk
9df4fa6a0e Merge pull request #6319 from basilgello/vcpkg-deps
Add cmake to vcpkg deps
2023-11-07 18:03:01 +08:00
Vasyl Gello
ddcb9ff819 Add cmake to vcpkg deps
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-07 12:02:26 +02:00
RustDesk
7e9f5509ca Merge pull request #6318 from basilgello/vcpkg-deps
Add vcpkg deps (zip etc)
2023-11-07 17:57:39 +08:00
Vasyl Gello
c596d75e8c Add vcpkg deps (zip etc)
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-07 11:55:31 +02:00
RustDesk
a0337d399c Merge pull request #6282 from basilgello/vcpkg-deps
Refactor vcpkg dependencies and get rid of thirdparty repos
2023-11-07 17:39:37 +08:00
Vasyl Gello
f0f52d7244 Avoid double build of bridge fies
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-07 10:55:44 +02:00
RustDesk
d55d796c00 Merge pull request #6314 from FastAct/patch-22
Update nl.rs
2023-11-07 16:14:07 +08:00
RustDesk
6da36b27aa Update nl.rs 2023-11-07 15:29:08 +08:00
FastAct
b4d3fc393e Update nl.rs 2023-11-07 07:42:25 +01:00
Vasyl Gello
c4b68280fd CI: Remove flutter-elinux
[skip ci]

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-07 08:31:14 +02:00
Vasyl Gello
97aeee7172 Fix CI
* Drop third party lib repos
  * Now all vcpkg dependencies are declared in vcpkg.json
  * Implement vcpkg binary caching in Github Actions

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-07 08:31:14 +02:00
Vasyl Gello
8bf57f1293 Refactor build_android_deps.sh
* Allow building one architecture or all 4 architectures
* Fix paths for ndk r25c+
* Bump libvpx to 1.13.1 to fix CVE-2023-5238
* Add ndk-compat from cpu-features to address missing
  android_getCpuFeatures symbol on android arm64 emulators

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-07 08:31:14 +02:00
Vasyl Gello
4584cebad5 Add custom ports not (yet) in upstream vcpkg
aom 3.7.0
libvpx 1.13.1: https://github.com/microsoft/vcpkg/pull/34814

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-07 08:31:11 +02:00
RustDesk
557773144b Merge pull request #6097 from mcfans/master
Physical keyboard to android support
2023-11-07 12:56:16 +08:00
mcfans
32a29a5556 chore: use a match instead of unwrap error 2023-11-07 12:51:16 +08:00
Vasyl Gello
9f4a844c9b Fix buildscripts
* Fix FDroid build on x86
  * Fix CI build on arm
  * Rename `install_oboe` to `install_android_deps`

... because we add ndk_compat and the function installs
android-specific dependencies.

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-07 06:27:43 +02:00
RustDesk
82b7650458 Merge pull request #6313 from fufesou/fix/crash_3.13_win_multidesktop
fix, crash 3.13, windows, multidesktop
2023-11-07 12:23:06 +08:00
fufesou
b7121c4447 fix, crash 3.13, windows, multidesktop
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-07 12:18:03 +08:00
mcfans
b522de3b56 Merge remote-tracking branch 'upstream/master'
# Conflicts:
#	Cargo.lock
#	src/server/connection.rs
2023-11-07 12:13:15 +08:00
RustDesk
eb94caf4b6 Merge pull request #6312 from fufesou/fix/remote_tab_menu
fix, remote tab menu
2023-11-07 12:00:30 +08:00
fufesou
0f59e0c950 fix, remote tab menu
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-07 11:56:07 +08:00
RustDesk
05a254c682 Merge pull request #6310 from 21pages/install_after_connect
fix install after connection
2023-11-07 11:37:52 +08:00
RustDesk
576ba2c745 Merge pull request #6308 from Kleofass/patch-4
Update lv.rs
2023-11-07 11:37:00 +08:00
RustDesk
3dafda6ace Merge pull request #6306 from bovirus/master
Update Italian language
2023-11-07 11:36:19 +08:00
RustDesk
2d048a3295 Merge pull request #6305 from FastAct/patch-21
Update nl.rs
2023-11-07 11:35:55 +08:00
21pages
f2d345e7b1 fix missing log when removed directory names have same prefix
Signed-off-by: 21pages <pages21@163.com>
2023-11-07 10:43:03 +08:00
21pages
6a0db02230 closeAllSubWindow before install
Signed-off-by: 21pages <pages21@163.com>
2023-11-07 10:42:57 +08:00
RustDesk
93f271912e Update nl.rs 2023-11-07 10:34:39 +08:00
Kleofass
55b78727ab Update lv.rs 2023-11-06 17:58:51 +02:00
bovirus
69b555c059 Update Italian language 2023-11-06 15:26:00 +01:00
FastAct
07f327d641 Update nl.rs 2023-11-06 15:11:47 +01:00
rustdesk
c69f4ba4a2 fix ci 2023-11-06 20:36:03 +08:00
rustdesk
d7e8d4d5c3 fix #6198 2023-11-06 20:12:01 +08:00
RustDesk
679a026e72 Merge pull request #6304 from yaseresmaeelpour/patch-1
Update fa.rs
2023-11-06 19:53:24 +08:00
yaseresmaeelpour
0db180a54f Update fa.rs
fix setup server tip message in farsi language
2023-11-06 14:52:46 +03:30
RustDesk
6c2de53e07 Merge pull request #6303 from 21pages/cm_file_log
Cm file createDir/delete log and block user input permission
2023-11-06 19:13:31 +08:00
21pages
af132aa62e permission allow->enable, block->blocking
Signed-off-by: 21pages <pages21@163.com>
2023-11-06 18:08:53 +08:00
21pages
d528fd3762 permisson block input
Signed-off-by: 21pages <pages21@163.com>
2023-11-06 15:07:03 +08:00
21pages
663d355a48 cm file delete/create log
Signed-off-by: 21pages <pages21@163.com>
2023-11-06 14:24:50 +08:00
RustDesk
27112e3480 Merge pull request #6286 from jxdv/cz-rs-update
update cs.rs
2023-11-06 11:00:24 +08:00
RustDesk
482d3ff352 Merge pull request #6297 from BestiaPL/master
Update pl.rs
2023-11-06 11:00:04 +08:00
RustDesk
a444d4af94 Merge pull request #6289 from crwusiz/master_kr
kor_translation update
2023-11-06 10:59:49 +08:00
RustDesk
6f77fda6fa Merge pull request #6294 from ClSlaid/docs/clipboard-files
docs: file copy paste explainations
2023-11-06 10:59:22 +08:00
Andrzej Rudnik
8f7d653e80 Update pl.rs 2023-11-06 00:30:59 +01:00
mcfans
e474b595ad fix: add proguard rules, avoid protobuf generated classes got obfuscated 2023-11-06 01:04:53 +08:00
ClSlaid
b4c6292397 docs: file copy paste explainations
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-11-05 18:11:05 +08:00
crwusiz
4aa994827c kor_translation update 2023-11-05 10:12:45 +09:00
jxdv
a521ccde47 update cs.rs 2023-11-04 14:29:03 +01:00
RustDesk
03d280054b Merge pull request #6285 from jxdv/sk-rs-update
update sk.rs
2023-11-04 21:22:23 +08:00
jxdv
c0a3279856 update sk.rs 2023-11-04 14:12:10 +01:00
jxdv
e8643632bf translations 2023-11-04 13:24:23 +01:00
mcfans
6d8272472a fix: set correct flag 2023-11-04 20:24:15 +08:00
mcfans
409d5b124a fix: add page up/down keymap 2023-11-04 17:57:19 +08:00
RustDesk
0c1f3d056a Merge pull request #6279 from sahilyeole/feat/list_view
Fix: change view dropdown not working on macos clicking
2023-11-04 17:26:05 +08:00
Sahil Yeole
0e702d8693 remove setState
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-04 14:50:04 +05:30
mcfans
b155cd9a5a fix: set same rect for correct layout and navigation and set correct selection 2023-11-04 17:08:49 +08:00
RustDesk
6a281fb7ba Merge pull request #6266 from sahilyeole/feat/mobile_uni_link
Feat: mobile uni link support (android + ios)
2023-11-04 10:59:29 +08:00
Sahil Yeole
203891c957 fix change view dropdown not working on macos clicking
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-03 20:12:52 +05:30
Sahil Yeole
1365df898f support connect, play & file-transfer on mobile uri
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-03 18:31:30 +05:30
RustDesk
a2bc02b4c5 Merge pull request #6275 from 21pages/opt_codec
opt codec
2023-11-03 15:06:46 +08:00
21pages
0d01aa4eea opt close on taskbar, not call onWindowClose multi times
Signed-off-by: 21pages <pages21@163.com>
2023-11-03 11:52:43 +08:00
21pages
017c73132c condition show more quality
Signed-off-by: 21pages <pages21@163.com>
2023-11-03 11:52:43 +08:00
21pages
534bfad50f opt codec
1. use src width/height to convert yuv
2. align dst yuv to avoid illegal memory access
3. init yuvfmt when new codec
4. move remote reset calls from empty conns judge to emtpy remote conns
   judge

Signed-off-by: 21pages <pages21@163.com>
2023-11-03 11:52:41 +08:00
RustDesk
429f44eb6a Merge pull request #6274 from KagamineP/patch-2
Update ru.rs
2023-11-03 11:36:06 +08:00
Dmitry Kiryanov
346c88b661 Update ru.rs 2023-11-03 08:30:28 +05:00
mcfans
c49853e7b4 fix: set text before update accessibility node 2023-11-03 10:47:46 +08:00
RustDesk
11232c6f23 Merge pull request #6270 from dignow/fix/android_rotation
fix, android rotate screen
2023-11-03 10:34:54 +08:00
RustDesk
9aa788f086 Merge pull request #6271 from Kleofass/patch-3
Update lv.rs
2023-11-03 10:34:26 +08:00
RustDesk
c35268c493 Merge pull request #6267 from flusheDData/master
Update es.rs
2023-11-03 10:34:09 +08:00
dignow
df0ad4486e fix build
Signed-off-by: dignow <linlong1265@gmail.com>
2023-11-03 09:23:28 +08:00
Kleofass
d369385657 Update lv.rs 2023-11-03 02:25:01 +02:00
dignow
059e067bdc fix, android rotate screen
Signed-off-by: dignow <linlong1265@gmail.com>
2023-11-03 07:17:40 +08:00
Sahil Yeole
050759c1c2 improve code format
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-03 01:55:56 +05:30
Sahil Yeole
6d264b4394 remove extra line
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-03 01:51:40 +05:30
Sahil Yeole
284330ed5f use desktop uri format for mobile
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-03 01:46:51 +05:30
Sahil Yeole
49205b604c add android intent url scheme
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-02 23:57:31 +05:30
Sahil Yeole
c14c27970f cold boot uni link mobile
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-02 23:57:31 +05:30
Sahil Yeole
b1682d5794 listen and connect to uni link mobile
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-02 23:57:31 +05:30
Sahil Yeole
d0b9dd9ae7 add ios uni link scheme
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-02 23:57:31 +05:30
mcfans
0a94b7473d fix: use onKeyDown to dispatch key event 2023-11-03 01:50:21 +08:00
flusheDData
587a928b84 Update es.rs
New terms added
2023-11-02 16:15:36 +01:00
RustDesk
48dbc06b29 Merge pull request #6264 from fufesou/fix/win_crash_tokio_1.28.1_upgrade_1.33.0
fix, win crash, tokio 1.28.1 to 1.33.0, revert
2023-11-02 14:56:40 +08:00
fufesou
967515a34f fix, win crash, tokio 1.28.1 to 1.33.0, revert
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-02 14:34:49 +08:00
RustDesk
8e0bce4da4 Merge pull request #6263 from xalt7x/ukrainian-translation
Update Ukrainian translation
2023-11-02 14:07:12 +08:00
Yevhen Popok
01078bd7b2 Update Ukrainian translation 2023-11-02 08:05:39 +02:00
RustDesk
a97fbb2d48 Merge pull request #6260 from fufesou/fix/build_mobile
fix build
2023-11-02 10:07:49 +08:00
RustDesk
6782f92703 Merge pull request #6262 from rustdesk/revert-6257-android-fixes
Revert "Fixes for Android (ndk r26b)"
2023-11-02 09:50:34 +08:00
RustDesk
9096c29fef Revert "Fixes for Android (ndk r26b)" 2023-11-02 09:50:02 +08:00
mcfans
100967c57b fix: set focusable before request focus 2023-11-02 00:37:21 +08:00
fufesou
c871af2711 fix build
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-01 21:03:57 +08:00
RustDesk
faf99ffe14 Merge pull request #6258 from fufesou/fix/remove_unused_capturer_on_peer
fix, remove unused capturer when switching display
2023-11-01 20:06:12 +08:00
fufesou
58d073b516 fix, remove unused capturer when switching display
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-01 19:53:37 +08:00
RustDesk
52a4d41c6f Merge pull request #6257 from basilgello/android-fixes
Fixes for Android (ndk r26b)
2023-11-01 18:36:50 +08:00
Vasyl Gello
0805b00c50 Fix build on android-x86 and rename function
... because we add ndk_compat and the function installs
android-specific dependencies.

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-01 11:41:17 +02:00
Vasyl Gello
c8f8bfd6f0 Refactor build_android_deps.sh
* Allow building one architecture or all 4 architectures
* Fix paths for ndk r25c+
* Bump libvpx to 1.13.1 to fix CVE-2023-5238
* Add ndk-compat from cpu-features to address missing
  android_getCpuFeatures symbol on android arm64 emulators

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2023-11-01 11:36:10 +02:00
RustDesk
1f51d37ae6 Merge pull request #6256 from 21pages/android_ci
Revert "fix android ci, replace use with try-finally"
2023-11-01 17:22:33 +08:00
rustdesk
f8a4b8de51 fix ci 2023-11-01 17:14:44 +08:00
rustdesk
eaa64d7b70 bump bridge to 1.80.1 2023-11-01 17:04:32 +08:00
21pages
4480fbf787 Revert "fix android ci, replace use with try-finally"
This reverts commit dfe96eb30c.
2023-11-01 16:38:20 +08:00
rustdesk
c2287033e3 XXPermissions:18.5 2023-11-01 16:31:37 +08:00
rustdesk
0a0fb5287a dump kotlin 2023-11-01 15:52:07 +08:00
rustdesk
52acbd7d2c fix ios ci 2023-11-01 15:45:48 +08:00
rustdesk
af4f84a84b fix ios ci 2023-11-01 15:16:33 +08:00
RustDesk
633241fcee Merge pull request #6253 from 21pages/upgrade_desktop_drop
upgrade desktop_drop to fix windows crash on close on flutter 3.13
2023-11-01 15:14:11 +08:00
21pages
f1f3d288cd upgrade desktop_drop to fix windows close on crash on flutter 3.13
https://github.com/MixinNetwork/flutter-plugins/pull/276

Signed-off-by: 21pages <pages21@163.com>
2023-11-01 15:09:02 +08:00
RustDesk
7c98da85a0 Merge pull request #6251 from 21pages/translate
opt "True color" translation
2023-11-01 12:00:51 +08:00
21pages
36f7d64352 opt "True color" translation
Signed-off-by: 21pages <pages21@163.com>
2023-11-01 11:46:51 +08:00
RustDesk
a4bbcbe5ff Merge pull request #6246 from Mr-Update/patch-2
Update de.rs
2023-11-01 10:56:29 +08:00
RustDesk
cbb4ec5aa1 Merge pull request #6244 from bovirus/master
Update Italian language
2023-11-01 10:55:29 +08:00
RustDesk
84fc5f7d67 Merge pull request #6247 from sahilyeole/feat/list_view
Fix small tiles view not restored
2023-11-01 10:54:52 +08:00
RustDesk
a4d84cdc21 Merge pull request #6249 from fufesou/refact/pubspec_flutter_rust_bridge
fix flutter_rust_bridge pub version
2023-11-01 10:54:25 +08:00
RustDesk
1a422f318c Merge pull request #6250 from 21pages/android_ci
fix android ci, replace use with try-finally
2023-11-01 10:54:01 +08:00
fufesou
8f202fd70d fix flutter_rust_bridge pub version
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-11-01 10:08:35 +08:00
21pages
dfe96eb30c fix android ci, replace use with try-finally
Signed-off-by: 21pages <pages21@163.com>
2023-11-01 09:22:49 +08:00
Sahil Yeole
3d7c6c14b3 fix remember list view
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-11-01 00:59:28 +05:30
Mr-Update
85a4e361d2 Update de.rs 2023-10-31 20:10:49 +01:00
bovirus
05b2fa97d3 Update Italian language 2023-10-31 19:09:39 +01:00
mcfans
47d57ddf70 fix: set focus and FLAG_RETRIEVE_INTERACTIVE_WINDOWS 2023-11-01 00:06:45 +08:00
mcfans
9521ac6adb chore: add some log 2023-10-31 21:14:32 +08:00
rustdesk
c2703d215b bump flutter to 3.13.9 and bridge to 1.79 2023-10-31 21:10:23 +08:00
RustDesk
f5c1133fc5 Merge pull request #6243 from fufesou/fix/remote_toolbar_border_radius
fix, remote toolbar, border radius
2023-10-31 19:27:39 +08:00
fufesou
f63d81826a fix, remote toolbar, border radius
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-31 19:15:13 +08:00
RustDesk
fa39982a8f Merge pull request #6242 from fufesou/refact/remote_toolbar_border
refact, remote toolbar, border
2023-10-31 17:09:21 +08:00
fufesou
4b52414e03 use get instead of static value
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-31 16:58:20 +08:00
fufesou
29b0a7659f refact, remote toolbar, border
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-31 16:52:18 +08:00
RustDesk
e69183ce12 Merge pull request #6239 from fufesou/refact/remote_toolbar_monitors_menu
refact, remote toolbar, monitors menu
2023-10-31 15:32:50 +08:00
fufesou
009c088a64 change active monitor style
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-31 15:14:43 +08:00
fufesou
2c1f948832 refact, remote toolbar, monitors menu
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-31 14:49:01 +08:00
RustDesk
b83e007405 Merge pull request #6238 from fufesou/fix/peer_info_changed_unexpected
fix, flutter, peer info event may be changed accidentally
2023-10-31 11:07:34 +08:00
fufesou
1d3ce2c029 fix, flutter, peer info event may be changed accidentally
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-31 11:03:35 +08:00
rustdesk
5ad0730a26 remove gen_flutter_rust_bridge in build.rs 2023-10-31 11:00:13 +08:00
RustDesk
52f1383903 Merge pull request #6237 from fufesou/fix/virtual_display_reset
fix, virtual dislay reset
2023-10-31 10:31:08 +08:00
RustDesk
12b8cbf3e0 Merge pull request #6229 from 21pages/444
yuv 444
2023-10-31 10:08:59 +08:00
fufesou
de44f8565d fix, virtual dislay reset
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-31 08:19:21 +08:00
RustDesk
7480ead76a Merge pull request #6227 from ClSlaid/feat/osx/pasteboard-file
[feat] osx pasteboard file copy and paste support
2023-10-30 22:52:35 +08:00
RustDesk
e96ae7a650 Merge pull request #6233 from fufesou/refact/displays_arrangement
Refact/displays arrangement
2023-10-30 22:39:50 +08:00
ClSlaid
ae524c4d0e patch: avoid compile x11 deps on mac
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-30 22:14:57 +08:00
fufesou
5adce88c37 fix, virtual display bugs
1. Clear the map on reset.
2. Replace `containsKey` to `contains` in flutter.

Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-30 21:40:13 +08:00
fufesou
06dda24431 refact, show global displays' arrangement
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-30 21:40:13 +08:00
rustdesk
fa046df923 add controlRight to file manager 2023-10-30 21:20:55 +08:00
rustdesk
2432eb39b1 fix ci 2023-10-30 21:04:33 +08:00
rustdesk
c211a2517f fix ci 2023-10-30 20:51:50 +08:00
rustdesk
73ea0a57a0 fix https://github.com/rustdesk/rustdesk/issues/6232 2023-10-30 20:50:32 +08:00
21pages
f05f86dc80 444
Signed-off-by: 21pages <pages21@163.com>
2023-10-30 18:46:56 +08:00
ClSlaid
e44b25c80f Merge branch 'rustdesk:master' into feat/osx/pasteboard-file 2023-10-30 17:55:29 +08:00
ClSlaid
bc3acc2826 patch: fix local file test
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-30 17:52:38 +08:00
Integral
007fb34ca5 Update cn.rs 2023-10-30 17:52:28 +08:00
RustDesk
46a363cce4 Merge pull request #5638 from ClSlaid/feat/x11/clipboard-file/init
[feat] x11 clipboard copy and paste file
2023-10-30 17:31:48 +08:00
RustDesk
3047ce57a3 Merge pull request #6222 from Integral-Tech/master
Update cn.rs
2023-10-30 15:41:52 +08:00
ClSlaid
7b9ce072d9 patch: fix local file test
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-30 15:41:39 +08:00
Integral
4d3e3f3aed Update cn.rs 2023-10-30 15:39:09 +08:00
ClSlaid
62563ad8a1 Merge branch 'feat/x11/clipboard-file/init' into feat/osx/clipboard-file
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-30 15:34:40 +08:00
mcfans
6fdce63359 fix: unified keyboard check logic in common.rs 2023-10-30 15:34:01 +08:00
ClSlaid
fdc4d6dda9 patch: remove redundant features
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-30 15:11:33 +08:00
RustDesk
a3b06ee83f Merge branch 'master' into feat/x11/clipboard-file/init 2023-10-30 14:57:36 +08:00
ClSlaid
43aa62e212 patch: fix active enable of file copy paste
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-30 12:00:44 +08:00
mcfans
70dd3f323e fix: update text selection for API level lower than 33 2023-10-30 07:46:29 +08:00
ClSlaid
803509d952 patch: add feature to build script
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-30 00:06:53 +08:00
ClSlaid
30e85c8654 patch: make linux build ok
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 23:42:04 +08:00
mcfans
7b24835c9e Merge remote-tracking branch 'upstream/master'
# Conflicts:
#	src/server/connection.rs
2023-10-29 23:32:43 +08:00
ClSlaid
7aee76f5de patch: don't show enable file clipboard when anyone unsupporting
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 23:11:30 +08:00
ClSlaid
80200a9983 patch: add has_file_clipboard field to PeerInfo
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 20:49:43 +08:00
ClSlaid
79f6b5c181 patch: forbid enable cliprdr without feature
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 20:10:39 +08:00
ClSlaid
dc02ce3f97 patch: only enable file copy and paste under features
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 19:50:31 +08:00
RustDesk
845d5a548f Merge pull request #6219 from bovirus/master
Update Italian language
2023-10-29 16:48:17 +08:00
RustDesk
14a8cf69ef Merge pull request #6215 from BestiaPL/master
Update pl.rs
2023-10-29 16:47:32 +08:00
RustDesk
c8a6e8005e Merge pull request #6213 from Mr-Update/patch-1
Update de.rs
2023-10-29 16:47:15 +08:00
RustDesk
e6bb2bfaae Merge pull request #6211 from jxd1337/py-improvements
feat: PEP8 improvements
2023-10-29 16:47:03 +08:00
RustDesk
8ba2d1cf72 Merge pull request #6210 from solokot/master
Update ru.rs
2023-10-29 16:45:53 +08:00
bovirus
a168fc3719 Update Italian language 2023-10-29 08:57:49 +01:00
ClSlaid
fd1dc15576 patch: re apply time limited api in common clipboard
when no content in text clipboard, it will wait forever

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 12:07:21 +08:00
ClSlaid
0b82874a52 patch: lossen x11 clipboard timeout
this should make the clipboard reading more stable

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 11:45:00 +08:00
ClSlaid
434242858f patch: implement statfs to improve OSX paste
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 07:43:10 +08:00
ClSlaid
3dfa0525bd feat: implement OSX file copy & paste
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 07:15:56 +08:00
ClSlaid
36d4baaa8e patch: fix macos clipboard
1. wrong namings of NsPasteboard
2. wrap Pasteboard in Lazy

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 03:53:06 +08:00
ClSlaid
7880cba0f9 feat: enable macos cliprdr
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 02:40:56 +08:00
Andrzej Rudnik
f8d64528b5 Update pl.rs 2023-10-28 20:19:50 +02:00
Mr-Update
f6ee61f29e Update de.rs 2023-10-28 20:19:17 +02:00
ClSlaid
2bb1310094 patch(0): implement cliprdr for macos
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-29 02:15:16 +08:00
ClSlaid
f6a137cd43 patch: make BufReader preload its buffer
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-28 23:25:30 +08:00
ClSlaid
a575fe4934 refactor: reload file hierarchies
rename libs/src/platform/{linux => unix}

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-28 22:44:10 +08:00
ClSlaid
4cd8d8a4a5 patch: faster preload with BufReader
1. seek avoided with self maintained offset
2. BufReader to read faster

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-28 22:33:51 +08:00
ClSlaid
7a802726fb fix: implement lazy reading to prevent avoid fd limit
drawback: through put reduced to 50%

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-28 20:52:08 +08:00
ClSlaid
251245d315 Merge remote-tracking branch 'rd/master' into feat/x11/clipboard-file/init
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-28 19:56:19 +08:00
ClSlaid
fbf5a84c4a patch: make log less verbose
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-28 17:00:29 +08:00
jxd1337
55412b94d2 assert without redundant () 2023-10-28 10:59:33 +02:00
jxd1337
f61fd02ac7 build.py, generate.py improvements 2023-10-28 10:57:50 +02:00
jxd1337
dc96b473cd lang.py PEP8 improvements 2023-10-28 10:50:39 +02:00
ClSlaid
abe40c84b0 patch(cliprdr): avoid too much open
1. force sync local files when processing file list format data request
2. avoid construct file list each time pulling file lists from clipboard

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-28 16:27:02 +08:00
ClSlaid
9976fc9723 fix: keep clipboard alive
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-28 15:31:12 +08:00
solokot
f8092f924a Update ru.rs 2023-10-28 06:29:50 +03:00
ClSlaid
075a877284 patch: increase FUSE block size and add retry
1. this should make file managers read small file in one request more
   likely
2. implemented retry, max times 3

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-28 11:14:16 +08:00
RustDesk
f748395bda Merge pull request #6208 from fufesou/fix/prompt_driver_cert_on_disconn
fix, prompt, driver cert , on disconnecting
2023-10-28 10:48:54 +08:00
RustDesk
12018360fd Merge pull request #6207 from cacing69/master
improve ID trans
2023-10-28 10:48:04 +08:00
RustDesk
821b2fda85 Merge pull request #6205 from Kleofass/patch-2
Update lv.rs
2023-10-28 10:47:48 +08:00
RustDesk
4cb2b29187 Merge pull request #6203 from leroyloren/master
Update cs.rs
2023-10-28 10:47:32 +08:00
fufesou
bb59778313 fix, prompt, driver cert , on disconnecting
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-28 10:42:31 +08:00
ClSlaid
ed0ded33b7 patch: fix arboard pollution
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-28 09:55:34 +08:00
Ibnul Mutaki
db19528c24 improve ID trans 2023-10-28 07:46:53 +07:00
Kleofass
7d1fb0a238 Update lv.rs 2023-10-28 02:09:39 +03:00
leroyloren
11956d9e16 Update cs.rs 2023-10-27 20:26:50 +02:00
RustDesk
cef782c388 Merge pull request #6199 from fufesou/feat/windows_virtual_displays
feat, win virtual display
2023-10-28 02:03:24 +08:00
RustDesk
e87e371c14 Merge branch 'master' into feat/windows_virtual_displays 2023-10-28 02:03:16 +08:00
RustDesk
877a455ae0 Merge pull request #6201 from Kleofass/patch-1
Update lv.rs
2023-10-28 01:56:00 +08:00
RustDesk
a47618e986 Merge pull request #6202 from jxd1337/cz-translation-improvements
Update CZ translation
2023-10-28 01:55:37 +08:00
jxd1337
2bbd759c25 Update CZ translation 2023-10-27 19:01:30 +02:00
Kleofass
4dd19884e0 Update lv.rs 2023-10-27 16:06:32 +03:00
fufesou
5ab0f499ce fix build
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-27 20:57:35 +08:00
ClSlaid
053723647b patch: try fix flutter file copy
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-27 20:40:23 +08:00
fufesou
725a44abd8 feat, win virtual display
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-27 20:12:33 +08:00
RustDesk
625f2d2410 Merge pull request #6195 from fufesou/fix/autocomplete
fix, autocomplete, fill id field
2023-10-27 17:06:16 +08:00
fufesou
e32748daf2 fix, autocomplete, fill id field
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-27 17:00:13 +08:00
RustDesk
8a2bd1cac3 Merge pull request #6194 from fufesou/fix/comments_infinite_loop
add comemnt
2023-10-27 16:45:57 +08:00
mcfans
9076f213e6 fix: hide map mode if peer is android 2023-10-27 16:43:50 +08:00
mcfans
e77edc56fd chore: update lock 2023-10-27 16:42:24 +08:00
fufesou
de356304c7 add comemnt
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-27 15:58:54 +08:00
RustDesk
8cc9e30f86 Merge pull request #6191 from Mr-Update/patch-4
Update de.rs
2023-10-27 15:11:12 +08:00
RustDesk
384323031b Merge pull request #6193 from fufesou/fix/ui_infinite_loop
fix, flutter, infinite loop
2023-10-27 15:09:52 +08:00
fufesou
b40a7b24d4 fix, flutter, infinite loop
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-27 15:01:20 +08:00
mcfans
67b2a433a8 fix: use enhanced accessibilty node find method 2023-10-27 13:18:35 +08:00
mcfans
07bdf02af4 chore: use updated old repo 2023-10-27 13:17:49 +08:00
Mr-Update
35b470dbac Update de.rs 2023-10-26 23:57:16 +02:00
RustDesk
42eb49b84c Merge pull request #6188 from leroyloren/master
Update cs.rs
2023-10-26 19:21:37 +08:00
leroyloren
9d3d11755f Update cs.rs 2023-10-26 13:19:34 +02:00
leroyloren
0fbc546696 Update cs.rs 2023-10-26 13:18:19 +02:00
RustDesk
220422ba0d Merge pull request #6187 from bovirus/master
Update Italian language
2023-10-26 18:59:27 +08:00
ClSlaid
2c9bae8111 Merge remote-tracking branch 'rd/master' into feat/x11/clipboard-file/init
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-26 18:16:08 +08:00
bovirus
320f6ddc6e Update Italian language 2023-10-26 12:05:59 +02:00
RustDesk
0da6d51150 Merge pull request #6186 from fufesou/fix/dialog_block_tab_on_remote_page
fix comments
2023-10-26 17:27:08 +08:00
RustDesk
65e3170cd9 Merge pull request #6185 from 21pages/taskbar_close
fix close on taskbar
2023-10-26 17:25:22 +08:00
fufesou
ee3750121c fix comments
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-26 17:04:09 +08:00
RustDesk
9736a4ddd0 Merge pull request #6184 from fufesou/fix/dialog_block_tab_on_remote_page
fix, dialog blocks tabs on the remote page
2023-10-26 16:57:17 +08:00
21pages
53e310ff77 fix close on taskbar
Signed-off-by: 21pages <pages21@163.com>
2023-10-26 16:56:30 +08:00
fufesou
17285720f1 fix, dialog blocks tabs on the remote page
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-26 16:52:54 +08:00
RustDesk
aa690cb9ab Merge pull request #6182 from solokot/master
Update ru.rs
2023-10-26 13:12:23 +08:00
RustDesk
6d3596087a Merge branch 'master' into master 2023-10-26 13:11:59 +08:00
rustdesk
0fe80e8e1f unify text style 2023-10-26 13:09:19 +08:00
solokot
f853f20b0c Update ru.rs 2023-10-26 07:54:13 +03:00
RustDesk
39e6fa35e1 Merge pull request #6165 from sahilyeole/feat/list_view
Feat single peer per row/list view
2023-10-26 11:59:59 +08:00
Sahil Yeole
23b911297e add langs for change peer view menu
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-26 05:39:15 +05:30
Sahil Yeole
6113d1e3eb Merge branch 'rustdesk:master' into feat/list_view 2023-10-26 05:32:00 +05:30
Sahil Yeole
55f3b93958 fix peer view menu position
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-26 05:25:54 +05:30
Sahil Yeole
c522987b6f fix list view on ab when id panel
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-26 05:16:27 +05:30
Sahil Yeole
8fe64755ec fix icon ui not updating
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-26 04:46:24 +05:30
Sahil Yeole
cc35328f28 add dropdown for peer view types
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-26 04:02:34 +05:30
RustDesk
a824a7fb73 Merge pull request #6168 from fufesou/fix/check_keyboard_mode_on_conn
fix, check session's keyboard mode on conn
2023-10-25 16:22:47 +08:00
RustDesk
075d7e52df Merge pull request #6173 from flusheDData/master
Update es.rs
2023-10-25 16:22:06 +08:00
flusheDData
cadbae31e4 Update es.rs
selinux_tip translation
2023-10-25 10:19:17 +02:00
RustDesk
39b5a67040 Update README.md 2023-10-25 13:19:37 +08:00
RustDesk
30580b2c57 Update README.md 2023-10-25 13:15:15 +08:00
RustDesk
00ab830ad1 Merge pull request #6166 from sahilyeole/feat/autocomplete
Fix ID text field ui not updating on peer card connection
2023-10-25 11:07:25 +08:00
fufesou
fcf3577f67 fix, check session's keyboard mode on conn
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-25 09:20:51 +08:00
Sahil Yeole
2cdfeb8dd0 fix id field ui not updating on peer card connection
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-25 02:36:23 +05:30
Sahil Yeole
ce300aa75f remove unused onSubmitted
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-25 02:05:17 +05:30
Sahil Yeole
a52caaec75 fix list view for group tab
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-25 01:26:41 +05:30
Sahil Yeole
3f3489b292 update peer view type tooltip
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-24 23:19:43 +05:30
Sahil Yeole
1e059c5649 update peer view type icons
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-24 23:15:42 +05:30
Sahil Yeole
d9e1b2df7f update peer view type options
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-24 22:33:51 +05:30
Sahil Yeole
94e51a8041 update enum for list view
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-24 22:14:59 +05:30
Sahil Yeole
81fe90f605 add ui for new list view
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-24 22:14:01 +05:30
RustDesk
69f643447d Merge pull request #6158 from solokot/master
Update ru.rs
2023-10-24 14:38:09 +08:00
solokot
d8355371e3 Update ru.rs 2023-10-24 08:42:17 +03:00
mcfans
cbe9b9c455 fix: hide map mode if peer is android 2023-10-24 13:36:33 +08:00
mcfans
dd33c0e582 fix: use seq for translate mode and use commitText 2023-10-24 13:34:52 +08:00
RustDesk
009b0aa361 Merge pull request #6156 from dignow/fix/win_install_cert
fix, win, install cert
2023-10-24 11:49:25 +08:00
dignow
f438176544 fix potential crash when uninstalling cert
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-24 11:41:01 +08:00
dignow
be8e8a0521 fix, win, install cert
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-24 10:51:01 +08:00
RustDesk
1a8b31bdb4 Merge pull request #6137 from sahilyeole/feat/autocomplete_mobile
Feat autocomplete mobile
2023-10-24 08:55:14 +08:00
RustDesk
8a47055273 Merge pull request #6155 from bovirus/master
Update Italian language
2023-10-24 08:53:59 +08:00
Sahil Yeole
feb6f7930e common code autocomplete
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-24 05:30:43 +05:30
bovirus
0271ea7dad Update Italian language 2023-10-23 17:19:05 +02:00
ClSlaid
505b73d20e patch: try fix flutter paste
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-23 20:17:36 +08:00
RustDesk
dcad8a9f79 Merge pull request #6150 from 21pages/hwcodec_check5
hwcodec check wait more time
2023-10-23 18:28:37 +08:00
21pages
9ce58115ab set timeout of hwcodec check to 30s
Signed-off-by: 21pages <pages21@163.com>
2023-10-23 16:12:49 +08:00
21pages
1c9d139ff5 opt android get_home, bad code get corrent result
Signed-off-by: 21pages <pages21@163.com>
2023-10-23 16:11:59 +08:00
Sahil Yeole
70794c8eb5 Merge branch 'master' into feat/autocomplete_mobile 2023-10-23 05:28:15 +05:30
Sahil Yeole
c90c4a2e78 fix empty peers desktop
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-23 05:03:44 +05:30
Sahil Yeole
6797e8af52 improve maxHeight desktop
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-23 05:01:39 +05:30
Sahil Yeole
5109802dfb update id on onchaged
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-23 04:17:28 +05:30
Sahil Yeole
935297b9e8 fix empty peers
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-23 04:03:05 +05:30
Sahil Yeole
dad209d1cd feat autocomplete mobile
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-23 03:21:54 +05:30
RustDesk
e1072cc8ca Merge pull request #6134 from Kleofass/patch-3
Update lv.rs
2023-10-22 23:06:37 +08:00
RustDesk
1a1ba80188 Merge pull request #6133 from fufesou/fix/android_physical_mouse_click_through_bottom_bar
fix, android physical mouse, click events, penetrated
2023-10-22 22:52:05 +08:00
Kleofass
c878c91e11 Update lv.rs 2023-10-22 16:51:38 +03:00
fufesou
c518521d74 fix, android physical mouse, click events, penetrated
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-22 20:35:43 +08:00
ClSlaid
ce9c9078e5 patch: fix copy to nautilus
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-22 19:51:17 +08:00
RustDesk
9fe525bca1 Merge pull request #6131 from Mr-Update/patch-3
Update de.rs
2023-10-22 15:55:50 +08:00
Mr-Update
352865ddaa Update de.rs 2023-10-22 09:50:27 +02:00
RustDesk
bc950ee40a Merge pull request #6128 from fufesou/feat/selinux_tip
add selinux tip
2023-10-22 14:42:05 +08:00
fufesou
5c23dfd633 selinux tip, remove rust dep 'selinux'
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-22 14:22:15 +08:00
RustDesk
4633a0450c Update cn.rs 2023-10-22 13:06:17 +08:00
RustDesk
842255766f Update en.rs 2023-10-22 13:05:37 +08:00
fufesou
827c32fafd add deps
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-22 10:56:41 +08:00
fufesou
f531cd23ee add selinux tip
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-21 23:24:54 +08:00
RustDesk
2408758360 Merge pull request #6125 from 21pages/reconnect
show reconnect timeout and dismiss all dialog when show reconnecting
2023-10-21 16:14:39 +08:00
ClSlaid
4c792f6f17 test: check x11 get clipboard
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-21 16:07:01 +08:00
ClSlaid
802ab90d87 patch: fix duplicated directory problem
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-21 15:54:40 +08:00
21pages
c268a0ab14 show reconnect timeout and dismiss all dialog when show reconnecting
Signed-off-by: 21pages <pages21@163.com>
2023-10-21 15:25:01 +08:00
RustDesk
b12c7f21b3 Merge pull request #6123 from dignow/fix/is_x11_on_conn
fix is x11, on conn
2023-10-21 14:59:28 +08:00
dignow
b3948910ff fix is x11, on conn
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-21 13:10:51 +08:00
RustDesk
9ee1261204 Merge pull request #6119 from leroyloren/master
Update cs.rs
2023-10-21 11:51:55 +08:00
ClSlaid
8e3aa0e9ce patch: less verbose debug logs
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-20 23:15:53 +08:00
ClSlaid
db62a01224 patch: fix dead lock in file transfer
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-20 22:27:39 +08:00
mcfans
d900f2c47c fix: add old code back for old version 2023-10-20 21:12:02 +08:00
leroyloren
82e1fe3f8b Update cs.rs 2023-10-20 14:34:12 +02:00
leroyloren
85eb82e69d Update cs.rs 2023-10-20 14:31:49 +02:00
ClSlaid
c529f8099d patch: fix FUSE permission
1. fuse flag check made wrong, fix it
2. but still mount will RO flag

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-20 19:21:40 +08:00
rustdesk
aa3c58917b remove wayland related from readme 2023-10-20 19:09:13 +08:00
RustDesk
bc591a2399 Merge pull request #6103 from flusheDData/master
Update es.rs
2023-10-20 18:40:49 +08:00
RustDesk
11d996bb56 Merge pull request #6115 from FastAct/patch-20
Update nl.rs
2023-10-20 18:40:36 +08:00
RustDesk
088a78455c Merge pull request #6104 from Mr-Update/patch-2
Update de.rs
2023-10-20 18:36:02 +08:00
Mr-Update
869d9d487b Update de.rs 2023-10-20 12:06:04 +02:00
FastAct
db3978fd4e Update nl.rs 2023-10-20 11:24:55 +02:00
RustDesk
ae7e46fe5e Merge pull request #6106 from nfsec/master
Dockerfile refactor
2023-10-20 14:05:29 +08:00
RustDesk
4bd85e1804 Merge pull request #6109 from dignow/fix/macos_close_sesions
Fix/macos close sesions
2023-10-20 14:02:45 +08:00
RustDesk
01f4434f6b Merge pull request #6107 from cacing69/master
improve ID trans
2023-10-20 14:01:12 +08:00
RustDesk
d8f808bf38 Update es.rs 2023-10-20 13:59:48 +08:00
RustDesk
807dc7d220 Merge pull request #6093 from sahilyeole/fix/remote_home_button
Fix remote home button in file transfer
2023-10-20 13:52:22 +08:00
RustDesk
b14d0c9f60 Merge pull request #6040 from sahilyeole/feat/autocomplete
Feat autocomplete/search while typing in remote ID field
2023-10-20 13:49:52 +08:00
dignow
738a1a330c change comment
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-20 09:17:10 +08:00
dignow
676b02c8de fix, macos, close sessions, confirm dialog and then hide
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-20 09:15:53 +08:00
Sahil Yeole
7136400a33 remove scheduler import
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-20 05:11:07 +05:30
Sahil Yeole
4651d9df68 fix textToFind
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-20 04:50:07 +05:30
Sahil Yeole
00555a8e9e fix text selection
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-20 04:44:53 +05:30
Sahil Yeole
e8c4615ff6 improve text selection
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-20 04:34:50 +05:30
Ibnul Mutaki
763467058b improve ID trans 2023-10-20 04:33:16 +07:00
Patryk Krawaczyński
c055ba2985 Update Dockerfile 2023-10-19 23:19:34 +02:00
Patryk Krawaczyński
7666541905 Rename entrypoint to entrypoint.sh
Rename entrypoint to entrypoint.sh which is correct with shebang of the file.
2023-10-19 23:13:00 +02:00
Sahil Yeole
f4b0b39beb show id if alias exists
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-20 02:40:54 +05:30
Sahil Yeole
cfc0925e75 reduce toLowerCase
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-20 01:35:54 +05:30
Mr-Update
4dcc368378 Update de.rs 2023-10-19 20:50:08 +02:00
Sahil Yeole
8207908d9e fix remote home button
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-20 00:14:14 +05:30
Sahil Yeole
e08da096dd remove home dir peer option
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-19 23:45:48 +05:30
mcfans
9a903a1ca3 feat: use current input method to send key 2023-10-20 00:32:59 +08:00
flusheDData
1b22bf0e08 Update es.rs
New terms added
2023-10-19 18:32:06 +02:00
ClSlaid
fc3187a781 feat: extend file list PDU to transfer UNIX PERM
1. used 4 bytes out of a reserved 16 bytes section to store perm u32

2. add FLAGS_FD_UNIX_MODE: u32 = 0x08, used with flags, indicating this
   message is from UNIX peer

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-20 00:23:16 +08:00
ClSlaid
7fbb4045e2 patch: fix FUSE load file
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-19 22:35:14 +08:00
ClSlaid
d0dc22794e patch: fix file list parsing
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-19 20:01:44 +08:00
RustDesk
0f235a80e0 Merge pull request #6099 from solokot/master
Update ru.rs
2023-10-19 19:52:08 +08:00
solokot
2a28046382 Update ru.rs 2023-10-19 14:44:42 +03:00
ClSlaid
169bbfd2db patch: fix clipboard local format map
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-19 17:53:05 +08:00
ClSlaid
1f52bb35ba patch: fix server file_transfer not enabled
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-19 17:20:44 +08:00
RustDesk
4a03b3d7d9 Merge pull request #6084 from 21pages/av1_record
AV1 record
2023-10-19 14:23:35 +08:00
RustDesk
2544a7e4ea Merge pull request #6094 from dignow/fix/open_individual_window_offset
open individual window, add offset
2023-10-19 14:22:35 +08:00
mcfans
26e77ba2c3 feat: modifier key 2023-10-19 14:19:19 +08:00
mcfans
28c11801f3 feat: keyboard map mode control side 2023-10-19 13:40:12 +08:00
mcfans
b3e2ab0f3b feat: map mode and translate mode receive side 2023-10-19 13:33:26 +08:00
dignow
fb12ba8a2b Change the offset depending on the platform
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-19 12:36:21 +08:00
21pages
7a5bc864fa fix client side record
Signed-off-by: 21pages <pages21@163.com>
2023-10-19 09:50:55 +08:00
dignow
2e85d4b55a add comment
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-19 07:55:55 +08:00
dignow
80951a8e6e open individual window, add offset
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-19 07:50:59 +08:00
Sahil Yeole
9acddede65 fix remote home button
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-19 04:29:51 +05:30
mcfans
22165ec1a5 feat: legacy mode android keyboard support 2023-10-19 00:16:22 +08:00
RustDesk
d7036aae48 Merge pull request #6086 from dignow/fix/try_video_capturing_on_close_window
try stop video capturing, on close window
2023-10-18 23:15:28 +08:00
dignow
fe3924b432 try stop video capturing, on close window
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-18 20:50:20 +08:00
21pages
510cffb305 av1 record, set zero codec private
Signed-off-by: 21pages <pages21@163.com>
2023-10-18 19:28:12 +08:00
Sahil Yeole
05c789ae50 remove fav peers from search
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-18 16:00:50 +05:30
Sahil Yeole
5d95d61aef remove fav peers from search
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-18 14:40:29 +05:30
21pages
c61fa71a70 Revert "hide recording button if using av1"
This reverts commit c2023e8ca3.
2023-10-18 13:48:12 +08:00
RustDesk
3145269f65 Merge pull request #6081 from xalt7x/ukrainian-translation
Update Ukrainian translation
2023-10-18 13:31:33 +08:00
RustDesk
2d00a1e265 Merge pull request #6080 from xalt7x/fix/eng-typos
Fix typos regarding displays
2023-10-18 13:30:08 +08:00
RustDesk
f7f178d6e3 Merge pull request #6079 from dignow/fix/change_display_resolution
Fix/change display resolution
2023-10-18 13:29:49 +08:00
RustDesk
18c591e0d0 Merge pull request #6078 from Kleofass/patch-2
Update lv.rs
2023-10-18 13:27:19 +08:00
xalt7x.service
749241f4e5 Fix typos regarding displays 2023-10-18 07:13:48 +03:00
xalt7x.service
997c368604 Update Ukrainian translation 2023-10-18 07:09:56 +03:00
dignow
ed28928c84 fix build linux
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-18 12:06:55 +08:00
dignow
b55c916e77 add comments
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-18 11:39:05 +08:00
dignow
4d537b2a9a fix/change_display_resolution, send change resolution message
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-18 11:35:28 +08:00
dignow
21f7d6c9b9 fix/change_display_resolution, send switch display msg
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-18 10:45:46 +08:00
dignow
c1b865d00e fix, change_display_resolution, add comments
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-18 09:59:02 +08:00
dignow
a32e740242 fix build linux
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-18 08:07:10 +08:00
dignow
c4f09b5598 fix, change display resolution
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-18 07:44:12 +08:00
Kleofass
5a89aa3b32 Update lv.rs 2023-10-17 23:58:54 +03:00
Sahil Yeole
149d57150c attempt fix text selection
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-17 21:29:05 +05:30
mcfans
bbc241748b feat: support android keyboard input 2023-10-17 22:12:52 +08:00
RustDesk
74ecea6307 Merge pull request #6076 from 21pages/android_log_ext
android write rust log to ExternalStorage/RustDesk/Logs
2023-10-17 21:41:41 +08:00
21pages
2e829956f4 android write rust log to ExternalStorage/RustDesk/Logs
Signed-off-by: 21pages <pages21@163.com>
2023-10-17 21:30:48 +08:00
Sahil Yeole
9e2c9cbba9 add border radius
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-17 18:57:53 +05:30
Sahil Yeole
1ad740800b remove spaces only if number
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-17 18:35:38 +05:30
Sahil Yeole
c00d4c1a7b update _fetchPeers
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-17 18:23:44 +05:30
Sahil Yeole
e0985ebb1c add peers loading indicator
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-17 18:22:27 +05:30
RustDesk
d5706442de Merge pull request #6075 from bovirus/master
Update Italian language
2023-10-17 20:43:25 +08:00
bovirus
ad5a173f3f Update Italian language 2023-10-17 14:39:55 +02:00
ClSlaid
fbb1d9247f patch: reduce logic in Fuse and SystemClipboard
1. also added more observability

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-17 16:57:55 +08:00
RustDesk
63591941b8 Merge pull request #6064 from dignow/feat/open_multi_windows
Feat/open multi windows
2023-10-17 15:39:22 +08:00
dignow
fdfeec54d7 Remove the unnecessary setState, as RxBool is used
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-17 14:42:35 +08:00
dignow
b2404809fc trivial, add final
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-17 14:31:04 +08:00
dignow
f9f463e799 fix, use RxBool to sync fullscreen state (remote toolbar)
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-17 14:29:14 +08:00
dignow
f1d3a553d1 open multi windows, add remote toolbar option
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-17 13:57:06 +08:00
dignow
e997b148e1 Change option text
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-17 13:16:53 +08:00
dignow
af906fac03 debug, open multi windows
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-17 11:11:45 +08:00
dignow
19945df0b3 open_multi_windows, check before move and set fullscreen
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-17 11:11:44 +08:00
dignow
bf83d552f8 feat, open multi windows
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-17 11:11:42 +08:00
RustDesk
f5d8e99fc7 Merge pull request #6063 from cacing69/master
improve ID lang trans
2023-10-17 11:06:25 +08:00
RustDesk
975037c5a0 Merge pull request #6061 from dignow/fix/remove_warns_misspelling
fix, remove warns, misspellings
2023-10-17 11:06:01 +08:00
Ibnul Mutaki
8b0b45e089 improve ID lang trans 2023-10-17 07:55:26 +07:00
Sahil Yeole
182f2ae26e add search for alias
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-17 01:34:00 +05:30
Sahil Yeole
26982787ee make getAllPeers async
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-16 23:36:43 +05:30
Sahil Yeole
4a42e3ef1b add padding autocomplete
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-16 23:06:14 +05:30
dignow
4f4498666e fix, remove warns, misspellings
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-16 23:19:07 +08:00
ClSlaid
8f9ba44c2c Merge remote-tracking branch 'rd/master' into feat/x11/clipboard-file/init
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-16 18:57:45 +08:00
ClSlaid
1f91d4fa7b patch: add more logs
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-16 18:42:02 +08:00
RustDesk
cf97d090f3 Merge pull request #6056 from solokot/master
Update ru.rs
2023-10-16 18:16:15 +08:00
RustDesk
9cd96a65d6 Merge pull request #6055 from BestiaPL/master
Update pl.rs
2023-10-16 18:15:57 +08:00
solokot
5fc75cb4cd Add files via upload 2023-10-16 13:06:04 +03:00
Andrzej Rudnik
27fe566412 Update pl.rs 2023-10-16 11:45:27 +02:00
RustDesk
a68c7bf019 Merge pull request #6051 from fufesou/fix/connect_the_same_peer
fix, connect the same peer
2023-10-16 10:54:46 +08:00
fufesou
11388849de fix, connect the same peer
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-10-16 10:47:16 +08:00
RustDesk
aa8fb55b30 Merge pull request #6050 from bovirus/master
Update Italian language
2023-10-16 10:18:13 +08:00
RustDesk
472bea2baa Merge pull request #6047 from dignow/fix/non_texture_render
fix, non-texture render, next_rgba
2023-10-16 10:15:45 +08:00
dignow
0a7a6c64ce remove 'Show displays as individual windows' if non-texture render
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-16 07:34:38 +08:00
dignow
fde8196874 fix several bugs
1. updateCurDisplay, canvas origin (x,y)
2. Do not show "Show displays as individual windows" on non-texture
   render version.

Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-16 07:26:55 +08:00
bovirus
ea8576d344 Update Italian language 2023-10-15 21:08:24 +02:00
ClSlaid
9adda25e00 patch: simplify FUSE
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-16 00:51:12 +08:00
dignow
2cc0bf22cb fix, non-texture render, next_rgba
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-16 00:20:41 +08:00
RustDesk
892ebc2e03 Merge pull request #6045 from 21pages/fix_ios_ci
fix ios ci
2023-10-15 16:28:49 +08:00
21pages
5c9ee03389 fix ios ci
don't know why call session_get_rgba

Signed-off-by: 21pages <pages21@163.com>
2023-10-15 16:05:29 +08:00
rustdesk
3702b0c694 fix ci 2023-10-15 11:56:56 +08:00
RustDesk
451d662af4 Merge pull request #6043 from SergeyMy/patch-2
Update ru.rs
2023-10-15 11:46:09 +08:00
RustDesk
7b8e0a2755 Merge pull request #6037 from Mr-Update/patch-1
Update de.rs
2023-10-15 11:45:58 +08:00
rustdesk
63ba4f4f91 fix ci 2023-10-15 11:44:44 +08:00
rustdesk
5f92465d0f move is_x11 out of loop, https://github.com/rustdesk/rustdesk/discussions/6042 2023-10-15 11:30:45 +08:00
SergeyMy
64c8c5a014 Update ru.rs 2023-10-15 08:21:38 +05:00
Sahil Yeole
2d6322f799 remove extra parameter from peer tile
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-15 04:59:03 +05:30
Sahil Yeole
8127ce18a3 optimise autocomplete
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-15 04:53:57 +05:30
Sahil Yeole
7b5801920b update focusnode and texteditingcontroller for autocomplete
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-15 03:34:53 +05:30
Mr-Update
c18c1e59df Update de.rs 2023-10-14 20:25:39 +02:00
RustDesk
4959e664a9 Merge pull request #6036 from Kleofass/patch-1
Update lv.rs
2023-10-14 23:54:36 +08:00
Kleofass
fb5ba257ef Update lv.rs 2023-10-14 18:43:17 +03:00
RustDesk
0ed1c0aa6b Merge pull request #6035 from cacing69/master
improve ID translation
2023-10-14 22:54:48 +08:00
Ibnul Mutaki
a12fac780b improve ID translation 2023-10-14 21:50:29 +07:00
RustDesk
0b16e13597 Merge pull request #6033 from cacing69/master
improve id translation
2023-10-14 22:35:25 +08:00
RustDesk
45a9e54631 Merge branch 'master' into master 2023-10-14 22:35:07 +08:00
Ibnul Mutaki
b969307c5e improve id translation 2023-10-14 21:33:05 +07:00
RustDesk
20325e87be Merge pull request #6032 from Mr-Update/patch-9
Update de.rs
2023-10-14 22:24:00 +08:00
RustDesk
fc48d9047a Merge branch 'master' into patch-9 2023-10-14 22:23:51 +08:00
RustDesk
16b4db5083 Merge pull request #6031 from 21pages/wallpaper_remove_unsupported
not show remove wallpaper option if unsupported
2023-10-14 22:22:37 +08:00
RustDesk
4c7487cd2e Merge pull request #6029 from SergeyMy/patch-1
Update ru.rs
2023-10-14 22:22:25 +08:00
RustDesk
271033e79d Merge branch 'master' into patch-1 2023-10-14 22:22:17 +08:00
RustDesk
d2ddcf2d38 Merge pull request #5945 from dignow/feat/multi_flutter_ui_sessions
feat, multi_flutter_ui_sessions
2023-10-14 22:21:24 +08:00
Mr-Update
8a2ab30302 Update de.rs
@grummbeer You are right.
2023-10-14 16:07:02 +02:00
Mr-Update
3070b0019e Update de.rs
@grummbeer You are right.
2023-10-14 16:01:09 +02:00
21pages
68ef1fc9e0 show wallpaper only when support, show test on checked
Signed-off-by: 21pages <pages21@163.com>
2023-10-14 19:22:42 +08:00
SergeyMy
c13d67dea5 Update ru.rs 2023-10-14 09:45:06 +05:00
dignow
b52cf070f5 multi flutter ui sessions, refact 'Show displays as individual windows'
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-14 12:31:57 +08:00
dignow
e363cd9813 'Choose Display Behavior' to 'Choose display behavior'
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-14 12:26:28 +08:00
dignow
c10fc26cce fix build, android
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-14 12:26:27 +08:00
dignow
3bb7123dd5 fix build, sciter
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-14 12:26:27 +08:00
dignow
2f2a7d1f89 feat, multi flutter ui sessions, change settings to 'Display'
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-14 12:26:27 +08:00
dignow
013d307bcd feat, multi_flutter_ui_sessions
Signed-off-by: dignow <linlong1265@gmail.com>
2023-10-14 12:26:24 +08:00
rustdesk
5e616dd502 bump to 1.2.4 2023-10-14 11:07:08 +08:00
RustDesk
f200bd2198 Merge pull request #5990 from 21pages/remove_wallpaper_win_linux
win,linux remove desktop wallpaper
2023-10-14 10:21:06 +08:00
RustDesk
bd36ee4f67 Merge branch 'master' into remove_wallpaper_win_linux 2023-10-14 10:20:55 +08:00
RustDesk
e0e5cc076d Merge pull request #6026 from bovirus/master
Update Italian language
2023-10-14 10:05:43 +08:00
Sahil Yeole
95e3fb24f3 add ontap for autocomplete
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-14 03:09:59 +05:30
Sahil Yeole
83d47aed2d update UI for autocomplete
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-14 01:55:04 +05:30
Sahil Yeole
bbd7cf306a handle id whitespaces for autocomplete
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-13 23:28:54 +05:30
Sahil Yeole
f6b5c752f4 add autocomplete feat
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-13 23:10:31 +05:30
Sahil Yeole
ff20acc367 add rust bindings to get peers data
Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
2023-10-13 23:01:19 +05:30
bovirus
46c2720fc7 Update Italian language 2023-10-13 17:16:13 +02:00
21pages
d3ce8203be win,linux remove desktop wallpaper
Signed-off-by: 21pages <pages21@163.com>
2023-10-13 13:58:11 +08:00
ClSlaid
796e2ec825 Merge remote-tracking branch 'rd/master' into feat/x11/clipboard-file/init
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-07 17:32:25 +08:00
ClSlaid
a597c3f835 patch: update UI, clear previous FUSE
- UI updated, now allow copy and paste file in Linux
- Too hard to implement graceful shutdown for rustdesk, just clear
  previously mounted FUSE should also works

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-10-07 17:26:20 +08:00
ClSlaid
d2a5edda46 Merge remote-tracking branch 'origin/master' into feat/x11/clipboard-file/init 2023-09-20 16:31:58 +08:00
ClSlaid
3a21efbaae patch: linux fuse unmount
todo: grosely exit

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
2023-09-09 19:24:38 +08:00
蔡略
e5bcfeaad5 patch: linux set clipboard
Signed-off-by: 蔡略 <cailue@bupt.edu.cn>
2023-09-09 09:48:31 +08:00
cailue
af131cd1e5 fix: review windows
make windows version able to run

Signed-off-by: cailue <cailue@bupt.edu.cn>
2023-09-08 20:37:14 +08:00
蔡略
a7bb90e7e6 Merge remote-tracking branch 'origin/master' into feat/x11/clipboard-file/init
Signed-off-by: 蔡略 <cailue@bupt.edu.cn>
2023-09-08 20:09:57 +08:00
蔡略
25cf36a948 feat: add x11 clipboard support
Signed-off-by: 蔡略 <cailue@bupt.edu.cn>
2023-09-08 19:39:00 +08:00
蔡略
4f7036a405 feat(part): implement fuse support for linux clipboard
Signed-off-by: 蔡略 <cailue@bupt.edu.cn>
2023-09-04 15:38:53 +08:00
cailue
c25d648321 refactor: adjust windows file layout
Signed-off-by: cailue <cailue@bupt.edu.cn>
2023-08-24 23:53:15 +08:00
332 changed files with 31811 additions and 12869 deletions

View File

@@ -1,20 +0,0 @@
name: 📝 Task
description: Create a task for the team to work on, used internally only. We will delete tasks created by non-team members.
title: "[Task]: "
labels: [Task]
body:
- type: checkboxes
attributes:
label: Are you our team member?
description: If you are not our team member, please go to discussions.
options:
- label: Yes, I am?
required: true
- type: textarea
attributes:
label: SubTasks
placeholder: |
- Sub Task 1
- Sub Task 2
validations:
required: false

View File

@@ -1,14 +1,14 @@
# This yaml shares the build bridge steps with ci and nightly.
name: Build flutter-rust-bridge
# 2023-04-19 15:48:00+00:00
# 2023-11-23 18:00:00+00:00
on:
workflow_call:
env:
FLUTTER_VERSION: "3.10.6"
FLUTTER_RUST_BRIDGE_VERSION: "1.75.3"
FLUTTER_VERSION: "3.16.9"
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
jobs:
generate_bridge:
runs-on: ${{ matrix.job.os }}
@@ -23,21 +23,35 @@ jobs:
}
steps:
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install prerequisites
run: |
sudo apt install ca-certificates -y
sudo apt update -y
sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang cmake libclang-dev ninja-build llvm-dev libclang-10-dev llvm-10-dev pkg-config
sudo apt-get install ca-certificates -y
sudo apt-get update -y
sudo apt-get install -y \
clang \
cmake \
curl \
gcc \
git \
g++ \
libclang-10-dev \
libclang-dev \
libgtk-3-dev \
llvm-10-dev \
llvm-dev \
nasm \
ninja-build \
pkg-config \
wget
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
targets: ${{ matrix.job.target }}
components: ''
- uses: Swatinem/rust-cache@v2
with:
@@ -74,5 +88,5 @@ jobs:
path: |
./src/bridge_generated.rs
./src/bridge_generated.io.rs
./flutter/lib/generated_bridge.dart
./flutter/lib/generated_bridge.dart
./flutter/lib/generated_bridge.freezed.dart

View File

@@ -1,8 +1,12 @@
name: CI
# env:
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
# for multiarch gcc compatibility
VCPKG_COMMIT_ID: "8eb57355a4ffb410a2e94c07b4dca2dffbee8e50"
on:
workflow_dispatch:
@@ -76,36 +80,63 @@ jobs:
- { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04 }
# - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
steps:
- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install prerequisites
shell: bash
run: |
case ${{ matrix.job.target }} in
x86_64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev;;
x86_64-unknown-linux-gnu)
sudo apt-get -y update
sudo apt-get install -y \
clang \
cmake \
curl \
gcc \
git \
g++ \
libasound2-dev \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
libgtk-3-dev \
libpulse-dev \
libxcb-randr0-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \
libxdo-dev \
libxfixes-dev \
nasm \
wget
;;
# arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
# aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
esac
- name: Restore from cache and install vcpkg
uses: lukka/run-vcpkg@v7
- name: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11
with:
setupOnly: true
vcpkgGitCommitId: '501db0f17ef6df184fcdbfbe0f87cde2313b6ab1' #2023.04.15
vcpkgDirectory: /opt/artifacts/vcpkg
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
- name: Install vcpkg dependencies
run: |
$VCPKG_ROOT/vcpkg install libvpx libyuv opus aom
shell: bash
$VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed"
shell: bash
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
targets: ${{ matrix.job.target }}
components: ''
- name: Show version information (Rust, cargo, GCC)
shell: bash
@@ -117,8 +148,8 @@ jobs:
cargo -V
rustc -V
- uses: Swatinem/rust-cache@v1
- uses: Swatinem/rust-cache@v2
- name: Build
uses: actions-rs/cargo@v1
with:
@@ -183,7 +214,9 @@ jobs:
;;
esac;
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
#deprecated echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_ENV
echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_OUTPUT
- name: Run tests
uses: actions-rs/cargo@v1

File diff suppressed because it is too large Load Diff

View File

@@ -19,4 +19,3 @@ jobs:
uses: ./.github/workflows/flutter-build.yml
with:
upload-artifact: false

View File

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

View File

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

View File

@@ -4,13 +4,11 @@ on: [workflow_dispatch]
env:
LLVM_VERSION: "10.0"
FLUTTER_VERSION: "3.10.6"
FLUTTER_VERSION: "3.16.9"
TAG_NAME: "tmp"
FLUTTER_RUST_BRIDGE_VERSION: "1.75.3"
# vcpkg version: 2022.05.10
# for multiarch gcc compatibility
VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1"
VERSION: "1.2.3"
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
VERSION: "1.2.4"
jobs:
build-for-history-windows:
@@ -23,7 +21,7 @@ jobs:
- { target: x86_64-pc-windows-msvc, os: windows-2019, arch: x86_64, date: 2023-08-04, ref: 72c198a1e94cc1e0242fce88f92b3f3caedcd0c3 }
steps:
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ matrix.job.ref }}
@@ -54,14 +52,18 @@ jobs:
Push-Location flutter ; flutter pub get ; Pop-Location
~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart
- name: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11
with:
vcpkgDirectory: C:\vcpkg
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
- name: Install vcpkg dependencies
run: |
cd C:\
git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1
$VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed"
shell: bash
- name: Build rustdesk
env:
VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg
run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver
- name: Build self-extracted executable
@@ -72,7 +74,7 @@ jobs:
popd
mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ matrix.job.date }}-${{ matrix.job.target }}.exe
- name: Publish Release
uses: softprops/action-gh-release@v1
with:

View File

@@ -1,92 +0,0 @@
name: Build vcpkg dependencies for linux clients
on:
workflow_call:
jobs:
build-vcpkg-deps-linux:
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: true
matrix:
job:
- { arch: armv7, os: ubuntu-20.04 }
- { arch: x86_64, os: ubuntu-20.04 }
- { arch: aarch64, os: ubuntu-20.04 }
steps:
- name: Create vcpkg artifacts folder
run: mkdir -p /opt/artifacts
- name: Cache Vcpkg
id: cache-vcpkg
uses: actions/cache@v3
with:
path: /opt/artifacts
key: vcpkg-${{ matrix.job.arch }}
- uses: rustdesk-org/run-on-arch-action@amd64-support
name: Run vcpkg install on ${{ matrix.job.arch }}
id: vcpkg
with:
arch: ${{ matrix.job.arch }}
distro: ubuntu18.04
githubToken: ${{ github.token }}
setup: |
ls -l "/opt/artifacts"
dockerRunArgs: |
--volume "/opt/artifacts:/artifacts"
shell: /bin/bash
install: |
apt update -y
case "${{ matrix.job.arch }}" in
x86_64)
apt update -y
apt install -y curl zip unzip tar git g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev libssl-dev
wget https://github.com/Kitware/CMake/releases/download/v3.27.5/cmake-3.27.5.tar.gz
apt remove -y --purge cmake
tar -zxvf cmake-3.27.5.tar.gz
cd cmake-3.27.5
./bootstrap
make
make install
cd -
cmake --version
gcc -v
;;
aarch64|armv7)
apt install -y curl zip unzip git
esac
run: |
# disable git safe.directory
git config --global --add safe.directory "*"
case "${{ matrix.job.arch }}" in
x86_64)
export VCPKG_FORCE_SYSTEM_BINARIES=1
pushd /artifacts
git clone https://github.com/microsoft/vcpkg.git || true
pushd vcpkg
git reset --hard ${{ env.VCPKG_COMMIT_ID }}
./bootstrap-vcpkg.sh
./vcpkg install libvpx libyuv opus aom
;;
aarch64)
pushd /artifacts
rm -rf rustdesk_thirdparty_lib
git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1
mkdir -p /artifacts/vcpkg/installed
mv ./rustdesk_thirdparty_lib/vcpkg/installed/arm64-linux /artifacts/vcpkg/installed/arm64-linux
;;
armv7)
pushd /artifacts
rm -rf rustdesk_thirdparty_lib
git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1
mkdir -p /artifacts/vcpkg/installed
mv ./rustdesk_thirdparty_lib/vcpkg/installed/arm-linux /artifacts/vcpkg/installed/arm-linux
;;
esac
- name: Upload artifacts
uses: actions/upload-artifact@master
with:
name: vcpkg-artifact-${{ matrix.job.arch }}
path: |
/opt/artifacts/vcpkg/installed

4
.gitignore vendored
View File

@@ -49,4 +49,6 @@ lib/generated_bridge.dart
.ssh
.devcontainer/.*
# build cache in examples
examples/**/target/
examples/**/target/
# ===
vcpkg_installed

3569
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,12 @@
[package]
name = "rustdesk"
version = "1.2.3"
version = "1.2.4"
authors = ["rustdesk <info@rustdesk.com>"]
edition = "2021"
build= "build.rs"
description = "A remote control software."
default-run = "rustdesk"
rust-version = "1.75"
[lib]
name = "librustdesk"
@@ -27,11 +28,19 @@ use_dasp = ["dasp"]
flutter = ["flutter_rust_bridge"]
default = ["use_dasp"]
hwcodec = ["scrap/hwcodec"]
gpucodec = ["scrap/gpucodec"]
mediacodec = ["scrap/mediacodec"]
linux_headless = ["pam" ]
virtual_display_driver = ["virtual_display"]
plugin_framework = []
linux-pkg-config = ["magnum-opus/linux-pkg-config", "scrap/linux-pkg-config"]
unix-file-copy-paste = [
"dep:x11-clipboard",
"dep:x11rb",
"dep:percent-encoding",
"dep:once_cell",
"clipboard/unix-file-copy-paste",
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -47,13 +56,12 @@ cfg-if = "1.0"
lazy_static = "1.4"
sha2 = "0.10"
repng = "0.2"
parity-tokio-ipc = { git = "https://github.com/open-trade/parity-tokio-ipc" }
parity-tokio-ipc = { git = "https://github.com/rustdesk-org/parity-tokio-ipc" }
runas = "=1.0" # https://github.com/mitsuhiko/rust-runas/issues/13
magnum-opus = { git = "https://github.com/rustdesk/magnum-opus" }
magnum-opus = { git = "https://github.com/rustdesk-org/magnum-opus" }
dasp = { version = "0.11", features = ["signal", "interpolate-linear", "interpolate"], optional = true }
rubato = { version = "0.12", optional = true }
samplerate = { version = "0.2", optional = true }
async-trait = "0.1"
uuid = { version = "1.3", features = ["v4"] }
clap = "4.2"
rpassword = "7.2"
@@ -62,18 +70,19 @@ num_cpus = "1.15"
bytes = { version = "1.4", features = ["serde"] }
default-net = "0.14"
wol-rs = "1.0"
flutter_rust_bridge = { version = "1.75", features = ["uuid"], optional = true}
flutter_rust_bridge = { version = "=1.80", features = ["uuid"], optional = true}
errno = "0.3"
rdev = { git = "https://github.com/fufesou/rdev" }
url = { version = "2.3", features = ["serde"] }
crossbeam-queue = "0.3"
hex = "0.4"
reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "json", "rustls-tls"], default-features=false }
chrono = "0.4"
cidr-utils = "0.5"
libloading = "0.8"
fon = "0.6"
zip = "0.6"
shutdown_hooks = "0.1"
totp-rs = { version = "5.4", default-features = false, features = ["gen_secret", "otpauth"] }
[target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies]
cpal = "0.15"
@@ -86,17 +95,17 @@ sys-locale = "0.3"
enigo = { path = "libs/enigo", features = [ "with_serde" ] }
clipboard = { path = "libs/clipboard" }
ctrlc = "3.2"
arboard = "3.2"
arboard = { version = "3.2", features = ["wayland-data-control"] }
system_shutdown = "4.0"
qrcode-generator = "4.1"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winuser", "wincrypt", "shellscalingapi"] }
winapi = { version = "0.3", features = ["winuser", "wincrypt", "shellscalingapi", "pdh", "synchapi", "memoryapi", "shellapi"] }
winreg = "0.11"
windows-service = "0.6"
virtual_display = { path = "libs/virtual_display", optional = true }
impersonate_system = { git = "https://github.com/21pages/impersonate-system" }
shared_memory = "0.12"
shutdown_hooks = "0.1"
tauri-winrt-notification = "0.1.2"
[target.'cfg(target_os = "macos")'.dependencies]
@@ -106,18 +115,27 @@ dispatch = "0.2"
core-foundation = "0.9"
core-graphics = "0.22"
include_dir = "0.7"
dark-light = "1.0"
fruitbasket = "0.10"
objc_id = "0.1"
[target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies]
tray-icon = { git = "https://github.com/rustdesk-org/tray-icon" }
tray-icon = { git = "https://github.com/tauri-apps/tray-icon" }
tao = { git = "https://github.com/rustdesk-org/tao", branch = "dev" }
image = "0.24"
[target.'cfg(any(target_os = "macos", target_os = "linux"))'.dependencies]
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" }
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
# https://github.com/rustdesk/rustdesk-server-pro/issues/189, using native-tls for better tls support
reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "json", "native-tls", "gzip"], default-features=false }
[target.'cfg(not(any(target_os = "macos", target_os = "windows")))'.dependencies]
reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "json", "rustls-tls", "rustls-tls-native-roots", "gzip"], default-features=false }
[target.'cfg(target_os = "linux")'.dependencies]
psimple = { package = "libpulse-simple-binding", version = "2.27" }
pulse = { package = "libpulse-binding", version = "2.27" }
@@ -129,10 +147,15 @@ dbus = "0.9"
dbus-crossroads = "0.5"
pam = { git="https://github.com/fufesou/pam", optional = true }
users = { version = "0.11" }
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch", optional = true}
x11rb = {version = "0.12", features = ["all-extensions"], optional = true}
percent-encoding = {version = "2.3", optional = true}
once_cell = {version = "1.18", optional = true}
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.13"
jni = "0.21"
android-wakelock = { git = "https://github.com/21pages/android-wakelock" }
[workspace]
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/portable"]
@@ -145,12 +168,11 @@ FileDescription = "RustDesk"
[target.'cfg(target_os="windows")'.build-dependencies]
winres = "0.1"
winapi = { version = "0.3", features = [ "winnt" ] }
winapi = { version = "0.3", features = [ "winnt", "pdh", "synchapi" ] }
[build-dependencies]
cc = "1.0"
hbb_common = { path = "libs/hbb_common" }
flutter_rust_bridge_codegen = "1.75"
os-version = "0.2"
[dev-dependencies]
@@ -170,3 +192,7 @@ panic = 'abort'
strip = true
#opt-level = 'z' # only have smaller size after strip
rpath = true
[profile.dev]
split-debuginfo = '...' # Platform-specific.
#strip = "debuginfo"

View File

@@ -1,21 +1,54 @@
FROM debian
FROM debian:bullseye-slim
WORKDIR /
RUN apt update -y && apt install -y g++ gcc git curl nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake unzip zip sudo libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev cmake ninja-build && rm -rf /var/lib/apt/lists/*
ARG DEBIAN_FRONTEND=noninteractive
RUN apt update -y && \
apt install --yes --no-install-recommends \
g++ \
gcc \
git \
curl \
nasm \
yasm \
libgtk-3-dev \
clang \
libxcb-randr0-dev \
libxdo-dev \
libxfixes-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \
libasound2-dev \
libpulse-dev \
make \
cmake \
unzip \
zip \
sudo \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
ca-certificates \
ninja-build && \
rm -rf /var/lib/apt/lists/*
RUN git clone --branch 2023.04.15 --depth=1 https://github.com/microsoft/vcpkg
RUN /vcpkg/bootstrap-vcpkg.sh -disableMetrics
RUN /vcpkg/vcpkg --disable-metrics install libvpx libyuv opus aom
RUN git clone --branch 2023.04.15 --depth=1 https://github.com/microsoft/vcpkg && \
/vcpkg/bootstrap-vcpkg.sh -disableMetrics && \
/vcpkg/vcpkg --disable-metrics install libvpx libyuv opus aom
RUN groupadd -r user && \
useradd -r -g user user --home /home/user && \
mkdir -p /home/user/rustdesk && \
chown -R user: /home/user && \
echo "user ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/user
RUN groupadd -r user && useradd -r -g user user --home /home/user && mkdir -p /home/user && chown user /home/user && echo "user ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/user
WORKDIR /home/user
RUN curl -LO https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so
USER user
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh
RUN chmod +x rustup.sh
RUN ./rustup.sh -y
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh && \
chmod +x rustup.sh && \
./rustup.sh -y
USER root
ENV HOME=/home/user
COPY ./entrypoint /
ENTRYPOINT ["/entrypoint"]
COPY ./entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -11,7 +11,7 @@
Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
[![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Frustdesk%2Fbounties%3Fstatus%3Dopen)](https://console.algora.io/org/rustdesk/bounties?status=open)
@@ -49,7 +49,7 @@ Go through [DEVCONTAINER.md](docs/DEVCONTAINER.md) for more info.
## Dependencies
Desktop versions use [Sciter](https://sciter.com/) or Flutter for GUI, this tutorial is for Sciter only.
Desktop versions use Flutter or Sciter (deprecated) for GUI, this tutorial is for Sciter only, since it is easier and more friendly to start. Check out our [CI](https://github.com/rustdesk/rustdesk/blob/master/.github/workflows/flutter-build.yml) for building Flutter version.
Please download Sciter dynamic library yourself.
@@ -80,11 +80,12 @@ sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxc
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
```
### openSUSE Tumbleweed
### openSUSE Tumbleweed
```sh
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
```
### Fedora 28 (CentOS 8)
```sh
@@ -135,34 +136,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Change Wayland to X11 (Xorg)
RustDesk does not support Wayland. Check [this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) to configuring Xorg as the default GNOME session.
## Wayland support
Wayland does not seem to provide any API for sending keypresses to other windows. Therefore, the RustDesk uses an API from a lower level, namely the `/dev/uinput` device (Linux kernel level).
When Wayland is the controlled side, you have to start in the following way:
```bash
# Start uinput service
$ sudo rustdesk --service
$ rustdesk
```
**Notice**: Wayland screen recording uses different interfaces. RustDesk currently only supports org.freedesktop.portal.ScreenCast.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Not support
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Support
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## How to build with Docker
Begin by cloning the repository and building the Docker container:
@@ -198,12 +171,13 @@ Please ensure that you are running these commands from the root of the RustDesk
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, config, tcp/udp wrapper, protobuf, fs functions for file transfer, and some other utility functions
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: screen capture
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: platform specific keyboard/mouse control
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
- **[libs/clipboard](https://github.com/rustdesk/rustdesk/tree/master/libs/clipboard)**: file copy and paste implementation for Windows, Linux, macOS.
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: obsolete Sciter UI (deprecated)
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: audio/clipboard/input/video services, and network connections
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: start a peer connection
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Communicate with [rustdesk-server](https://github.com/rustdesk/rustdesk-server), wait for remote direct (TCP hole punching) or relayed connection
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: platform specific code
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter code for mobile
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter code for desktop and mobile
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: JavaScript for Flutter web client
## Snapshots

View File

@@ -2,7 +2,7 @@
version: 1
script:
- rm -rf ./AppDir || true
- bsdtar -zxvf ../rustdesk-1.2.3.deb
- bsdtar -zxvf ../rustdesk-1.2.4.deb
- tar -xvf ./data.tar.xz
- mkdir ./AppDir
- mv ./usr ./AppDir/usr
@@ -18,7 +18,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.2.3
version: 1.2.4
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:

View File

@@ -2,7 +2,7 @@
version: 1
script:
- rm -rf ./AppDir || true
- bsdtar -zxvf ../rustdesk-1.2.3.deb
- bsdtar -zxvf ../rustdesk-1.2.4.deb
- tar -xvf ./data.tar.xz
- mkdir ./AppDir
- mv ./usr ./AppDir/usr
@@ -18,7 +18,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.2.3
version: 1.2.4
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:

View File

@@ -16,7 +16,7 @@ osx = platform.platform().startswith(
hbb_name = 'rustdesk' + ('.exe' if windows else '')
exe_path = 'target/release/' + hbb_name
if windows:
flutter_build_dir = 'build/windows/runner/Release/'
flutter_build_dir = 'build/windows/x64/runner/Release/'
elif osx:
flutter_build_dir = 'build/macos/Build/Products/Release/'
else:
@@ -24,18 +24,21 @@ else:
flutter_build_dir_2 = f'flutter/{flutter_build_dir}'
skip_cargo = False
def get_arch() -> str:
custom_arch = os.environ.get("ARCH")
if custom_arch is None:
return "amd64"
return custom_arch
def system2(cmd):
err = os.system(cmd)
if err != 0:
print(f"Error occurred when executing: {cmd}. Exiting.")
sys.exit(-1)
def get_version():
with open("Cargo.toml", encoding="utf-8") as fh:
for line in fh:
@@ -46,17 +49,11 @@ def get_version():
def parse_rc_features(feature):
available_features = {
'IddDriver': {
'platform': ['windows'],
'zip_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.3/RustDeskIddDriver_x64.zip',
'checksum_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.3/checksum_md5',
'exclude': ['README.md', 'certmgr.exe', 'install_cert_runas_admin.bat', 'RustDeskIddApp.exe'],
},
'PrivacyMode': {
'platform': ['windows'],
'zip_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.1'
'/TempTopMostWindow_x64_pic_en.zip',
'checksum_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.1/checksum_md5',
'zip_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.3'
'/TempTopMostWindow_x64.zip',
'checksum_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.3/checksum_md5',
'include': ['WindowInjection.dll'],
}
}
@@ -109,7 +106,7 @@ def make_parser():
nargs='+',
default='',
help='Integrate features, windows only.'
'Available: IddDriver, PrivacyMode. Special value is "ALL" and empty "". Default is empty.')
'Available: PrivacyMode. Special value is "ALL" and empty "". Default is empty.')
parser.add_argument('--flutter', action='store_true',
help='Build flutter package', default=False)
parser.add_argument(
@@ -118,11 +115,21 @@ def make_parser():
help='Enable feature hwcodec' + (
'' if windows or osx else ', need libva-dev, libvdpau-dev.')
)
parser.add_argument(
'--gpucodec',
action='store_true',
help='Enable feature gpucodec, only available on windows now.'
)
parser.add_argument(
'--portable',
action='store_true',
help='Build windows portable'
)
parser.add_argument(
'--unix-file-copy-paste',
action='store_true',
help='Build with unix file copy paste feature'
)
parser.add_argument(
'--flatpak',
action='store_true',
@@ -172,8 +179,8 @@ def generate_build_script_for_docker():
export VCPKG_ROOT=`pwd`/vcpkg
git clone https://github.com/microsoft/vcpkg
vcpkg/bootstrap-vcpkg.sh
vcpkg/vcpkg install libvpx libyuv opus
popd
$VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed"
# build rustdesk
./build.py --flutter --hwcodec
''')
@@ -185,6 +192,7 @@ def download_extract_features(features, res_dir):
import re
proxy = ''
def req(url):
if not proxy:
return url
@@ -196,9 +204,9 @@ def download_extract_features(features, res_dir):
for (feat, feat_info) in features.items():
includes = feat_info['include'] if 'include' in feat_info and feat_info['include'] else []
includes = [ re.compile(p) for p in includes ]
includes = [re.compile(p) for p in includes]
excludes = feat_info['exclude'] if 'exclude' in feat_info and feat_info['exclude'] else []
excludes = [ re.compile(p) for p in excludes ]
excludes = [re.compile(p) for p in excludes]
print(f'{feat} download begin')
download_filename = feat_info['zip_url'].split('/')[-1]
@@ -261,10 +269,10 @@ def external_resources(flutter, args, res_dir):
def get_features(args):
features = ['inline'] if not args.flutter else []
if windows:
features.append('virtual_display_driver')
if args.hwcodec:
features.append('hwcodec')
if args.gpucodec:
features.append('gpucodec')
if args.flutter:
features.append('flutter')
features.append('flutter_texture_render')
@@ -272,6 +280,8 @@ def get_features(args):
features.append('flatpak')
if args.appimage:
features.append('appimage')
if args.unix_file_copy_paste:
features.append('unix-file-copy-paste')
print("features:", features)
return features
@@ -350,6 +360,7 @@ def build_flutter_deb(version, features):
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
os.chdir("..")
def build_deb_from_folder(version, binary_folder):
os.chdir('flutter')
system2('mkdir -p tmpdeb/usr/bin/')
@@ -388,10 +399,12 @@ def build_deb_from_folder(version, binary_folder):
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
os.chdir("..")
def build_flutter_dmg(version, features):
if not skip_cargo:
# set minimum osx build target, now is 10.14, which is the same as the flutter xcode project
system2(f'MACOSX_DEPLOYMENT_TARGET=10.14 cargo build --features {features} --lib --release')
system2(
f'MACOSX_DEPLOYMENT_TARGET=10.14 cargo build --features {features} --lib --release')
# copy dylib
system2(
"cp target/release/liblibrustdesk.dylib target/release/librustdesk.dylib")
@@ -481,6 +494,7 @@ def main():
system2('mv target/release/rustdesk.exe target/release/RustDesk.exe')
pa = os.environ.get('P')
if pa:
# https://certera.com/kb/tutorial-guide-for-safenet-authentication-client-for-code-signing/
system2(
f'signtool sign /a /v /p {pa} /debug /f .\\cert.pfx /t http://timestamp.digicert.com '
'target\\release\\rustdesk.exe')
@@ -557,7 +571,8 @@ def main():
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/*
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app
'''.format(pa))
system2('create-dmg "RustDesk %s.dmg" "target/release/bundle/osx/RustDesk.app"' % version)
system2(
'create-dmg "RustDesk %s.dmg" "target/release/bundle/osx/RustDesk.app"' % version)
os.rename('RustDesk %s.dmg' %
version, 'rustdesk-%s.dmg' % version)
if pa:
@@ -577,7 +592,7 @@ def main():
else:
print('Not signed')
else:
# buid deb package
# build deb package
system2(
'mv target/release/bundle/deb/rustdesk*.deb ./rustdesk.deb')
system2('dpkg-deb -R rustdesk.deb tmpdeb')

View File

@@ -41,7 +41,7 @@ fn build_manifest() {
}
}
fn install_oboe() {
fn install_android_deps() {
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
if target_os != "android" {
return;
@@ -49,6 +49,8 @@ fn install_oboe() {
let mut target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
if target_arch == "x86_64" {
target_arch = "x64".to_owned();
} else if target_arch == "x86" {
target_arch = "x86".to_owned();
} else if target_arch == "aarch64" {
target_arch = "arm64".to_owned();
} else {
@@ -66,62 +68,16 @@ fn install_oboe() {
path.join("lib").to_str().unwrap()
)
);
println!("cargo:rustc-link-lib=ndk_compat");
println!("cargo:rustc-link-lib=oboe");
println!("cargo:rustc-link-lib=oboe_wrapper");
println!("cargo:rustc-link-lib=c++");
println!("cargo:rustc-link-lib=OpenSLES");
// I always got some strange link error with oboe, so as workaround, put oboe.cc into oboe src: src/common/AudioStreamBuilder.cpp
// also to avoid libc++_shared not found issue, cp ndk's libc++_shared.so to jniLibs, e.g.
// ./flutter_hbb/android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so
// let include = path.join("include");
//cc::Build::new().file("oboe.cc").include(include).compile("oboe_wrapper");
}
#[cfg(feature = "flutter")]
fn gen_flutter_rust_bridge() {
if !std::env::var("RUN_FFIGEN").is_ok() {
return;
}
use lib_flutter_rust_bridge_codegen::{
config_parse, frb_codegen, get_symbols_if_no_duplicates, RawOpts,
};
let llvm_path = match std::env::var("LLVM_HOME") {
Ok(path) => Some(vec![path]),
Err(_) => None,
};
// Tell Cargo that if the given file changes, to rerun this build script.
println!("cargo:rerun-if-changed=src/flutter_ffi.rs");
// Options for frb_codegen
let raw_opts = RawOpts {
// Path of input Rust code
rust_input: vec!["src/flutter_ffi.rs".to_string()],
// Path of output generated Dart code
dart_output: vec!["flutter/lib/generated_bridge.dart".to_string()],
// Path of output generated C header
c_output: Some(vec!["flutter/macos/Runner/bridge_generated.h".to_string()]),
/// Path to the installed LLVM
llvm_path,
// for other options use defaults
..Default::default()
};
// get opts from raw opts
let configs = config_parse(raw_opts);
// generation of rust api for ffi
let all_symbols = get_symbols_if_no_duplicates(&configs).unwrap();
for config in configs.iter() {
frb_codegen(config, &all_symbols).unwrap();
}
}
fn main() {
hbb_common::gen_version();
install_oboe();
// 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();
// if target_os == "android" || target_os == "ios" {
#[cfg(feature = "flutter")]
gen_flutter_rust_bridge();
// return;
// }
install_android_deps();
#[cfg(all(windows, feature = "inline"))]
build_manifest();
#[cfg(windows)]

101
docs/CODE_OF_CONDUCT-JP.md Normal file
View File

@@ -0,0 +1,101 @@
# コントリビューター規約 行動規範
## 私たちの誓い
私たちは、メンバー、貢献者、リーダーとして、年齢、体格、目に見える・見えない障害、
民族性、性の特徴、性自認と表現、経験のレベル、教育、社会経済的地位、国籍、個人の外見、
人種、宗教、性的自認と指向に関係なく、誰もがハラスメントのないコミュニティに参加できるようにすることを誓います。
私たちは、開かれた、歓迎された、多様で、包容力のある、健全な地域社会に貢献するように行動し、交流することを誓います。
## 私たちの基準
地域社会にとって好ましい環境にコントリビュートする行動の例には、以下のようなものがある:
* 他者への共感と優しさ
* 異なる意見、視点、経験を尊重すること
* 建設的なフィードバックを与え、潔く受け入れること
* 私たちの過ちによって影響を受けた人々に責任を受け入れ、謝罪し、経験から学ぶこと
* 私たち個人にとってだけでなく、地域社会全体にとって何が最善であるかに焦点を合わせること
許されない行為の例:
* 性的な言葉やイメージの使用、性的な注目や誘いかけ
* 荒らし、侮辱的または軽蔑的なコメント、個人的または政治的な攻撃
* 公的または私的な嫌がらせ
* 明示的な許可なく、他人の住所や電子メールアドレスなどの個人情報を公開すること
* 職業上不適切と見なされるその他の行為
## 執行責任
コミュニティリーダーは、許容される行動の基準を明確にし、実施する責任があり、
不適切、脅迫的、攻撃的、または有害と判断される行動に対しては、適切かつ公正な是正措置をとります
コミュニティリーダーは、本行動規範に沿わないコメント、コミット、コード、ウィキ編集、
課題、その他の貢献を削除、編集、拒否する権利と責任を有し、適切な場合にはモデレーション決定の理由を伝えます。
## スコープ
この行動規範は、すべてのコミュニティスペースで適用され、また個人が公的なスペースでコミュニティを公式に代表している場合にも適用されます。
当コミュニティを代表する例としては、公式 E メールアドレスの使用、公式ソーシャルメディアアカウントによる投稿、
オンラインまたはオフラインのイベントでの任命された代表としての行動などが挙げられます。
## 施行
虐待、ハラスメント、その他容認できない行為があった場合は、[info@rustdesk.com](mailto:info@rustdesk.com) の
執行担当コミュニティリーダーに報告することができる。
すべての苦情は、迅速かつ公正に検討・調査されます。
すべての地域社会の指導者は、いかなる事件の報告者のプライバシーと安全を尊重する義務がある。
## 執行ガイドライン
コミュニティリーダーは、本行動規範に違反すると判断した行為に対する結果を決定する際、
以下の「コミュニティへの影響に関するガイドライン」に従います:
### 1. 修正
**コミュニティへの影響**: 不適切な言葉の使用、またはプロフェッショナルでない、あるいは地域社会で歓迎されないとみなされるその他の行動。
**結果**: コミュニティリーダーからの私的な書面による警告。違反の性質と、
なぜその行為が不適切であったのかについての説明を明確にする。公的な謝罪が要求される場合もある。
### 2. 警告
**コミュニティへの影響**: 単一の出来事または一連の行動による違反。
**結果**: 行動を続けた場合の結果を伴う警告。一定期間、行動規範の実施者との勝手な交流を含め、
関係者と交流しないこと。これには、ソーシャルメディアなどの外部チャンネルだけでなく、
コミュニティスペースでの交流を避けることも含まれます。これらの条件に違反した場合、一時的または恒久的に追放される可能性があります。
### 3. 一時的な禁止
**コミュニティへの影響**: 継続的な不適切な行動を含む、コミュニティ基準に対する重大な違反。
**結果**: 一定期間、地域社会とのあらゆる交流や公的なコミュニケーションを一時的に禁止すること。
この期間中は、行動規範を執行する人々との未承諾の交流を含め、関係者との公私にわたる交流は許されない。
これらの条件に違反した場合、永久禁止となる可能性があります。
### 4. 永久禁止
**コミュニティへの影響**: 継続的な不適切な行動、個人に対する嫌がらせ、
または個人クラスに対する攻撃や中傷など、地域社会の基準に対する違反のパターンを示すこと。
**結果**: コミュニティ内でのあらゆる公的交流の永久禁止。
## 帰属
この行動規範は、[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0] に掲載されている
[コントリビューター規約][ホームページ]、バージョン 2.0 から引用したものです。
コミュニティインパクトガイドラインは、[Mozilla's code of conduct enforcement ladder][Mozilla CoC] に触発されました。
この行動規範に関するよくある質問については、[https://www.contributor-covenant.org/faq][FAQ] の FAQ をご覧ください。
翻訳は [https://www.contributor-covenant.org/translations][翻訳] にあります。
[ホームページ]: https://www.contributor-covenant.org
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[翻訳]: https://www.contributor-covenant.org/translations

41
docs/CONTRIBUTING-JP.md Normal file
View File

@@ -0,0 +1,41 @@
# RustDesk へのコントリビュート
RustDesk は皆さんからのコントリビュートを歓迎します。ご協力いただける方のガイドラインは
以下の通りです:
## コントリビューション
RustDesk またはその依存関係へのコントリビュートは、GitHub のプルリクエストの形で行ってください。
それぞれのプルリクエストは、コアコントリビューター(パッチの適用を許可されている人)によってレビューされ、
メインツリーに適用されるか、必要な変更についてのフィードバックが与えられます。
コアコントリビューターからのものであっても、すべてのコントリビューターはこのフォーマットに従うべきです。
ある issue に取り組みたい場合は、GitHub の issue にコメントすることで、まずその対応を主張してください。
これは、同じ issue に対するコントリビューターの重複作業を防ぐためです。
## プルリクエストのチェックリスト
- master ブランチからブランチし、必要であればプルリクエストを提出する前に現在の master ブランチにリベースしてください。
master と正しくマージできない場合、変更をリベースするよう求められる可能性があります。
- コミットは、各コミットが独立して正しい(すなわち、各コミットがコンパイルされ、テストに合格する)ことを保証しながら、
可能な限り小さくすべきです。
- コミットには、Developer Certificate of Origin (http://developercertificate.org) の sign-off を添えてください。
これは、あなた(および該当する場合はあなたの雇用主)が [プロジェクトのライセンス](../LICENCE) の条項に拘束されることに
同意していることを示すものです。git では、これは `git commit``-s` オプションを使います。
- もしあなたのパッチがレビューされなかったり、特定の人にレビューしてもらう必要がある場合、
プルリクエストやコメントでレビューを依頼するレビュアーに@返信したり、[email](mailto:info@rustdesk.com) でレビューを依頼することができます。
- 修正したバグや新機能に関連するテストを追加する。
具体的なgitの手順については、[GitHub workflow 101](https://github.com/servo/servo/wiki/GitHub-workflow)を参照してください。
## 行動規範
https://github.com/rustdesk/rustdesk/blob/master/docs/CODE_OF_CONDUCT.md
## コミュニケーション
RustDesk のコントリビューターは、[Discord](https://discord.gg/nDceKgxnkV) を良く使っています。

14
docs/DEVCONTAINER-JP.md Normal file
View File

@@ -0,0 +1,14 @@
docker コンテナで devcontainer を起動すると、デバッグモードの linux バイナリが作成されます。
現在 devcontainer では、Linux と android のビルドをデバッグモードとリリースモードの両方で提供しています。
以下は、特定のビルドを作成するためにプロジェクトのルートから実行するコマンドの表になります。
コマンド|ビルド タイプ|モード
-|-|-|
`.devcontainer/build.sh --debug linux`|Linux|debug
`.devcontainer/build.sh --release linux`|Linux|release
`.devcontainer/build.sh --debug android`|android-arm64|debug
`.devcontainer/build.sh --release android`|android-arm64|release

View File

@@ -118,10 +118,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### X11 (Xorg) إلى Wayland تغيير
افتراضية GNOME session ك Xorg إتبع [هذه](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) الخطوات لإعداد Wayland لا تدعم RustDesk
## Docker طريقة البناء باستخدام
ابدأ باستنساخ المستودع وبناء الكونتاينر:

View File

@@ -6,10 +6,10 @@
<a href="#file-structure">Struktura</a> •
<a href="#snapshot">Ukázky</a><br>
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
<b>Potřebujeme Vaši pomoc s překláním textů tohoto ČTIMNE, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">uživatelského rozhraní aplikace RustDesk</a> a <a href="https://github.com/rustdesk/doc.rustdesk.com">dokumentace k ní</a> do vašeho jazyka</b>
<b>Potřebujeme Vaši pomoc s překladem tohoto README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">uživatelského rozhraní aplikace RustDesk</a> a <a href="https://github.com/rustdesk/doc.rustdesk.com">dokumentace k ní</a> do vašeho jazyka</b>
</p>
Dopisujte si s námi: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
Popovídejte si s námi: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
@@ -44,7 +44,7 @@ Varianta pro mobilní platformy používá aplikační rámec (framework) Flutte
- Připravte si vývojové prostředí pro jazyky Rust a C++
- Nainstalujte [vcpkg](https://github.com/microsoft/vcpkg), a nastavte správně proměnnou prostsředí `VCPKG_ROOT`
- Nainstalujte [vcpkg](https://github.com/microsoft/vcpkg), a správně nastavte proměnnou prostředí `VCPKG_ROOT`
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
- Linux/MacOS: vcpkg install libvpx libyuv opus aom
@@ -111,10 +111,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Změna z Wayland na X11 (Xorg)
RustDesk (zatím) nepodporuje zobrazovací server Wayland. Jak nastavit Xorg jako výchozí pro relace v prostředí GNOME naleznete [zde](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/).
## Jak sestavit prostřednictvím Docker kontejnerizace
Začněte tím, že si naklonujete tento repozitář a sestavíte docker kontejner:
@@ -131,7 +127,7 @@ Poté pokaždé, když bude třeba aplikaci sestavit, spusťte následující p
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
Všimněte si, že prvotní sestavení může trvat déle (než se do mezipaměti uloží veškeré softwarové součásti, které jsou potřeba) následná opakování už budou rychlejší. Dále, pokud potřebujete příkazu pro sestavení zadat nějaké argumenty, je možné je zapsat na konec příkazu na pozici `<OPTIONAL-ARGS>`. Například, pokud byste chtěli sestavit optimalizovaně pro vydání, spustili byste výše uvedený příkaz následovaný `--release`. Výsledný spustitelný soubor se objeví v cílové složce na vašem systému a bude ho možné spustit pomocí:
Všimněte si, že prvotní sestavení může trvat déle (než se do mezipaměti uloží veškeré softwarové součásti, které jsou potřeba) následná opakování už budou rychlejší. Pokud navíc potřebujete zadat různé argumenty příkazu pro sestavení, můžete tak učinit na konci příkazu v pozici `<OPTIONAL-ARGS>`. Například, pokud byste chtěli sestavit optimalizovanou verzi pro vydání, spustili byste výše uvedený příkaz následovaný `--release`. Výsledný spustitelný soubor se objeví v cílové složce na vašem systému a bude ho možné spustit pomocí:
```sh
target/debug/rustdesk
@@ -143,7 +139,7 @@ Nebo, pokud spouštíte variantu pro vydání:
target/release/rustdesk
```
Zajistětě, abyste tyto příkazy spouštěli z kořene repozitáře s RustDesk, jinak aplikace nemusí být schopná nalézt potřebné prostředky (resources). Také si všimněte, že ostatní dílčí príkazy nástroje cargo, jako třeba `install` nebo `run` zatím nejsou prostřednictvím této metody podporovány, protože by vedly k instalaci či spuštění program uvnitř kontejneru namísto přímo v systému.
Ujistěte se, že tyto příkazy spouštíte z kořenového adresáře RustDesk, jinak aplikace nemusí být schopná nalézt potřebné prostředky (resources). Také si všimněte, že ostatní dílčí príkazy nástroje cargo, jako třeba `install` nebo `run` zatím nejsou prostřednictvím této metody podporovány, protože by vedly k instalaci či spuštění program uvnitř kontejneru namísto přímo v systému.
## Struktura souborů

View File

@@ -108,33 +108,6 @@ mv libsciter-gtk.so target/debug
cargo run
```
### Skift Wayland til X11 (Xorg)
RustDesk understøtter ikke Wayland. Tjek [dette](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) for at konfigurere Xorg som standard GNOME-session.
## Wayland-support
Wayland ser ikke ud til at levere nogen API til at sende tastetryk til andre vinduer. Derfor bruger rustdesk et API fra et lavere niveau, nemlig `/dev/uinput`-enheden (Linux-kerneniveau).
Når wayland er den kontrollerede side, skal du starte på følgende måde:
```bash
# Start uinput service
$ sudo rustdesk --service
$ rustdesk
```
**Bemærk**: Wayland-skærmoptagelse bruger forskellige grænseflader. RustDesk understøtter i øjeblikket kun org.freedesktop.portal.ScreenCast.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Not support
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Support
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## Sådan bygger du med Docker
```sh

View File

@@ -133,34 +133,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Wayland zu X11 (Xorg) ändern
RustDesk unterstützt Wayland nicht. Siehe [hier](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/), um Xorg als Standard-GNOME-Sitzung zu nutzen.
## Wayland-Unterstützung
Wayland scheint keine API für das Senden von Tastatureingaben an andere Fenster zu bieten. Daher verwendet RustDesk eine API von einer niedrigeren Ebene, nämlich dem Gerät `/dev/uinput` (Linux-Kernelebene).
Wenn Wayland die kontrollierte Seite ist, müssen Sie wie folgt vorgehen:
```bash
# Dienst uinput starten
$ sudo rustdesk --service
$ rustdesk
```
**Hinweis**: Die Wayland-Bildschirmaufnahme verwendet verschiedene Schnittstellen. RustDesk unterstützt derzeit nur org.freedesktop.portal.ScreenCast.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Keine Unterstützung
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Unterstützung
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## Auf Docker kompilieren
Beginnen Sie damit, das Repository zu klonen und den Docker-Container zu bauen:

View File

@@ -104,10 +104,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Ŝanĝi Wayland por X11 (Xorg)
RustDesk ne subtenas Wayland. Kontrolu [tion](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) por agordi Xorg kiel defaŭlta sesio GNOME.
## Kiel kompili kun Docker
Komencu klonante la deponejon kaj kompilu la konteneron Docker:

View File

@@ -113,34 +113,6 @@ mv libsciter-gtk.so target/debug
cargo run
```
### Cambia Wayland a X11 (Xorg)
RustDesk no soporta Wayland. Lee [esto](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) para configurar Xorg en la sesión por defecto de GNOME.
## Soporte para Wayland
Wayland no parece proporcionar ninguna API para enviar pulsaciones de teclas a otras ventanas. Por lo tanto, rustdesk usa una API de nivel bajo, a saber, el dispositivo `/dev/uinput` (a nivel del kernel de Linux).
Cuando wayland esta del lado controlado, hay que iniciar de la siguiente manera:
```bash
# Empezar el servicio uinput
$ sudo rustdesk --service
$ rustdesk
```
**Aviso**: La grabación de pantalla de Wayland utiliza diferentes interfaces. RustDesk actualmente sólo soporta org.freedesktop.portal.ScreenCast
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# No soportado
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Soportado
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## Como compilar con Docker
Empieza clonando el repositorio y compilando el contenedor de docker:

View File

@@ -112,10 +112,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### تغییر Wayland به (X11 (Xorg
راست‌دسک از Wayland پشتیبانی نمی کند. برای جایگزنی Xorg به عنوان پیش‌فرض GNOM، [اینجا](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) را کلیک کنید.
## نحوه ساخت با داکر
این مخزن Git را دریافت کنید و کانتینر را به روش زیر بسازید

View File

@@ -104,10 +104,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Vaihda Wayland-ympäristö X11 (Xorg)-ympäristöön
RustDesk ei tue Waylandia. Tarkista [tämä](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) asettamalla Xorg oletus GNOME-istuntoon.
## Kuinka rakennetaan Dockerin kanssa
Aloita kloonaamalla tietovarasto ja rakentamalla docker-säiliö:

View File

@@ -104,10 +104,6 @@ mv libsciter-gtk.so target/debug
Exécution du cargo
```
### Changer Wayland en X11 (Xorg)
RustDesk ne supporte pas Wayland. Lisez [cela](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) pour configurer Xorg comme la session GNOME par défaut.
## Comment construire avec Docker
Commencez par cloner le dépôt et construire le conteneur Docker :

View File

@@ -133,34 +133,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Αλλαγή του Wayland σε X11 (Xorg)
Το RustDesk δεν υποστηρίζει το πρωτόκολλο Wayland. Διαβάστε [εδώ](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) ώστε να ορίσετε το Xorg ως το προκαθορισμένο GNOME περιβάλλον.
## Υποστήριξη Wayland
Το Wayland προς το παρόν δεν διαθέτει κάποιο API το οποίο να στέλνει τα πατήματα πλήκτρων στα υπόλοιπα παράθυρα. Για τον λόγο αυτό, το Rustdesk χρησιμοποιεί ένα API από κατώτερο επίπεδο, όπως το `/dev/uinput` (Linux kernel level).
Σε περίπτωση που το Wayland είναι η ελεγχόμενη πλευρά, θα πρέπει να ξεκινήσετε με τον παρακάτω τρόπο:
```bash
# Start uinput service
$ sudo rustdesk --service
$ rustdesk
```
**Σημείωση**: Η εγγραφή οθόνης του Wayland χρησιμοποιεί διαφορετικές διεπαφές. Το RustDesk προς το παρόν υποστηρίζει μόνο org.freedesktop.portal.ScreenCast.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Not support
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Support
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## Πως να κάνετε build στο Docker
Ξεκινήστε κλωνοποιώντας το αποθετήριο και κάνοντας build το docker container:
@@ -189,7 +161,7 @@ target/debug/rustdesk
target/release/rustdesk
```
Βεβαιωθείτε ότι εκτελείτε αυτές τις εντολές από την αρχική διαδρομή του αποθετηρίου του Rustdesk, διαφορετικά η εφαρμογή ενδέχεται να μην είναι σε θέση να βρεί τους απαιτούμενους πόρους. Σημειώστε επίσης ότι άλλες υποεντολές, όπως το `install` ή το `run` δεν υποστηρίζονται επί του παρόντος μέσω αυτής της μεθόδου καθώς θα εγκαταστήσουν ή θα εκτελέσουν το πρόγραμμα εντός του container αντί του κεντρικού υπολογιστή.
Βεβαιωθείτε ότι εκτελείτε αυτές τις εντολές από την αρχική διαδρομή του αποθετηρίου του RustDesk, διαφορετικά η εφαρμογή ενδέχεται να μην είναι σε θέση να βρεί τους απαιτούμενους πόρους. Σημειώστε επίσης ότι άλλες υποεντολές, όπως το `install` ή το `run` δεν υποστηρίζονται επί του παρόντος μέσω αυτής της μεθόδου καθώς θα εγκαταστήσουν ή θα εκτελέσουν το πρόγραμμα εντός του container αντί του κεντρικού υπολογιστή.
## Δομή φακέλων

View File

@@ -116,10 +116,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Válts Wayland-ról X11-re (Xorg)
A RustDesk nem támogatja a Waylendet. [Itt](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) található egy tutorial amelynek segítségével beállíthatod a Xorg-ot mint alap GNOME session.
## Hogyan építs Dockerrel
Kezdjünk a repo clónozásával, majd pedig a Docker container megépítésével:

View File

@@ -128,37 +128,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Mengubah Wayland ke X11 (Xorg)
RustDesk tidak mendukung Wayland. Cek [ini](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) untuk mengonfigurasi Xorg sebagai sesi standar di GNOME.
## Kompatibilitas dengan Wayland
Sepertinya Wayland tidak memiliki API untuk mengirimkan ketukan tombol ke jendela lain. Maka dari itu, RustDesk menggunakan API dari level yang lebih rendah, lebih tepatnya perangkat `/dev/uinput` (linux kernel level)
Saat Wayland menjadi sisi yang dikendalikan atau sisi yang sedang diremote, kamu harus memulai dengan cara ini
```bash
# Start uinput service
$ sudo rustdesk --service
$ rustdesk
```
**Harap Diperhatikan**: Saat Perekaman layar menggunakan Wayland antarmuka (UI) yang ditampilkan akan berbeda. Untuk saat ini RustDesk hanya mendukung org.freedesktop.portal.ScreenCast.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Not support
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Support
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## Cara Build dengan Docker
Mulailah dengan melakukan kloning (clone) repositori dan build dengan docker container:

View File

@@ -109,11 +109,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Cambiare Wayland in X11 (Xorg)
RustDesk non supporta Wayland.
Controlla [qui](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) per configurare Xorg come sessione predefinita di GNOME.
## Come compilare con Docker
Clona il repository e compila i container docker:

View File

@@ -114,11 +114,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Wayland の場合、X11Xorgに変更します
RustDeskはWaylandをサポートしていません。
[こちら](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) を確認して、XorgをデフォルトのGNOMEセッションとして構成します。
## Dockerでビルドする方法
リポジトリのクローンを作成し、Dockerコンテナを構築することから始めます。

View File

@@ -112,10 +112,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Wayland 일 경우, X11(Xorg)로 변경
RustDesk는 Wayland를 지원하지 않습니다. [링크](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/)를 확인해서 Xorg 기본값의 GNOME 세션을 구성합니다.
## Docker에 빌드하는 방법
레포지토리를 클론하고, Docker 컨테이너 구성하는 것으로 시작합니다.

View File

@@ -103,10 +103,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### വേലാൻഡ് X11 (Xorg) ആയി മാറ്റുക
RustDesk Wayland-നെ പിന്തുണയ്ക്കുന്നില്ല. സ്ഥിരസ്ഥിതി ഗ്നോം സെഷനായി Xorg കോൺഫിഗർ ചെയ്യുന്നതിന് [ഇത്](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) പരിശോധിക്കുക.
## ഡോക്കർ ഉപയോഗിച്ച് എങ്ങനെ നിർമ്മിക്കാം
റെപ്പോസിറ്റോറി ക്ലോണുചെയ്‌ത് ഡോക്കർ കണ്ടെയ്‌നർ നിർമ്മിക്കുന്നതിലൂടെ ആരംഭിക്കുക:

View File

@@ -130,34 +130,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Wissel van Wayland naar X11 (Xorg)
RustDesk ondersteunt Wayland niet. Lees [hier](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) hoe je Xorg als standaardsessie kunt instellen voor GNOME.
## Wayland support
Wayland lijkt geen API te bieden voor het verzenden van toetsaanslagen naar andere vensters. Daarom gebruikt de rustdesk een API van een lager niveau, namelijk het `/dev/uinput` apparaat (Linux kernel niveau).
Als wayland de gecontroleerde kant is, moet je op de volgende manier beginnen:
```bash
# Start uinput service
$ sudo rustdesk --service
$ rustdesk
```
**Let op**: Wayland schermopname gebruikt verschillende interfaces. RustDesk ondersteunt momenteel alleen org.freedesktop.portal.ScreenCast.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Not support
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Support
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## Bouwen met Docker
Begin met het klonen van de repository en het bouwen van de docker container:

View File

@@ -128,34 +128,6 @@ mv libsciter-gtk.so target/debug
cargo run
```
### Zmień Wayland na X11 (Xorg)
RustDesk nie obsługuje Waylanda. Sprawdź [tutaj](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/), jak skonfigurować Xorg jako domyślną sesję GNOME.
## Wspracie Wayland
Wygląda na to, że Wayland nie wspiera żadnego API do wysyłania naciśnięć klawiszy do innych okien. Dlatego rustdesk używa API z niższego poziomu, urządzenia o nazwie `/dev/uinput` (poziom jądra Linux).
Gdy po stronie kontrolowanej pracuje Wayland, musisz uruchomić program w następujący sposób:
```bash
# Start uinput service
$ sudo rustdesk --service
$ rustdesk
```
**Uwaga**: Nagrywanie ekranu Wayland wykorzystuje różne interfejsy. RustDesk obecnie obsługuje tylko org.freedesktop.portal.ScreenCast.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Not support
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Support
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## Jak kompilować za pomocą Dockera
Rozpocznij od sklonowania repozytorium i stworzenia kontenera docker:

View File

@@ -104,10 +104,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Mude Wayland para X11 (Xorg)
RustDesk não suporta Wayland. Veja [esse link](https://docs.fedoraproject.org/pt_BR/quick-docs/configuring-xorg-as-default-gnome-session/) para configurar o Xorg como a sessão padrão do GNOME.
## Como compilar com Docker
Comece clonando o repositório e montando o container docker:

View File

@@ -114,10 +114,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Смените Wayland на X11 (Xorg)
RustDesk не поддерживает Wayland. Смотрите [этот документ](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) для настройки Xorg в качестве сеанса GNOME по умолчанию.
## Как собрать с помощью Docker
Начните с клонирования репозитория и создания docker-контейнера:

View File

@@ -138,34 +138,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Wayland'ı X11 (Xorg) Olarak Değiştirme
RustDesk, Wayland'ı desteklemez. Xorg'u GNOME oturumu olarak varsayılan olarak ayarlamak için [burayı](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) kontrol edin.
## Wayland Desteği
Wayland'ın diğer pencerelere tuş vuruşu göndermek için herhangi bir API sağlamadığı görünmektedir. Bu nedenle, RustDesk daha düşük bir seviyeden, yani Linux çekirdek seviyesindeki `/dev/uinput` cihazının API'sini kullanır.
Wayland tarafı kontrol edildiğinde, aşağıdaki şekilde başlatmanız gerekir:
```bash
# uinput servisini başlatın
$ sudo rustdesk --service
$ rustdesk
```
**Uyarı**: Wayland ekran kaydı farklı arayüzler kullanır. RustDesk şu anda yalnızca org.freedesktop.portal.ScreenCast'ı destekler.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Desteklenmez
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Desteklenir
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## Docker ile Derleme Nasıl Yapılır
Öncelikle deposunu klonlayın ve Docker konteynerini oluşturun:

View File

@@ -1,29 +1,31 @@
<p align="center">
<img src="../res/logo-header.svg" alt="RustDesk - Ваш віддалений робочий стіл"><br>
<img src="../res/logo-header.svg" alt="RustDesk - Ваша віддалена стільниця"><br>
<a href="#безкоштовні-загальнодоступні-сервери">Сервери</a> •
<a href="#первинні-кроки-для-складання">Складання</a> •
<a href="#кроки-для-збірки">Збирання</a> •
<a href="#як-зібрати-за-допомогою-docker">Docker</a> •
<a href="#структура-файлів">Структура</a> •
<a href="#знімки">Знімки</a><br>
[<a href="../README.md">English</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
<b>Нам потрібна ваша допомога для перекладу цього README і <a href="https://github.com/rustdesk/rustdesk/tree/master/src/rustdesk/tree/master/src/lang">RustDesk UI</a> на вашу рідну мову</B>
[<a href="../README.md">English</a>] | [<a href="docs/README-CS.md">česky</a>] | [<a href="docs/README-ZH.md">中文</a>] | [<a href="docs/README-HU.md">Magyar</a>] | [<a href="docs/README-ES.md">Español</a>] | [<a href="docs/README-FA.md">فارسی</a>] | [<a href="docs/README-FR.md">Français</a>] | [<a href="docs/README-DE.md">Deutsch</a>] | [<a href="docs/README-PL.md">Polski</a>] | [<a href="docs/README-ID.md">Indonesian</a>] | [<a href="docs/README-FI.md">Suomi</a>] | [<a href="docs/README-ML.md">മലയാളം</a>] | [<a href="docs/README-JP.md">日本語</a>] | [<a href="docs/README-NL.md">Nederlands</a>] | [<a href="docs/README-IT.md">Italiano</a>] | [<a href="docs/README-RU.md">Русский</a>] | [<a href="docs/README-PTBR.md">Português (Brasil)</a>] | [<a href="docs/README-EO.md">Esperanto</a>] | [<a href="docs/README-KR.md">한국어</a>] | [<a href="docs/README-AR.md">العربي</a>] | [<a href="docs/README-VN.md">Tiếng Việt</a>] | [<a href="docs/README-DA.md">Dansk</a>] | [<a href="docs/README-GR.md">Ελληνικά</a>] | [<a href="docs/README-TR.md">Türkçe</a>]<br>
<b>Нам потрібна ваша допомога для перекладу цього README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">інтерфейсу</a> та <a href="https://github.com/rustdesk/doc.rustdesk.com">документації</a> RustDesk на вашу рідну мову</B>
</p>
Спілкування з нами: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
Ще одне програмне забезпечення для віддаленого робочого столу, написане на Rust. Працює з коробки, не потребує налаштування. Ви повністю контролюєте свої дані, не турбуючись про безпеку. Ви можете використовувати наш сервер ретрансляції, [налаштувати свій власний](https://rustdesk.com/server), або [написати свій власний сервер ретрансляції](https://github.com/rustdesk/rustdesk-server-demo).
[![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Frustdesk%2Fbounties%3Fstatus%3Dopen)](https://console.algora.io/org/rustdesk/bounties?status=open)
Ще один застосунок для віддаленого керування стільницею, написаний на Rust. Працює з коробки, не потребує налаштування. Ви повністю контролюєте свої дані, не турбуючись про безпеку. Ви можете використовувати наш сервер ретрансляції, [налаштувати свій власний](https://rustdesk.com/server), або [написати свій власний сервер ретрансляції](https://github.com/rustdesk/rustdesk-server-demo).
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
RustDesk вітає внесок кожного. Дивіться [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) для допомоги на початку роботи.
RustDesk вітає внесок кожного. Ознайомтеся з [CONTRIBUTING.md](docs/CONTRIBUTING.md), щоб отримати допомогу на початковому етапі.
[**FAQ**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
[**ЧаПи**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
[**Як працює RustDesk?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
[**ЗАВАНТАЖЕННЯ ЗАСТОСУНКУ**](https://github.com/rustdesk/rustdesk/releases)
[**ЗАВАНТАЖИТИ ЗАСТОСУНОК**](https://github.com/rustdesk/rustdesk/releases)
[**НІЧНІ ЗБІРКИ**](https://github.com/rustdesk/rustdesk/releases/tag/nightly)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
@@ -34,38 +36,40 @@ RustDesk вітає внесок кожного. Дивіться [`docs/CONTRIB
Нижче наведені сервери, для безкоштовного використання, вони можуть змінюватися з часом. Якщо ви не перебуваєте поруч з одним із них, ваша мережа може працювати повільно.
| Місцезнаходження | Постачальник | Технічні характеристики |
| --------- | ------------- | ------------------ |
| Німеччина | Hetzner | 2 VCPU / 4GB RAM |
| Німеччина | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4GB RAM |
| Україна (Київ) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dev Container
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
Якщо у вас уже встановлено VS Code і Docker, ви можете натиснути значок вище, щоб почати. Клацання призведе до того, що VS Code автоматично встановить розширення Dev Containers, якщо це необхідно, клонує виcхідний код у том контейнера та розгорне контейнер dev для використання.
Якщо у вас уже встановлено VS Code та Docker, ви можете натиснути значок вище, щоб розпочати. Клацання призведе до того, що VS Code автоматично встановить розширення Dev Containers, якщо це необхідно, клонує вихідний код у том контейнера та розгорне контейнер dev для використання.
Дивіться [DEVCONTAINER.md](docs/DEVCONTAINER.md) для додаткової інфо.
Дивіться [DEVCONTAINER.md](docs/DEVCONTAINER.md) для додаткової інформації
## Залежності
Настільні версії використовують [sciter](https://sciter.com/) для графічного інтерфейсу, завантажте динамічну бібліотеку sciter самостійно.
Стільничні версії використовують Flutter чи Sciter (застаріле) для графічного інтерфейсу. Ця інструкція лише для Sciter, оскільки він є більш простим та дружнім для початківців. Перегляньте [CI](https://github.com/rustdesk/rustdesk/blob/master/.github/workflows/flutter-build.yml) для збірки версії на Flutter.
Будь ласка, завантажте динамічну бібліотеку Sciter самостійно.
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
[macOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
Мобільні версії використовують Flutter. У майбутньому ми перенесемо настільну версію зі Sciter на Flutter.
## Кроки для збірки
## Первинні кроки для складання
- Підготуйте середовище розробки Rust і середовище збірки C++.
- Підготуйте середовище розробки Rust і середовище збирання C++.
- Встановіть [vcpkg](https://github.com/microsoft/vcpkg), і правильно встановіть змінну `VCPKG_ROOT`.
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
- Linux/MacOS: vcpkg install libvpx libyuv opus aom
- Linux/macOS: vcpkg install libvpx libyuv opus aom
- Запустіть `cargo run`
## [Збирання](https://rustdesk.com/docs/en/dev/build/)
## Як зібрати на Linux
### Ubuntu 18 (Debian 10)
@@ -76,11 +80,12 @@ sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxc
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
```
### openSUSE Tumbleweed
### openSUSE Tumbleweed
```sh
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
```
### Fedora 28 (CentOS 8)
```sh
@@ -99,7 +104,7 @@ sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-c
git clone https://github.com/microsoft/vcpkg
cd vcpkg
git checkout 2023.04.15
cd ...
cd ..
vcpkg/bootstrap-vcpkg.sh
export VCPKG_ROOT=$HOME/vcpkg
vcpkg/vcpkg install libvpx libyuv opus aom
@@ -118,7 +123,7 @@ cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/
cd
```
### Збірка
### Збирання
```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
@@ -131,10 +136,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Змініть Wayland на X11 (Xorg)
RustDesk не підтримує Wayland. Дивіться [цей документ](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) для налаштування Xorg як сеансу GNOME за замовчуванням.
## Як зібрати за допомогою Docker
Почніть з клонування сховища та створення docker-контейнера:
@@ -145,7 +146,7 @@ cd rustdesk
docker build -t "rustdesk-builder" .
```
Потім кожного разу, коли вам потрібно зібрати додаток, запускайте таку команду:
Надалі щоразу, коли вам буде потрібно зібрати застосунок, запускайте таку команду:
```sh
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
@@ -170,6 +171,7 @@ target/release/rustdesk
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: відеокодек, конфіг, обгортка tcp/udp, protobuf, функції fs для передавання файлів і деякі інші службові функції
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: захоплення екрана
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: специфічне для платформи керування клавіатурою/мишею
- **[libs/clipboard](https://github.com/rustdesk/rustdesk/tree/master/libs/clipboard)**: реалізація копіювання та вставлення файлів для Windows, Linux, macOS.
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: графічний інтерфейс користувача
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: сервіси аудіо/буфера обміну/вводу/відео та мережевих підключень
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: однорангове з'єднання

View File

@@ -116,10 +116,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Chuyển từ Wayland sang X11 (Xorg)
RustDesk hiện không hỗ trợ Wayland. Hãy xem [đường linh ở đây](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) cách để cài đặt Xorg làm session mặc định của GNOME.
## Cách để build sử dụng Docker
Bắt đầu bằng cách sao chép repo này về máy tính và build cái Docker cointainer:

View File

@@ -18,7 +18,7 @@ Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https:
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
RustDesk 期待各位的贡献. 如何参与开发? 详情请看 [CONTRIBUTING.md](docs/CONTRIBUTING.md).
RustDesk 期待各位的贡献. 如何参与开发? 详情请看 [CONTRIBUTING.md](CONTRIBUTING.md).
[**FAQ**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
@@ -134,39 +134,6 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### 把 Wayland 修改成 X11 (Xorg)
RustDesk 暂时不支持 Wayland不过正在积极开发中。
> [点我](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/)
查看如何将 Xorg 设置成默认的 GNOME session.
## Wayland 支持
Wayland 似乎没有提供任何将按键发送到其他窗口的 API. 因此, RustDesk 使用较低级别的 API, 即 `/dev/uinput` devices (Linux kernal level).
当 Wayland 是受控方时,您必须以下列方式开始操作:
```bash
# Start uinput service
$ sudo rustdesk --service
$ rustdesk
```
**Notice**: Wayland 屏幕录制使用不同的接口. RustDesk 目前只支持 org.freedesktop.portal.ScreenCast.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Not support
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Support
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## 使用 Docker 编译
克隆版本库并构建 Docker 容器:

9
docs/SECURITY-JP.md Normal file
View File

@@ -0,0 +1,9 @@
# セキュリティポリシー
## 脆弱性の報告
私たちはプロジェクトのセキュリティを非常に重視しています。私たちは、すべてのユーザーが脆弱性を発見した場合、私たちに報告することを奨励しています。
RustDesk プロジェクトにセキュリティの脆弱性を発見した場合は、info@rustdesk.com までメールで責任を持って報告してください。
現時点では、バグ報奨金制度はありません。私たちは大きな問題を解決しようとしている小さなチームです。コミュニティ全体のために安全なアプリケーションを作り続けることができるよう、
責任を持って脆弱性を報告してください。

View File

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

View File

@@ -13,4 +13,4 @@ A few resources to get you started if this is your first Flutter project:
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
samples and guidance on mobile development, and a full API reference.

View File

@@ -1,3 +1,8 @@
import com.google.protobuf.gradle.*
plugins {
id "com.google.protobuf" version "0.9.4"
}
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@@ -31,10 +36,33 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
dependencies {
implementation 'com.google.protobuf:protobuf-javalite:3.20.1'
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.20.1'
}
generateProtoTasks {
all().configureEach { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}
android {
compileSdkVersion 33
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
main.proto.srcDirs += '../../../libs/hbb_common/protos'
main.proto.includes += "message.proto"
}
compileOptions {
@@ -65,6 +93,7 @@ android {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules'
}
}
}
@@ -75,7 +104,7 @@ flutter {
dependencies {
implementation "androidx.media:media:1.6.0"
implementation 'com.github.getActivity:XXPermissions:16.2'
implementation 'com.github.getActivity:XXPermissions:18.5'
implementation("org.jetbrains.kotlin:kotlin-stdlib") { version { strictly("$kotlin_version") } }
}

View File

@@ -0,0 +1,4 @@
# Keep class members from protobuf generated code.
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {
<fields>;
}

View File

@@ -61,6 +61,14 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Intent for deep linking-->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="rustdesk" />
</intent-filter>
</activity>
<activity
@@ -81,4 +89,4 @@
android:value="2" />
</application>
</manifest>
</manifest>

View File

@@ -10,12 +10,27 @@ import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.graphics.Path
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.EditText
import android.view.accessibility.AccessibilityEvent
import android.view.ViewGroup.LayoutParams
import android.view.accessibility.AccessibilityNodeInfo
import android.graphics.Rect
import android.accessibilityservice.AccessibilityServiceInfo
import android.accessibilityservice.AccessibilityServiceInfo.FLAG_INPUT_METHOD_EDITOR
import android.accessibilityservice.AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS
import android.view.inputmethod.EditorInfo
import androidx.annotation.RequiresApi
import java.util.*
import java.lang.Character
import kotlin.math.abs
import kotlin.math.max
import hbb.MessageOuterClass.KeyEvent
import hbb.MessageOuterClass.KeyboardMode
import hbb.KeyEventConverter
const val LIFT_DOWN = 9
const val LIFT_MOVE = 8
@@ -58,6 +73,8 @@ class InputService : AccessibilityService() {
private var isWheelActionsPolling = false
private var isWaitingLongPress = false
private var fakeEditTextForTextStateCalculation: EditText? = null
@RequiresApi(Build.VERSION_CODES.N)
fun onMouseInput(mask: Int, _x: Int, _y: Int) {
val x = max(0, _x)
@@ -252,9 +269,296 @@ class InputService : AccessibilityService() {
}
}
@RequiresApi(Build.VERSION_CODES.N)
fun onKeyEvent(data: ByteArray) {
val keyEvent = KeyEvent.parseFrom(data)
val keyboardMode = keyEvent.getMode()
var textToCommit: String? = null
if (keyboardMode == KeyboardMode.Legacy) {
if (keyEvent.hasChr() && keyEvent.getDown()) {
val chr = keyEvent.getChr()
if (chr != null) {
textToCommit = String(Character.toChars(chr))
}
}
} else if (keyboardMode == KeyboardMode.Translate) {
if (keyEvent.hasSeq() && keyEvent.getDown()) {
val seq = keyEvent.getSeq()
if (seq != null) {
textToCommit = seq
}
}
}
Log.d(logTag, "onKeyEvent $keyEvent textToCommit:$textToCommit")
if (Build.VERSION.SDK_INT >= 33) {
getInputMethod()?.let { inputMethod ->
inputMethod.getCurrentInputConnection()?.let { inputConnection ->
if (textToCommit != null) {
textToCommit?.let { text ->
inputConnection.commitText(text, 1, null)
}
} else {
KeyEventConverter.toAndroidKeyEvent(keyEvent).let { event ->
inputConnection.sendKeyEvent(event)
}
}
}
}
} else {
val handler = Handler(Looper.getMainLooper())
handler.post {
KeyEventConverter.toAndroidKeyEvent(keyEvent)?.let { event ->
val possibleNodes = possibleAccessibiltyNodes()
Log.d(logTag, "possibleNodes:$possibleNodes")
for (item in possibleNodes) {
val success = trySendKeyEvent(event, item, textToCommit)
if (success) {
break
}
}
}
}
}
}
private fun insertAccessibilityNode(list: LinkedList<AccessibilityNodeInfo>, node: AccessibilityNodeInfo) {
if (node == null) {
return
}
if (list.contains(node)) {
return
}
list.add(node)
}
private fun findChildNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
if (node == null) {
return null
}
if (node.isEditable() && node.isFocusable()) {
return node
}
val childCount = node.getChildCount()
for (i in 0 until childCount) {
val child = node.getChild(i)
if (child != null) {
if (child.isEditable() && child.isFocusable()) {
return child
}
if (Build.VERSION.SDK_INT < 33) {
child.recycle()
}
}
}
for (i in 0 until childCount) {
val child = node.getChild(i)
if (child != null) {
val result = findChildNode(child)
if (Build.VERSION.SDK_INT < 33) {
if (child != result) {
child.recycle()
}
}
if (result != null) {
return result
}
}
}
return null
}
private fun possibleAccessibiltyNodes(): LinkedList<AccessibilityNodeInfo> {
val linkedList = LinkedList<AccessibilityNodeInfo>()
val latestList = LinkedList<AccessibilityNodeInfo>()
val focusInput = findFocus(AccessibilityNodeInfo.FOCUS_INPUT)
var focusAccessibilityInput = findFocus(AccessibilityNodeInfo.FOCUS_ACCESSIBILITY)
val rootInActiveWindow = getRootInActiveWindow()
Log.d(logTag, "focusInput:$focusInput focusAccessibilityInput:$focusAccessibilityInput rootInActiveWindow:$rootInActiveWindow")
if (focusInput != null) {
if (focusInput.isFocusable() && focusInput.isEditable()) {
insertAccessibilityNode(linkedList, focusInput)
} else {
insertAccessibilityNode(latestList, focusInput)
}
}
if (focusAccessibilityInput != null) {
if (focusAccessibilityInput.isFocusable() && focusAccessibilityInput.isEditable()) {
insertAccessibilityNode(linkedList, focusAccessibilityInput)
} else {
insertAccessibilityNode(latestList, focusAccessibilityInput)
}
}
val childFromFocusInput = findChildNode(focusInput)
Log.d(logTag, "childFromFocusInput:$childFromFocusInput")
if (childFromFocusInput != null) {
insertAccessibilityNode(linkedList, childFromFocusInput)
}
val childFromFocusAccessibilityInput = findChildNode(focusAccessibilityInput)
if (childFromFocusAccessibilityInput != null) {
insertAccessibilityNode(linkedList, childFromFocusAccessibilityInput)
}
Log.d(logTag, "childFromFocusAccessibilityInput:$childFromFocusAccessibilityInput")
if (rootInActiveWindow != null) {
insertAccessibilityNode(linkedList, rootInActiveWindow)
}
for (item in latestList) {
insertAccessibilityNode(linkedList, item)
}
return linkedList
}
private fun trySendKeyEvent(event: android.view.KeyEvent, node: AccessibilityNodeInfo, textToCommit: String?): Boolean {
node.refresh()
this.fakeEditTextForTextStateCalculation?.setSelection(0,0)
this.fakeEditTextForTextStateCalculation?.setText(null)
val text = node.getText()
var isShowingHint = false
if (Build.VERSION.SDK_INT >= 26) {
isShowingHint = node.isShowingHintText()
}
var textSelectionStart = node.textSelectionStart
var textSelectionEnd = node.textSelectionEnd
if (text != null) {
if (textSelectionStart > text.length) {
textSelectionStart = text.length
}
if (textSelectionEnd > text.length) {
textSelectionEnd = text.length
}
if (textSelectionStart > textSelectionEnd) {
textSelectionStart = textSelectionEnd
}
}
var success = false
Log.d(logTag, "existing text:$text textToCommit:$textToCommit textSelectionStart:$textSelectionStart textSelectionEnd:$textSelectionEnd")
if (textToCommit != null) {
if ((textSelectionStart == -1) || (textSelectionEnd == -1)) {
val newText = textToCommit
this.fakeEditTextForTextStateCalculation?.setText(newText)
success = updateTextForAccessibilityNode(node)
} else if (text != null) {
this.fakeEditTextForTextStateCalculation?.setText(text)
this.fakeEditTextForTextStateCalculation?.setSelection(
textSelectionStart,
textSelectionEnd
)
this.fakeEditTextForTextStateCalculation?.text?.insert(textSelectionStart, textToCommit)
success = updateTextAndSelectionForAccessibiltyNode(node)
}
} else {
if (isShowingHint) {
this.fakeEditTextForTextStateCalculation?.setText(null)
} else {
this.fakeEditTextForTextStateCalculation?.setText(text)
}
if (textSelectionStart != -1 && textSelectionEnd != -1) {
Log.d(logTag, "setting selection $textSelectionStart $textSelectionEnd")
this.fakeEditTextForTextStateCalculation?.setSelection(
textSelectionStart,
textSelectionEnd
)
}
this.fakeEditTextForTextStateCalculation?.let {
// This is essiential to make sure layout object is created. OnKeyDown may not work if layout is not created.
val rect = Rect()
node.getBoundsInScreen(rect)
it.layout(rect.left, rect.top, rect.right, rect.bottom)
it.onPreDraw()
if (event.action == android.view.KeyEvent.ACTION_DOWN) {
val succ = it.onKeyDown(event.getKeyCode(), event)
Log.d(logTag, "onKeyDown $succ")
} else if (event.action == android.view.KeyEvent.ACTION_UP) {
val success = it.onKeyUp(event.getKeyCode(), event)
Log.d(logTag, "keyup $success")
} else {}
}
success = updateTextAndSelectionForAccessibiltyNode(node)
}
return success
}
fun updateTextForAccessibilityNode(node: AccessibilityNodeInfo): Boolean {
var success = false
this.fakeEditTextForTextStateCalculation?.text?.let {
val arguments = Bundle()
arguments.putCharSequence(
AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
it.toString()
)
success = node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
}
return success
}
fun updateTextAndSelectionForAccessibiltyNode(node: AccessibilityNodeInfo): Boolean {
var success = updateTextForAccessibilityNode(node)
if (success) {
val selectionStart = this.fakeEditTextForTextStateCalculation?.selectionStart
val selectionEnd = this.fakeEditTextForTextStateCalculation?.selectionEnd
if (selectionStart != null && selectionEnd != null) {
val arguments = Bundle()
arguments.putInt(
AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT,
selectionStart
)
arguments.putInt(
AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT,
selectionEnd
)
success = node.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments)
Log.d(logTag, "Update selection to $selectionStart $selectionEnd success:$success")
}
}
return success
}
override fun onAccessibilityEvent(event: AccessibilityEvent) {
}
override fun onServiceConnected() {
super.onServiceConnected()
ctx = this
val info = AccessibilityServiceInfo()
if (Build.VERSION.SDK_INT >= 33) {
info.flags = FLAG_INPUT_METHOD_EDITOR or FLAG_RETRIEVE_INTERACTIVE_WINDOWS
} else {
info.flags = FLAG_RETRIEVE_INTERACTIVE_WINDOWS
}
setServiceInfo(info)
fakeEditTextForTextStateCalculation = EditText(this)
// Size here doesn't matter, we won't show this view.
fakeEditTextForTextStateCalculation?.layoutParams = LayoutParams(100, 100)
fakeEditTextForTextStateCalculation?.onPreDraw()
val layout = fakeEditTextForTextStateCalculation?.getLayout()
Log.d(logTag, "fakeEditTextForTextStateCalculation layout:$layout")
Log.d(logTag, "onServiceConnected!")
}
@@ -263,7 +567,5 @@ class InputService : AccessibilityService() {
super.onDestroy()
}
override fun onAccessibilityEvent(event: AccessibilityEvent?) {}
override fun onInterrupt() {}
}

View File

@@ -0,0 +1,118 @@
package hbb;
import android.view.KeyEvent
import android.view.KeyCharacterMap
import hbb.MessageOuterClass.KeyboardMode
import hbb.MessageOuterClass.ControlKey
object KeyEventConverter {
fun toAndroidKeyEvent(keyEventProto: hbb.MessageOuterClass.KeyEvent): KeyEvent {
var chrValue = 0
var modifiers = 0
val keyboardMode = keyEventProto.getMode()
if (keyEventProto.hasChr()) {
if (keyboardMode == KeyboardMode.Map || keyboardMode == KeyboardMode.Translate) {
chrValue = keyEventProto.getChr()
} else {
chrValue = convertUnicodeToKeyCode(keyEventProto.getChr() as Int)
}
} else if (keyEventProto.hasControlKey()) {
chrValue = convertControlKeyToKeyCode(keyEventProto.getControlKey())
}
var modifiersList = keyEventProto.getModifiersList()
if (modifiersList != null) {
for (modifier in keyEventProto.getModifiersList()) {
val modifierValue = convertModifier(modifier)
modifiers = modifiers or modifierValue
}
}
var action = 0
if (keyEventProto.getDown()) {
action = KeyEvent.ACTION_DOWN
} else {
action = KeyEvent.ACTION_UP
}
return KeyEvent(0, 0, action, chrValue, 0, modifiers)
}
private fun convertModifier(controlKey: hbb.MessageOuterClass.ControlKey): Int {
// Add logic to map ControlKey values to Android KeyEvent key codes.
// You'll need to provide the mapping for each key.
return when (controlKey) {
ControlKey.Alt -> KeyEvent.META_ALT_ON
ControlKey.Control -> KeyEvent.META_CTRL_ON
ControlKey.CapsLock -> KeyEvent.META_CAPS_LOCK_ON
ControlKey.Meta -> KeyEvent.META_META_ON
ControlKey.NumLock -> KeyEvent.META_NUM_LOCK_ON
ControlKey.RShift -> KeyEvent.META_SHIFT_RIGHT_ON
ControlKey.Shift -> KeyEvent.META_SHIFT_ON
ControlKey.RAlt -> KeyEvent.META_ALT_RIGHT_ON
ControlKey.RControl -> KeyEvent.META_CTRL_RIGHT_ON
else -> 0 // Default to unknown.
}
}
private val tag = "KeyEventConverter"
private fun convertUnicodeToKeyCode(unicode: Int): Int {
val charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD)
android.util.Log.d(tag, "unicode: $unicode")
val events = charMap.getEvents(charArrayOf(unicode.toChar()))
if (events != null && events.size > 0) {
android.util.Log.d(tag, "keycode ${events[0].keyCode}")
return events[0].keyCode
}
return 0
}
private fun convertControlKeyToKeyCode(controlKey: hbb.MessageOuterClass.ControlKey): Int {
// Add logic to map ControlKey values to Android KeyEvent key codes.
// You'll need to provide the mapping for each key.
return when (controlKey) {
ControlKey.Alt -> KeyEvent.KEYCODE_ALT_LEFT
ControlKey.Backspace -> KeyEvent.KEYCODE_DEL
ControlKey.Control -> KeyEvent.KEYCODE_CTRL_LEFT
ControlKey.CapsLock -> KeyEvent.KEYCODE_CAPS_LOCK
ControlKey.Meta -> KeyEvent.KEYCODE_META_LEFT
ControlKey.NumLock -> KeyEvent.KEYCODE_NUM_LOCK
ControlKey.RShift -> KeyEvent.KEYCODE_SHIFT_RIGHT
ControlKey.Shift -> KeyEvent.KEYCODE_SHIFT_LEFT
ControlKey.RAlt -> KeyEvent.KEYCODE_ALT_RIGHT
ControlKey.RControl -> KeyEvent.KEYCODE_CTRL_RIGHT
ControlKey.DownArrow -> KeyEvent.KEYCODE_DPAD_DOWN
ControlKey.LeftArrow -> KeyEvent.KEYCODE_DPAD_LEFT
ControlKey.RightArrow -> KeyEvent.KEYCODE_DPAD_RIGHT
ControlKey.UpArrow -> KeyEvent.KEYCODE_DPAD_UP
ControlKey.End -> KeyEvent.KEYCODE_MOVE_END
ControlKey.Home -> KeyEvent.KEYCODE_MOVE_HOME
ControlKey.PageUp -> KeyEvent.KEYCODE_PAGE_UP
ControlKey.PageDown -> KeyEvent.KEYCODE_PAGE_DOWN
ControlKey.Insert -> KeyEvent.KEYCODE_INSERT
ControlKey.Escape -> KeyEvent.KEYCODE_ESCAPE
ControlKey.F1 -> KeyEvent.KEYCODE_F1
ControlKey.F2 -> KeyEvent.KEYCODE_F2
ControlKey.F3 -> KeyEvent.KEYCODE_F3
ControlKey.F4 -> KeyEvent.KEYCODE_F4
ControlKey.F5 -> KeyEvent.KEYCODE_F5
ControlKey.F6 -> KeyEvent.KEYCODE_F6
ControlKey.F7 -> KeyEvent.KEYCODE_F7
ControlKey.F8 -> KeyEvent.KEYCODE_F8
ControlKey.F9 -> KeyEvent.KEYCODE_F9
ControlKey.F10 -> KeyEvent.KEYCODE_F10
ControlKey.F11 -> KeyEvent.KEYCODE_F11
ControlKey.F12 -> KeyEvent.KEYCODE_F12
ControlKey.Space -> KeyEvent.KEYCODE_SPACE
ControlKey.Tab -> KeyEvent.KEYCODE_TAB
ControlKey.Return -> KeyEvent.KEYCODE_ENTER
ControlKey.Delete -> KeyEvent.KEYCODE_FORWARD_DEL
ControlKey.Clear -> KeyEvent.KEYCODE_CLEAR
ControlKey.Pause -> KeyEvent.KEYCODE_BREAK
else -> 0 // Default to unknown.
}
}
}

View File

@@ -44,7 +44,6 @@ import java.nio.ByteBuffer
import kotlin.math.max
import kotlin.math.min
const val DEFAULT_NOTIFY_TITLE = "RustDesk"
const val DEFAULT_NOTIFY_TEXT = "Service is running"
const val DEFAULT_NOTIFY_ID = 1
@@ -94,6 +93,12 @@ class MainService : Service() {
}
}
@Keep
@RequiresApi(Build.VERSION_CODES.N)
fun rustKeyEventInput(input: ByteArray) {
InputService.ctx?.onKeyEvent(input)
}
@Keep
fun rustGetByName(name: String): String {
return when (name) {
@@ -206,6 +211,7 @@ class MainService : Service() {
override fun onCreate() {
super.onCreate()
Log.d(logTag,"MainService onCreate")
init(this)
HandlerThread("Service", Process.THREAD_PRIORITY_BACKGROUND).apply {
start()
serviceLooper = looper
@@ -310,7 +316,6 @@ class MainService : Service() {
mediaProjection =
mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, it)
checkMediaPermission()
init(this)
_isReady = true
} ?: let {
Log.d(logTag, "getParcelableExtra intent null, invoke requestMediaProjection")

View File

@@ -1,5 +1,6 @@
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowsChanged"
android:canRetrieveWindowContent="true"
android:accessibilityFlags="flagDefault"
android:notificationTimeout="50"
android:description="@string/accessibility_service_description"

View File

@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.7.10'
ext.kotlin_version = '1.9.10'
repositories {
google()
jcenter()

View File

@@ -1,13 +1,17 @@
#!/bin/bash
# Build libyuv / opus / libvpx / oboe for Android
set -e -o pipefail
ANDROID_ABI=$1
# Build RustDesk dependencies for Android using vcpkg.json
# Required:
# 1. set VCPKG_ROOT / ANDROID_NDK path environment variables
# 2. vcpkg initialized
# 3. ndk, version: 22 (if ndk < 22 you need to change LD as `export LD=$TOOLCHAIN/bin/$NDK_LLVM_TARGET-ld`)
# 3. ndk, version: r25c or newer
if [ -z "$ANDROID_NDK" ]; then
echo "Failed! Please set ANDROID_NDK"
if [ -z "$ANDROID_NDK_HOME" ]; then
echo "Failed! Please set ANDROID_NDK_HOME"
exit 1
fi
@@ -18,107 +22,66 @@ fi
API_LEVEL="21"
# Get directory of this script
SCRIPTDIR="$(readlink -f "$0")"
SCRIPTDIR="$(dirname "$SCRIPTDIR")"
# Check if vcpkg.json is one level up - in root directory of RD
if [ ! -f "$SCRIPTDIR/../vcpkg.json" ]; then
echo "Failed! Please check where vcpkg.json is!"
exit 1
fi
# NDK llvm toolchain
HOST_TAG="linux-x86_64" # current platform, set as `ls $ANDROID_NDK/toolchains/llvm/prebuilt/`
TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG
function build {
ANDROID_ABI=$1
VCPKG_TARGET=$2
NDK_LLVM_TARGET=$3
LIBVPX_TARGET=$4
PREFIX=$VCPKG_ROOT/installed/$VCPKG_TARGET/
case "$ANDROID_ABI" in
arm64-v8a)
ABI=aarch64-linux-android$API_LEVEL
VCPKG_TARGET=arm64-android
;;
armeabi-v7a)
ABI=armv7a-linux-androideabi$API_LEVEL
VCPKG_TARGET=arm-neon-android
;;
x86_64)
ABI=x86_64-linux-android$API_LEVEL
VCPKG_TARGET=x64-android
;;
x86)
ABI=i686-linux-android$API_LEVEL
VCPKG_TARGET=x86-android
;;
*)
echo "ERROR: ANDROID_ABI must be one of: arm64-v8a, armeabi-v7a, x86_64, x86" >&2
return 1
esac
# 1
echo "*** [$ANDROID_ABI][Start] Build opus / libyuv from vcpkg"
export ANDROID_NDK_HOME=$ANDROID_NDK
pushd $VCPKG_ROOT
$VCPKG_ROOT/vcpkg install opus --triplet $VCPKG_TARGET
$VCPKG_ROOT/vcpkg install libyuv --triplet $VCPKG_TARGET
echo "*** [$ANDROID_ABI][Start] Build and install vcpkg dependencies"
pushd "$SCRIPTDIR/.."
$VCPKG_ROOT/vcpkg install --triplet $VCPKG_TARGET --x-install-root="$VCPKG_ROOT/installed"
popd
echo "*** [$ANDROID_ABI][Finished] Build opus / libyuv from vcpkg"
echo "*** [$ANDROID_ABI][Finished] Build and install vcpkg dependencies"
# 2
echo "*** [$ANDROID_ABI][Start] Build libvpx"
pushd build/libvpx
export AR=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-ar
export AS=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-as
export LD=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-ld.gold # if ndk < 22, use aarch64-linux-android-ld
export RANLIB=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-ranlib
export STRIP=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-strip
if [ -d "$VCPKG_ROOT/installed/arm-neon-android" ]; then
echo "*** [Start] Move arm-neon-android to arm-android"
if [ $NDK_LLVM_TARGET == "arm-linux-androideabi" ]
then
export CC=$TOOLCHAIN/bin/armv7a-linux-androideabi${API_LEVEL}-clang
export CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi${API_LEVEL}-clang++
else
export CC=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}${API_LEVEL}-clang
export CXX=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}${API_LEVEL}-clang++
fi
make clean
./configure --target=$LIBVPX_TARGET \
--enable-pic
--disable-webm-io \
--disable-unit-tests \
--disable-examples \
--disable-libyuv \
--disable-postproc \
--disable-tools \
--disable-docs \
--prefix=$PREFIX
make -j5
make install
mv "$VCPKG_ROOT/installed/arm-neon-android" "$VCPKG_ROOT/installed/arm-android"
popd
echo "*** [$ANDROID_ABI][Finished] Build libvpx"
# 3
echo "*** [$ANDROID_ABI][Start] Build oboe"
pushd build/oboe
make clean
cmake -DBUILD_SHARED_LIBS=true \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DANDROID_TOOLCHAIN=clang \
-DANDROID_STL=c++_shared \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DCMAKE_INSTALL_PREFIX=$PREFIX \
-DANDROID_ABI=$ANDROID_ABI \
-DANDROID_PLATFORM=android-$API_LEVEL
make -j5
make install
mv $PREFIX/lib/$ANDROID_ABI/liboboe.a $PREFIX/lib/
popd
echo "*** [$ANDROID_ABI][Finished] Build oboe"
echo "*** [$ANDROID_ABI][All Finished]"
echo "*** [Finished] Move arm-neon-android to arm-android"
fi
}
git clone -b v1.11.0 --depth=1 https://github.com/webmproject/libvpx.git build/libvpx
git clone -b 1.6.1 --depth=1 https://github.com/google/oboe build/oboe
patch -N -d build/oboe -p1 < ../src/oboe.patch
# VCPKG_TARGET ANDROID_ABI
# arm64-android arm64-v8a
# arm-android armeabi-v7a
# x64-android x86_64
# x86-android x86
# NDK_LLVM_TARGET
# aarch64-linux-android
# arm-linux-androideabi
# x86_64-linux-android
# i686-linux-android
# LIBVPX_TARGET :
# arm64-android-gcc
# armv7-android-gcc
# x86_64-android-gcc
# x86-android-gcc
# args: ANDROID_ABI VCPKG_TARGET NDK_LLVM_TARGET LIBVPX_TARGET
build arm64-v8a arm64-android aarch64-linux-android arm64-android-gcc
build armeabi-v7a arm-android arm-linux-androideabi armv7-android-gcc
# rm -rf build/libvpx
# rm -rf build/oboe
if [ ! -z "$ANDROID_ABI" ]; then
build "$ANDROID_ABI"
else
echo "Usage: build-android-deps.sh <ANDROID-ABI>" >&2
exit 1
fi

View File

@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>

View File

@@ -1,10 +1,10 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
platform :ios, '11.0'
platform :ios, '12.0'
project 'Runner', {
'Debug' => :debug,

View File

@@ -38,9 +38,6 @@ PODS:
- Flutter (1.0.0)
- flutter_keyboard_visibility (0.0.1):
- Flutter
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- image_picker_ios (0.0.1):
- Flutter
- MTBBarcodeScanner (5.0.11)
@@ -52,12 +49,12 @@ PODS:
- qr_code_scanner (0.2.0):
- Flutter
- MTBBarcodeScanner
- SDWebImage (5.15.5):
- SDWebImage/Core (= 5.15.5)
- SDWebImage/Core (5.15.5)
- sqflite (0.0.2):
- SDWebImage (5.18.11):
- SDWebImage/Core (= 5.18.11)
- SDWebImage/Core (5.18.11)
- sqflite (0.0.3):
- Flutter
- FMDB (>= 2.7.5)
- FlutterMacOS
- SwiftyGif (5.4.4)
- uni_links (0.0.1):
- Flutter
@@ -65,7 +62,8 @@ PODS:
- Flutter
- video_player_avfoundation (0.0.1):
- Flutter
- wakelock (0.0.1):
- FlutterMacOS
- wakelock_plus (0.0.1):
- Flutter
DEPENDENCIES:
@@ -77,17 +75,16 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
- uni_links (from `.symlinks/plugins/uni_links/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
SPEC REPOS:
trunk:
- DKImagePickerController
- DKPhotoGallery
- FMDB
- MTBBarcodeScanner
- SDWebImage
- SwiftyGif
@@ -110,37 +107,36 @@ EXTERNAL SOURCES:
qr_code_scanner:
:path: ".symlinks/plugins/qr_code_scanner/ios"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
:path: ".symlinks/plugins/sqflite/darwin"
uni_links:
:path: ".symlinks/plugins/uni_links/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/ios"
wakelock:
:path: ".symlinks/plugins/wakelock/ios"
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
SPEC CHECKSUMS:
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
SDWebImage: a3ba0b8faac7228c3c8eadd1a55c9c9fe5e16457
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
PODFILE CHECKSUM: 2aff76ba0ac13439479560d1d03e9b4479f5c9e1
PODFILE CHECKSUM: d4cb12ad5d3bdb3352770b1d3db237584e155156
COCOAPODS: 1.12.1

View File

@@ -159,7 +159,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@@ -347,7 +347,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -394,8 +394,6 @@
"-framework",
"\"DKPhotoGallery\"",
"-framework",
"\"FMDB\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"ImageIO\"",
@@ -434,7 +432,7 @@
"-framework",
"\"video_player_avfoundation\"",
"-framework",
"\"wakelock\"",
"\"wakelock_plus\"",
);
PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -493,7 +491,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -543,7 +541,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -592,8 +590,6 @@
"-framework",
"\"DKPhotoGallery\"",
"-framework",
"\"FMDB\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"ImageIO\"",
@@ -632,7 +628,7 @@
"-framework",
"\"video_player_avfoundation\"",
"-framework",
"\"wakelock\"",
"\"wakelock_plus\"",
);
PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -682,8 +678,6 @@
"-framework",
"\"DKPhotoGallery\"",
"-framework",
"\"FMDB\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"ImageIO\"",
@@ -722,7 +716,7 @@
"-framework",
"\"video_player_avfoundation\"",
"-framework",
"\"wakelock\"",
"\"wakelock_plus\"",
);
PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@@ -14,6 +14,6 @@ import Flutter
public func dummyMethodToEnforceBundling() {
dummy_method_to_enforce_bundling();
session_get_rgba(nil);
session_get_rgba(nil, 0);
}
}

View File

@@ -24,6 +24,21 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLIconFile</key>
<string></string>
<key>CFBundleURLName</key>
<string>com.carriez.rustdesk</string>
<key>CFBundleURLSchemes</key>
<array>
<string>rustdesk</string>
</array>
</dict>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>

View File

@@ -15,6 +15,7 @@ import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import 'package:flutter_hbb/desktop/widgets/refresh_wrapper.dart';
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/main.dart';
import 'package:flutter_hbb/models/desktop_render_texture.dart';
import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/utils/multi_window_manager.dart';
@@ -52,6 +53,9 @@ int androidVersion = 0;
int windowsBuildNumber = 0;
DesktopType? desktopType;
bool get isMainDesktopWindow =>
desktopType == DesktopType.main || desktopType == DesktopType.cm;
/// Check if the app is running with single view mode.
bool isSingleViewApp() {
return desktopType == DesktopType.cm;
@@ -97,59 +101,83 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
const ColorThemeExtension({
required this.border,
required this.border2,
required this.border3,
required this.highlight,
required this.drag_indicator,
required this.shadow,
required this.errorBannerBg,
required this.me,
required this.toastBg,
required this.toastText,
required this.divider,
});
final Color? border;
final Color? border2;
final Color? border3;
final Color? highlight;
final Color? drag_indicator;
final Color? shadow;
final Color? errorBannerBg;
final Color? me;
final Color? toastBg;
final Color? toastText;
final Color? divider;
static final light = ColorThemeExtension(
border: Color(0xFFCCCCCC),
border2: Color(0xFFBBBBBB),
border3: Colors.black26,
highlight: Color(0xFFE5E5E5),
drag_indicator: Colors.grey[800],
shadow: Colors.black,
errorBannerBg: Color(0xFFFDEEEB),
me: Colors.green,
toastBg: Colors.black.withOpacity(0.6),
toastText: Colors.white,
divider: Colors.black38,
);
static final dark = ColorThemeExtension(
border: Color(0xFF555555),
border2: Color(0xFFE5E5E5),
border3: Colors.white24,
highlight: Color(0xFF3F3F3F),
drag_indicator: Colors.grey,
shadow: Colors.grey,
errorBannerBg: Color(0xFF470F2D),
me: Colors.greenAccent,
toastBg: Colors.white.withOpacity(0.6),
toastText: Colors.black,
divider: Colors.white38,
);
@override
ThemeExtension<ColorThemeExtension> copyWith({
Color? border,
Color? border2,
Color? border3,
Color? highlight,
Color? drag_indicator,
Color? shadow,
Color? errorBannerBg,
Color? me,
Color? toastBg,
Color? toastText,
Color? divider,
}) {
return ColorThemeExtension(
border: border ?? this.border,
border2: border2 ?? this.border2,
border3: border3 ?? this.border3,
highlight: highlight ?? this.highlight,
drag_indicator: drag_indicator ?? this.drag_indicator,
shadow: shadow ?? this.shadow,
errorBannerBg: errorBannerBg ?? this.errorBannerBg,
me: me ?? this.me,
toastBg: toastBg ?? this.toastBg,
toastText: toastText ?? this.toastText,
divider: divider ?? this.divider,
);
}
@@ -162,11 +190,15 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
return ColorThemeExtension(
border: Color.lerp(border, other.border, t),
border2: Color.lerp(border2, other.border2, t),
border3: Color.lerp(border3, other.border3, t),
highlight: Color.lerp(highlight, other.highlight, t),
drag_indicator: Color.lerp(drag_indicator, other.drag_indicator, t),
shadow: Color.lerp(shadow, other.shadow, t),
errorBannerBg: Color.lerp(shadow, other.errorBannerBg, t),
me: Color.lerp(shadow, other.me, t),
toastBg: Color.lerp(shadow, other.toastBg, t),
toastText: Color.lerp(shadow, other.toastText, t),
divider: Color.lerp(shadow, other.divider, t),
);
}
}
@@ -186,10 +218,6 @@ class MyTheme {
static const Color dark = Colors.black87;
static const Color button = Color(0xFF2C8CFF);
static const Color hoverBorder = Color(0xFF999999);
static const Color bordDark = Colors.white24;
static const Color bordLight = Colors.black26;
static const Color dividerDark = Colors.white38;
static const Color dividerLight = Colors.black38;
// ListTile
static const ListTileThemeData listTileTheme = ListTileThemeData(
@@ -298,6 +326,8 @@ class MyTheme {
);
static ThemeData lightTheme = ThemeData(
// https://stackoverflow.com/questions/77537315/after-upgrading-to-flutter-3-16-the-app-bar-background-color-button-size-and
useMaterial3: false,
brightness: Brightness.light,
hoverColor: Color.fromARGB(255, 224, 224, 224),
scaffoldBackgroundColor: Colors.white,
@@ -376,6 +406,13 @@ class MyTheme {
MenuStyle(backgroundColor: MaterialStatePropertyAll(Colors.white))),
colorScheme: ColorScheme.light(
primary: Colors.blue, secondary: accent, background: grayBg),
popupMenuTheme: PopupMenuThemeData(
color: Colors.white,
shape: RoundedRectangleBorder(
side: BorderSide(
color: isDesktop ? Color(0xFFECECEC) : Colors.transparent),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
)),
).copyWith(
extensions: <ThemeExtension<dynamic>>[
ColorThemeExtension.light,
@@ -383,6 +420,7 @@ class MyTheme {
],
);
static ThemeData darkTheme = ThemeData(
useMaterial3: false,
brightness: Brightness.dark,
hoverColor: Color.fromARGB(255, 45, 46, 53),
scaffoldBackgroundColor: Color(0xFF18191E),
@@ -474,6 +512,11 @@ class MyTheme {
secondary: accent,
background: Color(0xFF24252B),
),
popupMenuTheme: PopupMenuThemeData(
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.white24),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
)),
).copyWith(
extensions: <ThemeExtension<dynamic>>[
ColorThemeExtension.dark,
@@ -844,16 +887,16 @@ class OverlayDialogManager {
}
}
void showToast(String text, {Duration timeout = const Duration(seconds: 2)}) {
void showToast(String text, {Duration timeout = const Duration(seconds: 3)}) {
final overlayState = globalKey.currentState?.overlay;
if (overlayState == null) return;
final entry = OverlayEntry(builder: (_) {
final entry = OverlayEntry(builder: (context) {
return IgnorePointer(
child: Align(
alignment: const Alignment(0.0, 0.8),
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.6),
color: MyTheme.color(context).toastBg,
borderRadius: const BorderRadius.all(
Radius.circular(20),
),
@@ -862,11 +905,11 @@ void showToast(String text, {Duration timeout = const Duration(seconds: 2)}) {
child: Text(
text,
textAlign: TextAlign.center,
style: const TextStyle(
style: TextStyle(
decoration: TextDecoration.none,
fontWeight: FontWeight.w300,
fontSize: 18,
color: Colors.white),
color: MyTheme.color(context).toastText),
),
)));
});
@@ -954,7 +997,7 @@ class CustomAlertDialog extends StatelessWidget {
void msgBox(SessionID sessionId, String type, String title, String text,
String link, OverlayDialogManager dialogManager,
{bool? hasCancel, ReconnectHandle? reconnect}) {
{bool? hasCancel, ReconnectHandle? reconnect, int? reconnectTimeout}) {
dialogManager.dismissAll();
List<Widget> buttons = [];
bool hasOk = false;
@@ -994,22 +1037,21 @@ void msgBox(SessionID sessionId, String type, String title, String text,
dialogManager.dismissAll();
}));
}
if (reconnect != null && title == "Connection Error") {
if (reconnect != null &&
title == "Connection Error" &&
reconnectTimeout != null) {
// `enabled` is used to disable the dialog button once the button is clicked.
final enabled = true.obs;
final button = Obx(
() => dialogButton(
'Reconnect',
isOutline: true,
onPressed: enabled.isTrue
? () {
// Disable the button
enabled.value = false;
reconnect(dialogManager, sessionId, false);
}
: null,
),
);
final button = Obx(() => _ReconnectCountDownButton(
second: reconnectTimeout,
onPressed: enabled.isTrue
? () {
// Disable the button
enabled.value = false;
reconnect(dialogManager, sessionId, false);
}
: null,
));
buttons.insert(0, button);
}
if (link.isNotEmpty) {
@@ -1054,7 +1096,7 @@ Widget msgboxIcon(String type) {
if (type == 'on-uac' || type == 'on-foreground-elevated') {
iconData = Icons.admin_panel_settings;
}
if (type == "info") {
if (type.contains('info')) {
iconData = Icons.info;
}
if (iconData != null) {
@@ -1490,8 +1532,8 @@ Future<void> saveWindowPosition(WindowType type, {int? windowId}) async {
late Offset position;
late Size sz;
late bool isMaximized;
bool isFullscreen = stateGlobal.fullscreen ||
(Platform.isMacOS && stateGlobal.closeOnFullscreen);
bool isFullscreen = stateGlobal.fullscreen.isTrue ||
(Platform.isMacOS && stateGlobal.closeOnFullscreen == true);
setFrameIfMaximized() {
if (isMaximized) {
final pos = bind.getLocalFlutterOption(k: kWindowPrefix + type.name);
@@ -1669,8 +1711,10 @@ Future<Offset?> _adjustRestoreMainWindowOffset(
/// Restore window position and size on start
/// Note that windowId must be provided if it's subwindow
//
// display is used to set the offset of the window in individual display mode.
Future<bool> restoreWindowPosition(WindowType type,
{int? windowId, String? peerId}) async {
{int? windowId, String? peerId, int? display}) async {
if (bind
.mainGetEnv(key: "DISABLE_RUSTDESK_RESTORE_WINDOW_POSITION")
.isNotEmpty) {
@@ -1706,14 +1750,22 @@ Future<bool> restoreWindowPosition(WindowType type,
debugPrint("no window position saved, ignoring position restoration");
return false;
}
if (type == WindowType.RemoteDesktop &&
!isRemotePeerPos &&
windowId != null) {
if (lpos.offsetWidth != null) {
lpos.offsetWidth = lpos.offsetWidth! + windowId * 20;
if (type == WindowType.RemoteDesktop) {
if (!isRemotePeerPos && windowId != null) {
if (lpos.offsetWidth != null) {
lpos.offsetWidth = lpos.offsetWidth! + windowId * kNewWindowOffset;
}
if (lpos.offsetHeight != null) {
lpos.offsetHeight = lpos.offsetHeight! + windowId * kNewWindowOffset;
}
}
if (lpos.offsetHeight != null) {
lpos.offsetHeight = lpos.offsetHeight! + windowId * 20;
if (display != null) {
if (lpos.offsetWidth != null) {
lpos.offsetWidth = lpos.offsetWidth! + display * kNewWindowOffset;
}
if (lpos.offsetHeight != null) {
lpos.offsetHeight = lpos.offsetHeight! + display * kNewWindowOffset;
}
}
}
@@ -1942,6 +1994,7 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
List<String>? urlLinkToCmdArgs(Uri uri) {
String? command;
String? id;
final options = ["connect", "play", "file-transfer", "port-forward", "rdp"];
if (uri.authority.isEmpty &&
uri.path.split('').every((char) => char == '/')) {
return [];
@@ -1949,16 +2002,65 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
// For compatibility
command = '--connect';
id = uri.path.substring("/new/".length);
} else if (['connect', "play", 'file-transfer', 'port-forward', 'rdp']
.contains(uri.authority)) {
} else if (uri.authority == "config") {
if (Platform.isAndroid || Platform.isIOS) {
final config = uri.path.substring("/".length);
// add a timer to make showToast work
Timer(Duration(seconds: 1), () {
importConfig(null, null, config);
});
}
return null;
} else if (uri.authority == "password") {
if (Platform.isAndroid || Platform.isIOS) {
final password = uri.path.substring("/".length);
if (password.isNotEmpty) {
Timer(Duration(seconds: 1), () async {
await bind.mainSetPermanentPassword(password: password);
showToast(translate('Successful'));
});
}
}
} else if (options.contains(uri.authority)) {
final optionIndex = options.indexOf(uri.authority);
command = '--${uri.authority}';
if (uri.path.length > 1) {
id = uri.path.substring(1);
}
} else if (uri.authority.length > 2 && uri.path.length <= 1) {
if (isMobile && id != null) {
if (optionIndex == 0 || optionIndex == 1) {
connect(Get.context!, id);
} else if (optionIndex == 2) {
connect(Get.context!, id, isFileTransfer: true);
}
return null;
}
} else if (uri.authority.length > 2 &&
(uri.path.length <= 1 ||
(uri.path == '/r' || uri.path.startsWith('/r@')))) {
// rustdesk://<connect-id>
// rustdesk://<connect-id>/r
// rustdesk://<connect-id>/r@<server>
command = '--connect';
id = uri.authority;
if (uri.path.length > 1) {
id = id + uri.path;
}
}
var key = uri.queryParameters["key"];
if (id != null) {
if (key != null) {
id = "$id?key=$key";
}
}
if (isMobile) {
if (id != null) {
final forceRelay = uri.queryParameters["relay"] != null;
connect(Get.context!, id, forceRelay: forceRelay);
return null;
}
}
List<String> args = List.empty(growable: true);
@@ -2003,6 +2105,7 @@ connect(
bool isFileTransfer = false,
bool isTcpTunneling = false,
bool isRDP = false,
bool forceRelay = false,
}) async {
if (id == '') return;
if (!isDesktop || desktopType == DesktopType.main) {
@@ -2011,12 +2114,16 @@ connect(
final idController = Get.find<IDTextEditingController>();
idController.text = formatID(id);
}
if (Get.isRegistered<TextEditingController>()) {
final fieldTextEditingController = Get.find<TextEditingController>();
fieldTextEditingController.text = formatID(id);
}
} catch (_) {}
}
id = id.replaceAll(' ', '');
final oldId = id;
id = await bind.mainHandleRelayId(id: id);
final forceRelay = id != oldId;
final forceRelay2 = id != oldId || forceRelay;
assert(!(isFileTransfer && isTcpTunneling && isRDP),
"more than one connect type");
@@ -2027,7 +2134,7 @@ connect(
isFileTransfer: isFileTransfer,
isTcpTunneling: isTcpTunneling,
isRDP: isRDP,
forceRelay: forceRelay,
forceRelay: forceRelay2,
);
} else {
await rustDeskWinManager.call(WindowType.Main, kWindowConnect, {
@@ -2317,7 +2424,7 @@ Widget dialogButton(String text,
}
}
int version_cmp(String v1, String v2) {
int versionCmp(String v1, String v2) {
return bind.versionToNumber(v: v1) - bind.versionToNumber(v: v2);
}
@@ -2448,7 +2555,7 @@ Future<void> start_service(bool is_start) async {
}
}
typedef Future<bool> WhetherUseRemoteBlock();
typedef WhetherUseRemoteBlock = Future<bool> Function();
Widget buildRemoteBlock({required Widget child, WhetherUseRemoteBlock? use}) {
var block = false.obs;
return Obx(() => MouseRegion(
@@ -2589,3 +2696,377 @@ String getDesktopTabLabel(String peerId, String alias) {
}
return label;
}
sessionRefreshVideo(SessionID sessionId, PeerInfo pi) async {
if (pi.currentDisplay == kAllDisplayValue) {
for (int i = 0; i < pi.displays.length; i++) {
await bind.sessionRefresh(sessionId: sessionId, display: i);
}
} else {
await bind.sessionRefresh(sessionId: sessionId, display: pi.currentDisplay);
}
}
bool isChooseDisplayToOpenInNewWindow(PeerInfo pi, SessionID sessionId) =>
pi.isSupportMultiDisplay &&
useTextureRender &&
bind.sessionGetDisplaysAsIndividualWindows(sessionId: sessionId) == 'Y';
Future<List<Rect>> getScreenListWayland() async {
final screenRectList = <Rect>[];
if (isMainDesktopWindow) {
for (var screen in await window_size.getScreenList()) {
final scale = kIgnoreDpi ? 1.0 : screen.scaleFactor;
double l = screen.frame.left;
double t = screen.frame.top;
double r = screen.frame.right;
double b = screen.frame.bottom;
final rect = Rect.fromLTRB(l / scale, t / scale, r / scale, b / scale);
screenRectList.add(rect);
}
} else {
final screenList = await rustDeskWinManager.call(
WindowType.Main, kWindowGetScreenList, '');
try {
for (var screen in jsonDecode(screenList.result) as List<dynamic>) {
final scale = kIgnoreDpi ? 1.0 : screen['scaleFactor'];
double l = screen['frame']['l'];
double t = screen['frame']['t'];
double r = screen['frame']['r'];
double b = screen['frame']['b'];
final rect = Rect.fromLTRB(l / scale, t / scale, r / scale, b / scale);
screenRectList.add(rect);
}
} catch (e) {
debugPrint('Failed to parse screenList: $e');
}
}
return screenRectList;
}
Future<List<Rect>> getScreenListNotWayland() async {
final screenRectList = <Rect>[];
final displays = bind.mainGetDisplays();
if (displays.isEmpty) {
return screenRectList;
}
try {
for (var display in jsonDecode(displays) as List<dynamic>) {
// to-do: scale factor ?
// final scale = kIgnoreDpi ? 1.0 : screen.scaleFactor;
double l = display['x'].toDouble();
double t = display['y'].toDouble();
double r = (display['x'] + display['w']).toDouble();
double b = (display['y'] + display['h']).toDouble();
screenRectList.add(Rect.fromLTRB(l, t, r, b));
}
} catch (e) {
debugPrint('Failed to parse displays: $e');
}
return screenRectList;
}
Future<List<Rect>> getScreenRectList() async {
return bind.mainCurrentIsWayland()
? await getScreenListWayland()
: await getScreenListNotWayland();
}
openMonitorInTheSameTab(int i, FFI ffi, PeerInfo pi,
{bool updateCursorPos = true}) {
final displays = i == kAllDisplayValue
? List.generate(pi.displays.length, (index) => index)
: [i];
bind.sessionSwitchDisplay(
isDesktop: isDesktop,
sessionId: ffi.sessionId,
value: Int32List.fromList(displays),
);
ffi.ffiModel.switchToNewDisplay(i, ffi.sessionId, ffi.id,
updateCursorPos: updateCursorPos);
}
// Open new tab or window to show this monitor.
// For now just open new window.
//
// screenRect is used to move the new window to the specified screen and set fullscreen.
openMonitorInNewTabOrWindow(int i, String peerId, PeerInfo pi,
{Rect? screenRect}) {
final args = {
'window_id': stateGlobal.windowId,
'peer_id': peerId,
'display': i,
'display_count': pi.displays.length,
};
if (screenRect != null) {
args['screen_rect'] = {
'l': screenRect.left,
't': screenRect.top,
'r': screenRect.right,
'b': screenRect.bottom,
};
}
DesktopMultiWindow.invokeMethod(
kMainWindowId, kWindowEventOpenMonitorSession, jsonEncode(args));
}
tryMoveToScreenAndSetFullscreen(Rect? screenRect) async {
if (screenRect == null) {
return;
}
final wc = WindowController.fromWindowId(stateGlobal.windowId);
final curFrame = await wc.getFrame();
final frame =
Rect.fromLTWH(screenRect.left + 30, screenRect.top + 30, 600, 400);
if (stateGlobal.fullscreen.isTrue &&
curFrame.left <= frame.left &&
curFrame.top <= frame.top &&
curFrame.width >= frame.width &&
curFrame.height >= frame.height) {
return;
}
await wc.setFrame(frame);
// An duration is needed to avoid the window being restored after fullscreen.
Future.delayed(Duration(milliseconds: 300), () async {
stateGlobal.setFullscreen(true);
});
}
parseParamScreenRect(Map<String, dynamic> params) {
Rect? screenRect;
if (params['screen_rect'] != null) {
double l = params['screen_rect']['l'];
double t = params['screen_rect']['t'];
double r = params['screen_rect']['r'];
double b = params['screen_rect']['b'];
screenRect = Rect.fromLTRB(l, t, r, b);
}
return screenRect;
}
get isInputSourceFlutter => stateGlobal.getInputSource() == "Input source 2";
class _ReconnectCountDownButton extends StatefulWidget {
_ReconnectCountDownButton({
Key? key,
required this.second,
required this.onPressed,
}) : super(key: key);
final VoidCallback? onPressed;
final int second;
@override
State<_ReconnectCountDownButton> createState() =>
_ReconnectCountDownButtonState();
}
class _ReconnectCountDownButtonState extends State<_ReconnectCountDownButton> {
late int _countdownSeconds = widget.second;
Timer? _timer;
@override
void initState() {
super.initState();
_startCountdownTimer();
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
void _startCountdownTimer() {
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
if (_countdownSeconds <= 0) {
timer.cancel();
} else {
setState(() {
_countdownSeconds--;
});
}
});
}
@override
Widget build(BuildContext context) {
return dialogButton(
'${translate('Reconnect')} (${_countdownSeconds}s)',
onPressed: widget.onPressed,
isOutline: true,
);
}
}
importConfig(List<TextEditingController>? controllers, List<RxString>? errMsgs,
String? text) {
if (text != null && text.isNotEmpty) {
try {
final sc = ServerConfig.decode(text);
if (sc.idServer.isNotEmpty) {
Future<bool> success = setServerConfig(controllers, errMsgs, sc);
success.then((value) {
if (value) {
showToast(translate('Import server configuration successfully'));
} else {
showToast(translate('Invalid server configuration'));
}
});
} else {
showToast(translate('Invalid server configuration'));
}
return sc;
} catch (e) {
showToast(translate('Invalid server configuration'));
}
} else {
showToast(translate('Clipboard is empty'));
}
}
Future<bool> setServerConfig(
List<TextEditingController>? controllers,
List<RxString>? errMsgs,
ServerConfig config,
) async {
config.idServer = config.idServer.trim();
config.relayServer = config.relayServer.trim();
config.apiServer = config.apiServer.trim();
config.key = config.key.trim();
if (controllers != null) {
controllers[0].text = config.idServer;
controllers[1].text = config.relayServer;
controllers[2].text = config.apiServer;
controllers[3].text = config.key;
}
// id
if (config.idServer.isNotEmpty && errMsgs != null) {
errMsgs[0].value =
translate(await bind.mainTestIfValidServer(server: config.idServer));
if (errMsgs[0].isNotEmpty) {
return false;
}
}
// relay
if (config.relayServer.isNotEmpty && errMsgs != null) {
errMsgs[1].value =
translate(await bind.mainTestIfValidServer(server: config.relayServer));
if (errMsgs[1].isNotEmpty) {
return false;
}
}
// api
if (config.apiServer.isNotEmpty && errMsgs != null) {
if (!config.apiServer.startsWith('http://') &&
!config.apiServer.startsWith('https://')) {
errMsgs[2].value =
'${translate("API Server")}: ${translate("invalid_http")}';
return false;
}
}
final oldApiServer = await bind.mainGetApiServer();
// should set one by one
await bind.mainSetOption(
key: 'custom-rendezvous-server', value: config.idServer);
await bind.mainSetOption(key: 'relay-server', value: config.relayServer);
await bind.mainSetOption(key: 'api-server', value: config.apiServer);
await bind.mainSetOption(key: 'key', value: config.key);
final newApiServer = await bind.mainGetApiServer();
if (oldApiServer.isNotEmpty &&
oldApiServer != newApiServer &&
gFFI.userModel.isLogin) {
gFFI.userModel.logOut(apiServer: oldApiServer);
}
return true;
}
ColorFilter? svgColor(Color? color) {
if (color == null) {
return null;
} else {
return ColorFilter.mode(color, BlendMode.srcIn);
}
}
// ignore: must_be_immutable
class ComboBox extends StatelessWidget {
late final List<String> keys;
late final List<String> values;
late final String initialKey;
late final Function(String key) onChanged;
late final bool enabled;
late String current;
ComboBox({
Key? key,
required this.keys,
required this.values,
required this.initialKey,
required this.onChanged,
this.enabled = true,
}) : super(key: key);
@override
Widget build(BuildContext context) {
var index = keys.indexOf(initialKey);
if (index < 0) {
index = 0;
}
var ref = values[index].obs;
current = keys[index];
return Container(
decoration: BoxDecoration(
border: Border.all(
color: enabled
? MyTheme.color(context).border2 ?? MyTheme.border
: MyTheme.border,
),
borderRadius:
BorderRadius.circular(8), //border raiuds of dropdown button
),
height: 42, // should be the height of a TextField
child: Obx(() => DropdownButton<String>(
isExpanded: true,
value: ref.value,
elevation: 16,
underline: Container(),
style: TextStyle(
color: enabled
? Theme.of(context).textTheme.titleMedium?.color
: disabledTextColor(context, enabled)),
icon: const Icon(
Icons.expand_more_sharp,
size: 20,
).marginOnly(right: 15),
onChanged: enabled
? (String? newValue) {
if (newValue != null && newValue != ref.value) {
ref.value = newValue;
current = newValue;
onChanged(keys[values.indexOf(newValue)]);
}
}
: null,
items: values.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: const TextStyle(fontSize: 15),
overflow: TextOverflow.ellipsis,
).marginOnly(left: 15),
);
}).toList(),
)),
).marginOnly(bottom: 5);
}
}
Color? disabledTextColor(BuildContext context, bool enabled) {
return enabled
? null
: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6);
}

View File

@@ -11,9 +11,11 @@ class HttpType {
static const kAuthReqTypeMobile = "mobile";
static const kAuthReqTypeSMSCode = "sms_code";
static const kAuthReqTypeEmailCode = "email_code";
static const kAuthReqTypeTfaCode = "tfa_code";
static const kAuthResTypeToken = "access_token";
static const kAuthResTypeEmailCheck = "email_check";
static const kAuthResTypeTfaCheck = "tfa_check";
}
enum UserStatus { kDisabled, kNormal, kUnverified }
@@ -118,6 +120,8 @@ class LoginRequest {
bool? autoLogin;
String? type;
String? verificationCode;
String? tfaCode;
String? secret;
LoginRequest(
{this.username,
@@ -126,7 +130,9 @@ class LoginRequest {
this.uuid,
this.autoLogin,
this.type,
this.verificationCode});
this.verificationCode,
this.tfaCode,
this.secret});
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
@@ -139,6 +145,8 @@ class LoginRequest {
if (verificationCode != null) {
data['verificationCode'] = verificationCode;
}
if (tfaCode != null) data['tfaCode'] = tfaCode;
if (secret != null) data['secret'] = secret;
Map<String, dynamic> deviceInfo = {};
try {
@@ -154,13 +162,18 @@ class LoginRequest {
class LoginResponse {
String? access_token;
String? type;
String? tfa_type;
String? secret;
UserPayload? user;
LoginResponse({this.access_token, this.type, this.user});
LoginResponse(
{this.access_token, this.type, this.tfa_type, this.secret, this.user});
LoginResponse.fromJson(Map<String, dynamic> json) {
access_token = json['access_token'];
type = json['type'];
tfa_type = json['tfa_type'];
secret = json['secret'];
user = json['user'] != null ? UserPayload.fromJson(json['user']) : null;
}
}

View File

@@ -1,3 +1,4 @@
import 'package:flutter_hbb/common.dart';
import 'package:get/get.dart';
import '../consts.dart';
@@ -10,7 +11,7 @@ class PrivacyModeState {
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
final RxBool state = false.obs;
final RxString state = ''.obs;
Get.put(state, tag: key);
}
}
@@ -20,11 +21,11 @@ class PrivacyModeState {
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
} else {
Get.find<RxBool>(tag: key).value = false;
Get.find<RxString>(tag: key).value = '';
}
}
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
static RxString find(String id) => Get.find<RxString>(tag: tag(id));
}
class BlockInputState {
@@ -318,6 +319,7 @@ initSharedStates(String id) {
FingerprintState.init(id);
PeerBoolOption.init(id, 'zoom-cursor', () => false);
UnreadChatCountState.init(id);
if (isMobile) ConnectionTypeState.init(id); // desktop in other places
}
removeSharedStates(String id) {
@@ -330,4 +332,5 @@ removeSharedStates(String id) {
FingerprintState.delete(id);
PeerBoolOption.delete(id, 'zoom-cursor');
UnreadChatCountState.delete(id);
if (isMobile) ConnectionTypeState.delete(id);
}

View File

@@ -0,0 +1,205 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import '../../../models/platform_model.dart';
import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/common/widgets/peer_card.dart';
Future<List<Peer>> getAllPeers() async {
Map<String, dynamic> recentPeers = jsonDecode(bind.mainLoadRecentPeersSync());
Map<String, dynamic> lanPeers = jsonDecode(bind.mainLoadLanPeersSync());
Map<String, dynamic> abPeers = jsonDecode(bind.mainLoadAbSync());
Map<String, dynamic> groupPeers = jsonDecode(bind.mainLoadGroupSync());
Map<String, dynamic> combinedPeers = {};
void mergePeers(Map<String, dynamic> peers) {
if (peers.containsKey("peers")) {
dynamic peerData = peers["peers"];
if (peerData is String) {
try {
peerData = jsonDecode(peerData);
} catch (e) {
print("Error decoding peers: $e");
return;
}
}
if (peerData is List) {
for (var peer in peerData) {
if (peer is Map && peer.containsKey("id")) {
String id = peer["id"];
if (!combinedPeers.containsKey(id)) {
combinedPeers[id] = peer;
}
}
}
}
}
}
mergePeers(recentPeers);
mergePeers(lanPeers);
mergePeers(abPeers);
mergePeers(groupPeers);
List<Peer> parsedPeers = [];
for (var peer in combinedPeers.values) {
parsedPeers.add(Peer.fromJson(peer));
}
return parsedPeers;
}
class AutocompletePeerTile extends StatefulWidget {
final VoidCallback onSelect;
final Peer peer;
const AutocompletePeerTile({
Key? key,
required this.onSelect,
required this.peer,
}) : super(key: key);
@override
AutocompletePeerTileState createState() => AutocompletePeerTileState();
}
class AutocompletePeerTileState extends State<AutocompletePeerTile> {
List _frontN<T>(List list, int n) {
if (list.length <= n) {
return list;
} else {
return list.sublist(0, n);
}
}
@override
Widget build(BuildContext context) {
final double tileRadius = 5;
final name =
'${widget.peer.username}${widget.peer.username.isNotEmpty && widget.peer.hostname.isNotEmpty ? '@' : ''}${widget.peer.hostname}';
final greyStyle = TextStyle(
fontSize: 11,
color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6));
final child = GestureDetector(
onTap: () => widget.onSelect(),
child: Padding(
padding: EdgeInsets.only(left: 5, right: 5),
child: Container(
height: 42,
margin: EdgeInsets.only(bottom: 5),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
decoration: BoxDecoration(
color: str2color(
'${widget.peer.id}${widget.peer.platform}', 0x7f),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(tileRadius),
bottomLeft: Radius.circular(tileRadius),
),
),
alignment: Alignment.center,
width: 42,
height: null,
child: Padding(
padding: EdgeInsets.all(6),
child: getPlatformImage(widget.peer.platform,
size: 30))),
Expanded(
child: Container(
padding: EdgeInsets.only(left: 10),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.only(
topRight: Radius.circular(tileRadius),
bottomRight: Radius.circular(tileRadius),
),
),
child: Row(
children: [
Expanded(
child: Container(
margin: EdgeInsets.only(top: 2),
child: Container(
margin: EdgeInsets.only(top: 2),
child: Column(
children: [
Container(
margin:
EdgeInsets.only(top: 2),
child: Row(children: [
getOnline(
8, widget.peer.online),
Expanded(
child: Text(
widget.peer.alias.isEmpty
? formatID(
widget.peer.id)
: widget.peer.alias,
overflow:
TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.titleSmall,
)),
widget.peer.alias.isNotEmpty
? Padding(
padding:
const EdgeInsets
.only(
left: 5,
right: 5),
child: Text(
"(${widget.peer.id})",
style: greyStyle,
overflow:
TextOverflow
.ellipsis,
))
: Container(),
])),
Align(
alignment: Alignment.centerLeft,
child: Text(
name,
style: greyStyle,
textAlign: TextAlign.start,
overflow:
TextOverflow.ellipsis,
),
),
],
)))),
],
)),
)
],
))));
final colors = _frontN(widget.peer.tags, 25)
.map((e) => gFFI.abModel.getTagColor(e))
.toList();
return Tooltip(
message: isMobile
? ''
: widget.peer.tags.isNotEmpty
? '${translate('Tags')}: ${widget.peer.tags.join(', ')}'
: '',
child: Stack(children: [
child,
if (colors.isNotEmpty)
Positioned(
top: 5,
right: 10,
child: CustomPaint(
painter: TagPainter(radius: 3, colors: colors),
),
)
]),
);
}
}

View File

@@ -1,10 +1,14 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common/shared_state.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:get/get.dart';
import 'package:qr_flutter/qr_flutter.dart';
import '../../common.dart';
import '../../models/model.dart';
@@ -359,6 +363,8 @@ class DialogTextField extends StatelessWidget {
final Widget? suffixIcon;
final TextEditingController controller;
final FocusNode? focusNode;
final TextInputType? keyboardType;
final List<TextInputFormatter>? inputFormatters;
static const kUsernameTitle = 'Username';
static const kUsernameIcon = Icon(Icons.account_circle_outlined);
@@ -374,6 +380,8 @@ class DialogTextField extends StatelessWidget {
this.prefixIcon,
this.suffixIcon,
this.hintText,
this.keyboardType,
this.inputFormatters,
required this.title,
required this.controller})
: super(key: key);
@@ -398,6 +406,8 @@ class DialogTextField extends StatelessWidget {
focusNode: focusNode,
autofocus: true,
obscureText: obscureText,
keyboardType: keyboardType,
inputFormatters: inputFormatters,
),
),
],
@@ -405,17 +415,260 @@ class DialogTextField extends StatelessWidget {
}
}
abstract class ValidationField extends StatelessWidget {
ValidationField({Key? key}) : super(key: key);
String? validate();
bool get isReady;
}
class Dialog2FaField extends ValidationField {
Dialog2FaField({
Key? key,
required this.controller,
this.autoFocus = true,
this.reRequestFocus = false,
this.title,
this.hintText,
this.errorText,
this.readyCallback,
this.onChanged,
}) : super(key: key);
final TextEditingController controller;
final bool autoFocus;
final bool reRequestFocus;
final String? title;
final String? hintText;
final String? errorText;
final VoidCallback? readyCallback;
final VoidCallback? onChanged;
final errMsg = translate('2FA code must be 6 digits.');
@override
Widget build(BuildContext context) {
return DialogVerificationCodeField(
title: title ?? translate('2FA code'),
controller: controller,
errorText: errorText,
autoFocus: autoFocus,
reRequestFocus: reRequestFocus,
hintText: hintText,
readyCallback: readyCallback,
onChanged: _onChanged,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
],
);
}
String get text => controller.text;
bool get isAllDigits => text.codeUnits.every((e) => e >= 48 && e <= 57);
@override
bool get isReady => text.length == 6 && isAllDigits;
@override
String? validate() => isReady ? null : errMsg;
_onChanged(StateSetter setState, SimpleWrapper<String?> errText) {
onChanged?.call();
if (text.length > 6) {
setState(() => errText.value = errMsg);
return;
}
if (!isAllDigits) {
setState(() => errText.value = errMsg);
return;
}
if (isReady) {
readyCallback?.call();
return;
}
if (errText.value != null) {
setState(() => errText.value = null);
}
}
}
class DialogEmailCodeField extends ValidationField {
DialogEmailCodeField({
Key? key,
required this.controller,
this.autoFocus = true,
this.reRequestFocus = false,
this.hintText,
this.errorText,
this.readyCallback,
this.onChanged,
}) : super(key: key);
final TextEditingController controller;
final bool autoFocus;
final bool reRequestFocus;
final String? hintText;
final String? errorText;
final VoidCallback? readyCallback;
final VoidCallback? onChanged;
final errMsg = translate('Email verification code must be 6 characters.');
@override
Widget build(BuildContext context) {
return DialogVerificationCodeField(
title: translate('Verification code'),
controller: controller,
errorText: errorText,
autoFocus: autoFocus,
reRequestFocus: reRequestFocus,
hintText: hintText,
readyCallback: readyCallback,
helperText: translate('verification_tip'),
onChanged: _onChanged,
keyboardType: TextInputType.visiblePassword,
);
}
String get text => controller.text;
@override
bool get isReady => text.length == 6;
@override
String? validate() => isReady ? null : errMsg;
_onChanged(StateSetter setState, SimpleWrapper<String?> errText) {
onChanged?.call();
if (text.length > 6) {
setState(() => errText.value = errMsg);
return;
}
if (isReady) {
readyCallback?.call();
return;
}
if (errText.value != null) {
setState(() => errText.value = null);
}
}
}
class DialogVerificationCodeField extends StatefulWidget {
DialogVerificationCodeField({
Key? key,
required this.controller,
required this.title,
this.autoFocus = true,
this.reRequestFocus = false,
this.helperText,
this.hintText,
this.errorText,
this.textLength,
this.readyCallback,
this.onChanged,
this.keyboardType,
this.inputFormatters,
}) : super(key: key);
final TextEditingController controller;
final bool autoFocus;
final bool reRequestFocus;
final String title;
final String? helperText;
final String? hintText;
final String? errorText;
final int? textLength;
final VoidCallback? readyCallback;
final Function(StateSetter setState, SimpleWrapper<String?> errText)?
onChanged;
final TextInputType? keyboardType;
final List<TextInputFormatter>? inputFormatters;
@override
State<DialogVerificationCodeField> createState() =>
_DialogVerificationCodeField();
}
class _DialogVerificationCodeField extends State<DialogVerificationCodeField> {
final _focusNode = FocusNode();
Timer? _timer;
Timer? _timerReRequestFocus;
SimpleWrapper<String?> errorText = SimpleWrapper(null);
String _preText = '';
@override
void initState() {
super.initState();
if (widget.autoFocus) {
_timer =
Timer(Duration(milliseconds: 50), () => _focusNode.requestFocus());
if (widget.onChanged != null) {
widget.controller.addListener(() {
final text = widget.controller.text.trim();
if (text == _preText) return;
widget.onChanged!(setState, errorText);
_preText = text;
});
}
}
// software secure keyboard will take the focus since flutter 3.13
// request focus again when android account password obtain focus
if (Platform.isAndroid && widget.reRequestFocus) {
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
_timerReRequestFocus?.cancel();
_timerReRequestFocus = Timer(
Duration(milliseconds: 100), () => _focusNode.requestFocus());
}
});
}
}
@override
void dispose() {
_timer?.cancel();
_timerReRequestFocus?.cancel();
_focusNode.unfocus();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DialogTextField(
title: widget.title,
controller: widget.controller,
errorText: widget.errorText ?? errorText.value,
focusNode: _focusNode,
helperText: widget.helperText,
keyboardType: widget.keyboardType,
inputFormatters: widget.inputFormatters,
);
}
}
class PasswordWidget extends StatefulWidget {
PasswordWidget({
Key? key,
required this.controller,
this.autoFocus = true,
this.reRequestFocus = false,
this.hintText,
this.errorText,
}) : super(key: key);
final TextEditingController controller;
final bool autoFocus;
final bool reRequestFocus;
final String? hintText;
final String? errorText;
@@ -427,6 +680,7 @@ class _PasswordWidgetState extends State<PasswordWidget> {
bool _passwordVisible = false;
final _focusNode = FocusNode();
Timer? _timer;
Timer? _timerReRequestFocus;
@override
void initState() {
@@ -435,11 +689,23 @@ class _PasswordWidgetState extends State<PasswordWidget> {
_timer =
Timer(Duration(milliseconds: 50), () => _focusNode.requestFocus());
}
// software secure keyboard will take the focus since flutter 3.13
// request focus again when android account password obtain focus
if (Platform.isAndroid && widget.reRequestFocus) {
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
_timerReRequestFocus?.cancel();
_timerReRequestFocus = Timer(
Duration(milliseconds: 100), () => _focusNode.requestFocus());
}
});
}
}
@override
void dispose() {
_timer?.cancel();
_timerReRequestFocus?.cancel();
_focusNode.unfocus();
_focusNode.dispose();
super.dispose();
@@ -972,7 +1238,7 @@ void showRestartRemoteDevice(PeerInfo pi, String id, SessionID sessionId,
title: Row(children: [
Icon(Icons.warning_rounded, color: Colors.redAccent, size: 28),
Flexible(
child: Text(translate("Restart Remote Device"))
child: Text(translate("Restart remote device"))
.paddingOnly(left: 10)),
]),
content: Text(
@@ -1244,11 +1510,24 @@ void showConfirmSwitchSidesDialog(
}
customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
double qualityInitValue = 50;
double fpsInitValue = 30;
double initQuality = kDefaultQuality;
double initFps = kDefaultFps;
bool qualitySet = false;
bool fpsSet = false;
bool? direct;
try {
direct =
ConnectionTypeState.find(id).direct.value == ConnectionType.strDirect;
} catch (_) {}
bool hideFps = (await bind.mainIsUsingPublicServer() && direct != true) ||
versionCmp(ffi.ffiModel.pi.version, '1.2.0') < 0;
bool hideMoreQuality =
(await bind.mainIsUsingPublicServer() && direct != true) ||
versionCmp(ffi.ffiModel.pi.version, '1.2.2') < 0;
setCustomValues({double? quality, double? fps}) async {
debugPrint("setCustomValues quality:$quality, fps:$fps");
if (quality != null) {
qualitySet = true;
await bind.sessionSetCustomImageQuality(
@@ -1261,12 +1540,12 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
if (!qualitySet) {
qualitySet = true;
await bind.sessionSetCustomImageQuality(
sessionId: sessionId, value: qualityInitValue.toInt());
sessionId: sessionId, value: initQuality.toInt());
}
if (!fpsSet) {
if (!hideFps && !fpsSet) {
fpsSet = true;
await bind.sessionSetCustomFps(
sessionId: sessionId, fps: fpsInitValue.toInt());
sessionId: sessionId, fps: initFps.toInt());
}
}
@@ -1277,32 +1556,30 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
// quality
final quality = await bind.sessionGetCustomImageQuality(sessionId: sessionId);
qualityInitValue =
quality != null && quality.isNotEmpty ? quality[0].toDouble() : 50.0;
if (qualityInitValue < 10 || qualityInitValue > 2000) {
qualityInitValue = 50;
initQuality = quality != null && quality.isNotEmpty
? quality[0].toDouble()
: kDefaultQuality;
if (initQuality < kMinQuality ||
initQuality > (!hideMoreQuality ? kMaxMoreQuality : kMaxQuality)) {
initQuality = kDefaultQuality;
}
// fps
final fpsOption =
await bind.sessionGetOption(sessionId: sessionId, arg: 'custom-fps');
fpsInitValue = fpsOption == null ? 30 : double.tryParse(fpsOption) ?? 30;
if (fpsInitValue < 5 || fpsInitValue > 120) {
fpsInitValue = 30;
initFps = fpsOption == null
? kDefaultFps
: double.tryParse(fpsOption) ?? kDefaultFps;
if (initFps < kMinFps || initFps > kMaxFps) {
initFps = kDefaultFps;
}
bool? direct;
try {
direct =
ConnectionTypeState.find(id).direct.value == ConnectionType.strDirect;
} catch (_) {}
bool notShowFps = (await bind.mainIsUsingPublicServer() && direct != true) ||
version_cmp(ffi.ffiModel.pi.version, '1.2.0') < 0;
final content = customImageQualityWidget(
initQuality: qualityInitValue,
initFps: fpsInitValue,
initQuality: initQuality,
initFps: initFps,
setQuality: (v) => setCustomValues(quality: v),
setFps: (v) => setCustomValues(fps: v),
showFps: !notShowFps);
showFps: !hideFps,
showMoreQuality: !hideMoreQuality);
msgBoxCommon(ffi.dialogManager, 'Custom Image Quality', content, [btnClose]);
}
@@ -1473,3 +1750,162 @@ void renameDialog(
);
});
}
void change2fa({Function()? callback}) async {
if (bind.mainHasValid2FaSync()) {
await bind.mainSetOption(key: "2fa", value: "");
callback?.call();
return;
}
var new2fa = (await bind.mainGenerate2Fa());
final secretRegex = RegExp(r'secret=([^&]+)');
final secret = secretRegex.firstMatch(new2fa)?.group(1);
String? errorText;
final controller = TextEditingController();
gFFI.dialogManager.show((setState, close, context) {
onVerify() async {
if (await bind.mainVerify2Fa(code: controller.text.trim())) {
callback?.call();
close();
} else {
errorText = translate('wrong-2fa-code');
}
}
final codeField = Dialog2FaField(
controller: controller,
errorText: errorText,
onChanged: () => setState(() => errorText = null),
title: translate('Verification code'),
readyCallback: () {
onVerify();
setState(() {});
},
);
getOnSubmit() => codeField.isReady ? onVerify : null;
return CustomAlertDialog(
title: Text(translate("enable-2fa-title")),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(translate("enable-2fa-desc"),
style: TextStyle(fontSize: 12))
.marginOnly(bottom: 12),
SizedBox(
width: 160,
height: 160,
child: QrImageView(
backgroundColor: Colors.white,
data: new2fa,
version: QrVersions.auto,
size: 160,
gapless: false,
)).marginOnly(bottom: 6),
SelectableText(secret ?? '', style: TextStyle(fontSize: 12))
.marginOnly(bottom: 12),
Row(children: [Expanded(child: codeField)]),
],
),
actions: [
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("OK", onPressed: getOnSubmit()),
],
onCancel: close,
);
});
}
void enter2FaDialog(
SessionID sessionId, OverlayDialogManager dialogManager) async {
final controller = TextEditingController();
final RxBool submitReady = false.obs;
dialogManager.dismissAll();
dialogManager.show((setState, close, context) {
cancel() {
close();
closeConnection();
}
submit() {
gFFI.send2FA(sessionId, controller.text.trim());
close();
dialogManager.showLoading(translate('Logging in...'),
onCancel: closeConnection);
}
late Dialog2FaField codeField;
codeField = Dialog2FaField(
controller: controller,
title: translate('Verification code'),
onChanged: () => submitReady.value = codeField.isReady,
);
return CustomAlertDialog(
title: Text(translate('enter-2fa-title')),
content: codeField,
actions: [
dialogButton('Cancel',
onPressed: cancel,
isOutline: true,
style: TextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color)),
Obx(() => dialogButton(
'OK',
onPressed: submitReady.isTrue ? submit : null,
)),
],
onSubmit: submit,
onCancel: cancel);
});
}
// This dialog should not be dismissed, otherwise it will be black screen, have not reproduced this.
void showWindowsSessionsDialog(
String type,
String title,
String text,
OverlayDialogManager dialogManager,
SessionID sessionId,
String peerId,
String sessions) {
List<dynamic> sessionsList = [];
try {
sessionsList = json.decode(sessions);
} catch (e) {
print(e);
}
List<String> sids = [];
List<String> names = [];
for (var session in sessionsList) {
sids.add(session['sid']);
names.add(session['name']);
}
String selectedUserValue = sids.first;
dialogManager.dismissAll();
dialogManager.show((setState, close, context) {
submit() {
bind.sessionSendSelectedSessionId(
sessionId: sessionId, sid: selectedUserValue);
close();
}
return CustomAlertDialog(
title: null,
content: msgboxContent(type, title, text),
actions: [
ComboBox(
keys: sids,
values: names,
initialKey: selectedUserValue,
onChanged: (value) {
selectedUserValue = value;
}),
dialogButton('Connect', onPressed: submit, isOutline: false),
],
);
});
}

View File

@@ -357,6 +357,7 @@ class LoginWidgetUserPass extends StatelessWidget {
PasswordWidget(
controller: pass,
autoFocus: false,
reRequestFocus: true,
errorText: passMsg,
),
// NOT use Offstage to wrap LinearProgressIndicator
@@ -389,8 +390,7 @@ class LoginWidgetUserPass extends StatelessWidget {
const kAuthReqTypeOidc = 'oidc/';
/// common login dialog for desktop
/// call this directly
// call this directly
Future<bool?> loginDialog() async {
var username =
TextEditingController(text: UserModel.getLocalUserInfo()?['name'] ?? '');
@@ -426,6 +426,55 @@ Future<bool?> loginDialog() async {
close(false);
}
handleLoginResponse(LoginResponse resp, bool storeIfAccessToken,
void Function([dynamic])? close) async {
switch (resp.type) {
case HttpType.kAuthResTypeToken:
if (resp.access_token != null) {
if (storeIfAccessToken) {
await bind.mainSetLocalOption(
key: 'access_token', value: resp.access_token!);
await bind.mainSetLocalOption(
key: 'user_info', value: jsonEncode(resp.user ?? {}));
}
if (close != null) {
close(true);
}
return;
}
break;
case HttpType.kAuthResTypeEmailCheck:
bool? isEmailVerification;
if (resp.tfa_type == null ||
resp.tfa_type == HttpType.kAuthResTypeEmailCheck) {
isEmailVerification = true;
} else if (resp.tfa_type == HttpType.kAuthResTypeTfaCheck) {
isEmailVerification = false;
} else {
passwordMsg = "Failed, bad tfa type from server";
}
if (isEmailVerification != null) {
if (isMobile) {
if (close != null) close(false);
verificationCodeDialog(
resp.user, resp.secret, isEmailVerification);
} else {
setState(() => isInProgress = false);
final res = await verificationCodeDialog(
resp.user, resp.secret, isEmailVerification);
if (res == true) {
if (close != null) close(false);
return;
}
}
}
break;
default:
passwordMsg = "Failed, bad response from server";
break;
}
}
onLogin() async {
// validate
if (username.text.isEmpty) {
@@ -446,35 +495,7 @@ Future<bool?> loginDialog() async {
uuid: await bind.mainGetUuid(),
autoLogin: true,
type: HttpType.kAuthReqTypeAccount));
switch (resp.type) {
case HttpType.kAuthResTypeToken:
if (resp.access_token != null) {
await bind.mainSetLocalOption(
key: 'access_token', value: resp.access_token!);
await bind.mainSetLocalOption(
key: 'user_info', value: jsonEncode(resp.user ?? {}));
close(true);
return;
}
break;
case HttpType.kAuthResTypeEmailCheck:
if (isMobile) {
close(true);
verificationCodeDialog(resp.user);
} else {
setState(() => isInProgress = false);
final res = await verificationCodeDialog(resp.user);
if (res == true) {
close(true);
return;
}
}
break;
default:
passwordMsg = "Failed, bad response from server";
break;
}
await handleLoginResponse(resp, true, close);
} on RequestException catch (err) {
passwordMsg = translate(err.cause);
} catch (err) {
@@ -505,15 +526,21 @@ Future<bool?> loginDialog() async {
.map((e) => ConfigOP(op: e['name'], icon: e['icon']))
.toList(),
curOP: curOP,
cbLogin: (Map<String, dynamic> authBody) {
cbLogin: (Map<String, dynamic> authBody) async {
LoginResponse? resp;
try {
// access_token is already stored in the rust side.
gFFI.userModel.getLoginResponseFromAuthBody(authBody);
resp =
gFFI.userModel.getLoginResponseFromAuthBody(authBody);
} catch (e) {
debugPrint(
'Failed to parse oidc login body: "$authBody"');
}
close(true);
if (resp != null) {
handleLoginResponse(resp, false, null);
}
},
),
],
@@ -572,6 +599,7 @@ Future<bool?> loginDialog() async {
],
),
onCancel: onDialogCancel,
onSubmit: onLogin,
);
});
@@ -582,37 +610,23 @@ Future<bool?> loginDialog() async {
return res;
}
Future<bool?> verificationCodeDialog(UserPayload? user) async {
Future<bool?> verificationCodeDialog(
UserPayload? user, String? secret, bool isEmailVerification) async {
var autoLogin = true;
var isInProgress = false;
String? errorText;
final code = TextEditingController();
final focusNode = FocusNode()..requestFocus();
Timer(Duration(milliseconds: 100), () => focusNode..requestFocus());
final res = await gFFI.dialogManager.show<bool>((setState, close, context) {
bool validate() {
return code.text.length >= 6;
}
code.addListener(() {
if (errorText != null) {
setState(() => errorText = null);
}
});
void onVerify() async {
if (!validate()) {
setState(
() => errorText = translate('Too short, at least 6 characters.'));
return;
}
setState(() => isInProgress = true);
try {
final resp = await gFFI.userModel.login(LoginRequest(
verificationCode: code.text,
tfaCode: isEmailVerification ? null : code.text,
secret: secret,
username: user?.name,
id: await bind.mainGetMyId(),
uuid: await bind.mainGetUuid(),
@@ -641,27 +655,37 @@ Future<bool?> verificationCodeDialog(UserPayload? user) async {
setState(() => isInProgress = false);
}
final codeField = isEmailVerification
? DialogEmailCodeField(
controller: code,
errorText: errorText,
readyCallback: onVerify,
onChanged: () => errorText = null,
)
: Dialog2FaField(
controller: code,
errorText: errorText,
readyCallback: onVerify,
onChanged: () => errorText = null,
);
getOnSubmit() => codeField.isReady ? onVerify : null;
return CustomAlertDialog(
title: Text(translate("Verification code")),
contentBoxConstraints: BoxConstraints(maxWidth: 300),
content: Column(
children: [
Offstage(
offstage: user?.email == null,
offstage: !isEmailVerification || user?.email == null,
child: TextField(
decoration: InputDecoration(
labelText: "Email", prefixIcon: Icon(Icons.email)),
readOnly: true,
controller: TextEditingController(text: user?.email),
)),
const SizedBox(height: 8),
DialogTextField(
title: '${translate("Verification code")}:',
controller: code,
errorText: errorText,
focusNode: focusNode,
helperText: translate('verification_tip'),
),
isEmailVerification ? const SizedBox(height: 8) : const Offstage(),
codeField,
/*
CheckboxListTile(
contentPadding: const EdgeInsets.all(0),
@@ -682,10 +706,10 @@ Future<bool?> verificationCodeDialog(UserPayload? user) async {
],
),
onCancel: close,
onSubmit: onVerify,
onSubmit: getOnSubmit(),
actions: [
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("Verify", onPressed: onVerify),
dialogButton("Verify", onPressed: getOnSubmit()),
]);
});

View File

@@ -27,45 +27,44 @@ class DraggableChatWindow extends StatelessWidget {
@override
Widget build(BuildContext context) {
return isIOS
? IOSDraggable (
position: position,
chatModel: chatModel,
width: width,
height: height,
builder: (context) {
return Column(
children: [
_buildMobileAppBar(context),
Expanded(
child: ChatPage(chatModel: chatModel),
),
],
);
},
)
: Draggable(
checkKeyboard: true,
position: position,
width: width,
height: height,
chatModel: chatModel,
builder: (context, onPanUpdate) {
final child =
Scaffold(
resizeToAvoidBottomInset: false,
appBar: CustomAppBar(
onPanUpdate: onPanUpdate,
appBar: isDesktop
? _buildDesktopAppBar(context)
: _buildMobileAppBar(context),
? IOSDraggable(
position: position,
chatModel: chatModel,
width: width,
height: height,
builder: (context) {
return Column(
children: [
_buildMobileAppBar(context),
Expanded(
child: ChatPage(chatModel: chatModel),
),
body: ChatPage(chatModel: chatModel),
);
return Container(
decoration:
BoxDecoration(border: Border.all(color: MyTheme.border)),
child: child);
});
],
);
},
)
: Draggable(
checkKeyboard: true,
position: position,
width: width,
height: height,
chatModel: chatModel,
builder: (context, onPanUpdate) {
final child = Scaffold(
resizeToAvoidBottomInset: false,
appBar: CustomAppBar(
onPanUpdate: onPanUpdate,
appBar: isDesktop
? _buildDesktopAppBar(context)
: _buildMobileAppBar(context),
),
body: ChatPage(chatModel: chatModel),
);
return Container(
decoration:
BoxDecoration(border: Border.all(color: MyTheme.border)),
child: child);
});
}
Widget _buildMobileAppBar(BuildContext context) {
@@ -354,14 +353,14 @@ class _DraggableState extends State<Draggable> {
}
class IOSDraggable extends StatefulWidget {
const IOSDraggable({
Key? key,
this.position = Offset.zero,
this.chatModel,
required this.width,
required this.height,
required this.builder})
: super(key: key);
const IOSDraggable(
{Key? key,
this.position = Offset.zero,
this.chatModel,
required this.width,
required this.height,
required this.builder})
: super(key: key);
final Offset position;
final ChatModel? chatModel;
@@ -370,10 +369,10 @@ class IOSDraggable extends StatefulWidget {
final Widget Function(BuildContext) builder;
@override
_IOSDraggableState createState() => _IOSDraggableState();
IOSDraggableState createState() => IOSDraggableState();
}
class _IOSDraggableState extends State<IOSDraggable> {
class IOSDraggableState extends State<IOSDraggable> {
late Offset _position;
late ChatModel? _chatModel;
late double _width;
@@ -423,7 +422,7 @@ class _IOSDraggableState extends State<IOSDraggable> {
_lastBottomHeight = bottomHeight;
}
@override
@override
Widget build(BuildContext context) {
checkKeyboard();
return Stack(
@@ -439,12 +438,12 @@ class _IOSDraggableState extends State<IOSDraggable> {
_chatModel?.setChatWindowPosition(_position);
},
child: Material(
child:
Container(
width: _width,
height: _height,
decoration: BoxDecoration(border: Border.all(color: MyTheme.border)),
child: widget.builder(context),
child: Container(
width: _width,
height: _height,
decoration:
BoxDecoration(border: Border.all(color: MyTheme.border)),
child: widget.builder(context),
),
),
),
@@ -499,6 +498,7 @@ class QualityMonitor extends StatelessWidget {
"${qualityMonitorModel.data.targetBitrate ?? '-'}kb"),
_row(
"Codec", qualityMonitorModel.data.codecFormat ?? '-'),
_row("Chroma", qualityMonitorModel.data.chroma ?? '-'),
],
),
)

View File

@@ -19,7 +19,7 @@ import 'dart:math' as math;
typedef PopupMenuEntryBuilder = Future<List<mod_menu.PopupMenuEntry<String>>>
Function(BuildContext);
enum PeerUiType { grid, list }
enum PeerUiType { grid, tile, list }
final peerCardUiType = PeerUiType.grid.obs;
@@ -495,7 +495,7 @@ abstract class BasePeerCard extends StatelessWidget {
return _connectCommonAction(
context,
id,
translate('Transfer File'),
translate('Transfer file'),
isFileTransfer: true,
);
}
@@ -505,7 +505,7 @@ abstract class BasePeerCard extends StatelessWidget {
return _connectCommonAction(
context,
id,
translate('TCP Tunneling'),
translate('TCP tunneling'),
isTcpTunneling: true,
);
}
@@ -568,7 +568,7 @@ abstract class BasePeerCard extends StatelessWidget {
MenuEntryBase<String> _createShortCutAction(String id) {
return MenuEntryButton<String>(
childBuilder: (TextStyle? style) => Text(
translate('Create Desktop Shortcut'),
translate('Create desktop shortcut'),
style: style,
),
proc: () {
@@ -600,8 +600,9 @@ abstract class BasePeerCard extends StatelessWidget {
await _openNewConnInAction(id, 'Open in New Tab', kOptionOpenInTabs);
_openInWindowsAction(String id) async => await _openNewConnInAction(
id, 'Open in New Window', kOptionOpenInWindows);
id, 'Open in new window', kOptionOpenInWindows);
// ignore: unused_element
_openNewConnInOptAction(String id) async =>
mainGetLocalBoolOptionSync(kOptionOpenNewConnInTabs)
? await _openInWindowsAction(id)
@@ -818,7 +819,7 @@ abstract class BasePeerCard extends StatelessWidget {
MenuEntryBase<String> _addToAb(Peer peer) {
return MenuEntryButton<String>(
childBuilder: (TextStyle? style) => Text(
translate('Add to Address Book'),
translate('Add to address book'),
style: style,
),
proc: () {

View File

@@ -75,9 +75,11 @@ class _PeerTabPageState extends State<PeerTabPage>
void initState() {
final uiType = bind.getLocalFlutterOption(k: 'peer-card-ui-type');
if (uiType != '') {
peerCardUiType.value = int.parse(uiType) == PeerUiType.list.index
? PeerUiType.list
: PeerUiType.grid;
peerCardUiType.value = int.parse(uiType) == 0
? PeerUiType.grid
: int.parse(uiType) == 1
? PeerUiType.tile
: PeerUiType.list;
}
hideAbTagsPanel.value =
bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty;
@@ -86,6 +88,9 @@ class _PeerTabPageState extends State<PeerTabPage>
Future<void> handleTabSelection(int tabIndex) async {
if (tabIndex < entries.length) {
if (tabIndex != gFFI.peerTabModel.currentTab) {
gFFI.peerTabModel.setCurrentTabCachedPeers([]);
}
gFFI.peerTabModel.setCurrentTab(tabIndex);
entries[tabIndex].load(hint: false);
}
@@ -215,29 +220,7 @@ class _PeerTabPageState extends State<PeerTabPage>
}
Widget _createPeerViewTypeSwitch(BuildContext context) {
final textColor = Theme.of(context).textTheme.titleLarge?.color;
final types = [PeerUiType.grid, PeerUiType.list];
return Obx(() => _hoverAction(
context: context,
onTap: () async {
final type = types
.elementAt(peerCardUiType.value == types.elementAt(0) ? 1 : 0);
await bind.setLocalFlutterOption(
k: 'peer-card-ui-type', v: type.index.toString());
peerCardUiType.value = type;
},
child: Tooltip(
message: peerCardUiType.value == PeerUiType.grid
? translate('List View')
: translate('Grid View'),
child: Icon(
peerCardUiType.value == PeerUiType.grid
? Icons.view_list_rounded
: Icons.grid_view_rounded,
size: 18,
color: textColor,
))));
return PeerViewDropdown();
}
Widget _createMultiSelection() {
@@ -257,7 +240,7 @@ class _PeerTabPageState extends State<PeerTabPage>
"assets/checkbox-outline.svg",
width: 18,
height: 18,
color: textColor,
colorFilter: svgColor(textColor),
)),
);
}
@@ -353,15 +336,26 @@ class _PeerTabPageState extends State<PeerTabPage>
Widget createMultiSelectionBar() {
final model = Provider.of<PeerTabModel>(context);
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
deleteSelection(),
addSelectionToFav(),
addSelectionToAb(),
editSelectionTags(),
Expanded(child: Container()),
selectionCount(model.selectedPeers.length),
selectAll(),
closeSelection(),
Offstage(
offstage: model.selectedPeers.isEmpty,
child: Row(
children: [
deleteSelection(),
addSelectionToFav(),
addSelectionToAb(),
editSelectionTags(),
],
),
),
Row(
children: [
selectionCount(model.selectedPeers.length),
selectAll(),
closeSelection(),
],
)
],
);
}
@@ -476,7 +470,7 @@ class _PeerTabPageState extends State<PeerTabPage>
});
},
child: Tooltip(
message: translate('Add to Address Book'),
message: translate('Add to address book'),
child: Icon(model.icons[PeerTabIndex.ab.index])),
).marginOnly(left: isMobile ? 11 : 6),
);
@@ -605,7 +599,7 @@ class _PeerTabPageState extends State<PeerTabPage>
"assets/chevron_up_chevron_down.svg",
width: 18,
height: 18,
color: textColor,
colorFilter: svgColor(textColor),
)),
onTap: showMenu,
);
@@ -644,8 +638,6 @@ class _PeerTabPageState extends State<PeerTabPage>
searchWidth -
(actions.length == 2 ? otherActionWidth : 0);
final availablePositions = rightWidth ~/ otherActionWidth;
debugPrint(
"dynamic action count:${dynamicActions.length}, available positions: $availablePositions");
if (availablePositions < dynamicActions.length &&
dynamicActions.length > 1) {
@@ -777,6 +769,87 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
}
}
class PeerViewDropdown extends StatefulWidget {
const PeerViewDropdown({super.key});
@override
State<PeerViewDropdown> createState() => _PeerViewDropdownState();
}
class _PeerViewDropdownState extends State<PeerViewDropdown> {
@override
Widget build(BuildContext context) {
final List<PeerUiType> types = [
PeerUiType.grid,
PeerUiType.tile,
PeerUiType.list
];
final style = TextStyle(
color: Theme.of(context).textTheme.titleLarge?.color,
fontSize: MenuConfig.fontSize,
fontWeight: FontWeight.normal);
List<PopupMenuEntry> items = List.empty(growable: true);
items.add(PopupMenuItem(
height: 36,
enabled: false,
child: Text(translate("Change view"), style: style)));
for (var e in PeerUiType.values) {
items.add(PopupMenuItem(
height: 36,
child: Obx(() => Center(
child: SizedBox(
height: 36,
child: getRadio<PeerUiType>(
Text(
translate(types.indexOf(e) == 0
? 'Big tiles'
: types.indexOf(e) == 1
? 'Small tiles'
: 'List'),
style: style),
e,
peerCardUiType.value,
dense: true, (PeerUiType? v) async {
if (v != null) {
peerCardUiType.value = v;
setState(() {});
await bind.setLocalFlutterOption(
k: "peer-card-ui-type",
v: peerCardUiType.value.index.toString(),
);
}
}),
),
))));
}
var menuPos = RelativeRect.fromLTRB(0, 0, 0, 0);
return _hoverAction(
context: context,
child: Tooltip(
message: translate('Change view'),
child: Icon(
peerCardUiType.value == PeerUiType.grid
? Icons.grid_view_rounded
: peerCardUiType.value == PeerUiType.tile
? Icons.view_list_rounded
: Icons.view_agenda_rounded,
size: 18,
)),
onTapDown: (details) {
final x = details.globalPosition.dx;
final y = details.globalPosition.dy;
menuPos = RelativeRect.fromLTRB(x, y, x, y);
},
onTap: () => showMenu(
context: context,
position: menuPos,
items: items,
elevation: 8,
));
}
}
class PeerSortDropdown extends StatefulWidget {
const PeerSortDropdown({super.key});

View File

@@ -9,6 +9,7 @@ import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:visibility_detector/visibility_detector.dart';
import 'package:window_manager/window_manager.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart';
import '../../common.dart';
import '../../models/peer_model.dart';
@@ -81,7 +82,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
final _curPeers = <String>{};
var _lastChangeTime = DateTime.now();
var _lastQueryPeers = <String>{};
var _lastQueryTime = DateTime.now().subtract(const Duration(hours: 1));
var _lastQueryTime = DateTime.now().add(const Duration(seconds: 30));
var _queryCount = 0;
var _exit = false;
@@ -188,12 +189,25 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
onVisibilityChanged: onVisibilityChanged,
child: widget.peerCardBuilder(peer),
);
final windowWidth = MediaQuery.of(context).size.width;
// `Provider.of<PeerTabModel>(context)` will causes infinete loop.
// Because `gFFI.peerTabModel.setCurrentTabCachedPeers(peers)` will trigger `notifyListeners()`.
//
// No need to listen the currentTab change event.
// Because the currentTab change event will trigger the peers change event,
// and the peers change event will trigger _buildPeersView().
final currentTab = Provider.of<PeerTabModel>(context, listen: false).currentTab;
final hideAbTagsPanel = bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty;
return isDesktop
? Obx(
() => SizedBox(
width: 220,
width: peerCardUiType.value != PeerUiType.list
? 220
: currentTab == PeerTabIndex.group.index || (currentTab == PeerTabIndex.ab.index && !hideAbTagsPanel)
? windowWidth - 390 :
windowWidth - 227,
height:
peerCardUiType.value == PeerUiType.grid ? 140 : 42,
peerCardUiType.value == PeerUiType.grid ? 140 : peerCardUiType.value != PeerUiType.list ? 42 : 45,
child: visibilityChild,
),
)
@@ -258,8 +272,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
if (_queryCount < _maxQueryCount) {
if (now.difference(_lastQueryTime) >= _queryInterval) {
if (_curPeers.isNotEmpty) {
platformFFI.ffiBind
.queryOnlines(ids: _curPeers.toList(growable: false));
bind.queryOnlines(ids: _curPeers.toList(growable: false));
_lastQueryTime = DateTime.now();
_queryCount += 1;
}
@@ -273,7 +286,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
_queryOnlines(bool isLoadEvent) {
if (_curPeers.isNotEmpty) {
platformFFI.ffiBind.queryOnlines(ids: _curPeers.toList(growable: false));
bind.queryOnlines(ids: _curPeers.toList(growable: false));
_lastQueryPeers = {..._curPeers};
if (isLoadEvent) {
_lastChangeTime = DateTime.now();

View File

@@ -34,7 +34,8 @@ class RawKeyFocusScope extends StatelessWidget {
canRequestFocus: true,
focusNode: focusNode,
onFocusChange: onFocusChange,
onKey: inputModel.handleRawKeyEvent,
onKey: (FocusNode data, RawKeyEvent e) =>
inputModel.handleRawKeyEvent(e),
child: child));
}
}

View File

@@ -2,6 +2,8 @@ import 'package:debounce_throttle/debounce_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/desktop_render_texture.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart';
@@ -10,11 +12,19 @@ customImageQualityWidget(
required double initFps,
required Function(double) setQuality,
required Function(double) setFps,
required bool showFps}) {
required bool showFps,
required bool showMoreQuality}) {
if (initQuality < kMinQuality ||
initQuality > (showMoreQuality ? kMaxMoreQuality : kMaxQuality)) {
initQuality = kDefaultQuality;
}
if (initFps < kMinFps || initFps > kMaxFps) {
initFps = kDefaultFps;
}
final qualityValue = initQuality.obs;
final fpsValue = initFps.obs;
final RxBool moreQualityChecked = RxBool(qualityValue.value > 100);
final RxBool moreQualityChecked = RxBool(qualityValue.value > kMaxQuality);
final debouncerQuality = Debouncer<double>(
Duration(milliseconds: 1000),
onChanged: (double v) {
@@ -47,9 +57,11 @@ customImageQualityWidget(
flex: 3,
child: Slider(
value: qualityValue.value,
min: 10.0,
max: moreQualityChecked.value ? 2000 : 100,
divisions: moreQualityChecked.value ? 199 : 18,
min: kMinQuality,
max: moreQualityChecked.value ? kMaxMoreQuality : kMaxQuality,
divisions: moreQualityChecked.value
? ((kMaxMoreQuality - kMinQuality) / 10).round()
: ((kMaxQuality - kMinQuality) / 5).round(),
onChanged: (double value) async {
qualityValue.value = value;
debouncerQuality.value = value;
@@ -69,7 +81,7 @@ customImageQualityWidget(
style: const TextStyle(fontSize: 15),
)),
// mobile doesn't have enough space
if (!isMobile)
if (showMoreQuality && !isMobile)
Expanded(
flex: 1,
child: Row(
@@ -85,7 +97,7 @@ customImageQualityWidget(
))
],
)),
if (isMobile)
if (showMoreQuality && isMobile)
Obx(() => Row(
children: [
Expanded(
@@ -109,9 +121,9 @@ customImageQualityWidget(
flex: 3,
child: Slider(
value: fpsValue.value,
min: 5.0,
max: 120.0,
divisions: 23,
min: kMinFps,
max: kMaxFps,
divisions: ((kMaxFps - kMinFps) / 5).round(),
onChanged: (double value) async {
fpsValue.value = value;
debouncerFps.value = value;
@@ -141,15 +153,10 @@ customImageQualitySetting() {
final fpsKey = 'custom-fps';
var initQuality =
(double.tryParse(bind.mainGetUserDefaultOption(key: qualityKey)) ?? 50.0);
if (initQuality < 10 || initQuality > 2000) {
initQuality = 50;
}
var initFps =
(double.tryParse(bind.mainGetUserDefaultOption(key: fpsKey)) ?? 30.0);
if (initFps < 5 || initFps > 120) {
initFps = 30;
}
(double.tryParse(bind.mainGetUserDefaultOption(key: qualityKey)) ??
kDefaultQuality);
var initFps = (double.tryParse(bind.mainGetUserDefaultOption(key: fpsKey)) ??
kDefaultFps);
return customImageQualityWidget(
initQuality: initQuality,
@@ -160,59 +167,8 @@ customImageQualitySetting() {
setFps: (v) {
bind.mainSetUserDefaultOption(key: fpsKey, value: v.toString());
},
showFps: true);
}
Future<bool> setServerConfig(
List<TextEditingController> controllers,
List<RxString> errMsgs,
ServerConfig config,
) async {
config.idServer = config.idServer.trim();
config.relayServer = config.relayServer.trim();
config.apiServer = config.apiServer.trim();
config.key = config.key.trim();
// id
if (config.idServer.isNotEmpty) {
errMsgs[0].value =
translate(await bind.mainTestIfValidServer(server: config.idServer));
if (errMsgs[0].isNotEmpty) {
return false;
}
}
// relay
if (config.relayServer.isNotEmpty) {
errMsgs[1].value =
translate(await bind.mainTestIfValidServer(server: config.relayServer));
if (errMsgs[1].isNotEmpty) {
return false;
}
}
// api
if (config.apiServer.isNotEmpty) {
if (!config.apiServer.startsWith('http://') &&
!config.apiServer.startsWith('https://')) {
errMsgs[2].value =
'${translate("API Server")}: ${translate("invalid_http")}';
return false;
}
}
final oldApiServer = await bind.mainGetApiServer();
// should set one by one
await bind.mainSetOption(
key: 'custom-rendezvous-server', value: config.idServer);
await bind.mainSetOption(key: 'relay-server', value: config.relayServer);
await bind.mainSetOption(key: 'api-server', value: config.apiServer);
await bind.mainSetOption(key: 'key', value: config.key);
final newApiServer = await bind.mainGetApiServer();
if (oldApiServer.isNotEmpty &&
oldApiServer != newApiServer &&
gFFI.userModel.isLogin) {
gFFI.userModel.logOut(apiServer: oldApiServer);
}
return true;
showFps: true,
showMoreQuality: true);
}
List<Widget> ServerConfigImportExportWidgets(
@@ -221,33 +177,7 @@ List<Widget> ServerConfigImportExportWidgets(
) {
import() {
Clipboard.getData(Clipboard.kTextPlain).then((value) {
final text = value?.text;
if (text != null && text.isNotEmpty) {
try {
final sc = ServerConfig.decode(text);
if (sc.idServer.isNotEmpty) {
controllers[0].text = sc.idServer;
controllers[1].text = sc.relayServer;
controllers[2].text = sc.apiServer;
controllers[3].text = sc.key;
Future<bool> success = setServerConfig(controllers, errMsgs, sc);
success.then((value) {
if (value) {
showToast(
translate('Import server configuration successfully'));
} else {
showToast(translate('Invalid server configuration'));
}
});
} else {
showToast(translate('Invalid server configuration'));
}
} catch (e) {
showToast(translate('Invalid server configuration'));
}
} else {
showToast(translate('Clipboard is empty'));
}
importConfig(controllers, errMsgs, value?.text);
});
}
@@ -265,7 +195,7 @@ List<Widget> ServerConfigImportExportWidgets(
return [
Tooltip(
message: translate('Import Server Config'),
message: translate('Import server config'),
child: IconButton(
icon: Icon(Icons.paste, color: Colors.grey), onPressed: import),
),
@@ -275,3 +205,35 @@ List<Widget> ServerConfigImportExportWidgets(
icon: Icon(Icons.copy, color: Colors.grey), onPressed: export))
];
}
List<(String, String)> otherDefaultSettings() {
List<(String, String)> v = [
('View Mode', 'view_only'),
if (isDesktop) ('show_monitors_tip', kKeyShowMonitorsToolbar),
if (isDesktop) ('Collapse toolbar', 'collapse_toolbar'),
('Show remote cursor', 'show_remote_cursor'),
if (isDesktop) ('Zoom cursor', 'zoom-cursor'),
('Show quality monitor', 'show_quality_monitor'),
('Mute', 'disable_audio'),
if (isDesktop) ('Enable file copy and paste', 'enable_file_transfer'),
('Disable clipboard', 'disable_clipboard'),
('Lock after session end', 'lock_after_session_end'),
('Privacy mode', 'privacy_mode'),
if (isMobile) ('Touch mode', 'touch-mode'),
('True color (4:4:4)', 'i444'),
('Reverse mouse wheel', 'reverse_mouse_wheel'),
('swap-left-right-mouse', 'swap-left-right-mouse'),
if (isDesktop && useTextureRender)
(
'Show displays as individual windows',
kKeyShowDisplaysAsIndividualWindows
),
if (isDesktop && useTextureRender)
(
'Use all my displays for the remote session',
kKeyUseAllMyDisplaysForTheRemoteSession
)
];
return v;
}

View File

@@ -9,6 +9,7 @@ import 'package:flutter_hbb/common/widgets/dialog.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/model.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/desktop_render_texture.dart';
import 'package:get/get.dart';
bool isEditOsPassword = false;
@@ -87,29 +88,34 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
);
}
// osAccount / osPassword
v.add(
TTextMenu(
child: Row(children: [
Text(translate(pi.is_headless ? 'OS Account' : 'OS Password')),
Offstage(
offstage: isDesktop,
child: Icon(Icons.edit, color: MyTheme.accent).marginOnly(left: 12),
)
]),
trailingIcon: Transform.scale(
scale: 0.8,
child: InkWell(
onTap: () => pi.is_headless
? showSetOSAccount(sessionId, ffi.dialogManager)
: handleOsPasswordEditIcon(sessionId, ffi.dialogManager),
child: Icon(Icons.edit),
if (perms['keyboard'] != false) {
v.add(
TTextMenu(
child: Row(children: [
Text(translate(pi.isHeadless ? 'OS Account' : 'OS Password')),
]),
trailingIcon: Transform.scale(
scale: isDesktop ? 0.8 : 1,
child: IconButton(
onPressed: () {
if (isMobile && Navigator.canPop(context)) {
Navigator.pop(context);
}
if (pi.isHeadless) {
showSetOSAccount(sessionId, ffi.dialogManager);
} else {
handleOsPasswordEditIcon(sessionId, ffi.dialogManager);
}
},
icon: Icon(Icons.edit, color: isMobile ? MyTheme.accent : null),
),
),
onPressed: () => pi.isHeadless
? showSetOSAccount(sessionId, ffi.dialogManager)
: handleOsPasswordAction(sessionId, ffi.dialogManager),
),
onPressed: () => pi.is_headless
? showSetOSAccount(sessionId, ffi.dialogManager)
: handleOsPasswordAction(sessionId, ffi.dialogManager),
),
);
);
}
// paste
if (isMobile && perms['keyboard'] != false && perms['clipboard'] != false) {
v.add(TTextMenu(
@@ -132,7 +138,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
if (isDesktop) {
v.add(
TTextMenu(
child: Text(translate('Transfer File')),
child: Text(translate('Transfer file')),
onPressed: () => connect(context, id, isFileTransfer: true)),
);
}
@@ -140,7 +146,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
if (isDesktop) {
v.add(
TTextMenu(
child: Text(translate('TCP Tunneling')),
child: Text(translate('TCP tunneling')),
onPressed: () => connect(context, id, isTcpTunneling: true)),
);
}
@@ -175,7 +181,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
pi.platform == kPeerPlatformMacOS)) {
v.add(
TTextMenu(
child: Text(translate('Restart Remote Device')),
child: Text(translate('Restart remote device')),
onPressed: () =>
showRestartRemoteDevice(pi, id, sessionId, ffi.dialogManager)),
);
@@ -190,6 +196,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
}
// blockUserInput
if (ffi.ffiModel.keyboard &&
ffi.ffiModel.permissions['block_input'] != false &&
pi.platform == kPeerPlatformWindows) // privacy-mode != true ??
{
v.add(TTextMenu(
@@ -208,7 +215,8 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
ffiModel.keyboard &&
pi.platform != kPeerPlatformAndroid &&
pi.platform != kPeerPlatformMacOS &&
version_cmp(pi.version, '1.2.0') >= 0) {
versionCmp(pi.version, '1.2.0') >= 0 &&
bind.peerGetDefaultSessionsCount(id: id) == 1) {
v.add(TTextMenu(
child: Text(translate('Switch Sides')),
onPressed: () =>
@@ -217,15 +225,13 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
// refresh
if (pi.version.isNotEmpty) {
v.add(TTextMenu(
child: Text(translate('Refresh')),
onPressed: () => bind.sessionRefresh(sessionId: sessionId)));
child: Text(translate('Refresh')),
onPressed: () => sessionRefreshVideo(sessionId, pi),
));
}
// record
var codecFormat = ffi.qualityMonitorModel.data.codecFormat;
if (!isDesktop &&
(ffi.recordingModel.start ||
(perms["recording"] != false &&
(codecFormat == "VP8" || codecFormat == "VP9")))) {
(ffi.recordingModel.start || (perms["recording"] != false))) {
v.add(TTextMenu(
child: Row(
children: [
@@ -377,7 +383,7 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
// show remote cursor
if (pi.platform != kPeerPlatformAndroid &&
!ffi.canvasModel.cursorEmbedded &&
!pi.is_wayland) {
!pi.isWayland) {
final state = ShowRemoteCursorState.find(id);
final enabled = !ffiModel.viewOnly;
final option = 'show-remote-cursor';
@@ -436,19 +442,23 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
child: Text(translate('Mute'))));
}
// file copy and paste
if (Platform.isWindows &&
pi.platform == kPeerPlatformWindows &&
perms['file'] != false) {
if (ffiModel.keyboard &&
perms['file'] != false &&
bind.mainHasFileClipboard() &&
pi.platformAdditions.containsKey(kPlatformAdditionsHasFileClipboard)) {
final enabled = !ffiModel.viewOnly;
final option = 'enable-file-transfer';
final value =
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
v.add(TToggleMenu(
value: value,
onChanged: (value) {
if (value == null) return;
bind.sessionToggleOption(sessionId: sessionId, value: option);
},
child: Text(translate('Allow file copy and paste'))));
onChanged: enabled
? (value) {
if (value == null) return;
bind.sessionToggleOption(sessionId: sessionId, value: option);
}
: null,
child: Text(translate('Enable file copy and paste'))));
}
// disable clipboard
if (ffiModel.keyboard && perms['clipboard'] != false) {
@@ -469,34 +479,158 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
}
// lock after session end
if (ffiModel.keyboard) {
final enabled = !ffiModel.viewOnly;
final option = 'lock-after-session-end';
final value =
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
v.add(TToggleMenu(
value: value,
onChanged: (value) {
if (value == null) return;
bind.sessionToggleOption(sessionId: sessionId, value: option);
},
onChanged: enabled
? (value) {
if (value == null) return;
bind.sessionToggleOption(sessionId: sessionId, value: option);
}
: null,
child: Text(translate('Lock after session end'))));
}
// privacy mode
if (ffiModel.keyboard && pi.features.privacyMode) {
final option = 'privacy-mode';
final rxValue = PrivacyModeState.find(id);
if (useTextureRender &&
pi.isSupportMultiDisplay &&
PrivacyModeState.find(id).isEmpty &&
pi.displaysCount.value > 1 &&
bind.mainGetUserDefaultOption(key: kKeyShowMonitorsToolbar) == 'Y') {
final value =
bind.sessionGetDisplaysAsIndividualWindows(sessionId: ffi.sessionId) ==
'Y';
v.add(TToggleMenu(
value: rxValue.value,
value: value,
onChanged: (value) {
if (value == null) return;
if (ffiModel.pi.currentDisplay != 0) {
msgBox(sessionId, 'custom-nook-nocancel-hasclose', 'info',
'Please switch to Display 1 first', '', ffi.dialogManager);
return;
}
bind.sessionToggleOption(sessionId: sessionId, value: option);
bind.sessionSetDisplaysAsIndividualWindows(
sessionId: sessionId, value: value ? 'Y' : '');
},
child: Text(translate('Privacy mode'))));
child: Text(translate('Show displays as individual windows'))));
}
final screenList = await getScreenRectList();
if (useTextureRender && pi.isSupportMultiDisplay && screenList.length > 1) {
final value = bind.sessionGetUseAllMyDisplaysForTheRemoteSession(
sessionId: ffi.sessionId) ==
'Y';
v.add(TToggleMenu(
value: value,
onChanged: (value) {
if (value == null) return;
bind.sessionSetUseAllMyDisplaysForTheRemoteSession(
sessionId: sessionId, value: value ? 'Y' : '');
},
child: Text(translate('Use all my displays for the remote session'))));
}
// 444
final codec_format = ffi.qualityMonitorModel.data.codecFormat;
if (versionCmp(pi.version, "1.2.4") >= 0 &&
(codec_format == "AV1" || codec_format == "VP9")) {
final option = 'i444';
final value =
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
v.add(TToggleMenu(
value: value,
onChanged: (value) async {
if (value == null) return;
await bind.sessionToggleOption(sessionId: sessionId, value: option);
bind.sessionChangePreferCodec(sessionId: sessionId);
},
child: Text(translate('True color (4:4:4)'))));
}
if (isMobile) {
v.addAll(toolbarKeyboardToggles(ffi));
}
return v;
}
var togglePrivacyModeTime = DateTime.now().subtract(const Duration(hours: 1));
List<TToggleMenu> toolbarPrivacyMode(
RxString privacyModeState, BuildContext context, String id, FFI ffi) {
final ffiModel = ffi.ffiModel;
final pi = ffiModel.pi;
final sessionId = ffi.sessionId;
getDefaultMenu(Future<void> Function(SessionID sid, String opt) toggleFunc) {
final enabled = !ffi.ffiModel.viewOnly;
return TToggleMenu(
value: privacyModeState.isNotEmpty,
onChanged: enabled
? (value) {
if (value == null) return;
if (ffiModel.pi.currentDisplay != 0 &&
ffiModel.pi.currentDisplay != kAllDisplayValue) {
msgBox(
sessionId,
'custom-nook-nocancel-hasclose',
'info',
'Please switch to Display 1 first',
'',
ffi.dialogManager);
return;
}
final option = 'privacy-mode';
toggleFunc(sessionId, option);
}
: null,
child: Text(translate('Privacy mode')));
}
final privacyModeImpls =
pi.platformAdditions[kPlatformAdditionsSupportedPrivacyModeImpl]
as List<dynamic>?;
if (privacyModeImpls == null) {
return [
getDefaultMenu((sid, opt) async {
bind.sessionToggleOption(sessionId: sid, value: opt);
togglePrivacyModeTime = DateTime.now();
})
];
}
if (privacyModeImpls.isEmpty) {
return [];
}
if (privacyModeImpls.length == 1) {
final implKey = (privacyModeImpls[0] as List<dynamic>)[0] as String;
return [
getDefaultMenu((sid, opt) async {
bind.sessionTogglePrivacyMode(
sessionId: sid, implKey: implKey, on: privacyModeState.isEmpty);
togglePrivacyModeTime = DateTime.now();
})
];
} else {
return privacyModeImpls.map((e) {
final implKey = (e as List<dynamic>)[0] as String;
final implName = (e)[1] as String;
return TToggleMenu(
child: Text(translate(implName)),
value: privacyModeState.value == implKey,
onChanged: (value) {
if (value == null) return;
togglePrivacyModeTime = DateTime.now();
bind.sessionTogglePrivacyMode(
sessionId: sessionId, implKey: implKey, on: value);
});
}).toList();
}
}
List<TToggleMenu> toolbarKeyboardToggles(FFI ffi) {
final ffiModel = ffi.ffiModel;
final pi = ffiModel.pi;
final sessionId = ffi.sessionId;
List<TToggleMenu> v = [];
// swap key
if (ffiModel.keyboard &&
((Platform.isMacOS && pi.platform != kPeerPlatformMacOS) ||
@@ -504,13 +638,53 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
final option = 'allow_swap_key';
final value =
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
onChanged(bool? value) {
if (value == null) return;
bind.sessionToggleOption(sessionId: sessionId, value: option);
}
final enabled = !ffi.ffiModel.viewOnly;
v.add(TToggleMenu(
value: value,
onChanged: (value) {
if (value == null) return;
bind.sessionToggleOption(sessionId: sessionId, value: option);
},
onChanged: enabled ? onChanged : null,
child: Text(translate('Swap control-command key'))));
}
// reverse mouse wheel
if (ffiModel.keyboard) {
var optionValue =
bind.sessionGetReverseMouseWheelSync(sessionId: sessionId) ?? '';
if (optionValue == '') {
optionValue = bind.mainGetUserDefaultOption(key: 'reverse_mouse_wheel');
}
onChanged(bool? value) async {
if (value == null) return;
await bind.sessionSetReverseMouseWheel(
sessionId: sessionId, value: value ? 'Y' : 'N');
}
final enabled = !ffi.ffiModel.viewOnly;
v.add(TToggleMenu(
value: optionValue == 'Y',
onChanged: enabled ? onChanged : null,
child: Text(translate('Reverse mouse wheel'))));
}
// swap left right mouse
if (ffiModel.keyboard) {
final option = 'swap-left-right-mouse';
final value =
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
onChanged(bool? value) {
if (value == null) return;
bind.sessionToggleOption(sessionId: sessionId, value: option);
}
final enabled = !ffi.ffiModel.viewOnly;
v.add(TToggleMenu(
value: value,
onChanged: enabled ? onChanged : null,
child: Text(translate('swap-left-right-mouse'))));
}
return v;
}

View File

@@ -3,11 +3,28 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
const int kMaxVirtualDisplayCount = 4;
const int kAllVirtualDisplay = -1;
const double kDesktopRemoteTabBarHeight = 28.0;
const int kInvalidWindowId = -1;
const int kMainWindowId = 0;
const kAllDisplayValue = -1;
const kKeyLegacyMode = 'legacy';
const kKeyMapMode = 'map';
const kKeyTranslateMode = 'translate';
const String kPlatformAdditionsIsWayland = "is_wayland";
const String kPlatformAdditionsHeadless = "headless";
const String kPlatformAdditionsIsInstalled = "is_installed";
const String kPlatformAdditionsVirtualDisplays = "virtual_displays";
const String kPlatformAdditionsHasFileClipboard = "has_file_clipboard";
const String kPlatformAdditionsSupportedPrivacyModeImpl = "supported_privacy_mode_impl";
const String kPeerPlatformWindows = "Windows";
const String kPeerPlatformLinux = "Linux";
const String kPeerPlatformMacOS = "Mac OS";
@@ -27,6 +44,8 @@ const String kAppTypeDesktopPortForward = "port forward";
const String kWindowMainWindowOnTop = "main_window_on_top";
const String kWindowGetWindowInfo = "get_window_info";
const String kWindowGetScreenList = "get_screen_list";
// This method is not used, maybe it can be removed.
const String kWindowDisableGrabKeyboard = "disable_grab_keyboard";
const String kWindowActionRebuild = "rebuild";
const String kWindowEventHide = "hide";
@@ -37,11 +56,13 @@ const String kWindowEventNewRemoteDesktop = "new_remote_desktop";
const String kWindowEventNewFileTransfer = "new_file_transfer";
const String kWindowEventNewPortForward = "new_port_forward";
const String kWindowEventActiveSession = "active_session";
const String kWindowEventActiveDisplaySession = "active_display_session";
const String kWindowEventGetRemoteList = "get_remote_list";
const String kWindowEventGetSessionIdList = "get_session_id_list";
const String kWindowEventMoveTabToNewWindow = "move_tab_to_new_window";
const String kWindowEventGetCachedSessionData = "get_cached_session_data";
const String kWindowEventOpenMonitorSession = "open_monitor_session";
const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
const String kOptionOpenInTabs = "allow-open-in-tabs";
@@ -60,6 +81,12 @@ const int kWindowMainId = 0;
const String kPointerEventKindTouch = "touch";
const String kPointerEventKindMouse = "mouse";
const String kKeyShowDisplaysAsIndividualWindows =
'displays_as_individual_windows';
const String kKeyUseAllMyDisplaysForTheRemoteSession =
'use_all_my_displays_for_the_remote_session';
const String kKeyShowMonitorsToolbar = 'show_monitors_toolbar';
// the executable name of the portable version
const String kEnvPortableExecutable = "RUSTDESK_APPNAME";
@@ -77,9 +104,26 @@ const int kDesktopMaxDisplaySize = 3840;
const double kDesktopFileTransferRowHeight = 30.0;
const double kDesktopFileTransferHeaderHeight = 25.0;
const double kMinFps = 5;
const double kDefaultFps = 30;
const double kMaxFps = 120;
const double kMinQuality = 10;
const double kDefaultQuality = 50;
const double kMaxQuality = 100;
const double kMaxMoreQuality = 2000;
double kNewWindowOffset = Platform.isWindows
? 56.0
: Platform.isLinux
? 50.0
: Platform.isMacOS
? 30.0
: 50.0;
EdgeInsets get kDragToResizeAreaPadding =>
!kUseCompatibleUiMode && Platform.isLinux
? stateGlobal.fullscreen || stateGlobal.isMaximized.value
? stateGlobal.fullscreen.isTrue || stateGlobal.isMaximized.value
? EdgeInsets.zero
: EdgeInsets.all(5.0)
: EdgeInsets.zero;
@@ -162,6 +206,12 @@ const kRemoteAudioDualWay = 'dual-way';
const kIgnoreDpi = true;
// ================================ mobile ================================
// Magic numbers, maybe need to avoid it or use a better way to get them.
const kMobileDelaySoftKeyboard = Duration(milliseconds: 30);
const kMobileDelaySoftKeyboardFocus = Duration(milliseconds: 30);
/// Android constants
const kActionApplicationDetailsSettings =
"android.settings.APPLICATION_DETAILS_SETTINGS";

View File

@@ -11,10 +11,12 @@ import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:window_manager/window_manager.dart';
import 'package:flutter_hbb/models/peer_model.dart';
import '../../common.dart';
import '../../common/formatter/id_formatter.dart';
import '../../common/widgets/peer_tab_page.dart';
import '../../common/widgets/autocomplete.dart';
import '../../models/platform_model.dart';
import '../widgets/button.dart';
@@ -35,12 +37,15 @@ class _ConnectionPageState extends State<ConnectionPage>
Timer? _updateTimer;
final RxBool _idInputFocused = false.obs;
final FocusNode _idFocusNode = FocusNode();
var svcStopped = Get.find<RxBool>(tag: 'stop-service');
var svcIsUsingPublicServer = true.obs;
bool isWindowMinimized = false;
List<Peer> peers = [];
bool isPeersLoading = false;
bool isPeersLoaded = false;
@override
void initState() {
@@ -58,12 +63,6 @@ class _ConnectionPageState extends State<ConnectionPage>
_updateTimer = periodic_immediate(Duration(seconds: 1), () async {
updateStatus();
});
_idFocusNode.addListener(() {
_idInputFocused.value = _idFocusNode.hasFocus;
// select all to faciliate removing text, just following the behavior of address input of chrome
_idController.selection = TextSelection(
baseOffset: 0, extentOffset: _idController.value.text.length);
});
Get.put<IDTextEditingController>(_idController);
windowManager.addListener(this);
}
@@ -76,6 +75,9 @@ class _ConnectionPageState extends State<ConnectionPage>
if (Get.isRegistered<IDTextEditingController>()) {
Get.delete<IDTextEditingController>();
}
if (Get.isRegistered<TextEditingController>()) {
Get.delete<TextEditingController>();
}
super.dispose();
}
@@ -142,8 +144,20 @@ class _ConnectionPageState extends State<ConnectionPage>
connect(context, id, isFileTransfer: isFileTransfer);
}
Future<void> _fetchPeers() async {
setState(() {
isPeersLoading = true;
});
await Future.delayed(Duration(milliseconds: 100));
peers = await getAllPeers();
setState(() {
isPeersLoading = false;
isPeersLoaded = true;
});
}
/// UI for the remote ID TextField.
/// Search for a peer and connect to it if the id exists.
/// Search for a peer.
Widget _buildRemoteIDTextField(BuildContext context) {
var w = Container(
width: 320 + 20 * 2,
@@ -157,51 +171,192 @@ class _ConnectionPageState extends State<ConnectionPage>
Row(
children: [
Expanded(
child: AutoSizeText(
translate('Control Remote Desktop'),
maxLines: 1,
style: Theme.of(context)
.textTheme
.titleLarge
?.merge(TextStyle(height: 1)),
),
),
child: Row(
children: [
AutoSizeText(
translate('Control Remote Desktop'),
maxLines: 1,
style: Theme.of(context)
.textTheme
.titleLarge
?.merge(TextStyle(height: 1)),
).marginOnly(right: 4),
Tooltip(
waitDuration: Duration(milliseconds: 0),
message: translate("id_input_tip"),
child: Icon(
Icons.help_outline_outlined,
size: 16,
color: Theme.of(context)
.textTheme
.titleLarge
?.color
?.withOpacity(0.5),
),
),
],
)),
],
).marginOnly(bottom: 15),
Row(
children: [
Expanded(
child: Obx(
() => TextField(
maxLength: 90,
autocorrect: false,
enableSuggestions: false,
keyboardType: TextInputType.visiblePassword,
focusNode: _idFocusNode,
style: const TextStyle(
fontFamily: 'WorkSans',
fontSize: 22,
height: 1.4,
),
maxLines: 1,
cursorColor:
Theme.of(context).textTheme.titleLarge?.color,
decoration: InputDecoration(
filled: false,
counterText: '',
hintText: _idInputFocused.value
? null
: translate('Enter Remote ID'),
contentPadding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 13)),
controller: _idController,
inputFormatters: [IDTextInputFormatter()],
onSubmitted: (s) {
onConnect();
},
),
),
),
child: Autocomplete<Peer>(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<Peer>.empty();
} else if (peers.isEmpty && !isPeersLoaded) {
Peer emptyPeer = Peer(
id: '',
username: '',
hostname: '',
alias: '',
platform: '',
tags: [],
hash: '',
forceAlwaysRelay: false,
rdpPort: '',
rdpUsername: '',
loginName: '',
);
return [emptyPeer];
} else {
String textWithoutSpaces =
textEditingValue.text.replaceAll(" ", "");
if (int.tryParse(textWithoutSpaces) != null) {
textEditingValue = TextEditingValue(
text: textWithoutSpaces,
selection: textEditingValue.selection,
);
}
String textToFind = textEditingValue.text.toLowerCase();
return peers
.where((peer) =>
peer.id.toLowerCase().contains(textToFind) ||
peer.username
.toLowerCase()
.contains(textToFind) ||
peer.hostname
.toLowerCase()
.contains(textToFind) ||
peer.alias.toLowerCase().contains(textToFind))
.toList();
}
},
fieldViewBuilder: (
BuildContext context,
TextEditingController fieldTextEditingController,
FocusNode fieldFocusNode,
VoidCallback onFieldSubmitted,
) {
fieldTextEditingController.text = _idController.text;
Get.put<TextEditingController>(fieldTextEditingController);
fieldFocusNode.addListener(() async {
_idInputFocused.value = fieldFocusNode.hasFocus;
if (fieldFocusNode.hasFocus && !isPeersLoading) {
_fetchPeers();
}
});
final textLength =
fieldTextEditingController.value.text.length;
// select all to facilitate removing text, just following the behavior of address input of chrome
fieldTextEditingController.selection =
TextSelection(baseOffset: 0, extentOffset: textLength);
return Obx(() => TextField(
autocorrect: false,
enableSuggestions: false,
keyboardType: TextInputType.visiblePassword,
focusNode: fieldFocusNode,
style: const TextStyle(
fontFamily: 'WorkSans',
fontSize: 22,
height: 1.4,
),
maxLines: 1,
cursorColor:
Theme.of(context).textTheme.titleLarge?.color,
decoration: InputDecoration(
filled: false,
counterText: '',
hintText: _idInputFocused.value
? null
: translate('Enter Remote ID'),
contentPadding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 13)),
controller: fieldTextEditingController,
inputFormatters: [IDTextInputFormatter()],
onChanged: (v) {
_idController.id = v;
},
));
},
onSelected: (option) {
setState(() {
_idController.id = option.id;
FocusScope.of(context).unfocus();
});
},
optionsViewBuilder: (BuildContext context,
AutocompleteOnSelected<Peer> onSelected,
Iterable<Peer> options) {
double maxHeight = options.length * 50;
if (options.length == 1) {
maxHeight = 52;
} else if (options.length == 3) {
maxHeight = 146;
} else if (options.length == 4) {
maxHeight = 193;
}
maxHeight = maxHeight.clamp(0, 200);
return Align(
alignment: Alignment.topLeft,
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 5,
spreadRadius: 1,
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Material(
elevation: 4,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: maxHeight,
maxWidth: 319,
),
child: peers.isEmpty && isPeersLoading
? Container(
height: 80,
child: Center(
child: CircularProgressIndicator(
strokeWidth: 2,
),
))
: Padding(
padding:
const EdgeInsets.only(top: 5),
child: ListView(
children: options
.map((peer) =>
AutocompletePeerTile(
onSelect: () =>
onSelected(peer),
peer: peer))
.toList(),
),
),
),
))),
);
},
)),
],
),
Padding(
@@ -212,7 +367,7 @@ class _ConnectionPageState extends State<ConnectionPage>
Button(
isOutline: true,
onTap: () => onConnect(isFileTransfer: true),
text: "Transfer File",
text: "Transfer file",
),
const SizedBox(
width: 17,
@@ -265,7 +420,7 @@ class _ConnectionPageState extends State<ConnectionPage>
onTap: () async {
await start_service(true);
},
child: Text(translate("Start Service"),
child: Text(translate("Start service"),
style: TextStyle(
decoration: TextDecoration.underline,
fontSize: em)))
@@ -308,7 +463,7 @@ class _ConnectionPageState extends State<ConnectionPage>
}
void onUsePublicServerGuide() {
const url = "https://rustdesk.com/blog/id-relay-set/";
const url = "https://rustdesk.com/pricing.html";
canLaunchUrlString(url).then((can) {
if (can) {
launchUrlString(url);

View File

@@ -187,12 +187,12 @@ class _DesktopHomePageState extends State<DesktopHomePage>
? Theme.of(context).scaffoldBackgroundColor
: Theme.of(context).colorScheme.background,
child: Tooltip(
message: translate('Settings'),
child: Icon(
Icons.more_vert_outlined,
size: 20,
color: hover.value ? textColor : textColor?.withOpacity(0.5),
)),
message: translate('Settings'),
child: Icon(
Icons.more_vert_outlined,
size: 20,
color: hover.value ? textColor : textColor?.withOpacity(0.5),
)),
),
),
onHover: (value) => hover.value = value,
@@ -256,27 +256,27 @@ class _DesktopHomePageState extends State<DesktopHomePage>
child: Obx(() => RotatedBox(
quarterTurns: 2,
child: Tooltip(
message: translate('Refresh Password'),
child: Icon(
Icons.refresh,
color: refreshHover.value
? textColor
: Color(0xFFDDDDDD),
size: 22,
))
)),
message: translate('Refresh Password'),
child: Icon(
Icons.refresh,
color: refreshHover.value
? textColor
: Color(0xFFDDDDDD),
size: 22,
)))),
onHover: (value) => refreshHover.value = value,
).marginOnly(right: 8, top: 4),
InkWell(
child: Obx(
() => Tooltip(
message: translate('Change Password'),
child: Icon(
Icons.edit,
color:
editHover.value ? textColor : Color(0xFFDDDDDD),
size: 22,
)).marginOnly(right: 8, top: 4),
message: translate('Change Password'),
child: Icon(
Icons.edit,
color: editHover.value
? textColor
: Color(0xFFDDDDDD),
size: 22,
)).marginOnly(right: 8, top: 4),
),
onTap: () => DesktopSettingPage.switch2page(1),
onHover: (value) => editHover.value = value,
@@ -329,19 +329,24 @@ class _DesktopHomePageState extends State<DesktopHomePage>
"Click to download", () async {
final Uri url = Uri.parse('https://rustdesk.com/download');
await launchUrl(url);
},
closeButton: true);
}, closeButton: true);
}
if (systemError.isNotEmpty) {
return buildInstallCard("", systemError, "", () {});
}
if (Platform.isWindows) {
if (!bind.mainIsInstalled()) {
return buildInstallCard(
"", "install_tip", "Install", bind.mainGotoInstall);
return buildInstallCard("", "install_tip", "Install", () async {
await rustDeskWinManager.closeAllSubWindows();
bind.mainGotoInstall();
});
} else if (bind.mainIsInstalledLowerVersion()) {
return buildInstallCard("Status", "Your installation is lower version.",
"Click to upgrade", bind.mainUpdateMe);
return buildInstallCard(
"Status", "Your installation is lower version.", "Click to upgrade",
() async {
await rustDeskWinManager.closeAllSubWindows();
bind.mainUpdateMe();
});
}
} else if (Platform.isMacOS) {
if (!bind.mainIsCanScreenRecording(prompt: false)) {
@@ -379,16 +384,42 @@ class _DesktopHomePageState extends State<DesktopHomePage>
// });
// }
} else if (Platform.isLinux) {
final LinuxCards = <Widget>[];
if (bind.isSelinuxEnforcing()) {
// Check is SELinux enforcing, but show user a tip of is SELinux enabled for simple.
final keyShowSelinuxHelpTip = "show-selinux-help-tip";
if (bind.mainGetLocalOption(key: keyShowSelinuxHelpTip) != 'N') {
LinuxCards.add(buildInstallCard(
"Warning",
"selinux_tip",
"",
() async {},
marginTop: LinuxCards.isEmpty ? 20.0 : 5.0,
help: 'Help',
link:
'https://rustdesk.com/docs/en/client/linux/#permissions-issue',
closeButton: true,
closeOption: keyShowSelinuxHelpTip,
));
}
}
if (bind.mainCurrentIsWayland()) {
return buildInstallCard(
LinuxCards.add(buildInstallCard(
"Warning", "wayland_experiment_tip", "", () async {},
marginTop: LinuxCards.isEmpty ? 20.0 : 5.0,
help: 'Help',
link: 'https://rustdesk.com/docs/en/manual/linux/#x11-required');
link: 'https://rustdesk.com/docs/en/client/linux/#x11-required'));
} else if (bind.mainIsLoginWayland()) {
return buildInstallCard("Warning",
LinuxCards.add(buildInstallCard("Warning",
"Login screen using Wayland is not supported", "", () async {},
marginTop: LinuxCards.isEmpty ? 20.0 : 5.0,
help: 'Help',
link: 'https://rustdesk.com/docs/en/manual/linux/#login-screen');
link: 'https://rustdesk.com/docs/en/client/linux/#login-screen'));
}
if (LinuxCards.isNotEmpty) {
return Column(
children: LinuxCards,
);
}
}
return Container();
@@ -396,102 +427,115 @@ class _DesktopHomePageState extends State<DesktopHomePage>
Widget buildInstallCard(String title, String content, String btnText,
GestureTapCallback onPressed,
{String? help, String? link, bool? closeButton}) {
void closeCard() {
setState(() {
isCardClosed = true;
});
{double marginTop = 20.0,
String? help,
String? link,
bool? closeButton,
String? closeOption}) {
void closeCard() async {
if (closeOption != null) {
await bind.mainSetLocalOption(key: closeOption, value: 'N');
if (bind.mainGetLocalOption(key: closeOption) == 'N') {
setState(() {
isCardClosed = true;
});
}
} else {
setState(() {
isCardClosed = true;
});
}
}
return Stack(
children: [
Container(
margin: EdgeInsets.only(top: 20),
margin: EdgeInsets.only(top: marginTop),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color.fromARGB(255, 226, 66, 188),
Color.fromARGB(255, 244, 114, 124),
],
)),
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: (title.isNotEmpty
? <Widget>[
Center(
child: Text(
translate(title),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 15),
).marginOnly(bottom: 6)),
]
: <Widget>[]) +
<Widget>[
Text(
translate(content),
style: TextStyle(
height: 1.5,
color: Colors.white,
fontWeight: FontWeight.normal,
fontSize: 13),
).marginOnly(bottom: 20)
] +
(btnText.isNotEmpty
? <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FixedWidthButton(
width: 150,
padding: 8,
isOutline: true,
text: translate(btnText),
textColor: Colors.white,
borderColor: Colors.white,
textSize: 20,
radius: 10,
onTap: onPressed,
)
])
]
: <Widget>[]) +
(help != null
? <Widget>[
Center(
child: InkWell(
onTap: () async =>
await launchUrl(Uri.parse(link!)),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color.fromARGB(255, 226, 66, 188),
Color.fromARGB(255, 244, 114, 124),
],
)),
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: (title.isNotEmpty
? <Widget>[
Center(
child: Text(
translate(help),
style: TextStyle(
decoration: TextDecoration.underline,
color: Colors.white,
fontSize: 12),
)).marginOnly(top: 6)),
]
: <Widget>[]))),
translate(title),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 15),
).marginOnly(bottom: 6)),
]
: <Widget>[]) +
<Widget>[
Text(
translate(content),
style: TextStyle(
height: 1.5,
color: Colors.white,
fontWeight: FontWeight.normal,
fontSize: 13),
).marginOnly(bottom: 20)
] +
(btnText.isNotEmpty
? <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FixedWidthButton(
width: 150,
padding: 8,
isOutline: true,
text: translate(btnText),
textColor: Colors.white,
borderColor: Colors.white,
textSize: 20,
radius: 10,
onTap: onPressed,
)
])
]
: <Widget>[]) +
(help != null
? <Widget>[
Center(
child: InkWell(
onTap: () async =>
await launchUrl(Uri.parse(link!)),
child: Text(
translate(help),
style: TextStyle(
decoration:
TextDecoration.underline,
color: Colors.white,
fontSize: 12),
)).marginOnly(top: 6)),
]
: <Widget>[]))),
),
if (closeButton != null && closeButton == true)
Positioned(
top: 18,
right: 0,
child: IconButton(
icon: Icon(
Icons.close,
color: Colors.white,
size: 20,
Positioned(
top: 18,
right: 0,
child: IconButton(
icon: Icon(
Icons.close,
color: Colors.white,
size: 20,
),
onPressed: closeCard,
),
onPressed: closeCard,
),
),
],
);
}
@@ -555,6 +599,22 @@ class _DesktopHomePageState extends State<DesktopHomePage>
Get.put<RxBool>(svcStopped, tag: 'stop-service');
rustDeskWinManager.registerActiveWindowListener(onActiveWindowChanged);
screenToMap(window_size.Screen screen) => {
'frame': {
'l': screen.frame.left,
't': screen.frame.top,
'r': screen.frame.right,
'b': screen.frame.bottom,
},
'visibleFrame': {
'l': screen.visibleFrame.left,
't': screen.visibleFrame.top,
'r': screen.visibleFrame.right,
'b': screen.visibleFrame.bottom,
},
'scaleFactor': screen.scaleFactor,
};
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
debugPrint(
"[Main] call ${call.method} with args ${call.arguments} from window $fromWindowId");
@@ -563,24 +623,13 @@ class _DesktopHomePageState extends State<DesktopHomePage>
} else if (call.method == kWindowGetWindowInfo) {
final screen = (await window_size.getWindowInfo()).screen;
if (screen == null) {
return "";
return '';
} else {
return jsonEncode({
'frame': {
'l': screen.frame.left,
't': screen.frame.top,
'r': screen.frame.right,
'b': screen.frame.bottom,
},
'visibleFrame': {
'l': screen.visibleFrame.left,
't': screen.visibleFrame.top,
'r': screen.visibleFrame.right,
'b': screen.visibleFrame.bottom,
},
'scaleFactor': screen.scaleFactor,
});
return jsonEncode(screenToMap(screen));
}
} else if (call.method == kWindowGetScreenList) {
return jsonEncode(
(await window_size.getScreenList()).map(screenToMap).toList());
} else if (call.method == kWindowActionRebuild) {
reloadCurrentWindow();
} else if (call.method == kWindowEventShow) {
@@ -604,8 +653,18 @@ class _DesktopHomePageState extends State<DesktopHomePage>
debugPrint("Failed to parse window id '${call.arguments}': $e");
}
if (windowId != null) {
await rustDeskWinManager.moveTabToNewWindow(windowId, args[1], args[2]);
await rustDeskWinManager.moveTabToNewWindow(
windowId, args[1], args[2]);
}
} else if (call.method == kWindowEventOpenMonitorSession) {
final args = jsonDecode(call.arguments);
final windowId = args['window_id'] as int;
final peerId = args['peer_id'] as String;
final display = args['display'] as int;
final displayCount = args['display_count'] as int;
final screenRect = parseParamScreenRect(args);
await rustDeskWinManager.openMonitorSession(
windowId, peerId, display, displayCount, screenRect);
}
});
_uniLinksSubscription = listenUniLinks();

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
@@ -322,6 +323,7 @@ class _GeneralState extends State<_General> {
'enable-confirm-closing-tabs',
isServer: false),
_OptionCheckBox(context, 'Adaptive bitrate', 'enable-abr'),
wallpaper(),
_OptionCheckBox(
context,
'Open connection in new tab',
@@ -348,11 +350,49 @@ class _GeneralState extends State<_General> {
return _Card(title: 'Other', children: children);
}
Widget wallpaper() {
return futureBuilder(future: () async {
final support = await bind.mainSupportRemoveWallpaper();
return support;
}(), hasData: (data) {
if (data is bool && data == true) {
final option = 'allow-remove-wallpaper';
bool value = mainGetBoolOptionSync(option);
return Row(
children: [
Flexible(
child: _OptionCheckBox(
context,
'Remove wallpaper during incoming sessions',
option,
update: () {
setState(() {});
},
),
),
if (value)
_CountDownButton(
text: 'Test',
second: 5,
onPressed: () {
bind.mainTestWallpaper(second: 5);
},
)
],
);
}
return Offstage();
});
}
Widget hwcodec() {
final hwcodec = bind.mainHasHwcodec();
final gpucodec = bind.mainHasGpucodec();
return Offstage(
offstage: !bind.mainHasHwcodec(),
offstage: !(hwcodec || gpucodec),
child: _Card(title: 'Hardware Codec', children: [
_OptionCheckBox(context, 'Enable hardware codec', 'enable-hwcodec'),
_OptionCheckBox(context, 'Enable hardware codec', 'enable-hwcodec')
]),
);
}
@@ -474,7 +514,7 @@ class _GeneralState extends State<_General> {
if (!keys.contains(currentKey)) {
currentKey = '';
}
return _ComboBox(
return ComboBox(
keys: keys,
values: values,
initialKey: currentKey,
@@ -526,6 +566,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
child: Column(children: [
permissions(context),
password(context),
_Card(title: '2FA', children: [tfa()]),
_Card(title: 'ID', children: [changeId()]),
more(context),
]),
@@ -534,6 +575,45 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
)).marginOnly(bottom: _kListViewBottomMargin));
}
Widget tfa() {
bool enabled = !locked;
// Simple temp wrapper for PR check
tmpWrapper() {
RxBool has2fa = bind.mainHasValid2FaSync().obs;
update() async {
has2fa.value = bind.mainHasValid2FaSync();
}
onChanged(bool? checked) async {
change2fa(callback: update);
}
return GestureDetector(
child: InkWell(
child: Obx(() => Row(
children: [
Checkbox(
value: has2fa.value,
onChanged: enabled ? onChanged : null)
.marginOnly(right: 5),
Expanded(
child: Text(
translate('enable-2fa-title'),
style:
TextStyle(color: disabledTextColor(context, enabled)),
))
],
)),
),
onTap: () {
onChanged(!has2fa.value);
},
).marginOnly(left: _kCheckBoxLeftMargin);
}
return tmpWrapper();
}
Widget changeId() {
return ChangeNotifierProvider.value(
value: gFFI.serverModel,
@@ -574,7 +654,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
}
return _Card(title: 'Permissions', children: [
_ComboBox(
ComboBox(
keys: [
'',
'full',
@@ -593,23 +673,27 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
}).marginOnly(left: _kContentHMargin),
Column(
children: [
_OptionCheckBox(context, 'Enable Keyboard/Mouse', 'enable-keyboard',
_OptionCheckBox(context, 'Enable keyboard/mouse', 'enable-keyboard',
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(context, 'Enable Clipboard', 'enable-clipboard',
_OptionCheckBox(context, 'Enable clipboard', 'enable-clipboard',
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(
context, 'Enable File Transfer', 'enable-file-transfer',
context, 'Enable file transfer', 'enable-file-transfer',
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(context, 'Enable Audio', 'enable-audio',
_OptionCheckBox(context, 'Enable audio', 'enable-audio',
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(context, 'Enable TCP Tunneling', 'enable-tunnel',
_OptionCheckBox(context, 'Enable TCP tunneling', 'enable-tunnel',
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(
context, 'Enable Remote Restart', 'enable-remote-restart',
context, 'Enable remote restart', 'enable-remote-restart',
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(
context, 'Enable Recording Session', 'enable-record-session',
context, 'Enable recording session', 'enable-record-session',
enabled: enabled, fakeValue: fakeValue),
if (Platform.isWindows)
_OptionCheckBox(
context, 'Enable blocking user input', 'enable-block-input',
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(context, 'Enable remote configuration modification',
'allow-remote-config-modification',
enabled: enabled, fakeValue: fakeValue),
@@ -677,7 +761,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
Text(
value,
style: TextStyle(
color: _disabledTextColor(
color: disabledTextColor(
context, onChanged != null)),
),
],
@@ -697,7 +781,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
final usePassword = model.approveMode != 'click';
return _Card(title: 'Password', children: [
_ComboBox(
ComboBox(
enabled: !locked,
keys: modeKeys,
values: modeValues,
@@ -730,7 +814,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
bool enabled = !locked;
return _Card(title: 'Security', children: [
shareRdp(context, enabled),
_OptionCheckBox(context, 'Deny LAN Discovery', 'enable-lan-discovery',
_OptionCheckBox(context, 'Deny LAN discovery', 'enable-lan-discovery',
reverse: true, enabled: enabled),
...directIp(context),
whitelist(),
@@ -757,7 +841,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
Expanded(
child: Text(translate('Enable RDP session sharing'),
style:
TextStyle(color: _disabledTextColor(context, enabled))),
TextStyle(color: disabledTextColor(context, enabled))),
)
],
).marginOnly(left: _kCheckBoxLeftMargin),
@@ -770,7 +854,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
update() => setState(() {});
RxBool applyEnabled = false.obs;
return [
_OptionCheckBox(context, 'Enable Direct IP Access', 'direct-server',
_OptionCheckBox(context, 'Enable direct IP access', 'direct-server',
update: update, enabled: !locked),
() {
// Simple temp wrapper for PR check
@@ -860,7 +944,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
child: Text(
translate('Use IP Whitelisting'),
style:
TextStyle(color: _disabledTextColor(context, enabled)),
TextStyle(color: disabledTextColor(context, enabled)),
))
],
)),
@@ -904,7 +988,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
child: Text(
translate('Hide connection management window'),
style: TextStyle(
color: _disabledTextColor(
color: disabledTextColor(
context, enabled && enableHideCm)),
),
),
@@ -1016,8 +1100,7 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
// Simple temp wrapper for PR check
tmpWrapper() {
// Setting page is not modal, oldOptions should only be used when getting options, never when setting.
Map<String, dynamic> oldOptions =
jsonDecode(bind.mainGetOptionsSync() as String);
Map<String, dynamic> oldOptions = jsonDecode(bind.mainGetOptionsSync());
old(String key) {
return (oldOptions[key] ?? '').trim();
}
@@ -1044,7 +1127,7 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
submit() async {
bool result = await setServerConfig(
controllers,
null,
errMsgs,
ServerConfig(
idServer: idController.text,
@@ -1108,6 +1191,7 @@ class _DisplayState extends State<_Display> {
scrollStyle(context),
imageQuality(context),
codec(context),
privacyModeImpl(context),
other(context),
]).marginOnly(bottom: _kListViewBottomMargin));
}
@@ -1247,6 +1331,42 @@ class _DisplayState extends State<_Display> {
]);
}
Widget privacyModeImpl(BuildContext context) {
final supportedPrivacyModeImpls = bind.mainSupportedPrivacyModeImpls();
late final List<dynamic> privacyModeImpls;
try {
privacyModeImpls = jsonDecode(supportedPrivacyModeImpls);
} catch (e) {
debugPrint('failed to parse supported privacy mode impls, err=$e');
return Offstage();
}
if (privacyModeImpls.length < 2) {
return Offstage();
}
final key = 'privacy-mode-impl-key';
onChanged(String value) async {
await bind.mainSetOption(key: key, value: value);
setState(() {});
}
String groupValue = bind.mainGetOptionSync(key: key);
if (groupValue.isEmpty) {
groupValue = bind.mainDefaultPrivacyModeImpl();
}
return _Card(
title: 'Privacy mode',
children: privacyModeImpls.map((impl) {
final d = impl as List<dynamic>;
return _Radio(context,
value: d[0] as String,
groupValue: groupValue,
label: d[1] as String,
onChanged: onChanged);
}).toList(),
);
}
Widget otherRow(String label, String key) {
final value = bind.mainGetUserDefaultOption(key: key) == 'Y';
onChanged(bool b) async {
@@ -1268,20 +1388,9 @@ class _DisplayState extends State<_Display> {
}
Widget other(BuildContext context) {
return _Card(title: 'Other Default Options', children: [
otherRow('View Mode', 'view_only'),
otherRow('show_monitors_tip', 'show_monitors_toolbar'),
otherRow('Collapse toolbar', 'collapse_toolbar'),
otherRow('Show remote cursor', 'show_remote_cursor'),
otherRow('Zoom cursor', 'zoom-cursor'),
otherRow('Show quality monitor', 'show_quality_monitor'),
otherRow('Mute', 'disable_audio'),
otherRow('Allow file copy and paste', 'enable_file_transfer'),
otherRow('Disable clipboard', 'disable_clipboard'),
otherRow('Lock after session end', 'lock_after_session_end'),
otherRow('Privacy mode', 'privacy_mode'),
otherRow('Reverse mouse wheel', 'reverse_mouse_wheel'),
]);
final children =
otherDefaultSettings().map((e) => otherRow(e.$1, e.$2)).toList();
return _Card(title: 'Other Default Options', children: children);
}
}
@@ -1577,12 +1686,6 @@ Widget _Card(
);
}
Color? _disabledTextColor(BuildContext context, bool enabled) {
return enabled
? null
: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6);
}
// ignore: non_constant_identifier_names
Widget _OptionCheckBox(BuildContext context, String label, String key,
{Function()? update,
@@ -1631,7 +1734,7 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
Expanded(
child: Text(
translate(label),
style: TextStyle(color: _disabledTextColor(context, enabled)),
style: TextStyle(color: disabledTextColor(context, enabled)),
))
],
),
@@ -1668,7 +1771,7 @@ Widget _Radio<T>(BuildContext context,
overflow: autoNewLine ? null : TextOverflow.ellipsis,
style: TextStyle(
fontSize: _kContentFontSize,
color: _disabledTextColor(context, enabled)))
color: disabledTextColor(context, enabled)))
.marginOnly(left: 5),
),
],
@@ -1718,7 +1821,7 @@ Widget _SubLabeledWidget(BuildContext context, String label, Widget child,
children: [
Text(
'${translate(label)}: ',
style: TextStyle(color: _disabledTextColor(context, enabled)),
style: TextStyle(color: disabledTextColor(context, enabled)),
),
SizedBox(
width: 10,
@@ -1782,7 +1885,7 @@ _LabeledTextField(
'${translate(label)}:',
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 16, color: _disabledTextColor(context, enabled)),
fontSize: 16, color: disabledTextColor(context, enabled)),
).marginOnly(right: 10)),
Expanded(
child: TextField(
@@ -1792,84 +1895,73 @@ _LabeledTextField(
decoration: InputDecoration(
errorText: errorText.isNotEmpty ? errorText : null),
style: TextStyle(
color: _disabledTextColor(context, enabled),
color: disabledTextColor(context, enabled),
)),
),
],
).marginOnly(bottom: 8);
}
// ignore: must_be_immutable
class _ComboBox extends StatelessWidget {
late final List<String> keys;
late final List<String> values;
late final String initialKey;
late final Function(String key) onChanged;
late final bool enabled;
late String current;
_ComboBox({
class _CountDownButton extends StatefulWidget {
_CountDownButton({
Key? key,
required this.keys,
required this.values,
required this.initialKey,
required this.onChanged,
this.enabled = true,
required this.text,
required this.second,
required this.onPressed,
}) : super(key: key);
final String text;
final VoidCallback? onPressed;
final int second;
@override
State<_CountDownButton> createState() => _CountDownButtonState();
}
class _CountDownButtonState extends State<_CountDownButton> {
bool _isButtonDisabled = false;
late int _countdownSeconds = widget.second;
Timer? _timer;
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
void _startCountdownTimer() {
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
if (_countdownSeconds <= 0) {
setState(() {
_isButtonDisabled = false;
});
timer.cancel();
} else {
setState(() {
_countdownSeconds--;
});
}
});
}
@override
Widget build(BuildContext context) {
var index = keys.indexOf(initialKey);
if (index < 0) {
index = 0;
}
var ref = values[index].obs;
current = keys[index];
return Container(
decoration: BoxDecoration(
border: Border.all(
color: enabled
? MyTheme.color(context).border2 ?? MyTheme.border
: MyTheme.border,
),
borderRadius:
BorderRadius.circular(8), //border raiuds of dropdown button
return ElevatedButton(
onPressed: _isButtonDisabled
? null
: () {
widget.onPressed?.call();
setState(() {
_isButtonDisabled = true;
_countdownSeconds = widget.second;
});
_startCountdownTimer();
},
child: Text(
_isButtonDisabled ? '$_countdownSeconds s' : translate(widget.text),
),
height: 42, // should be the height of a TextField
child: Obx(() => DropdownButton<String>(
isExpanded: true,
value: ref.value,
elevation: 16,
underline: Container(),
style: TextStyle(
color: enabled
? Theme.of(context).textTheme.titleMedium?.color
: _disabledTextColor(context, enabled)),
icon: const Icon(
Icons.expand_more_sharp,
size: 20,
).marginOnly(right: 15),
onChanged: enabled
? (String? newValue) {
if (newValue != null && newValue != ref.value) {
ref.value = newValue;
current = newValue;
onChanged(keys[values.indexOf(newValue)]);
}
}
: null,
items: values.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: const TextStyle(fontSize: _kContentFontSize),
overflow: TextOverflow.ellipsis,
).marginOnly(left: 15),
);
}).toList(),
)),
).marginOnly(bottom: 5);
);
}
}

View File

@@ -15,7 +15,7 @@ import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/models/file_model.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:wakelock/wakelock.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import '../../consts.dart';
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
@@ -91,7 +91,7 @@ class _FileManagerPageState extends State<FileManagerPage>
});
Get.put(_ffi, tag: 'ft_${widget.id}');
if (!Platform.isLinux) {
Wakelock.enable();
WakelockPlus.enable();
}
debugPrint("File manager page init success with id ${widget.id}");
_ffi.dialogManager.setOverlayState(_overlayKeyState);
@@ -104,7 +104,7 @@ class _FileManagerPageState extends State<FileManagerPage>
_ffi.close();
_ffi.dialogManager.dismissAll();
if (!Platform.isLinux) {
Wakelock.disable();
WakelockPlus.disable();
}
Get.delete<FFI>(tag: 'ft_${widget.id}');
});
@@ -182,10 +182,9 @@ class _FileManagerPageState extends State<FileManagerPage>
children: [
Transform.rotate(
angle: item.isRemoteToLocal ? pi : 0,
child: SvgPicture.asset(
"assets/arrow.svg",
color: Theme.of(context).tabBarTheme.labelColor,
),
child: SvgPicture.asset("assets/arrow.svg",
colorFilter: svgColor(
Theme.of(context).tabBarTheme.labelColor)),
).paddingOnly(left: 15),
const SizedBox(
width: 16.0,
@@ -262,7 +261,7 @@ class _FileManagerPageState extends State<FileManagerPage>
},
child: SvgPicture.asset(
"assets/refresh.svg",
color: Colors.white,
colorFilter: svgColor(Colors.white),
),
color: MyTheme.accent,
hoverColor: MyTheme.accent80,
@@ -272,7 +271,7 @@ class _FileManagerPageState extends State<FileManagerPage>
padding: EdgeInsets.only(right: 15),
child: SvgPicture.asset(
"assets/close.svg",
color: Colors.white,
colorFilter: svgColor(Colors.white),
),
onPressed: () {
jobController.jobTable.removeAt(index);
@@ -307,13 +306,14 @@ class _FileManagerPageState extends State<FileManagerPage>
children: [
SvgPicture.asset(
"assets/transfer.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter: svgColor(
Theme.of(context).tabBarTheme.labelColor),
height: 40,
).paddingOnly(bottom: 10),
Text(
translate("No transfers in progress"),
textAlign: TextAlign.center,
textScaleFactor: 1.20,
textScaler: TextScaler.linear(1.20),
style: TextStyle(
color:
Theme.of(context).tabBarTheme.labelColor),
@@ -522,7 +522,8 @@ class _FileManagerViewState extends State<FileManagerView> {
quarterTurns: 2,
child: SvgPicture.asset(
"assets/arrow.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter:
svgColor(Theme.of(context).tabBarTheme.labelColor),
),
),
color: Theme.of(context).cardColor,
@@ -537,7 +538,8 @@ class _FileManagerViewState extends State<FileManagerView> {
quarterTurns: 3,
child: SvgPicture.asset(
"assets/arrow.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter:
svgColor(Theme.of(context).tabBarTheme.labelColor),
),
),
color: Theme.of(context).cardColor,
@@ -603,7 +605,8 @@ class _FileManagerViewState extends State<FileManagerView> {
},
child: SvgPicture.asset(
"assets/search.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter:
svgColor(Theme.of(context).tabBarTheme.labelColor),
),
color: Theme.of(context).cardColor,
hoverColor: Theme.of(context).hoverColor,
@@ -613,7 +616,8 @@ class _FileManagerViewState extends State<FileManagerView> {
onPressed: null,
child: SvgPicture.asset(
"assets/close.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter:
svgColor(Theme.of(context).tabBarTheme.labelColor),
),
color: Theme.of(context).disabledColor,
hoverColor: Theme.of(context).hoverColor,
@@ -626,7 +630,8 @@ class _FileManagerViewState extends State<FileManagerView> {
},
child: SvgPicture.asset(
"assets/close.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter:
svgColor(Theme.of(context).tabBarTheme.labelColor),
),
color: Theme.of(context).cardColor,
hoverColor: Theme.of(context).hoverColor,
@@ -642,7 +647,8 @@ class _FileManagerViewState extends State<FileManagerView> {
},
child: SvgPicture.asset(
"assets/refresh.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter:
svgColor(Theme.of(context).tabBarTheme.labelColor),
),
color: Theme.of(context).cardColor,
hoverColor: Theme.of(context).hoverColor,
@@ -666,7 +672,8 @@ class _FileManagerViewState extends State<FileManagerView> {
},
child: SvgPicture.asset(
"assets/home.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter:
svgColor(Theme.of(context).tabBarTheme.labelColor),
),
color: Theme.of(context).cardColor,
hoverColor: Theme.of(context).hoverColor,
@@ -692,7 +699,7 @@ class _FileManagerViewState extends State<FileManagerView> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset("assets/folder_new.svg",
color: MyTheme.accent),
colorFilter: svgColor(MyTheme.accent)),
Text(
translate("Create Folder"),
).paddingOnly(
@@ -734,7 +741,8 @@ class _FileManagerViewState extends State<FileManagerView> {
},
child: SvgPicture.asset(
"assets/folder_new.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter:
svgColor(Theme.of(context).tabBarTheme.labelColor),
),
color: Theme.of(context).cardColor,
hoverColor: Theme.of(context).hoverColor,
@@ -749,7 +757,8 @@ class _FileManagerViewState extends State<FileManagerView> {
: null,
child: SvgPicture.asset(
"assets/trash.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter: svgColor(
Theme.of(context).tabBarTheme.labelColor),
),
color: Theme.of(context).cardColor,
hoverColor: Theme.of(context).hoverColor,
@@ -795,24 +804,24 @@ class _FileManagerViewState extends State<FileManagerView> {
quarterTurns: 2,
child: SvgPicture.asset(
"assets/arrow.svg",
color: selectedItems.items.isEmpty
colorFilter: svgColor(selectedItems.items.isEmpty
? Theme.of(context).brightness ==
Brightness.light
? MyTheme.grayBg
: MyTheme.darkGray
: Colors.white,
: Colors.white),
alignment: Alignment.bottomRight,
),
),
label: isLocal
? SvgPicture.asset(
"assets/arrow.svg",
color: selectedItems.items.isEmpty
colorFilter: svgColor(selectedItems.items.isEmpty
? Theme.of(context).brightness ==
Brightness.light
? MyTheme.grayBg
: MyTheme.darkGray
: Colors.white,
: Colors.white),
)
: Text(
translate('Receive'),
@@ -889,7 +898,7 @@ class _FileManagerViewState extends State<FileManagerView> {
),
child: SvgPicture.asset(
"assets/dots.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter: svgColor(Theme.of(context).tabBarTheme.labelColor),
),
color: Theme.of(context).cardColor,
hoverColor: Theme.of(context).hoverColor,
@@ -1000,9 +1009,10 @@ class _FileManagerViewState extends State<FileManagerView> {
entry.isFile
? "assets/file.svg"
: "assets/folder.svg",
color: Theme.of(context)
.tabBarTheme
.labelColor,
colorFilter: svgColor(
Theme.of(context)
.tabBarTheme
.labelColor),
),
Expanded(
child: Text(entry.name.nonBreaking,
@@ -1127,9 +1137,13 @@ class _FileManagerViewState extends State<FileManagerView> {
void _onSelectedChanged(SelectedItems selectedItems, List<Entry> entries,
Entry entry, bool isLocal) {
final isCtrlDown = RawKeyboard.instance.keysPressed
.contains(LogicalKeyboardKey.controlLeft);
final isShiftDown =
RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftLeft);
.contains(LogicalKeyboardKey.controlLeft) ||
RawKeyboard.instance.keysPressed
.contains(LogicalKeyboardKey.controlRight);
final isShiftDown = RawKeyboard.instance.keysPressed
.contains(LogicalKeyboardKey.shiftLeft) ||
RawKeyboard.instance.keysPressed
.contains(LogicalKeyboardKey.shiftRight);
if (isCtrlDown) {
if (selectedItems.items.contains(entry)) {
selectedItems.remove(entry);
@@ -1444,7 +1458,7 @@ class _FileManagerViewState extends State<FileManagerView> {
_locationStatus.value == LocationStatus.pathLocation
? "assets/folder.svg"
: "assets/search.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter: svgColor(Theme.of(context).tabBarTheme.labelColor),
),
Expanded(
child: TextField(

View File

@@ -1,5 +1,3 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
@@ -66,7 +64,6 @@ class _InstallPageBodyState extends State<_InstallPageBody>
late final TextEditingController controller;
final RxBool startmenu = true.obs;
final RxBool desktopicon = true.obs;
final RxBool driverCert = true.obs;
final RxBool showProgress = false.obs;
final RxBool btnEnabled = true.obs;
@@ -158,10 +155,6 @@ class _InstallPageBodyState extends State<_InstallPageBody>
Option(startmenu, label: 'Create start menu shortcuts')
.marginOnly(bottom: 7),
Option(desktopicon, label: 'Create desktop icon'),
Offstage(
offstage: !Platform.isWindows,
child: Option(driverCert, label: 'install_cert_tip'),
).marginOnly(top: 7),
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
@@ -253,43 +246,9 @@ class _InstallPageBodyState extends State<_InstallPageBody>
String args = '';
if (startmenu.value) args += ' startmenu';
if (desktopicon.value) args += ' desktopicon';
if (driverCert.value) args += ' driverCert';
bind.installInstallMe(options: args, path: controller.text);
}
if (driverCert.isTrue) {
final tag = 'install-info-install-cert-confirm';
final btns = [
OutlinedButton.icon(
icon: Icon(Icons.close_rounded, size: 16),
label: Text(translate('Cancel')),
onPressed: () => gFFI.dialogManager.dismissByTag(tag),
style: buttonStyle,
),
ElevatedButton.icon(
icon: Icon(Icons.done_rounded, size: 16),
label: Text(translate('OK')),
onPressed: () {
gFFI.dialogManager.dismissByTag(tag);
do_install();
},
style: buttonStyle,
)
];
gFFI.dialogManager.show(
(setState, close, context) => CustomAlertDialog(
title: null,
content: SelectionArea(
child:
msgboxContent('info', 'Warning', 'confirm_install_cert_tip')),
actions: btns,
onCancel: close,
),
tag: tag,
);
} else {
do_install();
}
do_install();
}
void selectInstallPath() async {

View File

@@ -8,7 +8,7 @@ import 'package:flutter_custom_cursor/cursor_manager.dart'
as custom_cursor_manager;
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:wakelock/wakelock.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:flutter_custom_cursor/flutter_custom_cursor.dart';
import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart';
@@ -17,6 +17,7 @@ import '../../common/widgets/overlay.dart';
import '../../common/widgets/remote_input.dart';
import '../../common.dart';
import '../../common/widgets/dialog.dart';
import '../../common/widgets/toolbar.dart';
import '../../models/model.dart';
import '../../models/desktop_render_texture.dart';
import '../../models/platform_model.dart';
@@ -28,6 +29,7 @@ import '../widgets/tabbar_widget.dart';
final SimpleWrapper<bool> _firstEnterImage = SimpleWrapper(false);
// Used to skip session close if "move to new window" is clicked.
final Map<String, bool> closeSessionOnDispose = {};
class RemotePage extends StatefulWidget {
@@ -36,6 +38,8 @@ class RemotePage extends StatefulWidget {
required this.id,
required this.sessionId,
required this.tabWindowId,
required this.display,
required this.displays,
required this.password,
required this.toolbarState,
required this.tabController,
@@ -46,6 +50,8 @@ class RemotePage extends StatefulWidget {
final String id;
final SessionID? sessionId;
final int? tabWindowId;
final int? display;
final List<int>? displays;
final String? password;
final ToolbarState toolbarState;
final String? switchUuid;
@@ -73,9 +79,8 @@ class _RemotePageState extends State<RemotePage>
late RxBool _zoomCursor;
late RxBool _remoteCursorMoved;
late RxBool _keyboardEnabled;
late RenderTexture _renderTexture;
final _blockableOverlayState = BlockableOverlayState();
var _blockableOverlayState = BlockableOverlayState();
final FocusNode _rawKeyFocusNode = FocusNode(debugLabel: "rawkeyFocusNode");
@@ -109,6 +114,8 @@ class _RemotePageState extends State<RemotePage>
switchUuid: widget.switchUuid,
forceRelay: widget.forceRelay,
tabWindowId: widget.tabWindowId,
display: widget.display,
displays: widget.displays,
);
WidgetsBinding.instance.addPostFrameCallback((_) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
@@ -116,11 +123,8 @@ class _RemotePageState extends State<RemotePage>
.showLoading(translate('Connecting...'), onCancel: closeConnection);
});
if (!Platform.isLinux) {
Wakelock.enable();
WakelockPlus.enable();
}
// Register texture.
_renderTexture = RenderTexture();
_renderTexture.create(sessionId);
_ffi.ffiModel.updateEventListener(sessionId, widget.id);
bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote);
@@ -179,7 +183,7 @@ class _RemotePageState extends State<RemotePage>
_isWindowBlur = false;
}
if (!Platform.isLinux) {
Wakelock.enable();
WakelockPlus.enable();
}
}
@@ -188,7 +192,7 @@ class _RemotePageState extends State<RemotePage>
void onWindowMaximize() {
super.onWindowMaximize();
if (!Platform.isLinux) {
Wakelock.enable();
WakelockPlus.enable();
}
}
@@ -196,7 +200,7 @@ class _RemotePageState extends State<RemotePage>
void onWindowMinimize() {
super.onWindowMinimize();
if (!Platform.isLinux) {
Wakelock.disable();
WakelockPlus.disable();
}
}
@@ -207,7 +211,7 @@ class _RemotePageState extends State<RemotePage>
// https://github.com/flutter/flutter/issues/64935
super.dispose();
debugPrint("REMOTE PAGE dispose session $sessionId ${widget.id}");
await _renderTexture.destroy(closeSession);
_ffi.textureModel.onRemotePageDispose(closeSession);
// ensure we leave this session, this is a double check
_ffi.inputModel.enterOrLeave(false);
DesktopMultiWindow.removeListener(this);
@@ -222,7 +226,7 @@ class _RemotePageState extends State<RemotePage>
overlays: SystemUiOverlay.values);
}
if (!Platform.isLinux) {
await Wakelock.disable();
await WakelockPlus.disable();
}
await Get.delete<FFI>(tag: widget.id);
removeSharedStates(widget.id);
@@ -245,10 +249,11 @@ class _RemotePageState extends State<RemotePage>
onEnterOrLeaveImageSetter: (func) =>
_onEnterOrLeaveImage4Toolbar = func,
onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Toolbar = null,
setRemoteState: setState,
);
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
body: Stack(
bodyWidget() {
return Stack(
children: [
Container(
color: Colors.black,
@@ -274,26 +279,55 @@ class _RemotePageState extends State<RemotePage>
},
inputModel: _ffi.inputModel,
child: getBodyForDesktop(context))),
Obx(() => Stack(
children: [
_ffi.ffiModel.pi.isSet.isTrue &&
_ffi.ffiModel.waitForFirstImage.isTrue
? emptyOverlay()
: () {
_ffi.ffiModel.tryShowAndroidActionsOverlay();
return Offstage();
}(),
// Use Overlay to enable rebuild every time on menu button click.
_ffi.ffiModel.pi.isSet.isTrue
? Overlay(initialEntries: [
OverlayEntry(builder: remoteToolbar)
])
: remoteToolbar(context),
_ffi.ffiModel.pi.isSet.isFalse ? emptyOverlay() : Offstage(),
],
)),
Stack(
children: [
_ffi.ffiModel.pi.isSet.isTrue &&
_ffi.ffiModel.waitForFirstImage.isTrue
? emptyOverlay()
: () {
_ffi.ffiModel.tryShowAndroidActionsOverlay();
return Offstage();
}(),
// Use Overlay to enable rebuild every time on menu button click.
_ffi.ffiModel.pi.isSet.isTrue
? Overlay(
initialEntries: [OverlayEntry(builder: remoteToolbar)])
: remoteToolbar(context),
_ffi.ffiModel.pi.isSet.isFalse ? emptyOverlay() : Offstage(),
],
),
],
),
);
}
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
body: Obx(() {
final imageReady = _ffi.ffiModel.pi.isSet.isTrue &&
_ffi.ffiModel.waitForFirstImage.isFalse;
if (imageReady) {
// If the privacy mode(disable physical displays) is switched,
// we should not dismiss the dialog immediately.
if (DateTime.now().difference(togglePrivacyModeTime) >
const Duration(milliseconds: 3000)) {
// `dismissAll()` is to ensure that the state is clean.
// It's ok to call dismissAll() here.
_ffi.dialogManager.dismissAll();
// Recreate the block state to refresh the state.
_blockableOverlayState = BlockableOverlayState();
_blockableOverlayState.applyFfi(_ffi);
}
// Block the whole `bodyWidget()` when dialog shows.
return BlockableOverlay(
underlying: bodyWidget(),
state: _blockableOverlayState,
);
} else {
// `_blockableOverlayState` is not recreated here.
// The toolbar's block state won't work properly when reconnecting, but that's okay.
return bodyWidget();
}
}),
);
}
@@ -402,16 +436,23 @@ class _RemotePageState extends State<RemotePage>
Future.delayed(Duration.zero, () {
Provider.of<CanvasModel>(context, listen: false).updateViewStyle();
});
return ImagePaint(
id: widget.id,
zoomCursor: _zoomCursor,
cursorOverImage: _cursorOverImage,
keyboardEnabled: _keyboardEnabled,
remoteCursorMoved: _remoteCursorMoved,
textureId: _renderTexture.textureId,
useTextureRender: RenderTexture.useTextureRender,
listenerBuilder: (child) =>
_buildRawTouchAndPointerRegion(child, enterView, leaveView),
final peerDisplay = CurrentDisplayState.find(widget.id);
return Obx(
() => _ffi.ffiModel.pi.isSet.isFalse
? Container(color: Colors.transparent)
: Obx(() {
_ffi.textureModel.updateCurrentDisplay(peerDisplay.value);
return ImagePaint(
id: widget.id,
zoomCursor: _zoomCursor,
cursorOverImage: _cursorOverImage,
keyboardEnabled: _keyboardEnabled,
remoteCursorMoved: _remoteCursorMoved,
listenerBuilder: (child) => _buildRawTouchAndPointerRegion(
child, enterView, leaveView),
ffi: _ffi,
);
}),
);
}))
];
@@ -442,24 +483,22 @@ class _RemotePageState extends State<RemotePage>
}
class ImagePaint extends StatefulWidget {
final FFI ffi;
final String id;
final RxBool zoomCursor;
final RxBool cursorOverImage;
final RxBool keyboardEnabled;
final RxBool remoteCursorMoved;
final RxInt textureId;
final bool useTextureRender;
final Widget Function(Widget)? listenerBuilder;
ImagePaint(
{Key? key,
required this.ffi,
required this.id,
required this.zoomCursor,
required this.cursorOverImage,
required this.keyboardEnabled,
required this.remoteCursorMoved,
required this.textureId,
required this.useTextureRender,
this.listenerBuilder})
: super(key: key);
@@ -469,8 +508,6 @@ class ImagePaint extends StatefulWidget {
class _ImagePaintState extends State<ImagePaint> {
bool _lastRemoteCursorMoved = false;
final ScrollController _horizontal = ScrollController();
final ScrollController _vertical = ScrollController();
String get id => widget.id;
RxBool get zoomCursor => widget.zoomCursor;
@@ -479,6 +516,11 @@ 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);
@@ -530,83 +572,99 @@ class _ImagePaintState extends State<ImagePaint> {
});
if (c.imageOverflow.isTrue && c.scrollStyle == ScrollStyle.scrollbar) {
final imageWidth = c.getDisplayWidth() * s;
final imageHeight = c.getDisplayHeight() * s;
final imageSize = Size(imageWidth, imageHeight);
late final Widget imageWidget;
if (widget.useTextureRender) {
imageWidget = SizedBox(
width: imageWidth,
height: imageHeight,
child: Obx(() => Texture(
textureId: widget.textureId.value,
filterQuality:
isViewOriginal() ? FilterQuality.none : FilterQuality.low,
)),
);
} else {
imageWidget = CustomPaint(
size: imageSize,
painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s),
);
}
final paintWidth = c.getDisplayWidth() * s;
final paintHeight = c.getDisplayHeight() * s;
final paintSize = Size(paintWidth, paintHeight);
final paintWidget = useTextureRender
? _BuildPaintTextureRender(
c, s, Offset.zero, paintSize, isViewOriginal())
: _buildScrollbarNonTextureRender(m, paintSize, s);
return NotificationListener<ScrollNotification>(
onNotification: (notification) {
final percentX = _horizontal.hasClients
? _horizontal.position.extentBefore /
(_horizontal.position.extentBefore +
_horizontal.position.extentInside +
_horizontal.position.extentAfter)
: 0.0;
final percentY = _vertical.hasClients
? _vertical.position.extentBefore /
(_vertical.position.extentBefore +
_vertical.position.extentInside +
_vertical.position.extentAfter)
: 0.0;
c.setScrollPercent(percentX, percentY);
c.updateScrollPercent();
return false;
},
child: mouseRegion(
child: Obx(() => _buildCrossScrollbarFromLayout(
context, _buildListener(imageWidget), c.size, imageSize)),
context,
_buildListener(paintWidget),
c.size,
paintSize,
c.scrollHorizontal,
c.scrollVertical,
)),
));
} else {
late final Widget imageWidget;
if (c.size.width > 0 && c.size.height > 0) {
if (widget.useTextureRender) {
final x = Platform.isLinux ? c.x.toInt().toDouble() : c.x;
final y = Platform.isLinux ? c.y.toInt().toDouble() : c.y;
imageWidget = Stack(
children: [
Positioned(
left: x,
top: y,
width: c.getDisplayWidth() * s,
height: c.getDisplayHeight() * s,
child: Texture(
textureId: widget.textureId.value,
filterQuality:
isViewOriginal() ? FilterQuality.none : FilterQuality.low,
final paintWidget = useTextureRender
? _BuildPaintTextureRender(
c,
s,
Offset(
Platform.isLinux ? c.x.toInt().toDouble() : c.x,
Platform.isLinux ? c.y.toInt().toDouble() : c.y,
),
)
],
);
} else {
imageWidget = CustomPaint(
size: Size(c.size.width, c.size.height),
painter:
ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s),
);
}
return mouseRegion(child: _buildListener(imageWidget));
c.size,
isViewOriginal())
: _buildScrollAuthNonTextureRender(m, c, s);
return mouseRegion(child: _buildListener(paintWidget));
} else {
return Container();
}
}
}
Widget _buildScrollbarNonTextureRender(
ImageModel m, Size imageSize, double s) {
return CustomPaint(
size: imageSize,
painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s),
);
}
Widget _buildScrollAuthNonTextureRender(
ImageModel m, CanvasModel c, double s) {
return CustomPaint(
size: Size(c.size.width, c.size.height),
painter: ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s),
);
}
Widget _BuildPaintTextureRender(
CanvasModel c, double s, Offset offset, Size size, bool isViewOriginal) {
final ffiModel = c.parent.target!.ffiModel;
final displays = ffiModel.pi.getCurDisplays();
final children = <Widget>[];
final rect = ffiModel.rect;
if (rect == null) {
return Container();
}
final curDisplay = ffiModel.pi.currentDisplay;
for (var i = 0; i < displays.length; i++) {
final textureId = widget.ffi.textureModel
.getTextureId(curDisplay == kAllDisplayValue ? i : curDisplay);
if (true) {
// both "textureId.value != -1" and "true" seems ok
children.add(Positioned(
left: (displays[i].x - rect.left) * s + offset.dx,
top: (displays[i].y - rect.top) * s + offset.dy,
width: displays[i].width * s,
height: displays[i].height * s,
child: Obx(() => Texture(
textureId: textureId.value,
filterQuality:
isViewOriginal ? FilterQuality.none : FilterQuality.low,
)),
));
}
}
return SizedBox(
width: size.width,
height: size.height,
child: Stack(children: children),
);
}
MouseCursor _buildCursorOfCache(
CursorModel cursor, double scale, CursorData? cache) {
if (cache == null) {
@@ -614,7 +672,8 @@ class _ImagePaintState extends State<ImagePaint> {
} else {
final key = cache.updateGetKey(scale);
if (!cursor.cachedKeys.contains(key)) {
debugPrint("Register custom cursor with key $key (${cache.hotx},${cache.hoty})");
debugPrint(
"Register custom cursor with key $key (${cache.hotx},${cache.hoty})");
// [Safety]
// It's ok to call async registerCursor in current synchronous context,
// because activating the cursor is also an async call and will always
@@ -646,7 +705,13 @@ class _ImagePaintState extends State<ImagePaint> {
}
Widget _buildCrossScrollbarFromLayout(
BuildContext context, Widget child, Size layoutSize, Size size) {
BuildContext context,
Widget child,
Size layoutSize,
Size size,
ScrollController horizontal,
ScrollController vertical,
) {
final scrollConfig = CustomMouseWheelScrollConfig(
scrollDuration: kDefaultScrollDuration,
scrollCurve: Curves.linearToEaseOut,
@@ -658,7 +723,7 @@ class _ImagePaintState extends State<ImagePaint> {
widget = ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: SingleChildScrollView(
controller: _horizontal,
controller: horizontal,
scrollDirection: Axis.horizontal,
physics: cursorOverImage.isTrue
? const NeverScrollableScrollPhysics()
@@ -680,7 +745,7 @@ class _ImagePaintState extends State<ImagePaint> {
widget = ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: SingleChildScrollView(
controller: _vertical,
controller: vertical,
physics: cursorOverImage.isTrue
? const NeverScrollableScrollPhysics()
: null,
@@ -699,13 +764,13 @@ class _ImagePaintState extends State<ImagePaint> {
}
if (layoutSize.width < size.width) {
widget = ImprovedScrolling(
scrollController: _horizontal,
scrollController: horizontal,
enableCustomMouseWheelScrolling: cursorOverImage.isFalse,
customMouseWheelScrollConfig: scrollConfig,
child: RawScrollbar(
thickness: kScrollbarThickness,
thumbColor: Colors.grey,
controller: _horizontal,
controller: horizontal,
thumbVisibility: false,
trackVisibility: false,
notificationPredicate: layoutSize.height < size.height
@@ -717,13 +782,13 @@ class _ImagePaintState extends State<ImagePaint> {
}
if (layoutSize.height < size.height) {
widget = ImprovedScrolling(
scrollController: _vertical,
scrollController: vertical,
enableCustomMouseWheelScrolling: cursorOverImage.isFalse,
customMouseWheelScrollConfig: scrollConfig,
child: RawScrollbar(
thickness: kScrollbarThickness,
thumbColor: Colors.grey,
controller: _vertical,
controller: vertical,
thumbVisibility: false,
trackVisibility: false,
child: widget,
@@ -731,7 +796,11 @@ class _ImagePaintState extends State<ImagePaint> {
);
}
return widget;
return Container(
child: widget,
width: layoutSize.width,
height: layoutSize.height,
);
}
Widget _buildListener(Widget child) {
@@ -770,11 +839,20 @@ class CursorPaint extends StatelessWidget {
double cy = c.y;
if (c.viewStyle.style == kRemoteViewStyleOriginal &&
c.scrollStyle == ScrollStyle.scrollbar) {
final d = c.parent.target!.ffiModel.display;
final imageWidth = d.width * c.scale;
final imageHeight = d.height * c.scale;
cx = -imageWidth * c.scrollX;
cy = -imageHeight * c.scrollY;
final rect = c.parent.target!.ffiModel.rect;
if (rect == null) {
// unreachable!
debugPrint('unreachable! The displays rect is null.');
return Container();
}
if (cx < 0) {
final imageWidth = rect.width * c.scale;
cx = -imageWidth * c.scrollX;
}
if (cy < 0) {
final imageHeight = rect.height * c.scale;
cy = -imageHeight * c.scrollY;
}
}
double x = (m.x - hotx) * c.scale + cx;

View File

@@ -48,6 +48,8 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
late ToolbarState _toolbarState;
String? peerId;
bool _isScreenRectSet = false;
int? _display;
var connectionMap = RxList<Widget>.empty(growable: true);
@@ -57,6 +59,12 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
peerId = params['id'];
final sessionId = params['session_id'];
final tabWindowId = params['tab_window_id'];
final display = params['display'];
final displays = params['displays'];
final screenRect = parseParamScreenRect(params);
_isScreenRectSet = screenRect != null;
_display = display as int?;
tryMoveToScreenAndSetFullscreen(screenRect);
if (peerId != null) {
ConnectionTypeState.init(peerId!);
tabController.onSelected = (id) {
@@ -80,6 +88,8 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
id: peerId!,
sessionId: sessionId == null ? null : SessionID(sessionId),
tabWindowId: tabWindowId,
display: display,
displays: displays?.cast<int>(),
password: params['password'],
toolbarState: _toolbarState,
tabController: tabController,
@@ -109,11 +119,18 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
final switchUuid = args['switch_uuid'];
final sessionId = args['session_id'];
final tabWindowId = args['tab_window_id'];
final display = args['display'];
final displays = args['displays'];
final screenRect = parseParamScreenRect(args);
windowOnTop(windowId());
tryMoveToScreenAndSetFullscreen(screenRect);
if (tabController.length == 0) {
if (Platform.isMacOS && stateGlobal.closeOnFullscreen) {
// Show the hidden window.
if (Platform.isMacOS && stateGlobal.closeOnFullscreen == true) {
stateGlobal.setFullscreen(true);
}
// Reset the state
stateGlobal.closeOnFullscreen = null;
}
ConnectionTypeState.init(id);
_toolbarState.setShow(
@@ -129,6 +146,8 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
id: id,
sessionId: sessionId == null ? null : SessionID(sessionId),
tabWindowId: tabWindowId,
display: display,
displays: displays?.cast<int>(),
password: args['password'],
toolbarState: _toolbarState,
tabController: tabController,
@@ -137,7 +156,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
),
));
} else if (call.method == kWindowDisableGrabKeyboard) {
stateGlobal.grabKeyboard = false;
// ???
} else if (call.method == "onDestroy") {
tabController.clear();
} else if (call.method == kWindowActionRebuild) {
@@ -148,6 +167,15 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
windowOnTop(windowId());
}
return jumpOk;
} else if (call.method == kWindowEventActiveDisplaySession) {
final args = jsonDecode(call.arguments);
final id = args['id'];
final display = args['display'];
final jumpOk = tabController.jumpToByKeyAndDisplay(id, display);
if (jumpOk) {
windowOnTop(windowId());
}
return jumpOk;
} else if (call.method == kWindowEventGetRemoteList) {
return tabController.state.value.tabs
.map((e) => e.key)
@@ -160,32 +188,37 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
.join(';');
} else if (call.method == kWindowEventGetCachedSessionData) {
// Ready to show new window and close old tab.
final peerId = call.arguments;
final args = jsonDecode(call.arguments);
final id = args['id'];
final close = args['close'];
try {
final remotePage = tabController.state.value.tabs
.firstWhere((tab) => tab.key == peerId)
.firstWhere((tab) => tab.key == id)
.page as RemotePage;
returnValue = remotePage.ffi.ffiModel.cachedPeerData.toString();
} catch (e) {
debugPrint('Failed to get cached session data: $e');
}
if (returnValue != null) {
closeSessionOnDispose[peerId] = false;
tabController.closeBy(peerId);
if (close && returnValue != null) {
closeSessionOnDispose[id] = false;
tabController.closeBy(id);
}
}
_update_remote_count();
return returnValue;
});
Future.delayed(Duration.zero, () {
restoreWindowPosition(
WindowType.RemoteDesktop,
windowId: windowId(),
peerId: tabController.state.value.tabs.isEmpty
? null
: tabController.state.value.tabs[0].key,
);
});
if (!_isScreenRectSet) {
Future.delayed(Duration.zero, () {
restoreWindowPosition(
WindowType.RemoteDesktop,
windowId: windowId(),
peerId: tabController.state.value.tabs.isEmpty
? null
: tabController.state.value.tabs[0].key,
display: _display,
);
});
}
}
@override
@@ -353,7 +386,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
pi.platform == kPeerPlatformMacOS)) {
menu.add(MenuEntryButton<String>(
childBuilder: (TextStyle? style) => Text(
translate('Restart Remote Device'),
translate('Restart remote device'),
style: style,
),
proc: () => showRestartRemoteDevice(
@@ -432,6 +465,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
c++;
}
}
loopCloseWindow();
}
ConnectionTypeState.delete(id);

View File

@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/models/chat_model.dart';
import 'package:flutter_hbb/models/cm_file_model.dart';
import 'package:flutter_hbb/utils/platform_channel.dart';
import 'package:get/get.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
@@ -482,8 +483,8 @@ class _CmHeaderState extends State<_CmHeader>
client.type_() != ClientType.file),
child: IconButton(
onPressed: () => checkClickTime(client.id, () {
if (client.type_() != ClientType.file) {
gFFI.chatModel.toggleCMSidePage();
if (client.type_() == ClientType.file) {
gFFI.chatModel.toggleCMFilePage();
} else {
gFFI.chatModel
.toggleCMChatPage(MessageKey(client.peerId, client.id));
@@ -519,6 +520,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
Function(bool)? onTap, String tooltipText) {
return Tooltip(
message: "$tooltipText: ${enabled ? "ON" : "OFF"}",
waitDuration: Duration.zero,
child: Container(
decoration: BoxDecoration(
color: enabled ? MyTheme.accent : Colors.grey[700],
@@ -535,7 +537,6 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
child: Icon(
iconData,
color: Colors.white,
size: 32,
),
),
],
@@ -547,9 +548,11 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
@override
Widget build(BuildContext context) {
final crossAxisCount = 4;
final spacing = 10.0;
return Container(
width: double.infinity,
height: 200.0,
height: 160.0,
margin: EdgeInsets.all(5.0),
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(
@@ -574,10 +577,10 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
).marginOnly(left: 4.0, bottom: 8.0),
Expanded(
child: GridView.count(
crossAxisCount: 3,
padding: EdgeInsets.symmetric(horizontal: 20.0),
mainAxisSpacing: 20.0,
crossAxisSpacing: 20.0,
crossAxisCount: crossAxisCount,
padding: EdgeInsets.symmetric(horizontal: spacing),
mainAxisSpacing: spacing,
crossAxisSpacing: spacing,
children: [
buildPermissionIcon(
client.keyboard,
@@ -589,7 +592,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
client.keyboard = enabled;
});
},
translate('Allow using keyboard and mouse'),
translate('Enable keyboard/mouse'),
),
buildPermissionIcon(
client.clipboard,
@@ -601,7 +604,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
client.clipboard = enabled;
});
},
translate('Allow using clipboard'),
translate('Enable clipboard'),
),
buildPermissionIcon(
client.audio,
@@ -613,7 +616,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
client.audio = enabled;
});
},
translate('Allow hearing sound'),
translate('Enable audio'),
),
buildPermissionIcon(
client.file,
@@ -625,7 +628,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
client.file = enabled;
});
},
translate('Allow file copy and paste'),
translate('Enable file copy and paste'),
),
buildPermissionIcon(
client.restart,
@@ -637,7 +640,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
client.restart = enabled;
});
},
translate('Allow remote restart'),
translate('Enable remote restart'),
),
buildPermissionIcon(
client.recording,
@@ -649,8 +652,24 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
client.recording = enabled;
});
},
translate('Allow recording session'),
)
translate('Enable recording session'),
),
// only windows support block input
if (Platform.isWindows)
buildPermissionIcon(
client.blockInput,
Icons.block,
(enabled) {
bind.cmSwitchPermission(
connId: client.id,
name: "block_input",
enabled: enabled);
setState(() {
client.blockInput = enabled;
});
},
translate('Enable blocking user input'),
)
],
),
),
@@ -975,6 +994,49 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
);
}
iconLabel(CmFileLog item) {
switch (item.action) {
case CmFileAction.none:
return Container();
case CmFileAction.localToRemote:
case CmFileAction.remoteToLocal:
return Column(
children: [
Transform.rotate(
angle: item.action == CmFileAction.remoteToLocal ? 0 : pi,
child: SvgPicture.asset(
"assets/arrow.svg",
colorFilter: svgColor(Theme.of(context).tabBarTheme.labelColor),
),
),
Text(item.action == CmFileAction.remoteToLocal
? translate('Send')
: translate('Receive'))
],
);
case CmFileAction.remove:
return Column(
children: [
Icon(
Icons.delete,
color: Theme.of(context).tabBarTheme.labelColor,
),
Text(translate('Delete'))
],
);
case CmFileAction.createDir:
return Column(
children: [
Icon(
Icons.create_new_folder,
color: Theme.of(context).tabBarTheme.labelColor,
),
Text(translate('Create Folder'))
],
);
}
}
Widget statusList() {
return PreferredSize(
preferredSize: const Size(200, double.infinity),
@@ -983,7 +1045,7 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
child: Obx(
() {
final jobTable = gFFI.cmFileModel.currentJobTable;
statusListView(List<JobProgress> jobs) => ListView.builder(
statusListView(List<CmFileLog> jobs) => ListView.builder(
controller: ScrollController(),
itemBuilder: (BuildContext context, int index) {
final item = jobs[index];
@@ -998,22 +1060,7 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
children: [
SizedBox(
width: 50,
child: Column(
children: [
Transform.rotate(
angle: item.isRemoteToLocal ? 0 : pi,
child: SvgPicture.asset(
"assets/arrow.svg",
color: Theme.of(context)
.tabBarTheme
.labelColor,
),
),
Text(item.isRemoteToLocal
? translate('Send')
: translate('Receive'))
],
),
child: iconLabel(item),
).paddingOnly(left: 15),
const SizedBox(
width: 16.0,
@@ -1048,8 +1095,9 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
),
),
Offstage(
offstage:
item.state == JobState.inProgress,
offstage: !(item.isTransfer() &&
item.state !=
JobState.inProgress),
child: Text(
translate(
item.display(),
@@ -1106,13 +1154,14 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
children: [
SvgPicture.asset(
"assets/transfer.svg",
color: Theme.of(context).tabBarTheme.labelColor,
colorFilter: svgColor(
Theme.of(context).tabBarTheme.labelColor),
height: 40,
).paddingOnly(bottom: 10),
Text(
translate("No transfers in progress"),
textAlign: TextAlign.center,
textScaleFactor: 1.20,
textScaler: TextScaler.linear(1.20),
style: TextStyle(
color:
Theme.of(context).tabBarTheme.labelColor),

View File

@@ -12,9 +12,8 @@ class DesktopRemoteScreen extends StatelessWidget {
final Map<String, dynamic> params;
DesktopRemoteScreen({Key? key, required this.params}) : super(key: key) {
if (!bind.mainStartGrabKeyboard()) {
stateGlobal.grabKeyboard = true;
}
bind.mainInitInputSource();
stateGlobal.getInputSource(force: true);
}
@override

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' hide TabBarTheme;
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/pages/remote_page.dart';
import 'package:flutter_hbb/main.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
@@ -74,8 +75,8 @@ CancelFunc showRightMenu(ToastBuilder builder,
return BotToast.showAttachedWidget(
target: target,
targetContext: context,
verticalOffset: 0,
horizontalOffset: 0,
verticalOffset: 0.0,
horizontalOffset: 0.0,
duration: Duration(seconds: 300),
animationDuration: Duration(milliseconds: 0),
animationReverseDuration: Duration(milliseconds: 0),
@@ -150,18 +151,20 @@ class DesktopTabController {
return false;
}
state.update((val) {
val!.selected = index;
Future.delayed(Duration(milliseconds: 100), (() {
if (val.pageController.hasClients) {
val.pageController.jumpToPage(index);
}
val.scrollController.itemCount = val.tabs.length;
if (val.scrollController.hasClients &&
val.scrollController.itemCount > index) {
val.scrollController
.scrollToItem(index, center: false, animate: true);
}
}));
if (val != null) {
val.selected = index;
Future.delayed(Duration(milliseconds: 100), (() {
if (val.pageController.hasClients) {
val.pageController.jumpToPage(index);
}
val.scrollController.itemCount = val.tabs.length;
if (val.scrollController.hasClients &&
val.scrollController.itemCount > index) {
val.scrollController
.scrollToItem(index, center: false, animate: true);
}
}));
}
});
if (callOnSelected) {
if (state.value.tabs.length > index) {
@@ -176,6 +179,19 @@ class DesktopTabController {
jumpTo(state.value.tabs.indexWhere((tab) => tab.key == key),
callOnSelected: callOnSelected);
bool jumpToByKeyAndDisplay(String key, int display) {
for (int i = 0; i < state.value.tabs.length; i++) {
final tab = state.value.tabs[i];
if (tab.key == key) {
final ffi = (tab.page as RemotePage).ffi;
if (ffi.ffiModel.pi.currentDisplay == display) {
return jumpTo(i, callOnSelected: true);
}
}
}
return false;
}
void closeBy(String? key) {
if (!isDesktop) return;
assert(onRemoved != null);
@@ -434,6 +450,7 @@ class DesktopTab extends StatelessWidget {
isMainWindow: isMainWindow,
tabType: tabType,
state: state,
tabController: controller,
tail: tail,
showMinimize: showMinimize,
showMaximize: showMaximize,
@@ -449,6 +466,7 @@ class WindowActionPanel extends StatefulWidget {
final bool isMainWindow;
final DesktopTabType tabType;
final Rx<DesktopTabState> state;
final DesktopTabController tabController;
final bool showMinimize;
final bool showMaximize;
@@ -461,6 +479,7 @@ class WindowActionPanel extends StatefulWidget {
required this.isMainWindow,
required this.tabType,
required this.state,
required this.tabController,
this.tail,
this.showMinimize = true,
this.showMaximize = true,
@@ -566,19 +585,25 @@ class WindowActionPanelState extends State<WindowActionPanel>
void onWindowClose() async {
mainWindowClose() async => await windowManager.hide();
notMainWindowClose(WindowController controller) async {
if (widget.tabController.length != 0) {
debugPrint("close not emtpy multiwindow from taskbar");
if (Platform.isWindows) {
await controller.show();
await controller.focus();
final res = await widget.onClose?.call() ?? true;
if (!res) return;
}
widget.tabController.clear();
}
await controller.hide();
await Future.wait([
rustDeskWinManager
.call(WindowType.Main, kWindowEventHide, {"id": kWindowId!}),
widget.onClose?.call() ?? Future.microtask(() => null)
]);
await rustDeskWinManager
.call(WindowType.Main, kWindowEventHide, {"id": kWindowId!});
}
macOSWindowClose(
Future<void> Function() restoreFunc,
Future<bool> Function() checkFullscreen,
Future<void> Function() closeFunc) async {
await restoreFunc();
Future<bool> Function() checkFullscreen,
Future<void> Function() closeFunc,
) async {
_macOSCheckRestoreCounter = 0;
_macOSCheckRestoreTimer =
Timer.periodic(Duration(milliseconds: 30), (timer) async {
@@ -598,26 +623,38 @@ class WindowActionPanelState extends State<WindowActionPanel>
}
// macOS specific workaround, the window is not hiding when in fullscreen.
if (Platform.isMacOS && await windowManager.isFullScreen()) {
stateGlobal.closeOnFullscreen = true;
stateGlobal.closeOnFullscreen ??= true;
await windowManager.setFullScreen(false);
await macOSWindowClose(
() async => await windowManager.setFullScreen(false),
() async => await windowManager.isFullScreen(),
mainWindowClose);
() async => await windowManager.isFullScreen(),
mainWindowClose,
);
} else {
stateGlobal.closeOnFullscreen = false;
stateGlobal.closeOnFullscreen ??= false;
await mainWindowClose();
}
} else {
// it's safe to hide the subwindow
final controller = WindowController.fromWindowId(kWindowId!);
if (Platform.isMacOS && await controller.isFullScreen()) {
stateGlobal.closeOnFullscreen = true;
await macOSWindowClose(
() async => await controller.setFullscreen(false),
() async => await controller.isFullScreen(),
() async => await notMainWindowClose(controller));
if (Platform.isMacOS) {
// onWindowClose() maybe called multiple times because of loopCloseWindow() in remote_tab_page.dart.
// use ??= to make sure the value is set on first call.
if (await widget.onClose?.call() ?? true) {
if (await controller.isFullScreen()) {
stateGlobal.closeOnFullscreen ??= true;
await controller.setFullscreen(false);
stateGlobal.setFullscreen(false, procWnd: false);
await macOSWindowClose(
() async => await controller.isFullScreen(),
() async => await notMainWindowClose(controller),
);
} else {
stateGlobal.closeOnFullscreen ??= false;
await notMainWindowClose(controller);
}
}
} else {
stateGlobal.closeOnFullscreen = false;
await notMainWindowClose(controller);
}
}

View File

@@ -91,7 +91,7 @@ Future<void> main(List<String> args) async {
debugPrint("--cm started");
desktopType = DesktopType.cm;
await windowManager.ensureInitialized();
runConnectionManagerScreen(args.contains('--hide'));
runConnectionManagerScreen();
} else if (args.contains('--install')) {
runInstallPage();
} else {
@@ -156,6 +156,7 @@ void runMobileApp() async {
await Future.wait([gFFI.abModel.loadCache(), gFFI.groupModel.loadCache()]);
gFFI.userModel.refreshCurrentUser();
runApp(App());
await initUniLinks();
}
void runMultiWindow(
@@ -198,8 +199,16 @@ void runMultiWindow(
}
switch (appType) {
case kAppTypeDesktopRemote:
await restoreWindowPosition(WindowType.RemoteDesktop,
windowId: kWindowId!, peerId: argument['id'] as String?);
// If screen rect is set, the window will be moved to the target screen and then set fullscreen.
if (argument['screen_rect'] == null) {
// display can be used to control the offset of the window.
await restoreWindowPosition(
WindowType.RemoteDesktop,
windowId: kWindowId!,
peerId: argument['id'] as String?,
display: argument['display'] as int?,
);
}
break;
case kAppTypeDesktopFileTransfer:
await restoreWindowPosition(WindowType.FileTransfer,
@@ -216,13 +225,14 @@ void runMultiWindow(
WindowController.fromWindowId(kWindowId!).show();
}
void runConnectionManagerScreen(bool hide) async {
void runConnectionManagerScreen() async {
await initEnv(kAppTypeConnectionManager);
_runApp(
'',
const DesktopServerPage(),
MyTheme.currentThemeMode(),
);
final hide = await bind.cmGetConfig(name: "hide_cm") == 'true';
gFFI.serverModel.hideCm = hide;
if (hide) {
await hideCmWindow(isStartup: true);
@@ -420,7 +430,7 @@ class _AppState extends State<App> {
? (context, child) => AccessibilityListener(
child: MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
textScaler: TextScaler.linear(1.0),
),
child: child ?? Container(),
),
@@ -442,7 +452,7 @@ class _AppState extends State<App> {
Widget _keepScaleBuilder(BuildContext context, Widget? child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
textScaler: TextScaler.linear(1.0),
),
child: child ?? Container(),
);

View File

@@ -6,10 +6,12 @@ import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_hbb/models/peer_model.dart';
import '../../common.dart';
import '../../common/widgets/login.dart';
import '../../common/widgets/peer_tab_page.dart';
import '../../common/widgets/autocomplete.dart';
import '../../consts.dart';
import '../../models/model.dart';
import '../../models/platform_model.dart';
@@ -42,10 +44,16 @@ class _ConnectionPageState extends State<ConnectionPage> {
/// Update url. If it's not null, means an update is available.
var _updateUrl = '';
List<Peer> peers = [];
bool isPeersLoading = false;
bool isPeersLoaded = false;
StreamSubscription? _uniLinksSubscription;
@override
void initState() {
super.initState();
_uniLinksSubscription = listenUniLinks();
if (_idController.text.isEmpty) {
() async {
final lastRemoteId = await bind.mainGetLastRemoteId();
@@ -116,6 +124,18 @@ class _ConnectionPageState extends State<ConnectionPage> {
color: Colors.white, fontWeight: FontWeight.bold))));
}
Future<void> _fetchPeers() async {
setState(() {
isPeersLoading = true;
});
await Future.delayed(Duration(milliseconds: 100));
peers = await getAllPeers();
setState(() {
isPeersLoading = false;
isPeersLoaded = true;
});
}
/// UI for the remote ID TextField.
/// Search for a peer and connect to it if the id exists.
Widget _buildRemoteIDTextField() {
@@ -133,36 +153,162 @@ class _ConnectionPageState extends State<ConnectionPage> {
Expanded(
child: Container(
padding: const EdgeInsets.only(left: 16, right: 16),
child: AutoSizeTextField(
minFontSize: 18,
autocorrect: false,
enableSuggestions: false,
keyboardType: TextInputType.visiblePassword,
// keyboardType: TextInputType.number,
style: const TextStyle(
fontFamily: 'WorkSans',
fontWeight: FontWeight.bold,
fontSize: 30,
color: MyTheme.idColor,
),
decoration: InputDecoration(
labelText: translate('Remote ID'),
// hintText: 'Enter your remote ID',
border: InputBorder.none,
helperStyle: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: MyTheme.darkGray,
),
labelStyle: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
letterSpacing: 0.2,
color: MyTheme.darkGray,
),
),
controller: _idController,
inputFormatters: [IDTextInputFormatter()],
child: Autocomplete<Peer>(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<Peer>.empty();
} else if (peers.isEmpty && !isPeersLoaded) {
Peer emptyPeer = Peer(
id: '',
username: '',
hostname: '',
alias: '',
platform: '',
tags: [],
hash: '',
forceAlwaysRelay: false,
rdpPort: '',
rdpUsername: '',
loginName: '',
);
return [emptyPeer];
} else {
String textWithoutSpaces =
textEditingValue.text.replaceAll(" ", "");
if (int.tryParse(textWithoutSpaces) != null) {
textEditingValue = TextEditingValue(
text: textWithoutSpaces,
selection: textEditingValue.selection,
);
}
String textToFind = textEditingValue.text.toLowerCase();
return peers
.where((peer) =>
peer.id.toLowerCase().contains(textToFind) ||
peer.username
.toLowerCase()
.contains(textToFind) ||
peer.hostname
.toLowerCase()
.contains(textToFind) ||
peer.alias.toLowerCase().contains(textToFind))
.toList();
}
},
fieldViewBuilder: (BuildContext context,
TextEditingController fieldTextEditingController,
FocusNode fieldFocusNode,
VoidCallback onFieldSubmitted) {
fieldTextEditingController.text = _idController.text;
fieldFocusNode.addListener(() async {
_idEmpty.value =
fieldTextEditingController.text.isEmpty;
if (fieldFocusNode.hasFocus && !isPeersLoading) {
_fetchPeers();
}
});
final textLength =
fieldTextEditingController.value.text.length;
// select all to facilitate removing text, just following the behavior of address input of chrome
fieldTextEditingController.selection = TextSelection(
baseOffset: 0, extentOffset: textLength);
return AutoSizeTextField(
controller: fieldTextEditingController,
focusNode: fieldFocusNode,
minFontSize: 18,
autocorrect: false,
enableSuggestions: false,
keyboardType: TextInputType.visiblePassword,
// keyboardType: TextInputType.number,
onChanged: (String text) {
_idController.id = text;
},
style: const TextStyle(
fontFamily: 'WorkSans',
fontWeight: FontWeight.bold,
fontSize: 30,
color: MyTheme.idColor,
),
decoration: InputDecoration(
labelText: translate('Remote ID'),
// hintText: 'Enter your remote ID',
border: InputBorder.none,
helperStyle: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: MyTheme.darkGray,
),
labelStyle: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
letterSpacing: 0.2,
color: MyTheme.darkGray,
),
),
inputFormatters: [IDTextInputFormatter()],
);
},
onSelected: (option) {
setState(() {
_idController.id = option.id;
FocusScope.of(context).unfocus();
});
},
optionsViewBuilder: (BuildContext context,
AutocompleteOnSelected<Peer> onSelected,
Iterable<Peer> options) {
double maxHeight = options.length * 50;
if (options.length == 1) {
maxHeight = 52;
} else if (options.length == 3) {
maxHeight = 146;
} else if (options.length == 4) {
maxHeight = 193;
}
maxHeight = maxHeight.clamp(0, 200);
return Align(
alignment: Alignment.topLeft,
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 5,
spreadRadius: 1,
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Material(
elevation: 4,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: maxHeight,
maxWidth: 320,
),
child: peers.isEmpty && isPeersLoading
? Container(
height: 80,
child: Center(
child:
CircularProgressIndicator(
strokeWidth: 2,
)))
: ListView(
padding:
EdgeInsets.only(top: 5),
children: options
.map((peer) =>
AutocompletePeerTile(
onSelect: () =>
onSelected(
peer),
peer: peer))
.toList(),
))))));
},
),
),
),
@@ -170,7 +316,9 @@ class _ConnectionPageState extends State<ConnectionPage> {
offstage: _idEmpty.value,
child: IconButton(
onPressed: () {
_idController.clear();
setState(() {
_idController.clear();
});
},
icon: Icon(Icons.clear, color: MyTheme.darkGray)),
)),
@@ -195,6 +343,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
@override
void dispose() {
_uniLinksSubscription?.cancel();
_idController.dispose();
if (Get.isRegistered<IDTextEditingController>()) {
Get.delete<IDTextEditingController>();

View File

@@ -3,9 +3,10 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:flutter_hbb/models/file_model.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart';
import 'package:toggle_switch/toggle_switch.dart';
import 'package:wakelock/wakelock.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import '../../common.dart';
import '../../common/widgets/dialog.dart';
@@ -73,7 +74,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
.showLoading(translate('Connecting...'), onCancel: closeConnection);
});
gFFI.ffiModel.updateEventListener(gFFI.sessionId, widget.id);
Wakelock.enable();
WakelockPlus.enable();
}
@override
@@ -81,7 +82,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
model.close().whenComplete(() {
gFFI.close();
gFFI.dialogManager.dismissAll();
Wakelock.disable();
WakelockPlus.disable();
});
super.dispose();
}
@@ -661,6 +662,7 @@ class BottomSheetBody extends StatelessWidget {
@override
BottomSheet build(BuildContext context) {
// ignore: no_leading_underscores_for_local_identifiers
final _actions = actions ?? [];
return BottomSheet(
builder: (BuildContext context) {

View File

@@ -13,15 +13,15 @@ abstract class PageShape extends Widget {
}
class HomePage extends StatefulWidget {
static final homeKey = GlobalKey<_HomePageState>();
static final homeKey = GlobalKey<HomePageState>();
HomePage() : super(key: homeKey);
@override
_HomePageState createState() => _HomePageState();
HomePageState createState() => HomePageState();
}
class _HomePageState extends State<HomePage> {
class HomePageState extends State<HomePage> {
var _selectedIndex = 0;
int get selectedIndex => _selectedIndex;
final List<PageShape> _pages = [];
@@ -154,7 +154,7 @@ class WebHomePage extends StatelessWidget {
// backgroundColor: MyTheme.grayBg,
appBar: AppBar(
centerTitle: true,
title: Text("RustDesk" + (isWeb ? " (Beta) " : "")),
title: Text("RustDesk${isWeb ? " (Beta) " : ""}"),
actions: connectionPage.appBarActions,
),
body: connectionPage,

View File

@@ -10,7 +10,7 @@ import 'package:flutter_hbb/models/chat_model.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:wakelock/wakelock.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import '../../common.dart';
import '../../common/widgets/overlay.dart';
@@ -20,6 +20,7 @@ import '../../models/input_model.dart';
import '../../models/model.dart';
import '../../models/platform_model.dart';
import '../../utils/image.dart';
import '../widgets/dialog.dart';
final initText = '1' * 1024;
@@ -59,7 +60,7 @@ class _RemotePageState extends State<RemotePage> {
gFFI.dialogManager
.showLoading(translate('Connecting...'), onCancel: closeConnection);
});
Wakelock.enable();
WakelockPlus.enable();
_physicalFocusNode.requestFocus();
gFFI.ffiModel.updateEventListener(sessionId, widget.id);
gFFI.inputModel.listenToMouse(true);
@@ -87,7 +88,7 @@ class _RemotePageState extends State<RemotePage> {
gFFI.dialogManager.dismissAll();
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
await Wakelock.disable();
await WakelockPlus.disable();
await keyboardSubscription.cancel();
removeSharedStates(widget.id);
}
@@ -113,6 +114,13 @@ class _RemotePageState extends State<RemotePage> {
gFFI.ffiModel.pi.version.isNotEmpty) {
gFFI.invokeMethod("enable_soft_keyboard", false);
}
} else {
_timer?.cancel();
_timer = Timer(kMobileDelaySoftKeyboardFocus, () {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
_mobileFocusNode.requestFocus();
});
}
// update for Scaffold
setState(() {});
@@ -202,12 +210,12 @@ class _RemotePageState extends State<RemotePage> {
_value = initText;
setState(() => _showEdit = false);
_timer?.cancel();
_timer = Timer(Duration(milliseconds: 30), () {
_timer = Timer(kMobileDelaySoftKeyboard, () {
// show now, and sleep a while to requestFocus to
// make sure edit ready, so that keyboard wont show/hide/show/hide happen
setState(() => _showEdit = true);
_timer?.cancel();
_timer = Timer(Duration(milliseconds: 30), () {
_timer = Timer(kMobileDelaySoftKeyboardFocus, () {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
_mobileFocusNode.requestFocus();
@@ -234,7 +242,7 @@ class _RemotePageState extends State<RemotePage> {
clientClose(sessionId, gFFI.dialogManager);
return false;
},
child: getRawPointerAndKeyBody(Scaffold(
child: Scaffold(
// workaround for https://github.com/rustdesk/rustdesk/issues/3131
floatingActionButtonLocation: keyboardIsVisible
? FABLocation(FloatingActionButtonLocation.endFloat, 0, -35)
@@ -280,7 +288,7 @@ class _RemotePageState extends State<RemotePage> {
: Offstage(),
],
)),
body: Overlay(
body: getRawPointerAndKeyBody(Overlay(
initialEntries: [
OverlayEntry(builder: (context) {
return Container(
@@ -363,6 +371,10 @@ class _RemotePageState extends State<RemotePage> {
? []
: gFFI.ffiModel.isPeerAndroid
? [
IconButton(
color: Colors.white,
icon: Icon(Icons.keyboard),
onPressed: openKeyboard),
IconButton(
color: Colors.white,
icon: const Icon(Icons.build),
@@ -481,10 +493,23 @@ class _RemotePageState extends State<RemotePage> {
final x = 120.0;
final y = size.height;
final menus = toolbarControls(context, id, gFFI);
getChild(TTextMenu menu) {
if (menu.trailingIcon != null) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
menu.child,
menu.trailingIcon!,
]);
} else {
return menu.child;
}
}
final more = menus
.asMap()
.entries
.map((e) => PopupMenuItem<int>(child: e.value.child, value: e.key))
.map((e) => PopupMenuItem<int>(child: getChild(e.value), value: e.key))
.toList();
() async {
var index = await showMenu(
@@ -755,14 +780,14 @@ void showOptions(
if (image != null) {
displays.add(Padding(padding: const EdgeInsets.only(top: 8), child: image));
}
if (pi.displays.length > 1) {
if (pi.displays.length > 1 && pi.currentDisplay != kAllDisplayValue) {
final cur = pi.currentDisplay;
final children = <Widget>[];
for (var i = 0; i < pi.displays.length; ++i) {
children.add(InkWell(
onTap: () {
if (i == cur) return;
bind.sessionSwitchDisplay(sessionId: gFFI.sessionId, value: i);
openMonitorInTheSameTab(i, gFFI, pi);
gFFI.dialogManager.dismissAll();
},
child: Ink(
@@ -800,6 +825,16 @@ void showOptions(
List<TToggleMenu> displayToggles =
await toolbarDisplayToggle(context, id, gFFI);
List<TToggleMenu> privacyModeList = [];
// privacy mode
final privacyModeState = PrivacyModeState.find(id);
if (gFFI.ffiModel.keyboard && gFFI.ffiModel.pi.features.privacyMode) {
privacyModeList = toolbarPrivacyMode(privacyModeState, context, id, gFFI);
if (privacyModeList.length == 1) {
displayToggles.add(privacyModeList[0]);
}
}
dialogManager.show((setState, close, context) {
var viewStyle =
(viewStyleRadios.isNotEmpty ? viewStyleRadios[0].groupValue : '').obs;
@@ -842,10 +877,21 @@ void showOptions(
title: e.value.child)))
.toList();
Widget privacyModeWidget = Offstage();
if (privacyModeList.length > 1) {
privacyModeWidget = ListTile(
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
title: Text(translate('Privacy mode')),
onTap: () => setPrivacyModeDialog(
dialogManager, privacyModeList, privacyModeState),
);
}
return CustomAlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: displays + radios + toggles),
children: displays + radios + toggles + [privacyModeWidget]),
);
}, clickMaskDismiss: true, backDismiss: true);
}

View File

@@ -8,6 +8,7 @@ import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:zxing2/qrcode.dart';
import '../../common.dart';
import '../../consts.dart';
import '../widgets/dialog.dart';
class ScanPage extends StatefulWidget {
@@ -60,7 +61,11 @@ class _ScanPageState extends State<ScanPage> {
var reader = QRCodeReader();
try {
var result = reader.decode(bitmap);
showServerSettingFromQr(result.text);
if (result.text.startsWith(kUniLinksPrefix)) {
handleUriLink(uriString: result.text);
} else {
showServerSettingFromQr(result.text);
}
} catch (e) {
showToast('No QR code found');
}

View File

@@ -211,25 +211,18 @@ class ServiceNotRunningNotification extends StatelessWidget {
ElevatedButton.icon(
icon: const Icon(Icons.play_arrow),
onPressed: () {
if (gFFI.userModel.userName.value.isEmpty && bind.mainGetLocalOption(key: "show-scam-warning") != "N") {
_showScamWarning(context, serverModel);
if (gFFI.userModel.userName.value.isEmpty &&
bind.mainGetLocalOption(key: "show-scam-warning") !=
"N") {
showScamWarning(context, serverModel);
} else {
serverModel.toggleService();
}
},
label: Text(translate("Start Service")))
label: Text(translate("Start service")))
],
));
}
void _showScamWarning(BuildContext context, ServerModel serverModel) {
showDialog(
context: context,
builder: (BuildContext context) {
return ScamWarningDialog(serverModel: serverModel);
},
);
}
}
class ScamWarningDialog extends StatefulWidget {
@@ -238,10 +231,10 @@ class ScamWarningDialog extends StatefulWidget {
ScamWarningDialog({required this.serverModel});
@override
_ScamWarningDialogState createState() => _ScamWarningDialogState();
ScamWarningDialogState createState() => ScamWarningDialogState();
}
class _ScamWarningDialogState extends State<ScamWarningDialog> {
class ScamWarningDialogState extends State<ScamWarningDialog> {
int _countdown = 12;
bool show_warning = false;
late Timer _timer;
@@ -277,147 +270,149 @@ class _ScamWarningDialogState extends State<ScamWarningDialog> {
final isButtonLocked = _countdown > 0;
return AlertDialog(
content: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Color(0xffe242bc),
Color(0xfff4727c),
],
),
borderRadius: BorderRadius.circular(20.0),
),
padding: EdgeInsets.all(25.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
content: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: SingleChildScrollView(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Color(0xffe242bc),
Color(0xfff4727c),
],
),
),
padding: EdgeInsets.all(25.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.warning_amber_sharp,
color: Colors.white,
),
SizedBox(width: 10),
Text(
translate("Warning"),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 20.0,
),
),
],
),
SizedBox(height: 20),
Center(
child: Image.asset('assets/scam.png',
width: 180,
),
),
SizedBox(height: 18),
Text(
translate("scam_title"),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 22.0,
),
),
SizedBox(height: 18),
SizedBox(
height: 220,
child: Scrollbar(
child: SingleChildScrollView(
child: Text(
translate("scam_text1")+"\n\n"
+translate("scam_text2")+"\n",
Row(
children: [
Icon(
Icons.warning_amber_sharp,
color: Colors.white,
),
SizedBox(width: 10),
Text(
translate("Warning"),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16.0,
fontSize: 20.0,
),
),
],
),
SizedBox(height: 20),
Center(
child: Image.asset(
'assets/scam.png',
width: 180,
),
),
),
),
Row(
children: <Widget>[
Checkbox(
value: show_warning,
onChanged: (value) {
setState((){
show_warning = value!;
});
},
),
SizedBox(height: 18),
Text(
translate("Don't show again"),
translate("scam_title"),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 15.0,
fontSize: 22.0,
),
),
SizedBox(height: 18),
Text(
"${translate("scam_text1")}\n\n${translate("scam_text2")}\n",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
Row(
children: <Widget>[
Checkbox(
value: show_warning,
onChanged: (value) {
setState(() {
show_warning = value!;
});
},
),
Text(
translate("Don't show again"),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
constraints: BoxConstraints(maxWidth: 150),
child: ElevatedButton(
onPressed: isButtonLocked
? null
: () {
Navigator.of(context).pop();
_serverModel.toggleService();
if (show_warning) {
bind.mainSetLocalOption(
key: "show-scam-warning", value: "N");
}
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueAccent,
),
child: Text(
isButtonLocked
? "${translate("I Agree")} (${_countdown}s)"
: translate("I Agree"),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 13.0,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
),
SizedBox(width: 15),
Container(
constraints: BoxConstraints(maxWidth: 150),
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
style: ElevatedButton.styleFrom(
primary: Colors.blueAccent,
),
child: Text(
translate("Decline"),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 13.0,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
),
],
),
],
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
constraints: BoxConstraints(maxWidth: 150),
child: ElevatedButton(
onPressed: isButtonLocked
? null
: () {
Navigator.of(context).pop();
_serverModel.toggleService();
if (show_warning) {
bind.mainSetLocalOption(key: "show-scam-warning", value: "N");
}
},
style: ElevatedButton.styleFrom(
primary: Colors.blueAccent,
),
child: Text(
isButtonLocked ? translate("I Agree")+" (${_countdown}s)" : translate("I Agree"),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 13.0,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
),
SizedBox(width: 15),
Container(
constraints: BoxConstraints(maxWidth: 150),
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
style: ElevatedButton.styleFrom(
primary: Colors.blueAccent,
),
child: Text(
translate("Decline"),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 13.0,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
),
],
)])),
contentPadding: EdgeInsets.all(0.0),
),
),
),
contentPadding: EdgeInsets.all(0.0),
);
}
}
@@ -557,11 +552,17 @@ class _PermissionCheckerState extends State<PermissionChecker> {
label: Text(translate("Stop service")))
.marginOnly(bottom: 8)
: SizedBox.shrink(),
PermissionRow(translate("Screen Capture"), serverModel.mediaOk,
serverModel.toggleService),
PermissionRow(
translate("Screen Capture"),
serverModel.mediaOk,
!serverModel.mediaOk &&
gFFI.userModel.userName.value.isEmpty &&
bind.mainGetLocalOption(key: "show-scam-warning") != "N"
? () => showScamWarning(context, serverModel)
: serverModel.toggleService),
PermissionRow(translate("Input Control"), serverModel.inputOk,
serverModel.toggleInput),
PermissionRow(translate("Transfer File"), serverModel.fileOk,
PermissionRow(translate("Transfer file"), serverModel.fileOk,
serverModel.toggleFile),
hasAudioPermission
? PermissionRow(translate("Audio Capture"), serverModel.audioOk,
@@ -801,3 +802,12 @@ void androidChannelInit() {
return "";
});
}
void showScamWarning(BuildContext context, ServerModel serverModel) {
showDialog(
context: context,
builder: (BuildContext context) {
return ScamWarningDialog(serverModel: serverModel);
},
);
}

View File

@@ -221,7 +221,18 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
final List<AbstractSettingsTile> enhancementsTiles = [];
final List<AbstractSettingsTile> shareScreenTiles = [
SettingsTile.switchTile(
title: Text(translate('Deny LAN Discovery')),
title: Text(translate('enable-2fa-title')),
initialValue: bind.mainHasValid2FaSync(),
onToggle: (_) async {
update() async {
setState(() {});
}
change2fa(callback: update);
},
),
SettingsTile.switchTile(
title: Text(translate('Deny LAN discovery')),
initialValue: _denyLANDiscovery,
onToggle: (v) async {
await bind.mainSetOption(
@@ -270,7 +281,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
},
),
SettingsTile.switchTile(
title: Text(translate('Enable Recording Session')),
title: Text(translate('Enable recording session')),
initialValue: _enableRecordSession,
onToggle: (v) async {
await bind.mainSetOption(
@@ -407,7 +418,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
enhancementsTiles.add(SettingsTile.switchTile(
initialValue: _enableStartOnBoot,
title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text("${translate('Start on Boot')} (beta)"),
Text("${translate('Start on boot')} (beta)"),
Text(
'* ${translate('Start the screen sharing service on boot, requires special permissions')}',
style: Theme.of(context).textTheme.bodySmall),
@@ -708,7 +719,7 @@ class ScanButton extends StatelessWidget {
}
class _DisplayPage extends StatefulWidget {
const _DisplayPage({super.key});
const _DisplayPage();
@override
State<_DisplayPage> createState() => __DisplayPageState();
@@ -789,21 +800,14 @@ class __DisplayPageState extends State<_DisplayPage> {
),
SettingsSection(
title: Text(translate('Other Default Options')),
tiles: [
otherRow('Show remote cursor', 'show_remote_cursor'),
otherRow('Show quality monitor', 'show_quality_monitor'),
otherRow('Mute', 'disable_audio'),
otherRow('Disable clipboard', 'disable_clipboard'),
otherRow('Lock after session end', 'lock_after_session_end'),
otherRow('Privacy mode', 'privacy_mode'),
otherRow('Touch mode', 'touch-mode'),
],
tiles:
otherDefaultSettings().map((e) => otherRow(e.$1, e.$2)).toList(),
),
]),
);
}
otherRow(String label, String key) {
SettingsTile otherRow(String label, String key) {
final value = bind.mainGetUserDefaultOption(key: key) == 'Y';
return SettingsTile.switchTile(
initialValue: value,

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:flutter_hbb/common/widgets/toolbar.dart';
import 'package:get/get.dart';
import '../../common.dart';
@@ -170,7 +171,7 @@ void showServerSettingsWithValue(
isInProgress = true;
});
bool ret = await setServerConfig(
controllers,
null,
errMsgs,
ServerConfig(
idServer: idCtrl.text.trim(),
@@ -259,6 +260,30 @@ void showServerSettingsWithValue(
});
}
void setPrivacyModeDialog(
OverlayDialogManager dialogManager,
List<TToggleMenu> privacyModeList,
RxString privacyModeState,
) async {
dialogManager.dismissAll();
dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title: Text(translate('Privacy mode')),
content: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: privacyModeList
.map((value) => CheckboxListTile(
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
title: value.child,
value: value.value,
onChanged: value.onChanged,
))
.toList()),
);
}, backDismiss: true, clickMaskDismiss: true);
}
Future<String?> validateAsync(String value) async {
value = value.trim();
if (value.isEmpty) {

View File

@@ -7,30 +7,27 @@ class GestureIcons {
GestureIcons._();
static const IconData icon_mouse = IconData(0xe65c, fontFamily: _family);
static const IconData icon_Tablet_Touch =
IconData(0xe9ce, fontFamily: _family);
static const IconData icon_gesture_f_drag =
static const IconData iconMouse = IconData(0xe65c, fontFamily: _family);
static const IconData iconTabletTouch = IconData(0xe9ce, fontFamily: _family);
static const IconData iconGestureFDrag =
IconData(0xe686, fontFamily: _family);
static const IconData icon_Mobile_Touch =
IconData(0xe9cd, fontFamily: _family);
static const IconData icon_gesture_press =
static const IconData iconMobileTouch = IconData(0xe9cd, fontFamily: _family);
static const IconData iconGesturePress =
IconData(0xe66c, fontFamily: _family);
static const IconData icon_gesture_tap =
IconData(0xe66f, fontFamily: _family);
static const IconData icon_gesture_pinch =
static const IconData iconGestureTap = IconData(0xe66f, fontFamily: _family);
static const IconData iconGesturePinch =
IconData(0xe66a, fontFamily: _family);
static const IconData icon_gesture_press_hold =
static const IconData iconGesturePressHold =
IconData(0xe66b, fontFamily: _family);
static const IconData icon_gesture_f_drag_up_down_ =
static const IconData iconGestureFDragUpDown_ =
IconData(0xe685, fontFamily: _family);
static const IconData icon_gesture_f_tap_ =
static const IconData iconGestureFTap_ =
IconData(0xe68e, fontFamily: _family);
static const IconData icon_gesture_f_swipe_right =
static const IconData iconGestureFSwipeRight =
IconData(0xe68f, fontFamily: _family);
static const IconData icon_gesture_f_double_tap =
static const IconData iconGestureFdoubleTap =
IconData(0xe691, fontFamily: _family);
static const IconData icon_gesture_f_three_fingers =
static const IconData iconGestureFThreeFingers =
IconData(0xe687, fontFamily: _family);
}
@@ -106,64 +103,64 @@ class _GestureHelpState extends State<GestureHelp> {
? [
GestureInfo(
width,
GestureIcons.icon_Mobile_Touch,
GestureIcons.iconMobileTouch,
translate("One-Finger Tap"),
translate("Left Mouse")),
GestureInfo(
width,
GestureIcons.icon_gesture_press_hold,
GestureIcons.iconGesturePressHold,
translate("One-Long Tap"),
translate("Right Mouse")),
GestureInfo(
width,
GestureIcons.icon_gesture_f_swipe_right,
GestureIcons.iconGestureFSwipeRight,
translate("One-Finger Move"),
translate("Mouse Drag")),
GestureInfo(
width,
GestureIcons.icon_gesture_f_three_fingers,
GestureIcons.iconGestureFThreeFingers,
translate("Three-Finger vertically"),
translate("Mouse Wheel")),
GestureInfo(
width,
GestureIcons.icon_gesture_f_drag,
GestureIcons.iconGestureFDrag,
translate("Two-Finger Move"),
translate("Canvas Move")),
GestureInfo(
width,
GestureIcons.icon_gesture_pinch,
GestureIcons.iconGesturePinch,
translate("Pinch to Zoom"),
translate("Canvas Zoom")),
]
: [
GestureInfo(
width,
GestureIcons.icon_Mobile_Touch,
GestureIcons.iconMobileTouch,
translate("One-Finger Tap"),
translate("Left Mouse")),
GestureInfo(
width,
GestureIcons.icon_gesture_press_hold,
GestureIcons.iconGesturePressHold,
translate("One-Long Tap"),
translate("Right Mouse")),
GestureInfo(
width,
GestureIcons.icon_gesture_f_swipe_right,
GestureIcons.iconGestureFSwipeRight,
translate("Double Tap & Move"),
translate("Mouse Drag")),
GestureInfo(
width,
GestureIcons.icon_gesture_f_three_fingers,
GestureIcons.iconGestureFThreeFingers,
translate("Three-Finger vertically"),
translate("Mouse Wheel")),
GestureInfo(
width,
GestureIcons.icon_gesture_f_drag,
GestureIcons.iconGestureFDrag,
translate("Two-Finger Move"),
translate("Canvas Move")),
GestureInfo(
width,
GestureIcons.icon_gesture_pinch,
GestureIcons.iconGesturePinch,
translate("Pinch to Zoom"),
translate("Canvas Zoom")),
],

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