From 06a52e1b540e1502ae088803244f270e6b73400b Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 13 Apr 2023 23:00:24 +0800 Subject: [PATCH] tmp commit Signed-off-by: fufesou --- Cargo.toml | 3 +- src/lib.rs | 5 +- src/platform/windows.rs | 62 ++++-- src/privacy_mode/mod.rs | 43 ++++ src/privacy_mode/privacy_win_idd.rs | 188 ++++++++++++++++++ .../privacy_win_mag.rs} | 15 +- src/server/connection.rs | 16 +- src/server/video_service.rs | 47 +++-- src/ui_cm_interface.rs | 4 +- 9 files changed, 318 insertions(+), 65 deletions(-) create mode 100644 src/privacy_mode/mod.rs create mode 100644 src/privacy_mode/privacy_win_idd.rs rename src/{win_privacy.rs => privacy_mode/privacy_win_mag.rs} (96%) diff --git a/Cargo.toml b/Cargo.toml index 3579501c7..f1c39a64d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ default = ["use_dasp"] hwcodec = ["scrap/hwcodec"] mediacodec = ["scrap/mediacodec"] linux_headless = ["pam", "users"] +virtual_display_driver = ["virtual_display"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -94,7 +95,7 @@ winit = "0.26" winapi = { version = "0.3", features = ["winuser", "wincrypt"] } winreg = "0.10" windows-service = "0.4" -virtual_display = { path = "libs/virtual_display" } +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" diff --git a/src/lib.rs b/src/lib.rs index 15c0ca037..7a7062b27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,5 +66,6 @@ pub mod clipboard_file; #[cfg(all(windows, feature = "with_rc"))] pub mod rc; -#[cfg(target_os = "windows")] -pub mod win_privacy; + +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub mod privacy_mode; diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 383259a32..ab76c8559 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1,7 +1,8 @@ use super::{CursorData, ResultType}; use crate::common::PORTABLE_APPNAME_RUNTIME_ENV_KEY; -use crate::ipc; -use crate::license::*; +#[cfg(feature = "privacy_win_mag")] +use crate::privacy_mode::privacy_win_mag; +use crate::{ipc, license::*, privacy_mode::WIN_MAG_INJECTED_PROCESS_EXE}; use hbb_common::{ allow_err, bail, config::{self, Config}, @@ -9,10 +10,12 @@ use hbb_common::{ message_proto::Resolution, sleep, timeout, tokio, }; +#[cfg(feature = "privacy_win_mag")] +use std::fs; use std::{ collections::HashMap, ffi::OsString, - fs, io, + io, io::prelude::*, mem, os::windows::process::CommandExt, @@ -836,10 +839,11 @@ fn get_default_install_path() -> String { format!("{}\\{}", pf, crate::get_app_name()) } +#[cfg(feature = "privacy_win_mag")] pub fn check_update_broker_process() -> ResultType<()> { // let (_, path, _, _) = get_install_info(); - let process_exe = crate::win_privacy::INJECTED_PROCESS_EXE; - let origin_process_exe = crate::win_privacy::ORIGIN_PROCESS_EXE; + let process_exe = privacy_win_mag::INJECTED_PROCESS_EXE; + let origin_process_exe = privacy_win_mag::ORIGIN_PROCESS_EXE; let exe_file = std::env::current_exe()?; if exe_file.parent().is_none() { @@ -922,17 +926,31 @@ pub fn copy_raw_cmd(src_raw: &str, _raw: &str, _path: &str) -> String { pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String { let main_exe = copy_raw_cmd(src_exe, exe, path); - return format!( - " - {main_exe} - copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\" - \"{src_exe}\" --extract \"{path}\" - ", - main_exe = main_exe, - path = path, - ORIGIN_PROCESS_EXE = crate::win_privacy::ORIGIN_PROCESS_EXE, - broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, - ); + #[cfg(feature = "privacy_win_mag")] + { + format!( + " + {main_exe} + copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\" + \"{src_exe}\" --extract \"{path}\" + ", + main_exe = main_exe, + path = path, + ORIGIN_PROCESS_EXE = privacy_win_mag::ORIGIN_PROCESS_EXE, + broker_exe = privacy_win_mag::INJECTED_PROCESS_EXE, + ) + } + #[cfg(not(feature = "privacy_win_mag"))] + { + format!( + " + {main_exe} + \"{src_exe}\" --extract \"{path}\" + ", + main_exe = main_exe, + path = path, + ) + } } pub fn update_me() -> ResultType<()> { @@ -963,7 +981,7 @@ pub fn update_me() -> ResultType<()> { ", copy_exe = copy_exe_cmd(&src_exe, &exe, &path), copy_dll = copy_dll, - broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, + broker_exe = WIN_MAG_INJECTED_PROCESS_EXE, app_name = crate::get_app_name(), lic = register_licence(), cur_pid = get_current_pid(), @@ -1257,7 +1275,7 @@ fn get_before_uninstall(kill_self: bool) -> String { netsh advfirewall firewall delete rule name=\"{app_name} Service\" ", app_name = app_name, - broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, + broker_exe = WIN_MAG_INJECTED_PROCESS_EXE, ext = ext, filter = filter, ) @@ -2180,6 +2198,14 @@ pub fn get_unicode_from_vk(vk: u32) -> Option { } } +pub fn is_process_consent_running() -> ResultType { + let output = std::process::Command::new("cmd") + .args(&["/C", "tasklist | findstr consent.exe"]) + .creation_flags(CREATE_NO_WINDOW) + .output()?; + Ok(output.status.success() && !output.stdout.is_empty()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/privacy_mode/mod.rs b/src/privacy_mode/mod.rs new file mode 100644 index 000000000..9b21177f8 --- /dev/null +++ b/src/privacy_mode/mod.rs @@ -0,0 +1,43 @@ +use crate::ipc::PrivacyModeState; +use hbb_common::{bail, ResultType}; + +#[cfg(all(windows, feature = "privacy_win_mag"))] +pub mod privacy_win_mag; +pub const WIN_MAG_INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; + +pub const OCCUPIED: &'static str = "Privacy occupied by another one"; +pub const TURN_OFF_OTHER_ID: &'static str = + "Failed to turn off privacy mode that belongs to someone else"; +pub const NO_DISPLAYS: &'static str = "No displays"; + +#[cfg(all(windows, feature = "virtual_display_driver"))] +pub mod privacy_win_idd; + +pub trait PrivacyMode { + fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType; + fn turn_off_privacy(&mut self, conn_id: i32, state: Option) + -> ResultType<()>; + + fn cur_conn_id(&self) -> i32; + + #[inline] + fn check_on_conn_id(&self, conn_id: i32) -> ResultType { + let pre_conn_id = self.cur_conn_id(); + if pre_conn_id == conn_id { + return Ok(true); + } + if pre_conn_id != 0 { + bail!(OCCUPIED); + } + Ok(false) + } + + #[inline] + fn check_off_conn_id(&self, conn_id: i32) -> ResultType<()> { + let pre_conn_id = self.cur_conn_id(); + if pre_conn_id != 0 && conn_id != 0 && pre_conn_id != conn_id { + bail!(TURN_OFF_OTHER_ID) + } + Ok(()) + } +} diff --git a/src/privacy_mode/privacy_win_idd.rs b/src/privacy_mode/privacy_win_idd.rs new file mode 100644 index 000000000..13798bf16 --- /dev/null +++ b/src/privacy_mode/privacy_win_idd.rs @@ -0,0 +1,188 @@ +use super::{PrivacyModeState, NO_DISPLAYS}; +use hbb_common::{allow_err, bail, lazy_static, log, ResultType}; +use scrap::dxgi; +use winapi::{ + shared::{ + minwindef::{DWORD, FALSE}, + ntdef::{NULL, WCHAR}, + }, + um::{ + errhandlingapi::GetLastError, + wingdi::{DEVMODEW, DISPLAY_DEVICEW, DISPLAY_DEVICE_ATTACHED_TO_DESKTOP, DM_POSITION}, + winuser::{ + ChangeDisplaySettingsExW, EnumDisplayDevicesW, EnumDisplaySettingsW, CDS_NORESET, + CDS_SET_PRIMARY, CDS_UPDATEREGISTRY, DISP_CHANGE_SUCCESSFUL, + EDD_GET_DEVICE_INTERFACE_NAME, ENUM_CURRENT_SETTINGS, + }, + }, +}; + +const IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device"; + +struct Display { + dm: DEVMODEW, + name: [WCHAR; 32], + primary: bool, +} + +pub struct PrivacyModeImpl { + conn_id: i32, + displays: Vec, + virtual_displays: Vec, + virtual_displays_added: Option, +} + +impl PrivacyModeImpl { + fn set_displays(&mut self) { + self.displays.clear(); + self.virtual_displays.clear(); + for display in dxgi::Displays::get_from_gdi().into_iter() { + if let Some(gdi_info) = display.gdi() { + if let Ok(s) = std::string::String::from_utf16(&gdi_info.dd.DeviceString) { + if s == IDD_DEVICE_STRING { + self.virtual_displays.push(Display { + dm: gdi_info.dm, + name: gdi_info.dd.DeviceName, + primary: gdi_info.is_primary, + }); + continue; + } + } + self.displays.push(Display { + dm: gdi_info.dm, + name: gdi_info.dd.DeviceName, + primary: gdi_info.is_primary, + }); + } + } + } + + fn restore(&self) {} + + fn set_primary_display(&mut self) -> ResultType<()> { + self.ensure_virtual_display()?; + if self.virtual_displays.is_empty() { + bail!("No virtual displays"); + } + let display = &self.virtual_displays[0]; + + let mut new_primary_dm: DEVMODEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; + new_primary_dm.dmSize = std::mem::size_of::() as _; + new_primary_dm.dmDriverExtra = 0; + unsafe { + if FALSE + == EnumDisplaySettingsW( + display.name.as_ptr(), + ENUM_CURRENT_SETTINGS, + &mut new_primary_dm, + ) + { + bail!( + "Failed EnumDisplaySettingsW, device name: {:?}, error code: {}", + std::string::String::from_utf16(&display.name), + GetLastError() + ); + } + + let mut i: DWORD = 0; + loop { + let mut flags = CDS_UPDATEREGISTRY | CDS_NORESET; + let mut dd: DISPLAY_DEVICEW = std::mem::MaybeUninit::uninit().assume_init(); + dd.cb = std::mem::size_of::() as _; + if FALSE + == EnumDisplayDevicesW(NULL as _, i, &mut dd, EDD_GET_DEVICE_INTERFACE_NAME) + { + break; + } + i += 1; + if (dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0 { + continue; + } + + if dd.DeviceName == display.name { + flags |= CDS_SET_PRIMARY; + } + + let mut dm: DEVMODEW = std::mem::MaybeUninit::uninit().assume_init(); + dm.dmSize = std::mem::size_of::() as _; + dm.dmDriverExtra = 0; + if FALSE + == EnumDisplaySettingsW(dd.DeviceName.as_ptr(), ENUM_CURRENT_SETTINGS, &mut dm) + { + bail!( + "Failed EnumDisplaySettingsW, device name: {:?}, error code: {}", + std::string::String::from_utf16(&dd.DeviceName), + GetLastError() + ); + } + + dm.u1.s2_mut().dmPosition.x -= new_primary_dm.u1.s2().dmPosition.x; + dm.u1.s2_mut().dmPosition.y -= new_primary_dm.u1.s2().dmPosition.y; + dm.dmFields |= DM_POSITION; + let rc = ChangeDisplaySettingsExW( + dd.DeviceName.as_ptr(), + &mut dm, + NULL as _, + flags, + NULL, + ); + + if rc != DISP_CHANGE_SUCCESSFUL { + log::error!( + "Failed ChangeDisplaySettingsEx, device name: {:?}, flags: {}, ret: {}", + std::string::String::from_utf16(&dd.DeviceName), + flags, + rc + ); + bail!("Failed ChangeDisplaySettingsEx, ret: {}", rc); + } + } + } + + Ok(()) + } + + fn ensure_virtual_display(&mut self) -> ResultType<()> { + if self.virtual_displays.is_empty() { + virtual_display::create_device()?; + } + if virtual_display::is_device_created() { + // to-do: add monitor index here + virtual_display::plug_in_monitor()?; + self.virtual_displays_added = Some(0); + } + + self.set_displays(); + Ok(()) + } +} + +impl super::PrivacyMode for PrivacyModeImpl { + fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType { + if self.check_on_conn_id(conn_id)? { + return Ok(true); + } + self.set_displays(); + if self.displays.is_empty() { + bail!(NO_DISPLAYS); + } + self.set_primary_display()?; + + bail!("unimplemented") + } + + fn turn_off_privacy( + &mut self, + conn_id: i32, + state: Option, + ) -> ResultType<()> { + self.check_off_conn_id(conn_id)?; + self.restore(); + bail!("unimplemented") + } + + #[inline] + fn cur_conn_id(&self) -> i32 { + self.conn_id + } +} diff --git a/src/win_privacy.rs b/src/privacy_mode/privacy_win_mag.rs similarity index 96% rename from src/win_privacy.rs rename to src/privacy_mode/privacy_win_mag.rs index 9944bf262..923c42859 100644 --- a/src/win_privacy.rs +++ b/src/privacy_mode/privacy_win_mag.rs @@ -5,7 +5,6 @@ use crate::{ use hbb_common::{allow_err, bail, lazy_static, log, tokio, ResultType}; use std::{ ffi::CString, - os::windows::process::CommandExt, sync::Mutex, time::{Duration, Instant}, }; @@ -25,16 +24,14 @@ use winapi::{ CreateProcessAsUserW, GetCurrentThreadId, QueueUserAPC, ResumeThread, PROCESS_INFORMATION, STARTUPINFOW, }, - winbase::{ - WTSGetActiveConsoleSessionId, CREATE_NO_WINDOW, CREATE_SUSPENDED, DETACHED_PROCESS, - }, + winbase::{WTSGetActiveConsoleSessionId, CREATE_SUSPENDED, DETACHED_PROCESS}, winnt::{MEM_COMMIT, PAGE_READWRITE}, winuser::*, }, }; pub const ORIGIN_PROCESS_EXE: &'static str = "C:\\Windows\\System32\\RuntimeBroker.exe"; -pub const INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; +pub const INJECTED_PROCESS_EXE: &'static str = super::WIN_MAG_INJECTED_PROCESS_EXE; pub const PRIVACY_WINDOW_NAME: &'static str = "RustDeskPrivacyWindow"; pub const GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT: u32 = 2; @@ -317,14 +314,6 @@ fn wait_find_privacy_hwnd(msecs: u128) -> ResultType { } } -pub fn is_process_consent_running() -> ResultType { - let output = std::process::Command::new("cmd") - .args(&["/C", "tasklist | findstr consent.exe"]) - .creation_flags(CREATE_NO_WINDOW) - .output()?; - Ok(output.status.success() && !output.stdout.is_empty()) -} - #[tokio::main(flavor = "current_thread")] async fn set_privacy_mode_state( conn_id: i32, diff --git a/src/server/connection.rs b/src/server/connection.rs index 8a6ba99b9..25dea58ad 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2346,13 +2346,13 @@ fn try_activate_screen() { mod privacy_mode { use super::*; + #[cfg(all(windows, feature = "privacy_win_mag"))] + use crate::privacy_mode::privacy_win_mag; pub(super) fn turn_off_privacy(_conn_id: i32) -> Message { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] { - use crate::win_privacy::*; - - let res = turn_off_privacy(_conn_id, None); + let res = privacy_win_mag::turn_off_privacy(_conn_id, None); match res { Ok(_) => crate::common::make_privacy_mode_msg( back_notification::PrivacyModeState::PrvOffSucceeded, @@ -2365,19 +2365,19 @@ mod privacy_mode { } } } - #[cfg(not(windows))] + #[cfg(not(all(windows, feature = "privacy_win_mag")))] { crate::common::make_privacy_mode_msg(back_notification::PrivacyModeState::PrvOffFailed) } } pub(super) fn turn_on_privacy(_conn_id: i32) -> ResultType { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] { - let plugin_exist = crate::win_privacy::turn_on_privacy(_conn_id)?; + let plugin_exist = privacy_win_mag::turn_on_privacy(_conn_id)?; Ok(plugin_exist) } - #[cfg(not(windows))] + #[cfg(not(all(windows, feature = "privacy_win_mag")))] { Ok(true) } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 4abeafff5..e6a4e3806 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -20,6 +20,10 @@ use super::{video_qos::VideoQoS, *}; #[cfg(windows)] +use crate::platform::windows::is_process_consent_running; +#[cfg(all(windows, feature = "privacy_win_mag"))] +use crate::privacy_mode::privacy_win_mag; +#[cfg(all(windows, feature = "privacy_win_mag"))] use hbb_common::get_version_number; use hbb_common::tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, @@ -41,7 +45,7 @@ use std::{ ops::{Deref, DerefMut}, time::{self, Duration, Instant}, }; -#[cfg(windows)] +#[cfg(all(windows, feature = "virtual_display_driver"))] use virtual_display; pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version."; @@ -69,9 +73,9 @@ lazy_static::lazy_static! { } fn is_capturer_mag_supported() -> bool { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] return scrap::CapturerMag::is_supported(); - #[cfg(not(windows))] + #[cfg(not(all(windows, feature = "privacy_win_mag")))] false } @@ -92,10 +96,10 @@ pub fn get_privacy_mode_conn_id() -> i32 { } pub fn is_privacy_mode_supported() -> bool { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] return *IS_CAPTURER_MAGNIFIER_SUPPORTED && get_version_number(&crate::VERSION) > get_version_number("1.1.9"); - #[cfg(not(windows))] + #[cfg(not(all(windows, feature = "privacy_win_mag")))] return false; } @@ -201,15 +205,13 @@ fn create_capturer( _current: usize, _portable_service_running: bool, ) -> ResultType> { - #[cfg(not(windows))] + #[cfg(not(all(windows, feature = "privacy_win_mag")))] let c: Option> = None; - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] let mut c: Option> = None; if privacy_mode_id > 0 { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] { - use crate::win_privacy::*; - match scrap::CapturerMag::new( display.origin(), display.width(), @@ -220,7 +222,7 @@ fn create_capturer( let mut ok = false; let check_begin = Instant::now(); while check_begin.elapsed().as_secs() < 5 { - match c1.exclude("", PRIVACY_WINDOW_NAME) { + match c1.exclude("", privacy_win_mag::PRIVACY_WINDOW_NAME) { Ok(false) => { ok = false; std::thread::sleep(std::time::Duration::from_millis(500)); @@ -229,7 +231,7 @@ fn create_capturer( bail!( "Failed to exclude privacy window {} - {}, err: {}", "", - PRIVACY_WINDOW_NAME, + privacy_win_mag::PRIVACY_WINDOW_NAME, e ); } @@ -243,7 +245,7 @@ fn create_capturer( bail!( "Failed to exclude privacy window {} - {} ", "", - PRIVACY_WINDOW_NAME + privacy_win_mag::PRIVACY_WINDOW_NAME ); } log::debug!("Create magnifier capture for {}", privacy_mode_id); @@ -275,7 +277,8 @@ fn create_capturer( }; } -#[cfg(windows)] +// to-do: do not close if in privacy mode. +#[cfg(all(windows, feature = "virtual_display_driver"))] fn ensure_close_virtual_device() -> ResultType<()> { let num_displays = Display::all()?.len(); if num_displays == 0 { @@ -309,11 +312,11 @@ pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool { fn check_uac_switch(privacy_mode_id: i32, capturer_privacy_mode_id: i32) -> ResultType<()> { if capturer_privacy_mode_id != 0 { if privacy_mode_id != capturer_privacy_mode_id { - if !crate::win_privacy::is_process_consent_running()? { + if !is_process_consent_running()? { bail!("consent.exe is running"); } } - if crate::win_privacy::is_process_consent_running()? { + if is_process_consent_running()? { bail!("consent.exe is running"); } } @@ -374,7 +377,7 @@ fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType Option { } fn run(sp: GenericService) -> ResultType<()> { - #[cfg(windows)] + #[cfg(all(windows, feature = "virtual_display_driver"))] ensure_close_virtual_device()?; // ensure_inited() is needed because release_resource() may be called. @@ -922,12 +925,12 @@ pub async fn switch_to_primary() { } #[inline] -#[cfg(not(windows))] +#[cfg(not(all(windows, feature = "virtual_display_driver")))] fn try_get_displays() -> ResultType> { Ok(Display::all()?) } -#[cfg(windows)] +#[cfg(all(windows, feature = "virtual_display_driver"))] fn try_get_displays() -> ResultType> { let mut displays = Display::all()?; if displays.len() == 0 { @@ -949,6 +952,8 @@ fn try_get_displays() -> ResultType> { } displays = Display::all()?; } else if displays.len() > 1 { + // to-do: do not close if in privacy mode. + // If more than one displays exists, close RustDeskVirtualDisplay if virtual_display::is_device_created() { virtual_display::close_device() @@ -991,7 +996,7 @@ fn start_uac_elevation_check() { if !crate::platform::is_installed() && !crate::platform::is_root() { std::thread::spawn(|| loop { std::thread::sleep(std::time::Duration::from_secs(1)); - if let Ok(uac) = crate::win_privacy::is_process_consent_running() { + if let Ok(uac) = is_process_consent_running() { *IS_UAC_RUNNING.lock().unwrap() = uac; } if !crate::platform::is_elevated(None).unwrap_or(false) { diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 8612b5751..b738bd4ac 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -495,7 +495,7 @@ impl IpcTaskRunner { #[cfg(not(any(target_os = "android", target_os = "ios")))] #[tokio::main(flavor = "current_thread")] pub async fn start_ipc(cm: ConnectionManager) { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] std::thread::spawn(move || { log::info!("try create privacy mode window"); if let Err(e) = crate::platform::windows::check_update_broker_process() { @@ -504,7 +504,7 @@ pub async fn start_ipc(cm: ConnectionManager) { e ); } - allow_err!(crate::win_privacy::start()); + allow_err!(crate::privacy_mode::privacy_win_mag::start()); }); match ipc::new_listener("_cm").await {