portable service
Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
parent
eb60ab0b79
commit
8e1545b432
75
Cargo.lock
generated
75
Cargo.lock
generated
@ -1253,7 +1253,7 @@ dependencies = [
|
||||
"libc",
|
||||
"memalloc",
|
||||
"system-configuration",
|
||||
"windows",
|
||||
"windows 0.30.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4413,6 +4413,8 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json 1.0.85",
|
||||
"sha2",
|
||||
"shared_memory",
|
||||
"shutdown_hooks",
|
||||
"simple_rc",
|
||||
"sys-locale",
|
||||
"sysinfo",
|
||||
@ -4761,12 +4763,31 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shared_memory"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba8593196da75d9dc4f69349682bd4c2099f8cde114257d1ef7ef1b33d1aba54"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"nix 0.23.1",
|
||||
"rand 0.8.5",
|
||||
"win-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
||||
|
||||
[[package]]
|
||||
name = "shutdown_hooks"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6057adedbec913419c92996f395ba69931acbd50b7d56955394cd3f7bedbfa45"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
@ -5844,6 +5865,15 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
|
||||
|
||||
[[package]]
|
||||
name = "win-sys"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b7b128a98c1cfa201b09eb49ba285887deb3cbe7466a98850eb1adabb452be5"
|
||||
dependencies = [
|
||||
"windows 0.34.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
@ -5909,6 +5939,19 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.30.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc 0.34.0",
|
||||
"windows_i686_gnu 0.34.0",
|
||||
"windows_i686_msvc 0.34.0",
|
||||
"windows_x86_64_gnu 0.34.0",
|
||||
"windows_x86_64_msvc 0.34.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-service"
|
||||
version = "0.4.0"
|
||||
@ -5959,6 +6002,12 @@ version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
@ -5977,6 +6026,12 @@ version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
@ -5995,6 +6050,12 @@ version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
@ -6013,6 +6074,12 @@ version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
@ -6031,6 +6098,12 @@ version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
|
@ -92,6 +92,8 @@ winreg = "0.10"
|
||||
windows-service = "0.4"
|
||||
virtual_display = { path = "libs/virtual_display" }
|
||||
impersonate_system = { git = "https://github.com/21pages/impersonate-system" }
|
||||
shared_memory = "0.12.4"
|
||||
shutdown_hooks = "0.1.0"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc = "0.2"
|
||||
|
@ -501,11 +501,46 @@ class _CmControlPanel extends StatelessWidget {
|
||||
}
|
||||
|
||||
buildAuthorized(BuildContext context) {
|
||||
final bool canElevate = bind.cmCanElevate();
|
||||
final model = Provider.of<ServerModel>(context);
|
||||
final offstage = !(canElevate && model.showElevation);
|
||||
final width = offstage ? 200.0 : 100.0;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Offstage(
|
||||
offstage: offstage,
|
||||
child: Ink(
|
||||
width: width,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green[700],
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
child: InkWell(
|
||||
onTap: () =>
|
||||
checkClickTime(client.id, () => handleElevate(context)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.security_sharp,
|
||||
color: Colors.white,
|
||||
),
|
||||
Text(
|
||||
translate("Elevate"),
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
Offstage(
|
||||
offstage: offstage,
|
||||
child: SizedBox(
|
||||
width: 30,
|
||||
)),
|
||||
Ink(
|
||||
width: 200,
|
||||
width: width,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.redAccent, borderRadius: BorderRadius.circular(10)),
|
||||
@ -552,11 +587,50 @@ class _CmControlPanel extends StatelessWidget {
|
||||
}
|
||||
|
||||
buildUnAuthorized(BuildContext context) {
|
||||
final bool canElevate = bind.cmCanElevate();
|
||||
final model = Provider.of<ServerModel>(context);
|
||||
final offstage = !(canElevate && model.showElevation);
|
||||
final width = offstage ? 100.0 : 85.0;
|
||||
final spacerWidth = offstage ? 30.0 : 5.0;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Offstage(
|
||||
offstage: offstage,
|
||||
child: Ink(
|
||||
height: 40,
|
||||
width: width,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green[700],
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
child: InkWell(
|
||||
onTap: () => checkClickTime(client.id, () {
|
||||
handleAccept(context);
|
||||
handleElevate(context);
|
||||
windowManager.minimize();
|
||||
}),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.security_sharp,
|
||||
color: Colors.white,
|
||||
),
|
||||
Text(
|
||||
translate("Accept"),
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
Offstage(
|
||||
offstage: offstage,
|
||||
child: SizedBox(
|
||||
width: spacerWidth,
|
||||
)),
|
||||
Ink(
|
||||
width: 100,
|
||||
width: width,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: MyTheme.accent, borderRadius: BorderRadius.circular(10)),
|
||||
@ -576,10 +650,10 @@ class _CmControlPanel extends StatelessWidget {
|
||||
)),
|
||||
),
|
||||
SizedBox(
|
||||
width: 30,
|
||||
width: spacerWidth,
|
||||
),
|
||||
Ink(
|
||||
width: 100,
|
||||
width: width,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
@ -611,6 +685,12 @@ class _CmControlPanel extends StatelessWidget {
|
||||
model.sendLoginResponse(client, true);
|
||||
}
|
||||
|
||||
void handleElevate(BuildContext context) {
|
||||
final model = Provider.of<ServerModel>(context, listen: false);
|
||||
model.setShowElevation(false);
|
||||
bind.cmElevatePortable(connId: client.id);
|
||||
}
|
||||
|
||||
void handleClose(BuildContext context) async {
|
||||
await bind.cmRemoveDisconnectedConnection(connId: client.id);
|
||||
if (await bind.cmGetClientsLength() == 0) {
|
||||
|
@ -192,6 +192,9 @@ class FfiModel with ChangeNotifier {
|
||||
}
|
||||
} else if (name == 'alias') {
|
||||
handleAliasChanged(evt);
|
||||
} else if (name == 'show_elevation') {
|
||||
final show = evt['show'].toString() == 'true';
|
||||
parent.target?.serverModel.setShowElevation(show);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ class ServerModel with ChangeNotifier {
|
||||
bool _inputOk = false;
|
||||
bool _audioOk = false;
|
||||
bool _fileOk = false;
|
||||
bool _showElevation = true;
|
||||
int _connectStatus = 0; // Rendezvous Server status
|
||||
String _verificationMethod = "";
|
||||
String _temporaryPasswordLength = "";
|
||||
@ -51,6 +52,8 @@ class ServerModel with ChangeNotifier {
|
||||
|
||||
bool get fileOk => _fileOk;
|
||||
|
||||
bool get showElevation => _showElevation;
|
||||
|
||||
int get connectStatus => _connectStatus;
|
||||
|
||||
String get verificationMethod {
|
||||
@ -530,6 +533,13 @@ class ServerModel with ChangeNotifier {
|
||||
final index = _clients.indexWhere((client) => client.id == id);
|
||||
tabController.jumpTo(index);
|
||||
}
|
||||
|
||||
void setShowElevation(bool show) {
|
||||
if (_showElevation != show) {
|
||||
_showElevation = show;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Client {
|
||||
|
@ -61,7 +61,7 @@ impl TraitCapturer for Capturer {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Frame<'a>(&'a [u8]);
|
||||
pub struct Frame<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> ops::Deref for Frame<'a> {
|
||||
type Target = [u8];
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use hbb_common::anyhow::{anyhow, Context};
|
||||
use hbb_common::message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame};
|
||||
use hbb_common::{ResultType, get_time};
|
||||
use hbb_common::{get_time, ResultType};
|
||||
|
||||
use crate::codec::EncoderApi;
|
||||
use crate::STRIDE_ALIGN;
|
||||
@ -233,7 +233,9 @@ impl EncoderApi for VpxEncoder {
|
||||
|
||||
impl VpxEncoder {
|
||||
pub fn encode(&mut self, pts: i64, data: &[u8], stride_align: usize) -> Result<EncodeFrames> {
|
||||
assert!(2 * data.len() >= 3 * self.width * self.height);
|
||||
if 2 * data.len() < 3 * self.width * self.height {
|
||||
return Err(Error::FailedCall("len not enough".to_string()));
|
||||
}
|
||||
|
||||
let mut image = Default::default();
|
||||
call_vpx_ptr!(vpx_img_wrap(
|
||||
|
@ -80,6 +80,11 @@ pub fn core_main() -> Option<Vec<String>> {
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
if !crate::platform::is_installed() && (_is_elevate || _is_run_as_system) {
|
||||
crate::platform::elevate_or_run_as_system(click_setup, _is_elevate, _is_run_as_system);
|
||||
return None;
|
||||
}
|
||||
if args.is_empty() {
|
||||
std::thread::spawn(move || crate::start_server(false));
|
||||
} else {
|
||||
@ -128,6 +133,13 @@ pub fn core_main() -> Option<Vec<String>> {
|
||||
} else if args[0] == "--tray" {
|
||||
crate::tray::start_tray();
|
||||
return None;
|
||||
} else if args[0] == "--portable-service" {
|
||||
crate::platform::elevate_or_run_as_system(
|
||||
click_setup,
|
||||
_is_elevate,
|
||||
_is_run_as_system,
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if args[0] == "--remove" {
|
||||
|
@ -469,6 +469,10 @@ pub mod connection_manager {
|
||||
fn change_language(&self) {
|
||||
self.push_event("language", vec![]);
|
||||
}
|
||||
|
||||
fn show_elevation(&self, show: bool) {
|
||||
self.push_event("show_elevation", vec![("show", &show.to_string())]);
|
||||
}
|
||||
}
|
||||
|
||||
impl FlutterHandler {
|
||||
|
@ -1010,6 +1010,14 @@ pub fn cm_switch_permission(conn_id: i32, name: String, enabled: bool) {
|
||||
crate::ui_cm_interface::switch_permission(conn_id, name, enabled)
|
||||
}
|
||||
|
||||
pub fn cm_can_elevate() -> SyncReturn<bool> {
|
||||
SyncReturn(crate::ui_cm_interface::can_elevate())
|
||||
}
|
||||
|
||||
pub fn cm_elevate_portable(conn_id: i32) {
|
||||
crate::ui_cm_interface::elevate_portable(conn_id);
|
||||
}
|
||||
|
||||
pub fn main_get_icon() -> String {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
|
||||
return ui_interface::get_icon();
|
||||
|
14
src/ipc.rs
14
src/ipc.rs
@ -130,6 +130,19 @@ pub enum DataControl {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(tag = "t", content = "c")]
|
||||
pub enum DataPortableService {
|
||||
Ping,
|
||||
Pong,
|
||||
ConnCount(Option<usize>),
|
||||
Mouse(Vec<u8>),
|
||||
Key(Vec<u8>),
|
||||
RequestStart,
|
||||
WillClose,
|
||||
CmShowElevation(bool),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(tag = "t", content = "c")]
|
||||
pub enum Data {
|
||||
@ -187,6 +200,7 @@ pub enum Data {
|
||||
Language(String),
|
||||
Empty,
|
||||
Disconnected,
|
||||
DataPortableService(DataPortableService),
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", "此电脑"),
|
||||
("or", "或"),
|
||||
("Continue with", "使用"),
|
||||
("Elevate", "提权"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", "Este PC"),
|
||||
("or", "o"),
|
||||
("Continue with", "Continuar con"),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", "Ce PC"),
|
||||
("or", "ou"),
|
||||
("Continue with", "Continuer avec"),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", "Ez a számítógép"),
|
||||
("or", "vagy"),
|
||||
("Continue with", "Folytatás a következővel"),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", "Este PC"),
|
||||
("or", "ou"),
|
||||
("Continue with", "Continuar com"),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", "Этот компьютер"),
|
||||
("or", "или"),
|
||||
("Continue with", "Продолжить с"),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", "提權"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This PC", ""),
|
||||
("or", ""),
|
||||
("Continue with", ""),
|
||||
("Elevate", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ pub fn get_cursor() -> ResultType<Option<u64>> {
|
||||
unsafe {
|
||||
let mut ci: CURSORINFO = mem::MaybeUninit::uninit().assume_init();
|
||||
ci.cbSize = std::mem::size_of::<CURSORINFO>() as _;
|
||||
if GetCursorInfo(&mut ci) == FALSE {
|
||||
if crate::portable_service::client::get_cursor_info(&mut ci) == FALSE {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
if ci.flags & CURSOR_SHOWING == 0 {
|
||||
@ -1542,9 +1542,11 @@ pub fn elevate_or_run_as_system(is_setup: bool, is_elevate: bool, is_run_as_syst
|
||||
} else {
|
||||
"--run-as-system"
|
||||
};
|
||||
|
||||
if is_root() {
|
||||
log::debug!("portable run as system user");
|
||||
if is_run_as_system {
|
||||
log::info!("run portable service");
|
||||
crate::portable_service::server::run_portable_service();
|
||||
}
|
||||
} else {
|
||||
match is_elevated(None) {
|
||||
Ok(elevated) => {
|
||||
|
@ -49,6 +49,8 @@ pub const NAME_POS: &'static str = "";
|
||||
}
|
||||
|
||||
mod connection;
|
||||
#[cfg(windows)]
|
||||
pub mod portable_service;
|
||||
mod service;
|
||||
mod video_qos;
|
||||
pub mod video_service;
|
||||
@ -60,6 +62,7 @@ type ConnMap = HashMap<i32, ConnInner>;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref CHILD_PROCESS: Childs = Default::default();
|
||||
pub static ref CONN_COUNT: Arc<Mutex<usize>> = Default::default();
|
||||
}
|
||||
|
||||
pub struct Server {
|
||||
@ -259,6 +262,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
self.connections.insert(conn.id(), conn);
|
||||
*CONN_COUNT.lock().unwrap() = self.connections.len();
|
||||
}
|
||||
|
||||
pub fn remove_connection(&mut self, conn: &ConnInner) {
|
||||
@ -266,6 +270,7 @@ impl Server {
|
||||
s.on_unsubscribe(conn.id());
|
||||
}
|
||||
self.connections.remove(&conn.id());
|
||||
*CONN_COUNT.lock().unwrap() = self.connections.len();
|
||||
}
|
||||
|
||||
pub fn close_connections(&mut self) {
|
||||
|
@ -236,8 +236,12 @@ impl Connection {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
std::thread::spawn(move || Self::handle_input(rx_input, tx_cloned));
|
||||
let mut second_timer = time::interval(Duration::from_secs(1));
|
||||
#[cfg(windows)]
|
||||
let mut last_uac = false;
|
||||
#[cfg(windows)]
|
||||
let mut last_foreground_window_elevated = false;
|
||||
#[cfg(windows)]
|
||||
let is_installed = crate::platform::is_installed();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
@ -341,6 +345,12 @@ impl Connection {
|
||||
};
|
||||
conn.send(msg_out).await;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
ipc::Data::DataPortableService(ipc::DataPortableService::RequestStart) => {
|
||||
if let Err(e) = crate::portable_service::client::start_portable_service() {
|
||||
log::error!("Failed to start portable service from cm:{:?}", e);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
@ -417,18 +427,24 @@ impl Connection {
|
||||
}
|
||||
},
|
||||
_ = second_timer.tick() => {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use crate::portable_service::client::{PORTABLE_SERVICE_STATUS, PortableServiceStatus::*};
|
||||
let uac = crate::video_service::IS_UAC_RUNNING.lock().unwrap().clone();
|
||||
if last_uac != uac {
|
||||
last_uac = uac;
|
||||
if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted {
|
||||
let mut misc = Misc::new();
|
||||
misc.set_uac(uac);
|
||||
let mut msg = Message::new();
|
||||
msg.set_misc(misc);
|
||||
conn.inner.send(msg.into());
|
||||
}
|
||||
}
|
||||
let foreground_window_elevated = crate::video_service::IS_FOREGROUND_WINDOW_ELEVATED.lock().unwrap().clone();
|
||||
if last_foreground_window_elevated != foreground_window_elevated {
|
||||
last_foreground_window_elevated = foreground_window_elevated;
|
||||
if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted {
|
||||
let mut misc = Misc::new();
|
||||
misc.set_foreground_window_elevated(foreground_window_elevated);
|
||||
let mut msg = Message::new();
|
||||
@ -436,6 +452,13 @@ impl Connection {
|
||||
conn.inner.send(msg.into());
|
||||
}
|
||||
}
|
||||
if !is_installed {
|
||||
let show_elevation = PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted;
|
||||
conn.send_to_cm(ipc::Data::DataPortableService(ipc::DataPortableService::CmShowElevation(show_elevation)));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = test_delay_timer.tick() => {
|
||||
if last_recv_time.elapsed() >= SEC30 {
|
||||
conn.on_close("Timeout", true).await;
|
||||
|
@ -287,7 +287,23 @@ pub fn handle_mouse(evt: &MouseEvent, conn: i32) {
|
||||
QUEUE.exec_async(move || handle_mouse_(&evt, conn));
|
||||
return;
|
||||
}
|
||||
handle_mouse_(evt, conn);
|
||||
if !active_mouse_(conn) {
|
||||
return;
|
||||
}
|
||||
let evt_type = evt.mask & 0x7;
|
||||
if evt_type == 0 {
|
||||
let time = get_time();
|
||||
*LATEST_PEER_INPUT_CURSOR.lock().unwrap() = Input {
|
||||
time,
|
||||
conn,
|
||||
x: evt.x,
|
||||
y: evt.y,
|
||||
};
|
||||
}
|
||||
#[cfg(windows)]
|
||||
crate::portable_service::client::handle_mouse(evt);
|
||||
#[cfg(not(windows))]
|
||||
handle_mouse_(evt);
|
||||
}
|
||||
|
||||
pub fn fix_key_down_timeout_loop() {
|
||||
@ -415,8 +431,7 @@ fn active_mouse_(conn: i32) -> bool {
|
||||
let lock = LATEST_PEER_INPUT_CURSOR.lock().unwrap();
|
||||
(lock.x, lock.y)
|
||||
};
|
||||
let mut can_active =
|
||||
in_actived_dist(last_in_x, x) && in_actived_dist(last_in_y, y);
|
||||
let mut can_active = in_actived_dist(last_in_x, x) && in_actived_dist(last_in_y, y);
|
||||
// The cursor may not have been moved to last input position if system is busy now.
|
||||
// While this is not a common case, we check it again after some time later.
|
||||
if !can_active {
|
||||
@ -425,8 +440,7 @@ fn active_mouse_(conn: i32) -> bool {
|
||||
std::thread::sleep(std::time::Duration::from_micros(10));
|
||||
// Sleep here can also somehow suppress delay accumulation.
|
||||
if let Some((x2, y2)) = crate::get_cursor_pos() {
|
||||
can_active =
|
||||
in_actived_dist(last_in_x, x2) && in_actived_dist(last_in_y, y2);
|
||||
can_active = in_actived_dist(last_in_x, x2) && in_actived_dist(last_in_y, y2);
|
||||
}
|
||||
}
|
||||
if !can_active {
|
||||
@ -440,15 +454,11 @@ fn active_mouse_(conn: i32) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_mouse_(evt: &MouseEvent, conn: i32) {
|
||||
pub fn handle_mouse_(evt: &MouseEvent) {
|
||||
if EXITING.load(Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
|
||||
if !active_mouse_(conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
crate::platform::windows::try_change_desktop();
|
||||
let buttons = evt.mask >> 3;
|
||||
@ -477,14 +487,6 @@ fn handle_mouse_(evt: &MouseEvent, conn: i32) {
|
||||
}
|
||||
match evt_type {
|
||||
0 => {
|
||||
let time = get_time();
|
||||
*LATEST_PEER_INPUT_CURSOR.lock().unwrap() = Input {
|
||||
time,
|
||||
conn,
|
||||
x: evt.x,
|
||||
y: evt.y,
|
||||
};
|
||||
|
||||
en.mouse_move_to(evt.x, evt.y);
|
||||
}
|
||||
1 => match buttons {
|
||||
@ -698,6 +700,9 @@ pub fn handle_key(evt: &KeyEvent) {
|
||||
QUEUE.exec_async(move || handle_key_(&evt));
|
||||
return;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
crate::portable_service::client::handle_key(evt);
|
||||
#[cfg(not(windows))]
|
||||
handle_key_(evt);
|
||||
}
|
||||
|
||||
@ -949,7 +954,7 @@ fn legacy_keyboard_mode(evt: &KeyEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_key_(evt: &KeyEvent) {
|
||||
pub fn handle_key_(evt: &KeyEvent) {
|
||||
if EXITING.load(Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
|
779
src/server/portable_service.rs
Normal file
779
src/server/portable_service.rs
Normal file
@ -0,0 +1,779 @@
|
||||
use core::slice;
|
||||
use hbb_common::{
|
||||
allow_err,
|
||||
anyhow::anyhow,
|
||||
bail,
|
||||
config::Config,
|
||||
log,
|
||||
message_proto::{KeyEvent, MouseEvent},
|
||||
protobuf::Message,
|
||||
sleep,
|
||||
tokio::{self, sync::mpsc},
|
||||
ResultType,
|
||||
};
|
||||
use scrap::{Capturer, Frame, TraitCapturer};
|
||||
use shared_memory::*;
|
||||
use std::{
|
||||
mem::size_of,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::{Arc, Mutex},
|
||||
time::Duration,
|
||||
};
|
||||
use winapi::{
|
||||
shared::minwindef::{BOOL, FALSE, TRUE},
|
||||
um::winuser::{self, CURSORINFO, PCURSORINFO},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ipc::{self, new_listener, Connection, Data, DataPortableService},
|
||||
video_service::get_current_display,
|
||||
};
|
||||
|
||||
use super::video_qos;
|
||||
|
||||
const SIZE_COUNTER: usize = size_of::<i32>() * 2;
|
||||
const FRAME_ALIGN: usize = 64;
|
||||
|
||||
const ADDR_CURSOR_PARA: usize = 0;
|
||||
const ADDR_CURSOR_COUNTER: usize = ADDR_CURSOR_PARA + size_of::<CURSORINFO>();
|
||||
|
||||
const ADDR_CAPTURER_PARA: usize = ADDR_CURSOR_COUNTER + SIZE_COUNTER;
|
||||
const ADDR_CAPTURE_FRAME_SIZE: usize = ADDR_CAPTURER_PARA + size_of::<CapturerPara>();
|
||||
const ADDR_CAPTURE_WOULDBLOCK: usize = ADDR_CAPTURE_FRAME_SIZE + size_of::<i32>();
|
||||
const ADDR_CAPTURE_FRAME_COUNTER: usize = ADDR_CAPTURE_WOULDBLOCK + size_of::<i32>();
|
||||
|
||||
const ADDR_CAPTURE_FRAME: usize =
|
||||
(ADDR_CAPTURE_FRAME_COUNTER + SIZE_COUNTER + FRAME_ALIGN - 1) / FRAME_ALIGN * FRAME_ALIGN;
|
||||
|
||||
const IPC_PROFIX: &str = "_portable_service";
|
||||
pub const SHMEM_NAME: &str = "_portable_service";
|
||||
const MAX_NACK: usize = 3;
|
||||
const IPC_CONN_TIMEOUT: Duration = Duration::from_secs(3);
|
||||
|
||||
pub enum PortableServiceStatus {
|
||||
NonStart,
|
||||
Running,
|
||||
}
|
||||
|
||||
impl Default for PortableServiceStatus {
|
||||
fn default() -> Self {
|
||||
Self::NonStart
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SharedMemory {
|
||||
inner: Shmem,
|
||||
}
|
||||
|
||||
unsafe impl Send for SharedMemory {}
|
||||
unsafe impl Sync for SharedMemory {}
|
||||
|
||||
impl Deref for SharedMemory {
|
||||
type Target = Shmem;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for SharedMemory {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedMemory {
|
||||
pub fn create(name: &str, size: usize) -> ResultType<Self> {
|
||||
let flink = Self::flink(name.to_string());
|
||||
let shmem = match ShmemConf::new()
|
||||
.size(size)
|
||||
.flink(&flink)
|
||||
.force_create_flink()
|
||||
.create()
|
||||
{
|
||||
Ok(m) => m,
|
||||
Err(ShmemError::LinkExists) => {
|
||||
bail!(
|
||||
"Unable to force create shmem flink {}, which should not happen.",
|
||||
flink
|
||||
)
|
||||
}
|
||||
Err(e) => {
|
||||
bail!("Unable to create shmem flink {} : {}", flink, e);
|
||||
}
|
||||
};
|
||||
log::info!("Create shared memory, size:{}, flink:{}", size, flink);
|
||||
Self::set_all_perm(&flink);
|
||||
Ok(SharedMemory { inner: shmem })
|
||||
}
|
||||
|
||||
pub fn open_existing(name: &str) -> ResultType<Self> {
|
||||
let flink = Self::flink(name.to_string());
|
||||
let shmem = match ShmemConf::new().flink(&flink).allow_raw(true).open() {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
bail!("Unable to open existing shmem flink {} : {}", flink, e);
|
||||
}
|
||||
};
|
||||
log::info!("open existing shared memory, flink:{:?}", flink);
|
||||
Ok(SharedMemory { inner: shmem })
|
||||
}
|
||||
|
||||
pub fn write(&self, addr: usize, data: &[u8]) {
|
||||
unsafe {
|
||||
assert!(addr + data.len() <= self.inner.len());
|
||||
let ptr = self.inner.as_ptr().add(addr);
|
||||
let shared_mem_slice = slice::from_raw_parts_mut(ptr, data.len());
|
||||
shared_mem_slice.copy_from_slice(data);
|
||||
}
|
||||
}
|
||||
|
||||
fn flink(name: String) -> String {
|
||||
let mut shmem_flink = format!("shared_memory{}", name);
|
||||
if cfg!(windows) {
|
||||
let df = "C:\\ProgramData";
|
||||
let df = if std::path::Path::new(df).exists() {
|
||||
df.to_owned()
|
||||
} else {
|
||||
std::env::var("TEMP").unwrap_or("C:\\Windows\\TEMP".to_owned())
|
||||
};
|
||||
let df = format!("{}\\{}", df, *hbb_common::config::APP_NAME.read().unwrap());
|
||||
std::fs::create_dir(&df).ok();
|
||||
shmem_flink = format!("{}\\{}", df, shmem_flink);
|
||||
} else {
|
||||
shmem_flink = Config::ipc_path("").replace("ipc", "") + &shmem_flink;
|
||||
}
|
||||
return shmem_flink;
|
||||
}
|
||||
|
||||
fn set_all_perm(_p: &str) {
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
std::fs::set_permissions(_p, std::fs::Permissions::from_mode(0o0777)).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod utils {
|
||||
use core::slice;
|
||||
use std::mem::size_of;
|
||||
|
||||
pub fn i32_to_vec(i: i32) -> Vec<u8> {
|
||||
i.to_ne_bytes().to_vec()
|
||||
}
|
||||
|
||||
pub fn ptr_to_i32(ptr: *const u8) -> i32 {
|
||||
unsafe {
|
||||
let v = slice::from_raw_parts(ptr, size_of::<i32>());
|
||||
i32::from_ne_bytes([v[0], v[1], v[2], v[3]])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn counter_ready(counter: *const u8) -> bool {
|
||||
unsafe {
|
||||
let wptr = counter;
|
||||
let rptr = counter.add(size_of::<i32>());
|
||||
let iw = ptr_to_i32(wptr);
|
||||
let ir = ptr_to_i32(rptr);
|
||||
if ir != iw {
|
||||
std::ptr::copy_nonoverlapping(wptr, rptr as *mut _, size_of::<i32>());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increase_counter(ptr: *mut u8) {
|
||||
unsafe {
|
||||
let i = ptr_to_i32(ptr);
|
||||
let v = i32_to_vec(i + 1);
|
||||
std::ptr::copy_nonoverlapping(v.as_ptr(), ptr, size_of::<i32>());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn align(v: usize, align: usize) -> usize {
|
||||
(v + align - 1) / align * align
|
||||
}
|
||||
}
|
||||
|
||||
// functions called in seperate SYSTEM user process.
|
||||
pub mod server {
|
||||
use hbb_common::tokio::time::Instant;
|
||||
|
||||
use super::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref EXIT: Arc<Mutex<bool>> = Default::default();
|
||||
}
|
||||
|
||||
pub fn run_portable_service() {
|
||||
let shmem = Arc::new(SharedMemory::open_existing(SHMEM_NAME).unwrap());
|
||||
let shmem1 = shmem.clone();
|
||||
let shmem2 = shmem.clone();
|
||||
let mut threads = vec![];
|
||||
threads.push(std::thread::spawn(|| {
|
||||
run_get_cursor_info(shmem1);
|
||||
}));
|
||||
threads.push(std::thread::spawn(|| {
|
||||
run_capture(shmem2);
|
||||
}));
|
||||
threads.push(std::thread::spawn(|| {
|
||||
run_ipc_server();
|
||||
}));
|
||||
threads.push(std::thread::spawn(|| {
|
||||
run_exit_check();
|
||||
}));
|
||||
for th in threads.drain(..) {
|
||||
th.join().unwrap();
|
||||
log::info!("all thread joined");
|
||||
}
|
||||
}
|
||||
|
||||
fn run_exit_check() {
|
||||
loop {
|
||||
if EXIT.lock().unwrap().clone() {
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
log::info!("exit from seperate check thread");
|
||||
std::process::exit(0);
|
||||
}
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
|
||||
fn run_get_cursor_info(shmem: Arc<SharedMemory>) {
|
||||
loop {
|
||||
if EXIT.lock().unwrap().clone() {
|
||||
break;
|
||||
}
|
||||
unsafe {
|
||||
let para = shmem.as_ptr().add(ADDR_CURSOR_PARA) as *mut CURSORINFO;
|
||||
(*para).cbSize = size_of::<CURSORINFO>() as _;
|
||||
let result = winuser::GetCursorInfo(para);
|
||||
if result == TRUE {
|
||||
utils::increase_counter(shmem.as_ptr().add(ADDR_CURSOR_COUNTER));
|
||||
}
|
||||
}
|
||||
// more frequent in case of `Error of mouse_cursor service`
|
||||
std::thread::sleep(Duration::from_millis(15));
|
||||
}
|
||||
}
|
||||
|
||||
fn run_capture(shmem: Arc<SharedMemory>) {
|
||||
let mut c = None;
|
||||
let mut last_current_display = usize::MAX;
|
||||
let mut last_use_yuv = false;
|
||||
let mut last_timeout_ms: i32 = 33;
|
||||
let mut spf = Duration::from_millis(last_timeout_ms as _);
|
||||
loop {
|
||||
if EXIT.lock().unwrap().clone() {
|
||||
break;
|
||||
}
|
||||
let start = std::time::Instant::now();
|
||||
unsafe {
|
||||
let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA);
|
||||
let para = para_ptr as *const CapturerPara;
|
||||
let current_display = (*para).current_display;
|
||||
let use_yuv = (*para).use_yuv;
|
||||
let timeout_ms = (*para).timeout_ms;
|
||||
if c.is_none() {
|
||||
let use_yuv = true;
|
||||
*crate::video_service::CURRENT_DISPLAY.lock().unwrap() = current_display;
|
||||
let (_, _current, display) = get_current_display().unwrap();
|
||||
match Capturer::new(display, use_yuv) {
|
||||
Ok(mut v) => {
|
||||
c = {
|
||||
last_current_display = current_display;
|
||||
last_use_yuv = use_yuv;
|
||||
// dxgi failed at loadFrame on my PC.
|
||||
// to-do: try dxgi on another PC.
|
||||
v.set_gdi();
|
||||
Some(v)
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to create gdi capturer:{:?}", e);
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if current_display != last_current_display || use_yuv != last_use_yuv {
|
||||
log::info!(
|
||||
"display:{}->{}, use_yuv:{}->{}",
|
||||
last_current_display,
|
||||
current_display,
|
||||
last_use_yuv,
|
||||
use_yuv
|
||||
);
|
||||
c = None;
|
||||
continue;
|
||||
}
|
||||
if timeout_ms != last_timeout_ms
|
||||
&& timeout_ms >= 1000 / video_qos::MAX_FPS as i32
|
||||
&& timeout_ms <= 1000 / video_qos::MIN_FPS as i32
|
||||
{
|
||||
last_timeout_ms = timeout_ms;
|
||||
spf = Duration::from_millis(timeout_ms as _);
|
||||
}
|
||||
}
|
||||
match c.as_mut().unwrap().frame(spf) {
|
||||
Ok(f) => {
|
||||
let len = f.0.len();
|
||||
let len_slice = utils::i32_to_vec(len as _);
|
||||
shmem.write(ADDR_CAPTURE_FRAME_SIZE, &len_slice);
|
||||
shmem.write(ADDR_CAPTURE_FRAME, f.0);
|
||||
shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE));
|
||||
utils::increase_counter(shmem.as_ptr().add(ADDR_CAPTURE_FRAME_COUNTER));
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() != std::io::ErrorKind::WouldBlock {
|
||||
log::error!("capture frame failed:{:?}", e);
|
||||
crate::platform::try_change_desktop();
|
||||
c = None;
|
||||
shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(FALSE));
|
||||
continue;
|
||||
} else {
|
||||
shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let elapsed = start.elapsed();
|
||||
if elapsed < spf {
|
||||
std::thread::sleep(spf - elapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn run_ipc_server() {
|
||||
use DataPortableService::*;
|
||||
|
||||
let postfix = IPC_PROFIX;
|
||||
let last_recv_time = Arc::new(Mutex::new(Instant::now()));
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(1));
|
||||
|
||||
match new_listener(postfix).await {
|
||||
Ok(mut incoming) => loop {
|
||||
tokio::select! {
|
||||
Some(result) = incoming.next() => {
|
||||
match result {
|
||||
Ok(stream) => {
|
||||
log::info!("Got new connection");
|
||||
let last_recv_time_cloned = last_recv_time.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut stream = Connection::new(stream);
|
||||
let postfix = postfix.to_owned();
|
||||
let mut timer = tokio::time::interval(Duration::from_secs(1));
|
||||
let mut nack = 0;
|
||||
let mut old_conn_count = 0;
|
||||
loop {
|
||||
tokio::select! {
|
||||
res = stream.next() => {
|
||||
if res.is_ok() {
|
||||
*last_recv_time_cloned.lock().unwrap() = Instant::now();
|
||||
}
|
||||
match res {
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"ipc{} connection closed: {}",
|
||||
postfix,
|
||||
err
|
||||
);
|
||||
*EXIT.lock().unwrap() = true;
|
||||
break;
|
||||
}
|
||||
Ok(Some(Data::DataPortableService(data))) => match data {
|
||||
Ping => {
|
||||
allow_err!(
|
||||
stream
|
||||
.send(&Data::DataPortableService(Pong))
|
||||
.await
|
||||
);
|
||||
}
|
||||
Pong => {
|
||||
nack = 0;
|
||||
}
|
||||
ConnCount(Some(n)) => {
|
||||
if old_conn_count != 0 && n == 0 {
|
||||
log::info!("Connection count decrease to 0, exit");
|
||||
stream.send(&Data::DataPortableService(WillClose)).await.ok();
|
||||
*EXIT.lock().unwrap() = true;
|
||||
break;
|
||||
}
|
||||
old_conn_count = n;
|
||||
}
|
||||
Mouse(v) => {
|
||||
if let Ok(evt) = MouseEvent::parse_from_bytes(&v) {
|
||||
crate::input_service::handle_mouse_(&evt);
|
||||
}
|
||||
}
|
||||
Key(v) => {
|
||||
if let Ok(evt) = KeyEvent::parse_from_bytes(&v) {
|
||||
crate::input_service::handle_key_(&evt);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ = timer.tick() => {
|
||||
nack+=1;
|
||||
if nack > MAX_NACK {
|
||||
log::info!("max ping nack, exit");
|
||||
*EXIT.lock().unwrap() = true;
|
||||
break;
|
||||
}
|
||||
stream.send(&Data::DataPortableService(Ping)).await.ok();
|
||||
stream.send(&Data::DataPortableService(ConnCount(None))).await.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Couldn't get portable client: {:?}", err);
|
||||
*EXIT.lock().unwrap() = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = interval.tick() => {
|
||||
if last_recv_time.lock().unwrap().elapsed() > IPC_CONN_TIMEOUT {
|
||||
log::error!("receive data timeout");
|
||||
*EXIT.lock().unwrap() = true;
|
||||
}
|
||||
if EXIT.lock().unwrap().clone() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log::error!("Failed to start cm ipc server: {}", err);
|
||||
*EXIT.lock().unwrap() = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// functions called in main process.
|
||||
pub mod client {
|
||||
use hbb_common::anyhow::Context;
|
||||
|
||||
use super::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref SHMEM: Arc<Mutex<Option<SharedMemory>>> = Default::default();
|
||||
pub static ref PORTABLE_SERVICE_STATUS: Arc<Mutex<PortableServiceStatus>> = Default::default();
|
||||
static ref SENDER : Mutex<mpsc::UnboundedSender<ipc::Data>> = Mutex::new(client::start_ipc_client());
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PortableServiceStatus {
|
||||
NotStarted,
|
||||
Starting,
|
||||
Running,
|
||||
}
|
||||
|
||||
impl Default for PortableServiceStatus {
|
||||
fn default() -> Self {
|
||||
Self::NotStarted
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start_portable_service() -> ResultType<()> {
|
||||
if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::NotStarted {
|
||||
if SHMEM.lock().unwrap().is_none() {
|
||||
let displays = scrap::Display::all()?;
|
||||
if displays.is_empty() {
|
||||
bail!("no display available!");
|
||||
}
|
||||
let mut max_pixel = 0;
|
||||
let align = 64;
|
||||
for d in displays {
|
||||
let pixel = utils::align(d.width(), align) * utils::align(d.height(), align);
|
||||
if max_pixel < pixel {
|
||||
max_pixel = pixel;
|
||||
}
|
||||
}
|
||||
let shmem_size = utils::align(ADDR_CAPTURE_FRAME + max_pixel * 4, align);
|
||||
// os error 112, no enough space
|
||||
*SHMEM.lock().unwrap() = Some(crate::portable_service::SharedMemory::create(
|
||||
crate::portable_service::SHMEM_NAME,
|
||||
shmem_size,
|
||||
)?);
|
||||
shutdown_hooks::add_shutdown_hook(drop_shmem);
|
||||
}
|
||||
if crate::common::run_me(vec!["--portable-service"]).is_err() {
|
||||
*SHMEM.lock().unwrap() = None;
|
||||
bail!("Failed to run portable service process");
|
||||
}
|
||||
*PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::Starting;
|
||||
let _sender = SENDER.lock().unwrap();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
extern "C" fn drop_shmem() {
|
||||
log::info!("drop shared memory");
|
||||
*SHMEM.lock().unwrap() = None;
|
||||
}
|
||||
|
||||
pub struct CapturerPortable;
|
||||
|
||||
impl CapturerPortable {
|
||||
pub fn new(current_display: usize, use_yuv: bool) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self::set_para(CapturerPara {
|
||||
current_display,
|
||||
use_yuv,
|
||||
timeout_ms: 33,
|
||||
});
|
||||
CapturerPortable {}
|
||||
}
|
||||
|
||||
fn set_para(para: CapturerPara) {
|
||||
let mut option = SHMEM.lock().unwrap();
|
||||
let shmem = option.as_mut().unwrap();
|
||||
let para_ptr = ¶ as *const CapturerPara as *const u8;
|
||||
let para_data;
|
||||
unsafe {
|
||||
para_data = slice::from_raw_parts(para_ptr, size_of::<CapturerPara>());
|
||||
}
|
||||
shmem.write(ADDR_CAPTURER_PARA, para_data);
|
||||
}
|
||||
}
|
||||
|
||||
impl TraitCapturer for CapturerPortable {
|
||||
fn set_use_yuv(&mut self, use_yuv: bool) {
|
||||
let mut option = SHMEM.lock().unwrap();
|
||||
let shmem = option.as_mut().unwrap();
|
||||
unsafe {
|
||||
let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA);
|
||||
let para = para_ptr as *const CapturerPara;
|
||||
if use_yuv != (*para).use_yuv {
|
||||
Self::set_para(CapturerPara {
|
||||
current_display: (*para).current_display,
|
||||
use_yuv,
|
||||
timeout_ms: (*para).timeout_ms,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn frame<'a>(&'a mut self, timeout: Duration) -> std::io::Result<Frame<'a>> {
|
||||
let mut option = SHMEM.lock().unwrap();
|
||||
let shmem = option.as_mut().unwrap();
|
||||
unsafe {
|
||||
let base = shmem.as_ptr();
|
||||
let para_ptr = base.add(ADDR_CAPTURER_PARA);
|
||||
let para = para_ptr as *const CapturerPara;
|
||||
if timeout.as_millis() != (*para).timeout_ms as _ {
|
||||
Self::set_para(CapturerPara {
|
||||
current_display: (*para).current_display,
|
||||
use_yuv: (*para).use_yuv,
|
||||
timeout_ms: timeout.as_millis() as _,
|
||||
});
|
||||
}
|
||||
if utils::counter_ready(base.add(ADDR_CAPTURE_FRAME_COUNTER)) {
|
||||
let frame_len_ptr = base.add(ADDR_CAPTURE_FRAME_SIZE);
|
||||
let frame_len = utils::ptr_to_i32(frame_len_ptr);
|
||||
let frame_ptr = base.add(ADDR_CAPTURE_FRAME);
|
||||
let data = slice::from_raw_parts(frame_ptr, frame_len as usize);
|
||||
Ok(Frame(data))
|
||||
} else {
|
||||
let ptr = base.add(ADDR_CAPTURE_WOULDBLOCK);
|
||||
let wouldblock = utils::ptr_to_i32(ptr);
|
||||
if wouldblock == TRUE {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::WouldBlock,
|
||||
"wouldblock error".to_string(),
|
||||
))
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"other error".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_gdi(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_gdi(&mut self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn start_ipc_client() -> mpsc::UnboundedSender<Data> {
|
||||
let (tx, rx) = mpsc::unbounded_channel::<Data>();
|
||||
std::thread::spawn(move || start_ipc_client_async(rx));
|
||||
tx
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn start_ipc_client_async(rx: mpsc::UnboundedReceiver<Data>) {
|
||||
use DataPortableService::*;
|
||||
let mut rx = rx;
|
||||
let mut connect_failed = 0;
|
||||
loop {
|
||||
if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::NotStarted
|
||||
{
|
||||
sleep(1.).await;
|
||||
continue;
|
||||
}
|
||||
if let Ok(mut c) = ipc::connect(1000, IPC_PROFIX).await {
|
||||
let mut nack = 0;
|
||||
let mut timer = tokio::time::interval(Duration::from_secs(1));
|
||||
loop {
|
||||
tokio::select! {
|
||||
res = c.next() => {
|
||||
match res {
|
||||
Err(err) => {
|
||||
log::error!("ipc connection closed: {}", err);
|
||||
break;
|
||||
}
|
||||
Ok(Some(Data::DataPortableService(data))) => {
|
||||
match data {
|
||||
Ping => {
|
||||
c.send(&Data::DataPortableService(Pong)).await.ok();
|
||||
}
|
||||
Pong => {
|
||||
nack = 0;
|
||||
*PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::Running;
|
||||
},
|
||||
ConnCount(None) => {
|
||||
let cnt = crate::server::CONN_COUNT.lock().unwrap().clone();
|
||||
c.send(&Data::DataPortableService(ConnCount(Some(cnt)))).await.ok();
|
||||
},
|
||||
WillClose => {
|
||||
log::info!("portable service will close, set status to not started");
|
||||
*PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted;
|
||||
break;
|
||||
}
|
||||
_=>{}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ = timer.tick() => {
|
||||
nack+=1;
|
||||
if nack > MAX_NACK {
|
||||
// In fact, this will not happen, ipc will be closed before max nack.
|
||||
log::error!("max ipc nack, set status to not started");
|
||||
*PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted;
|
||||
break;
|
||||
}
|
||||
c.send(&Data::DataPortableService(Ping)).await.ok();
|
||||
}
|
||||
Some(data) = rx.recv() => {
|
||||
allow_err!(c.send(&data).await);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
connect_failed += 1;
|
||||
if connect_failed > IPC_CONN_TIMEOUT.as_secs() {
|
||||
connect_failed = 0;
|
||||
*PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted;
|
||||
log::info!(
|
||||
"connect failed {} times, set status to not started",
|
||||
connect_failed
|
||||
);
|
||||
}
|
||||
log::info!(
|
||||
"client ip connect failed, status:{:?}",
|
||||
PORTABLE_SERVICE_STATUS.lock().unwrap().clone(),
|
||||
);
|
||||
}
|
||||
sleep(1.).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn client_ipc_send(data: Data) -> ResultType<()> {
|
||||
let sender = SENDER.lock().unwrap();
|
||||
sender
|
||||
.send(data)
|
||||
.map_err(|e| anyhow!("ipc send error:{:?}", e))
|
||||
}
|
||||
|
||||
fn get_cursor_info_(shmem: &mut SharedMemory, pci: PCURSORINFO) -> BOOL {
|
||||
unsafe {
|
||||
let shmem_addr_para = shmem.as_ptr().add(ADDR_CURSOR_PARA);
|
||||
if utils::counter_ready(shmem.as_ptr().add(ADDR_CURSOR_COUNTER)) {
|
||||
std::ptr::copy_nonoverlapping(shmem_addr_para, pci as _, size_of::<CURSORINFO>());
|
||||
return TRUE;
|
||||
}
|
||||
FALSE
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_mouse_(evt: &MouseEvent) -> ResultType<()> {
|
||||
let mut v = vec![];
|
||||
evt.write_to_vec(&mut v)?;
|
||||
client_ipc_send(Data::DataPortableService(DataPortableService::Mouse(v)))
|
||||
}
|
||||
|
||||
fn handle_key_(evt: &KeyEvent) -> ResultType<()> {
|
||||
let mut v = vec![];
|
||||
evt.write_to_vec(&mut v)?;
|
||||
client_ipc_send(Data::DataPortableService(DataPortableService::Key(v)))
|
||||
}
|
||||
|
||||
pub fn create_capturer(
|
||||
current_display: usize,
|
||||
display: scrap::Display,
|
||||
use_yuv: bool,
|
||||
) -> ResultType<Box<dyn TraitCapturer>> {
|
||||
if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running {
|
||||
log::info!("Create shared memeory capturer");
|
||||
return Ok(Box::new(CapturerPortable::new(current_display, use_yuv)));
|
||||
} else {
|
||||
log::debug!("Create capturer dxgi|gdi");
|
||||
return Ok(Box::new(
|
||||
Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cursor_info(pci: PCURSORINFO) -> BOOL {
|
||||
if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running {
|
||||
get_cursor_info_(&mut SHMEM.lock().unwrap().as_mut().unwrap(), pci)
|
||||
} else {
|
||||
unsafe { winuser::GetCursorInfo(pci) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_mouse(evt: &MouseEvent) {
|
||||
if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running {
|
||||
handle_mouse_(evt).ok();
|
||||
} else {
|
||||
crate::input_service::handle_mouse_(evt);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_key(evt: &KeyEvent) {
|
||||
if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running {
|
||||
handle_key_(evt).ok();
|
||||
} else {
|
||||
crate::input_service::handle_key_(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct CapturerPara {
|
||||
current_display: usize,
|
||||
use_yuv: bool,
|
||||
timeout_ms: i32,
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
const FPS: u8 = 30;
|
||||
const MIN_FPS: u8 = 10;
|
||||
const MAX_FPS: u8 = 120;
|
||||
pub const FPS: u8 = 30;
|
||||
pub const MIN_FPS: u8 = 10;
|
||||
pub const MAX_FPS: u8 = 120;
|
||||
trait Percent {
|
||||
fn as_percent(&self) -> u32;
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
// https://slhck.info/video/2017/03/01/rate-control.html
|
||||
|
||||
use super::{video_qos::VideoQoS, *};
|
||||
#[cfg(windows)]
|
||||
use crate::portable_service::client::{PortableServiceStatus, PORTABLE_SERVICE_STATUS};
|
||||
use hbb_common::tokio::sync::{
|
||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
Mutex as TokioMutex,
|
||||
@ -49,7 +51,7 @@ pub const SCRAP_X11_REF_URL: &str = "https://rustdesk.com/docs/en/manual/linux/#
|
||||
pub const NAME: &'static str = "video";
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref CURRENT_DISPLAY: Arc<Mutex<usize>> = Arc::new(Mutex::new(usize::MAX));
|
||||
pub static ref CURRENT_DISPLAY: Arc<Mutex<usize>> = Arc::new(Mutex::new(usize::MAX));
|
||||
static ref LAST_ACTIVE: Arc<Mutex<Instant>> = Arc::new(Mutex::new(Instant::now()));
|
||||
static ref SWITCH: Arc<Mutex<bool>> = Default::default();
|
||||
static ref FRAME_FETCHED_NOTIFIER: (UnboundedSender<(i32, Option<Instant>)>, Arc<TokioMutex<UnboundedReceiver<(i32, Option<Instant>)>>>) = {
|
||||
@ -188,6 +190,7 @@ fn create_capturer(
|
||||
privacy_mode_id: i32,
|
||||
display: Display,
|
||||
use_yuv: bool,
|
||||
current: usize,
|
||||
) -> ResultType<Box<dyn TraitCapturer>> {
|
||||
#[cfg(not(windows))]
|
||||
let c: Option<Box<dyn TraitCapturer>> = None;
|
||||
@ -244,17 +247,18 @@ fn create_capturer(
|
||||
}
|
||||
}
|
||||
|
||||
let c = match c {
|
||||
Some(c1) => c1,
|
||||
match c {
|
||||
Some(c1) => return Ok(c1),
|
||||
None => {
|
||||
let c1 =
|
||||
Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?;
|
||||
log::debug!("Create capturer dxgi|gdi");
|
||||
Box::new(c1)
|
||||
#[cfg(windows)]
|
||||
return crate::portable_service::client::create_capturer(current, display, use_yuv);
|
||||
#[cfg(not(windows))]
|
||||
return Ok(Box::new(
|
||||
Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
@ -277,8 +281,8 @@ fn ensure_close_virtual_device() -> ResultType<()> {
|
||||
pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool {
|
||||
let test_begin = Instant::now();
|
||||
while test_begin.elapsed().as_millis() < timeout_millis as _ {
|
||||
if let Ok((_, _, display)) = get_current_display() {
|
||||
if let Ok(_) = create_capturer(privacy_mode_id, display, true) {
|
||||
if let Ok((_, current, display)) = get_current_display() {
|
||||
if let Ok(_) = create_capturer(privacy_mode_id, display, true, current) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -369,7 +373,7 @@ fn get_capturer(use_yuv: bool) -> ResultType<CapturerInfo> {
|
||||
} else {
|
||||
log::info!("In privacy mode, the peer side cannot watch the screen");
|
||||
}
|
||||
let capturer = create_capturer(captuerer_privacy_mode_id, display, use_yuv)?;
|
||||
let capturer = create_capturer(captuerer_privacy_mode_id, display, use_yuv, current)?;
|
||||
Ok(CapturerInfo {
|
||||
origin,
|
||||
width,
|
||||
@ -468,6 +472,11 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
let recorder: Arc<Mutex<Option<Recorder>>> = Default::default();
|
||||
#[cfg(windows)]
|
||||
start_uac_elevation_check();
|
||||
#[cfg(windows)]
|
||||
let portable_service_status = crate::portable_service::client::PORTABLE_SERVICE_STATUS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
let mut would_block_count = 0u32;
|
||||
@ -498,10 +507,17 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
if codec_name != Encoder::current_hw_encoder_name() {
|
||||
bail!("SWITCH");
|
||||
}
|
||||
#[cfg(windows)]
|
||||
if portable_service_status != PORTABLE_SERVICE_STATUS.lock().unwrap().clone() {
|
||||
bail!("SWITCH");
|
||||
}
|
||||
check_privacy_mode_changed(&sp, c.privacy_mode_id)?;
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if crate::platform::windows::desktop_changed() {
|
||||
if crate::platform::windows::desktop_changed()
|
||||
&& PORTABLE_SERVICE_STATUS.lock().unwrap().clone()
|
||||
== PortableServiceStatus::NotStarted
|
||||
{
|
||||
bail!("Desktop changed");
|
||||
}
|
||||
}
|
||||
@ -874,7 +890,7 @@ pub(super) fn get_current_display_2(mut all: Vec<Display>) -> ResultType<(usize,
|
||||
return Ok((n, current, all.remove(current)));
|
||||
}
|
||||
|
||||
fn get_current_display() -> ResultType<(usize, usize, Display)> {
|
||||
pub fn get_current_display() -> ResultType<(usize, usize, Display)> {
|
||||
get_current_display_2(try_get_displays()?)
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ div.permissions {
|
||||
}
|
||||
|
||||
div.permissions > div {
|
||||
size: 48px;
|
||||
size: 42px;
|
||||
background: color(accent);
|
||||
}
|
||||
|
||||
|
14
src/ui/cm.rs
14
src/ui/cm.rs
@ -51,6 +51,10 @@ impl InvokeUiCM for SciterHandler {
|
||||
fn change_language(&self) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn show_elevation(&self, show: bool) {
|
||||
self.call("showElevation", &make_args!(show));
|
||||
}
|
||||
}
|
||||
|
||||
impl SciterHandler {
|
||||
@ -123,6 +127,14 @@ impl SciterConnectionManager {
|
||||
fn t(&self, name: String) -> String {
|
||||
crate::client::translate(name)
|
||||
}
|
||||
|
||||
fn can_elevate(&self) -> bool {
|
||||
crate::ui_cm_interface::can_elevate()
|
||||
}
|
||||
|
||||
fn elevate_portable(&self, id: i32) {
|
||||
crate::ui_cm_interface::elevate_portable(id);
|
||||
}
|
||||
}
|
||||
|
||||
impl sciter::EventHandler for SciterConnectionManager {
|
||||
@ -141,5 +153,7 @@ impl sciter::EventHandler for SciterConnectionManager {
|
||||
fn authorize(i32);
|
||||
fn switch_permission(i32, String, bool);
|
||||
fn send_msg(i32, String);
|
||||
fn can_elevate();
|
||||
fn elevate_portable(i32);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ view.windowFrame = is_osx ? #extended : #solid;
|
||||
var body;
|
||||
var connections = [];
|
||||
var show_chat = false;
|
||||
var show_elevation = true;
|
||||
|
||||
class Body: Reactor.Component
|
||||
{
|
||||
@ -27,6 +28,7 @@ class Body: Reactor.Component
|
||||
};
|
||||
var right_style = show_chat ? "" : "display: none";
|
||||
var disconnected = c.disconnected;
|
||||
var show_elevation_btn = handler.can_elevate() && show_elevation;
|
||||
// below size:* is work around for Linux, it alreayd set in css, but not work, shit sciter
|
||||
return <div .content style="size:*">
|
||||
<div .left-panel>
|
||||
@ -55,9 +57,11 @@ class Body: Reactor.Component
|
||||
{c.port_forward ? <div>Port Forwarding: {c.port_forward}</div> : ""}
|
||||
<div style="size:*"/>
|
||||
<div .buttons>
|
||||
{auth ? "" : <button .button tabindex="-1" #accept>{translate('Accept')}</button>}
|
||||
{auth ? "" : <button .button tabindex="-1" .outline #dismiss>{translate('Dismiss')}</button>}
|
||||
{auth && !disconnected ? <button .button tabindex="-1" #disconnect>{translate('Disconnect')}</button> : ""}
|
||||
{!auth && show_elevation_btn ? <button .button tabindex="-1" #elevate_accept style="width:50;background-color:green;">{translate('Elevate')}</button> : "" }
|
||||
{auth ? "" : <button .button tabindex="-1" style={!auth && show_elevation_btn ? "width:50;" : ""} #accept>{translate('Accept')}</button>}
|
||||
{auth ? "" : <button .button tabindex="-1" style={!auth && show_elevation_btn ? "width:50;" : ""} .outline #dismiss>{translate('Dismiss')}</button>}
|
||||
{auth && !disconnected && show_elevation_btn ? <button .button tabindex="-1" #elevate style="width:50;background-color:green;">{translate('Elevate')}</button> : "" }
|
||||
{auth && !disconnected ? <button .button tabindex="-1" #disconnect style={auth && show_elevation_btn ? "width:50;" : ""} >{translate('Disconnect')}</button> : ""}
|
||||
{auth && disconnected ? <button .button tabindex="-1" #close>{translate('Close')}</button> : ""}
|
||||
</div>
|
||||
{c.is_file_transfer || c.port_forward ? "" : <div .chaticon>{svg_chat}</div>}
|
||||
@ -144,6 +148,32 @@ class Body: Reactor.Component
|
||||
});
|
||||
}
|
||||
|
||||
event click $(button#elevate_accept) {
|
||||
var { cid, connection } = this;
|
||||
checkClickTime(function() {
|
||||
connection.authorized = true;
|
||||
show_elevation = false;
|
||||
body.update();
|
||||
handler.elevate_portable(cid);
|
||||
handler.authorize(cid);
|
||||
self.timer(30ms, function() {
|
||||
view.windowState = View.WINDOW_MINIMIZED;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
event click $(button#elevate) {
|
||||
var { cid, connection } = this;
|
||||
checkClickTime(function() {
|
||||
show_elevation = false;
|
||||
body.update();
|
||||
handler.elevate_portable(cid);
|
||||
self.timer(30ms, function() {
|
||||
view.windowState = View.WINDOW_MINIMIZED;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
event click $(button#dismiss) {
|
||||
var cid = this.cid;
|
||||
checkClickTime(function() {
|
||||
@ -386,6 +416,13 @@ handler.newMessage = function(id, text) {
|
||||
update();
|
||||
}
|
||||
|
||||
handler.showElevation = function(show) {
|
||||
if (show != show_elevation) {
|
||||
show_elevation = show;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
view << event statechange {
|
||||
adjustBorder();
|
||||
}
|
||||
|
@ -85,6 +85,8 @@ pub trait InvokeUiCM: Send + Clone + 'static + Sized {
|
||||
fn change_theme(&self, dark: String);
|
||||
|
||||
fn change_language(&self);
|
||||
|
||||
fn show_elevation(&self, show: bool);
|
||||
}
|
||||
|
||||
impl<T: InvokeUiCM> Deref for ConnectionManager<T> {
|
||||
@ -171,6 +173,10 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
||||
|
||||
self.ui_handler.remove_connection(id, close);
|
||||
}
|
||||
|
||||
fn show_elevation(&self, show: bool) {
|
||||
self.ui_handler.show_elevation(show);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -362,6 +368,9 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
LocalConfig::set_option("lang".to_owned(), lang);
|
||||
self.cm.change_language();
|
||||
}
|
||||
Data::DataPortableService(ipc::DataPortableService::CmShowElevation(show)) => {
|
||||
self.cm.show_elevation(show);
|
||||
}
|
||||
_ => {
|
||||
|
||||
}
|
||||
@ -757,3 +766,28 @@ fn cm_inner_send(id: i32, data: Data) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_elevate() -> bool {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use crate::portable_service::client::{
|
||||
PortableServiceStatus::NotStarted, PORTABLE_SERVICE_STATUS,
|
||||
};
|
||||
return !crate::platform::is_installed()
|
||||
&& PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted;
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn elevate_portable(id: i32) {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let lock = CLIENTS.read().unwrap();
|
||||
if let Some(s) = lock.get(&id) {
|
||||
allow_err!(s.tx.send(ipc::Data::DataPortableService(
|
||||
ipc::DataPortableService::RequestStart
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,7 @@ use hbb_common::password_security;
|
||||
use hbb_common::{
|
||||
allow_err,
|
||||
config::{self, Config, LocalConfig, PeerConfig},
|
||||
directories_next, log,
|
||||
sleep,
|
||||
directories_next, log, sleep,
|
||||
tokio::{self, sync::mpsc, time},
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user