Merge remote-tracking branch 'github/master' into sigma

# Conflicts:
#	flutter/lib/desktop/widgets/remote_menubar.dart
This commit is contained in:
sjpark
2023-02-25 11:37:12 +09:00
238 changed files with 8183 additions and 4234 deletions

View File

@@ -100,7 +100,7 @@ impl SciterConnectionManager {
}
fn get_icon(&mut self) -> String {
crate::get_icon()
super::get_icon()
}
fn check_click_time(&mut self, id: i32) {

View File

@@ -435,9 +435,6 @@ function toggleMenuState() {
var c = handler.get_option("codec-preference");
if (!c) c = "auto";
values.push(c);
var a = handler.get_audio_mode();
if (!a) a = "guest-to-host";
values.push(a);
for (var el in $$(menu#display-options li)) {
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
}
@@ -484,6 +481,14 @@ handler.updatePi = function(v) {
}
}
handler.updateDisplays = function(v) {
pi.displays = v;
header.update();
if (is_port_forward) {
view.windowState = View.WINDOW_MINIMIZED;
}
}
function updatePrivacyMode() {
var el = $(li#privacy-mode);
if (el) {

View File

@@ -6,15 +6,15 @@ use cocoa::{
base::{id, nil, YES},
foundation::{NSAutoreleasePool, NSString},
};
use objc::runtime::Class;
use objc::{
class,
declare::ClassDecl,
msg_send,
runtime::{BOOL, Object, Sel},
runtime::{Object, Sel, BOOL},
sel, sel_impl,
};
use objc::runtime::Class;
use sciter::{Host, make_args};
use sciter::{make_args, Host};
use hbb_common::log;
@@ -102,7 +102,10 @@ unsafe fn set_delegate(handler: Option<Box<dyn AppHandler>>) {
sel!(handleMenuItem:),
handle_menu_item as extern "C" fn(&mut Object, Sel, id),
);
decl.add_method(sel!(handleEvent:withReplyEvent:), handle_apple_event as extern fn(&Object, Sel, u64, u64));
decl.add_method(
sel!(handleEvent:withReplyEvent:),
handle_apple_event as extern "C" fn(&Object, Sel, u64, u64),
);
let decl = decl.register();
let delegate: id = msg_send![decl, alloc];
let () = msg_send![delegate, init];
@@ -138,10 +141,7 @@ extern "C" fn application_should_handle_open_untitled_file(
if !LAUNCHED {
return YES;
}
log::debug!("icon clicked on finder");
if std::env::args().nth(1) == Some("--server".to_owned()) {
crate::platform::macos::check_main_window();
}
crate::platform::macos::handle_application_should_open_untitled_file();
let inner: *mut c_void = *this.get_ivar(APP_HANDLER_IVAR);
let inner = &mut *(inner as *mut DelegateState);
(*inner).command(AWAKE);
@@ -180,22 +180,11 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
}
}
/// The function to handle the url scheme sent by the system.
///
/// 1. Try to send the url scheme from ipc.
/// 2. If failed to send the url scheme, we open a new main window to handle this url scheme.
pub fn handle_url_scheme(url: String) {
if let Err(err) = crate::ipc::send_url_scheme(url.clone()) {
log::debug!("Send the url to the existing flutter process failed, {}. Let's open a new program to handle this.", err);
let _ = crate::run_me(vec![url]);
}
}
extern fn handle_apple_event(_this: &Object, _cmd: Sel, event: u64, _reply: u64) {
extern "C" fn handle_apple_event(_this: &Object, _cmd: Sel, event: u64, _reply: u64) {
let event = event as *mut Object;
let url = fruitbasket::parse_url_event(event);
log::debug!("an event was received: {}", url);
std::thread::spawn(move || handle_url_scheme(url));
std::thread::spawn(move || crate::handle_url_scheme(url));
}
unsafe fn make_menu_item(title: &str, key: &str, tag: u32) -> *mut Object {
@@ -258,10 +247,3 @@ pub fn show_dock() {
NSApp().setActivationPolicy_(NSApplicationActivationPolicyRegular);
}
}
pub fn make_tray() {
unsafe {
set_delegate(None);
}
crate::tray::make_tray();
}

View File

@@ -1,17 +1,17 @@
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
sync::{Arc, Mutex},
sync::{Arc, Mutex, RwLock},
};
use sciter::{
dom::{
Element,
event::{BEHAVIOR_EVENTS, EVENT_GROUPS, EventReason, PHASE_MASK}, HELEMENT,
event::{EventReason, BEHAVIOR_EVENTS, EVENT_GROUPS, PHASE_MASK},
Element, HELEMENT,
},
make_args,
video::{video_destination, AssetPtr, COLOR_SPACE},
Value,
video::{AssetPtr, COLOR_SPACE, video_destination},
};
use hbb_common::{
@@ -53,6 +53,20 @@ impl SciterHandler {
allow_err!(e.call_method(func, &super::value_crash_workaround(args)[..]));
}
}
fn make_displays_array(displays: &Vec<DisplayInfo>) -> Value {
let mut displays_value = Value::array(0);
for d in displays.iter() {
let mut display = Value::map();
display.set_item("x", d.x);
display.set_item("y", d.y);
display.set_item("width", d.width);
display.set_item("height", d.height);
display.set_item("cursor_embedded", d.cursor_embedded);
displays_value.push(display);
}
displays_value
}
}
impl InvokeUiSession for SciterHandler {
@@ -201,7 +215,7 @@ impl InvokeUiSession for SciterHandler {
self.call("adaptSize", &make_args!());
}
fn on_rgba(&self, data: &[u8]) {
fn on_rgba(&self, data: &mut Vec<u8>) {
VIDEO
.lock()
.unwrap()
@@ -215,22 +229,18 @@ impl InvokeUiSession for SciterHandler {
pi_sciter.set_item("hostname", pi.hostname.clone());
pi_sciter.set_item("platform", pi.platform.clone());
pi_sciter.set_item("sas_enabled", pi.sas_enabled);
let mut displays = Value::array(0);
for ref d in pi.displays.iter() {
let mut display = Value::map();
display.set_item("x", d.x);
display.set_item("y", d.y);
display.set_item("width", d.width);
display.set_item("height", d.height);
display.set_item("cursor_embedded", d.cursor_embedded);
displays.push(display);
}
pi_sciter.set_item("displays", displays);
pi_sciter.set_item("displays", Self::make_displays_array(&pi.displays));
pi_sciter.set_item("current_display", pi.current_display);
self.call("updatePi", &make_args!(pi_sciter));
}
fn set_displays(&self, displays: &Vec<DisplayInfo>) {
self.call(
"updateDisplays",
&make_args!(Self::make_displays_array(displays)),
);
}
fn on_connected(&self, conn_type: ConnType) {
match conn_type {
ConnType::RDP => {}
@@ -282,6 +292,13 @@ impl InvokeUiSession for SciterHandler {
fn on_voice_call_incoming(&self) {
self.call("onVoiceCallIncoming", &make_args!());
}
/// RGBA is directly rendered by [on_rgba]. No need to store the rgba for the sciter ui.
fn get_rgba(&self) -> *const u8 {
std::ptr::null()
}
fn next_rgba(&self) {}
}
pub struct SciterSession(Session<SciterHandler>);
@@ -339,7 +356,7 @@ impl sciter::EventHandler for SciterSession {
let site = AssetPtr::adopt(ptr as *mut video_destination);
log::debug!("[video] start video");
*VIDEO.lock().unwrap() = Some(site);
self.reconnect();
self.reconnect(false);
}
}
BEHAVIOR_EVENTS::VIDEO_INITIALIZED => {
@@ -388,7 +405,7 @@ impl sciter::EventHandler for SciterSession {
fn transfer_file();
fn tunnel();
fn lock_screen();
fn reconnect();
fn reconnect(bool);
fn get_chatbox();
fn get_icon();
fn get_home_dir();
@@ -447,6 +464,9 @@ impl SciterSession {
id: id.clone(),
password: password.clone(),
args,
server_keyboard_enabled: Arc::new(RwLock::new(true)),
server_file_transfer_enabled: Arc::new(RwLock::new(true)),
server_clipboard_enabled: Arc::new(RwLock::new(true)),
..Default::default()
};
@@ -460,7 +480,11 @@ impl SciterSession {
ConnType::DEFAULT_CONN
};
session.lc.write().unwrap().initialize(id, conn_type, None);
session
.lc
.write()
.unwrap()
.initialize(id, conn_type, None, false);
Self(session)
}
@@ -486,7 +510,7 @@ impl SciterSession {
}
pub fn get_icon(&self) -> String {
crate::get_icon()
super::get_icon()
}
fn supported_hwcodec(&self) -> Value {

View File

@@ -1,575 +0,0 @@
use crate::{
ipc::{connect, Data, PrivacyModeState},
platform::windows::get_user_token,
};
use hbb_common::{allow_err, bail, lazy_static, log, tokio, ResultType};
use std::{
ffi::CString,
os::windows::process::CommandExt,
sync::Mutex,
time::{Duration, Instant},
};
use winapi::{
ctypes::c_int,
shared::{
minwindef::{DWORD, FALSE, HMODULE, LOBYTE, LPARAM, LRESULT, UINT, WPARAM},
ntdef::{HANDLE, NULL},
windef::{HHOOK, HWND, POINT},
},
um::{
errhandlingapi::GetLastError,
handleapi::CloseHandle,
libloaderapi::{GetModuleHandleA, GetModuleHandleExA, GetProcAddress},
memoryapi::{VirtualAllocEx, WriteProcessMemory},
processthreadsapi::{
CreateProcessAsUserW, GetCurrentThreadId, QueueUserAPC, ResumeThread,
PROCESS_INFORMATION, STARTUPINFOW,
},
winbase::{
WTSGetActiveConsoleSessionId, CREATE_NO_WINDOW, 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 PRIVACY_WINDOW_NAME: &'static str = "RustDeskPrivacyWindow";
pub const GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT: u32 = 2;
pub const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 4;
const WM_USER_EXIT_HOOK: u32 = WM_USER + 1;
lazy_static::lazy_static! {
static ref DLL_FOUND: Mutex<bool> = Mutex::new(false);
static ref CONN_ID: Mutex<i32> = Mutex::new(0);
static ref CUR_HOOK_THREAD_ID: Mutex<DWORD> = Mutex::new(0);
static ref WND_HANDLERS: Mutex<WindowHandlers> = Mutex::new(WindowHandlers{hthread: 0, hprocess: 0});
}
struct WindowHandlers {
hthread: u64,
hprocess: u64,
}
impl Drop for WindowHandlers {
fn drop(&mut self) {
unsafe {
if self.hthread != 0 {
CloseHandle(self.hthread as _);
}
self.hthread = 0;
if self.hprocess != 0 {
CloseHandle(self.hprocess as _);
}
self.hprocess = 0;
}
}
}
pub fn turn_on_privacy(conn_id: i32) -> ResultType<bool> {
let exe_file = std::env::current_exe()?;
if let Some(cur_dir) = exe_file.parent() {
if !cur_dir.join("WindowInjection.dll").exists() {
return Ok(false);
}
} else {
bail!(
"Invalid exe parent for {}",
exe_file.to_string_lossy().as_ref()
);
}
if !*DLL_FOUND.lock().unwrap() {
log::info!("turn_on_privacy, dll not found when started, try start");
start()?;
std::thread::sleep(std::time::Duration::from_millis(1_000));
}
let pre_conn_id = *CONN_ID.lock().unwrap();
if pre_conn_id == conn_id {
return Ok(true);
}
if pre_conn_id != 0 {
bail!("Privacy occupied by another one");
}
let hwnd = wait_find_privacy_hwnd(0)?;
if hwnd.is_null() {
bail!("No privacy window created");
}
privacy_hook::hook()?;
unsafe {
ShowWindow(hwnd as _, SW_SHOW);
}
*CONN_ID.lock().unwrap() = conn_id;
Ok(true)
}
pub fn turn_off_privacy(conn_id: i32, state: Option<PrivacyModeState>) -> ResultType<()> {
let pre_conn_id = *CONN_ID.lock().unwrap();
if pre_conn_id != 0 && conn_id != 0 && pre_conn_id != conn_id {
bail!("Failed to turn off privacy mode that belongs to someone else")
}
privacy_hook::unhook()?;
unsafe {
let hwnd = wait_find_privacy_hwnd(0)?;
if !hwnd.is_null() {
ShowWindow(hwnd, SW_HIDE);
}
}
if pre_conn_id != 0 {
if let Some(state) = state {
allow_err!(set_privacy_mode_state(pre_conn_id, state, 1_000));
}
*CONN_ID.lock().unwrap() = 0;
}
Ok(())
}
pub fn start() -> ResultType<()> {
let mut wnd_handlers = WND_HANDLERS.lock().unwrap();
if wnd_handlers.hprocess != 0 {
return Ok(());
}
let exe_file = std::env::current_exe()?;
if exe_file.parent().is_none() {
bail!("Cannot get parent of current exe file");
}
let cur_dir = exe_file.parent().unwrap();
let dll_file = cur_dir.join("WindowInjection.dll");
if !dll_file.exists() {
bail!(
"Failed to find required file {}",
dll_file.to_string_lossy().as_ref()
);
}
*DLL_FOUND.lock().unwrap() = true;
let hwnd = wait_find_privacy_hwnd(1_000)?;
if !hwnd.is_null() {
log::info!("Privacy window is ready");
return Ok(());
}
// let cmdline = cur_dir.join("MiniBroker.exe").to_string_lossy().to_string();
let cmdline = cur_dir
.join(INJECTED_PROCESS_EXE)
.to_string_lossy()
.to_string();
unsafe {
let cmd_utf16: Vec<u16> = cmdline.encode_utf16().chain(Some(0).into_iter()).collect();
let mut start_info = STARTUPINFOW {
cb: 0,
lpReserved: NULL as _,
lpDesktop: NULL as _,
lpTitle: NULL as _,
dwX: 0,
dwY: 0,
dwXSize: 0,
dwYSize: 0,
dwXCountChars: 0,
dwYCountChars: 0,
dwFillAttribute: 0,
dwFlags: 0,
wShowWindow: 0,
cbReserved2: 0,
lpReserved2: NULL as _,
hStdInput: NULL as _,
hStdOutput: NULL as _,
hStdError: NULL as _,
};
let mut proc_info = PROCESS_INFORMATION {
hProcess: NULL as _,
hThread: NULL as _,
dwProcessId: 0,
dwThreadId: 0,
};
let session_id = WTSGetActiveConsoleSessionId();
let token = get_user_token(session_id, true);
if token.is_null() {
bail!("Failed to get token of current user");
}
let create_res = CreateProcessAsUserW(
token,
NULL as _,
cmd_utf16.as_ptr() as _,
NULL as _,
NULL as _,
FALSE,
CREATE_SUSPENDED | DETACHED_PROCESS,
NULL,
NULL as _,
&mut start_info,
&mut proc_info,
);
CloseHandle(token);
if 0 == create_res {
bail!(
"Failed to create privacy window process {}, code {}",
cmdline,
GetLastError()
);
};
inject_dll(
proc_info.hProcess,
proc_info.hThread,
dll_file.to_string_lossy().as_ref(),
)?;
if 0xffffffff == ResumeThread(proc_info.hThread) {
// CloseHandle
CloseHandle(proc_info.hThread);
CloseHandle(proc_info.hProcess);
bail!(
"Failed to create privacy window process, {}",
GetLastError()
);
}
wnd_handlers.hthread = proc_info.hThread as _;
wnd_handlers.hprocess = proc_info.hProcess as _;
let hwnd = wait_find_privacy_hwnd(1_000)?;
if hwnd.is_null() {
bail!("Failed to get hwnd after started");
}
}
Ok(())
}
unsafe fn inject_dll<'a>(hproc: HANDLE, hthread: HANDLE, dll_file: &'a str) -> ResultType<()> {
let dll_file_utf16: Vec<u16> = dll_file.encode_utf16().chain(Some(0).into_iter()).collect();
let buf = VirtualAllocEx(
hproc,
NULL as _,
dll_file_utf16.len() * 2,
MEM_COMMIT,
PAGE_READWRITE,
);
if buf.is_null() {
bail!("Failed VirtualAllocEx");
}
let mut written: usize = 0;
if 0 == WriteProcessMemory(
hproc,
buf,
dll_file_utf16.as_ptr() as _,
dll_file_utf16.len() * 2,
&mut written,
) {
bail!("Failed WriteProcessMemory");
}
let kernel32_modulename = CString::new("kernel32")?;
let hmodule = GetModuleHandleA(kernel32_modulename.as_ptr() as _);
if hmodule.is_null() {
bail!("Failed GetModuleHandleA");
}
let load_librarya_name = CString::new("LoadLibraryW")?;
let load_librarya = GetProcAddress(hmodule, load_librarya_name.as_ptr() as _);
if load_librarya.is_null() {
bail!("Failed GetProcAddress of LoadLibraryW");
}
if 0 == QueueUserAPC(Some(std::mem::transmute(load_librarya)), hthread, buf as _) {
bail!("Failed QueueUserAPC");
}
Ok(())
}
fn wait_find_privacy_hwnd(msecs: u128) -> ResultType<HWND> {
let tm_begin = Instant::now();
let wndname = CString::new(PRIVACY_WINDOW_NAME)?;
loop {
unsafe {
let hwnd = FindWindowA(NULL as _, wndname.as_ptr() as _);
if !hwnd.is_null() {
return Ok(hwnd);
}
}
if msecs == 0 || tm_begin.elapsed().as_millis() > msecs {
return Ok(NULL as _);
}
std::thread::sleep(Duration::from_millis(100));
}
}
pub fn is_process_consent_running() -> ResultType<bool> {
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,
state: PrivacyModeState,
ms_timeout: u64,
) -> ResultType<()> {
let mut c = connect(ms_timeout, "_cm").await?;
c.send(&Data::PrivacyModeState((conn_id, state))).await
}
pub(super) mod privacy_hook {
use super::*;
use std::sync::mpsc::{channel, Sender};
fn do_hook(tx: Sender<String>) -> ResultType<(HHOOK, HHOOK)> {
let invalid_ret = (0 as HHOOK, 0 as HHOOK);
let mut cur_hook_thread_id = CUR_HOOK_THREAD_ID.lock().unwrap();
if *cur_hook_thread_id != 0 {
// unreachable!
tx.send("Already hooked".to_owned())?;
return Ok(invalid_ret);
}
unsafe {
let mut hm_keyboard = 0 as HMODULE;
if 0 == GetModuleHandleExA(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
DefWindowProcA as _,
&mut hm_keyboard as _,
) {
tx.send(format!(
"Failed to GetModuleHandleExA, error: {}",
GetLastError()
))?;
return Ok(invalid_ret);
}
let mut hm_mouse = 0 as HMODULE;
if 0 == GetModuleHandleExA(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
DefWindowProcA as _,
&mut hm_mouse as _,
) {
tx.send(format!(
"Failed to GetModuleHandleExA, error: {}",
GetLastError()
))?;
return Ok(invalid_ret);
}
let hook_keyboard = SetWindowsHookExA(
WH_KEYBOARD_LL,
Some(privacy_mode_hook_keyboard),
hm_keyboard,
0,
);
if hook_keyboard.is_null() {
tx.send(format!(" SetWindowsHookExA keyboard {}", GetLastError()))?;
return Ok(invalid_ret);
}
let hook_mouse =
SetWindowsHookExA(WH_MOUSE_LL, Some(privacy_mode_hook_mouse), hm_mouse, 0);
if hook_mouse.is_null() {
if FALSE == UnhookWindowsHookEx(hook_keyboard) {
// Fatal error
log::error!(" UnhookWindowsHookEx keyboard {}", GetLastError());
}
tx.send(format!(" SetWindowsHookExA mouse {}", GetLastError()))?;
return Ok(invalid_ret);
}
*cur_hook_thread_id = GetCurrentThreadId();
tx.send("".to_owned())?;
return Ok((hook_keyboard, hook_mouse));
}
}
pub fn hook() -> ResultType<()> {
let (tx, rx) = channel();
std::thread::spawn(move || {
let hook_keyboard;
let hook_mouse;
unsafe {
match do_hook(tx.clone()) {
Ok(hooks) => {
hook_keyboard = hooks.0;
hook_mouse = hooks.1;
}
Err(e) => {
// Fatal error
tx.send(format!("Unexpected err when hook {}", e)).unwrap();
return;
}
}
if hook_keyboard.is_null() {
return;
}
let mut msg = MSG {
hwnd: NULL as _,
message: 0 as _,
wParam: 0 as _,
lParam: 0 as _,
time: 0 as _,
pt: POINT {
x: 0 as _,
y: 0 as _,
},
};
while FALSE != GetMessageA(&mut msg, NULL as _, 0, 0) {
if msg.message == WM_USER_EXIT_HOOK {
break;
}
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
if FALSE == UnhookWindowsHookEx(hook_keyboard as _) {
// Fatal error
log::error!("Failed UnhookWindowsHookEx keyboard {}", GetLastError());
}
if FALSE == UnhookWindowsHookEx(hook_mouse as _) {
// Fatal error
log::error!("Failed UnhookWindowsHookEx mouse {}", GetLastError());
}
*CUR_HOOK_THREAD_ID.lock().unwrap() = 0;
}
});
match rx.recv() {
Ok(msg) => {
if msg == "" {
Ok(())
} else {
bail!(msg)
}
}
Err(e) => {
bail!("Failed to wait hook result {}", e)
}
}
}
pub fn unhook() -> ResultType<()> {
unsafe {
let cur_hook_thread_id = CUR_HOOK_THREAD_ID.lock().unwrap();
if *cur_hook_thread_id != 0 {
if FALSE == PostThreadMessageA(*cur_hook_thread_id, WM_USER_EXIT_HOOK, 0, 0) {
bail!("Failed to post message to exit hook, {}", GetLastError());
}
}
}
Ok(())
}
#[no_mangle]
pub extern "system" fn privacy_mode_hook_keyboard(
code: c_int,
w_param: WPARAM,
l_param: LPARAM,
) -> LRESULT {
if code < 0 {
unsafe {
return CallNextHookEx(NULL as _, code, w_param, l_param);
}
}
let ks = l_param as PKBDLLHOOKSTRUCT;
let w_param2 = w_param as UINT;
unsafe {
if (*ks).dwExtraInfo != enigo::ENIGO_INPUT_EXTRA_VALUE {
// Disable alt key. Alt + Tab will switch windows.
if (*ks).flags & LLKHF_ALTDOWN == LLKHF_ALTDOWN {
return 1;
}
match w_param2 {
WM_KEYDOWN => {
// Disable all keys other than P and Ctrl.
if ![80, 162, 163].contains(&(*ks).vkCode) {
return 1;
}
// NOTE: GetKeyboardState may not work well...
// Check if Ctrl + P is pressed
let cltr_down = (GetKeyState(VK_CONTROL) as u16) & (0x8000 as u16) > 0;
let key = LOBYTE((*ks).vkCode as _);
if cltr_down && (key == 'p' as u8 || key == 'P' as u8) {
// Ctrl + P is pressed, turn off privacy mode
if let Err(e) =
turn_off_privacy(0, Some(crate::ipc::PrivacyModeState::OffByPeer))
{
log::error!("Failed to off_privacy {}", e);
}
}
}
WM_KEYUP => {
log::trace!("WM_KEYUP {}", (*ks).vkCode);
}
_ => {
log::trace!("KEYBOARD OTHER {} {}", w_param2, (*ks).vkCode);
}
}
}
}
unsafe { CallNextHookEx(NULL as _, code, w_param, l_param) }
}
#[no_mangle]
pub extern "system" fn privacy_mode_hook_mouse(
code: c_int,
w_param: WPARAM,
l_param: LPARAM,
) -> LRESULT {
if code < 0 {
unsafe {
return CallNextHookEx(NULL as _, code, w_param, l_param);
}
}
let ms = l_param as PMOUSEHOOKSTRUCT;
unsafe {
if (*ms).dwExtraInfo != enigo::ENIGO_INPUT_EXTRA_VALUE {
return 1;
}
}
unsafe { CallNextHookEx(NULL as _, code, w_param, l_param) }
}
}
mod test {
#[test]
fn privacy_hook() {
//use super::*;
// privacy_hook::hook().unwrap();
// std::thread::sleep(std::time::Duration::from_millis(50));
// privacy_hook::unhook().unwrap();
}
}