kamft_desktop 0.0.4

Kamft desktop utilities including screenshot capture and WeChat Work integration
use super::screenshot;
use crate::core::log;
use enigo::{Direction, Enigo, Key, Keyboard, Settings};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
/// 是否正在切换桌面(防止重复触发)
static SWITCHING: AtomicBool = AtomicBool::new(false);

/// 发送 Win+Ctrl+方向键 切换虚拟桌面
fn switch_desktop(right: bool) {
    if SWITCHING.swap(true, Ordering::SeqCst) {
        return;
    }

    let mut enigo = match Enigo::new(&Settings::default()) {
        Ok(e) => e,
        Err(e) => {
            log::warn!("Enigo 初始化失败: {}", e);
            SWITCHING.store(false, Ordering::SeqCst);
            return;
        }
    };

    let arrow = if right {
        Key::RightArrow
    } else {
        Key::LeftArrow
    };

    let _ = enigo.key(Key::Meta, Direction::Press);
    let _ = enigo.key(Key::Control, Direction::Press);
    thread::sleep(Duration::from_millis(10));
    let _ = enigo.key(arrow, Direction::Click);
    thread::sleep(Duration::from_millis(10));
    let _ = enigo.key(Key::Control, Direction::Release);
    let _ = enigo.key(Key::Meta, Direction::Release);

    thread::sleep(Duration::from_millis(300));
    SWITCHING.store(false, Ordering::SeqCst);
}

/// 启动全局快捷键监听(在独立线程中运行)
/// F1: 上一个虚拟桌面
/// F2: 下一个虚拟桌面
/// F3: 全屏/多屏截图 → 上传云端
pub fn start_listener() {
    let (tx, rx) = mpsc::channel::<bool>();
    thread::spawn(move || {
        #[cfg(target_os = "windows")]
        {
            use std::ptr::null_mut;
            use winapi::shared::minwindef::{HINSTANCE, LPARAM, LRESULT, WPARAM};
            use winapi::um::libloaderapi::GetModuleHandleW;
            use winapi::um::winuser::{
                CallNextHookEx, GetMessageW, SetWindowsHookExW, UnhookWindowsHookEx, HC_ACTION,
                KBDLLHOOKSTRUCT, MSG, WH_KEYBOARD_LL,
            };

            static mut FIRST_KEY: bool = true;

            unsafe extern "system" fn low_level_keyboard_proc(
                n_code: i32,
                w_param: WPARAM,
                l_param: LPARAM,
            ) -> LRESULT {
                if n_code == HC_ACTION {
                    let kb = unsafe { &*(l_param as *const KBDLLHOOKSTRUCT) };
                    let vk = kb.vkCode as i32;

                    unsafe {
                        if FIRST_KEY {
                            FIRST_KEY = false;
                            log::info!("[DEBUG] 首次捕获按键: vkCode={}, wParam={}", vk, w_param);
                        }
                    }

                    // WM_KEYDOWN = 0x0100, WM_SYSKEYDOWN = 0x0104
                    if w_param == 0x0100 || w_param == 0x0104 {
                        match vk {
                            0x70 => {
                                log::info!("[F1] 切换到上一个桌面(已拦截)");
                                switch_desktop(false);
                                return 1; // 拦截,阻止传递到目标窗口
                            }
                            0x71 => {
                                log::info!("[F2] 切换到下一个桌面(已拦截)");
                                switch_desktop(true);
                                return 1; // 拦截,阻止传递到目标窗口
                            }
                            0x72 => {
                                log::info!("[F3] 截图上传");
                                // 在独立线程中执行截图上传,避免阻塞钩子回调
                                thread::spawn(|| {
                                    screenshot::capture_and_upload();
                                });
                            }
                            _ => {}
                        }
                    }
                }
                unsafe { CallNextHookEx(null_mut(), n_code, w_param, l_param) }
            }

            let h_instance = unsafe { GetModuleHandleW(null_mut()) as HINSTANCE };
            let hook = unsafe {
                SetWindowsHookExW(WH_KEYBOARD_LL, Some(low_level_keyboard_proc), h_instance, 0)
            };

            if hook.is_null() {
                log::warn!("全局键盘钩子注册失败");
                let _ = tx.send(false);
                return;
            }

            log::info!("全局键盘钩子已注册");
            log::info!("  F1: 上一个桌面 | F2: 下一个桌面 | F3: 截图上传");
            let _ = tx.send(true);

            // 消息循环 — 低级键盘钩子需要消息泵
            let mut msg: MSG = unsafe { std::mem::zeroed() };
            loop {
                let ret = unsafe { GetMessageW(&mut msg, null_mut(), 0, 0) };
                if ret <= 0 {
                    break;
                }
            }

            unsafe { UnhookWindowsHookEx(hook) };
            log::info!("全局键盘钩子已卸载");
        }

        #[cfg(not(target_os = "windows"))]
        {
            log::warn!("当前平台暂不支持全局快捷键");
            let _ = tx.send(false);
        }
    });

    // 等待钩子注册完成
    match rx.recv_timeout(Duration::from_secs(3)) {
        Ok(true) => log::info!("快捷键监听线程已就绪"),
        Ok(false) => log::warn!("快捷键监听启动失败"),
        Err(_) => log::warn!("快捷键监听启动超时"),
    }
}