diff --git a/Cargo.lock b/Cargo.lock index 712d8e6c7..888115066 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,8 +124,7 @@ checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android-wakelock" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "296e5b7c23adb32743194b1810604b772f2be10f0b0387365cb3ba09cd5c1851" +source = "git+https://github.com/21pages/android-wakelock#d0292e5a367e627c4fa6f1ca6bdfad005dca7d90" dependencies = [ "jni 0.21.1", "log", diff --git a/Cargo.toml b/Cargo.toml index 354f443ad..c41d0744b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,7 @@ cidr-utils = "0.5" libloading = "0.8" fon = "0.6" zip = "0.6" +shutdown_hooks = "0.1" [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] cpal = "0.15" @@ -95,7 +96,6 @@ clipboard = { path = "libs/clipboard" } ctrlc = "3.2" arboard = "3.2" system_shutdown = "4.0" -shutdown_hooks = "0.1" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winuser", "wincrypt", "shellscalingapi", "pdh", "synchapi", "memoryapi"] } @@ -147,7 +147,7 @@ once_cell = {version = "1.18", optional = true} [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.13" jni = "0.21" -android-wakelock = "0.1" +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"] diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index f6844e837..7430e71c1 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -76,10 +76,10 @@ class _PeerTabPageState extends State final uiType = bind.getLocalFlutterOption(k: 'peer-card-ui-type'); if (uiType != '') { peerCardUiType.value = int.parse(uiType) == 0 - ? PeerUiType.grid - : int.parse(uiType) == 1 - ? PeerUiType.tile - : PeerUiType.list; + ? PeerUiType.grid + : int.parse(uiType) == 1 + ? PeerUiType.tile + : PeerUiType.list; } hideAbTagsPanel.value = bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty; @@ -624,8 +624,6 @@ class _PeerTabPageState extends State 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) { @@ -767,7 +765,11 @@ class PeerViewDropdown extends StatefulWidget { class _PeerViewDropdownState extends State { @override Widget build(BuildContext context) { - final List types = [PeerUiType.grid, PeerUiType.tile, PeerUiType.list]; + final List types = [ + PeerUiType.grid, + PeerUiType.tile, + PeerUiType.list + ]; final style = TextStyle( color: Theme.of(context).textTheme.titleLarge?.color, fontSize: MenuConfig.fontSize, @@ -777,20 +779,23 @@ class _PeerViewDropdownState extends State { height: 36, enabled: false, child: Text(translate("Change view"), style: style))); - for (var e in PeerUiType.values) { + for (var e in PeerUiType.values) { items.add(PopupMenuItem( height: 36, child: Obx(() => Center( child: SizedBox( height: 36, child: getRadio( - Text(translate( - types.indexOf(e) == 0 ? 'Big tiles' : types.indexOf(e) == 1 ? 'Small tiles' : 'List' - ), style: style), - e, + 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 { + dense: true, (PeerUiType? v) async { if (v != null) { peerCardUiType.value = v; setState(() {}); @@ -798,18 +803,18 @@ class _PeerViewDropdownState extends State { 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( + context: context, + child: Tooltip( + message: translate('Change view'), + child: Icon( peerCardUiType.value == PeerUiType.grid ? Icons.grid_view_rounded : peerCardUiType.value == PeerUiType.tile @@ -817,22 +822,20 @@ class _PeerViewDropdownState extends State { : 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, - ) - ); + 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}); diff --git a/libs/scrap/src/android/ffi.rs b/libs/scrap/src/android/ffi.rs index cba492920..e4a8877de 100644 --- a/libs/scrap/src/android/ffi.rs +++ b/libs/scrap/src/android/ffi.rs @@ -260,6 +260,7 @@ fn init_ndk_context() -> JniResult<()> { unsafe { ndk_context::release_android_context(); } + *lock = false; } if let (Some(jvm), Some(ctx)) = ( JVM.read().unwrap().as_ref(), diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 864a03cb7..70a2364fd 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -310,7 +310,7 @@ impl RecorderApi for WebmRecorder { impl Drop for WebmRecorder { fn drop(&mut self) { - std::mem::replace(&mut self.webm, None).map_or(false, |webm| webm.finalize(None)); + let _ = std::mem::replace(&mut self.webm, None).map_or(false, |webm| webm.finalize(None)); let mut state = RecordState::WriteTail; if !self.written || self.start.elapsed().as_secs() < MIN_SECS { std::fs::remove_file(&self.ctx.filename).ok(); diff --git a/src/platform/mod.rs b/src/platform/mod.rs index f75e14242..3f81c87fd 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -82,35 +82,28 @@ pub const PA_SAMPLE_RATE: u32 = 48000; #[cfg(target_os = "android")] #[derive(Default)] -pub struct WakeLock { - lock: Option, -} +pub struct WakeLock(Option); #[cfg(target_os = "android")] impl WakeLock { pub fn new(tag: &str) -> Self { let tag = format!("{}:{tag}", crate::get_app_name()); match android_wakelock::partial(tag) { - Ok(lock) => Self { lock: Some(lock) }, + Ok(lock) => Self(Some(lock)), Err(e) => { hbb_common::log::error!("Failed to get wakelock: {e:?}"); Self::default() } } } +} - pub fn acquire(&self) -> Option { - match self.lock.as_ref() { - Some(lock) => match lock.acquire() { - Ok(guard) => Some(guard), - Err(e) => { - hbb_common::log::error!("Failed to acquire wakelock guard: {e:?}"); - None - } - }, - None => None, - } - } +pub fn get_wake_lock(_display: bool) -> WakeLock { + hbb_common::log::info!("new wakelock, require display on: {_display}"); + #[cfg(target_os = "android")] + return crate::platform::WakeLock::new("server"); + #[cfg(not(target_os = "android"))] + return crate::platform::WakeLock::new(_display, true, true); } pub(crate) struct InstallingService; // please use new diff --git a/src/server/connection.rs b/src/server/connection.rs index 72a757f74..e7611332b 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -70,6 +70,7 @@ lazy_static::lazy_static! { static ref ALIVE_CONNS: Arc::>> = Default::default(); static ref AUTHED_CONNS: Arc::>> = Default::default(); static ref SWITCH_SIDES_UUID: Arc::>> = Default::default(); + static ref WAKE_LOCK: Arc::>> = Default::default(); } #[cfg(any(target_os = "windows", target_os = "linux"))] @@ -1248,8 +1249,6 @@ impl Connection { } fn on_remote_authorized(&self) { - use std::sync::Once; - static _ONCE: Once = Once::new(); self.update_codec_on_login(); #[cfg(any(target_os = "windows", target_os = "linux"))] if !Config::get_option("allow-remove-wallpaper").is_empty() { @@ -1259,9 +1258,6 @@ impl Connection { match crate::platform::WallPaperRemover::new() { Ok(remover) => { *wallpaper = Some(remover); - _ONCE.call_once(|| { - shutdown_hooks::add_shutdown_hook(shutdown_hook); - }); } Err(e) => { log::info!("create wallpaper remover failed: {:?}", e); @@ -2372,7 +2368,8 @@ impl Connection { if t.on { if !virtual_display_manager::is_virtual_display_supported() { - self.send(make_msg("idd_not_support_under_win10_2004_tip".to_string())).await; + self.send(make_msg("idd_not_support_under_win10_2004_tip".to_string())) + .await; } else { if let Err(e) = virtual_display_manager::plug_in_index_modes(t.display as _, Vec::new()) @@ -3230,9 +3227,14 @@ impl LinuxHeadlessHandle { } } -#[cfg(any(target_os = "windows", target_os = "linux"))] -extern "C" fn shutdown_hook() { - *WALLPAPER_REMOVER.lock().unwrap() = None; +extern "C" fn connection_shutdown_hook() { + // https://stackoverflow.com/questions/35980148/why-does-an-atexit-handler-panic-when-it-accesses-stdout + // Please make sure there is no print in the call stack + *WAKE_LOCK.lock().unwrap() = None; + #[cfg(any(target_os = "windows", target_os = "linux"))] + { + *WALLPAPER_REMOVER.lock().unwrap() = None; + } } mod raii { @@ -3262,8 +3264,38 @@ mod raii { impl AuthedConnID { pub fn new(id: i32, conn_type: AuthConnType) -> Self { AUTHED_CONNS.lock().unwrap().push((id, conn_type)); + Self::check_wake_lock(); + use std::sync::Once; + static _ONCE: Once = Once::new(); + _ONCE.call_once(|| { + shutdown_hooks::add_shutdown_hook(connection_shutdown_hook); + }); Self(id, conn_type) } + + fn check_wake_lock() { + let mut wake_lock = WAKE_LOCK.lock().unwrap(); + let remote_count = AUTHED_CONNS + .lock() + .unwrap() + .iter() + .filter(|c| c.1 == AuthConnType::Remote) + .count(); + let display = remote_count > 0; + if let Some((_, last_display)) = *wake_lock { + if last_display != display { + *wake_lock = None; + } + } + let empty = AUTHED_CONNS.lock().unwrap().is_empty(); + if empty { + *wake_lock = None; + } else { + if wake_lock.is_none() { + *wake_lock = Some((crate::platform::get_wake_lock(display), display)); + } + } + } } impl Drop for AuthedConnID { @@ -3271,9 +3303,14 @@ mod raii { if self.1 == AuthConnType::Remote { scrap::codec::Encoder::update(self.0, scrap::codec::EncodingUpdate::Remove); } - let mut lock = AUTHED_CONNS.lock().unwrap(); - lock.retain(|&c| c.0 != self.0); - if lock.iter().filter(|c| c.1 == AuthConnType::Remote).count() == 0 { + AUTHED_CONNS.lock().unwrap().retain(|&c| c.0 != self.0); + let remote_count = AUTHED_CONNS + .lock() + .unwrap() + .iter() + .filter(|c| c.1 == AuthConnType::Remote) + .count(); + if remote_count == 0 { #[cfg(any(target_os = "windows", target_os = "linux"))] { *WALLPAPER_REMOVER.lock().unwrap() = None; @@ -3283,6 +3320,7 @@ mod raii { #[cfg(all(windows, feature = "virtual_display_driver"))] let _ = virtual_display_manager::reset_all(); } + Self::check_wake_lock(); } } } diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index d33fc16ef..ddca2e774 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -618,10 +618,11 @@ pub mod client { } pub extern "C" fn drop_portable_service_shared_memory() { + // https://stackoverflow.com/questions/35980148/why-does-an-atexit-handler-panic-when-it-accesses-stdout + // Please make sure there is no print in the call stack let mut lock = SHMEM.lock().unwrap(); if lock.is_some() { *lock = None; - log::info!("drop shared memory"); } } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 8834935b4..cb1eea3ee 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -363,13 +363,6 @@ fn get_capturer(current: usize, portable_service_running: bool) -> ResultType ResultType<()> { - #[cfg(not(target_os = "android"))] - let _wake_lock = get_wake_lock(); - #[cfg(target_os = "android")] - let wake_lock = crate::platform::WakeLock::new("video service"); - #[cfg(target_os = "android")] - let _lock_guard = wake_lock.acquire(); - // Wayland only support one video capturer for now. It is ok to call ensure_inited() here. // // ensure_inited() is needed because clear() may be called. @@ -735,19 +728,6 @@ fn start_uac_elevation_check() { }); } -#[cfg(not(target_os = "android"))] -fn get_wake_lock() -> crate::platform::WakeLock { - let (display, idle, sleep) = if cfg!(windows) { - (true, false, false) - } else if cfg!(linux) { - (false, false, true) - } else { - //macos - (true, false, false) - }; - crate::platform::WakeLock::new(display, idle, sleep) -} - #[inline] fn try_broadcast_display_changed( sp: &GenericService,