procmod-overlay 3.0.0

Game overlay rendering with transparent click-through windows
Documentation
#[cfg(not(target_os = "windows"))]
fn main() {
    eprintln!("this example only runs on windows");
}

#[cfg(target_os = "windows")]
fn main() -> procmod_overlay::Result<()> {
    use procmod_overlay::{
        Color, InputEvent, InteractionMode, MouseButton, Overlay, OverlayTarget,
    };
    use std::io::Write;
    use std::time::{Duration, Instant};

    let game = game_window::create("Procmod Interaction Target", 640, 480);
    std::thread::sleep(Duration::from_millis(250));
    game.set_foreground();
    let mut overlay = Overlay::new(OverlayTarget::Title("Procmod Interaction Target".into()))?;
    overlay.set_interaction_mode(InteractionMode::Interactive)?;
    let mut log = std::fs::File::create("interaction-events.txt").unwrap();
    let started = Instant::now();
    let mut running = true;
    while running && started.elapsed() < Duration::from_secs(20) {
        overlay.begin_frame()?;
        overlay.rect_filled(160.0, 140.0, 320.0, 180.0, Color::rgba(20, 25, 35, 235));
        overlay.rect(160.0, 140.0, 320.0, 180.0, Color::CYAN);
        overlay.text(190.0, 180.0, "Interactive overlay", 22.0, Color::WHITE);
        overlay.text(
            190.0,
            220.0,
            "Click here, then press Escape",
            16.0,
            Color::CYAN,
        );
        overlay.end_frame()?;

        for event in overlay.drain_input_events() {
            writeln!(log, "{event:?}").unwrap();
            match event {
                InputEvent::MouseButton {
                    button: MouseButton::Left,
                    pressed: false,
                    ..
                } => writeln!(log, "CLICK_CONFIRMED").unwrap(),
                InputEvent::Key {
                    virtual_key: 0x1b, ..
                } => {
                    overlay.set_interaction_mode(InteractionMode::PassThrough)?;
                    writeln!(log, "PASS_THROUGH_RESTORED").unwrap();
                    running = false;
                }
                _ => {}
            }
        }
        std::thread::sleep(Duration::from_millis(8));
    }
    drop(overlay);
    writeln!(log, "TARGET_FOREGROUND={}", game.is_foreground()).unwrap();
    Ok(())
}

#[cfg(target_os = "windows")]
mod game_window {
    use std::ffi::OsStr;
    use std::os::windows::ffi::OsStrExt;
    use windows::core::PCWSTR;
    use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM};
    use windows::Win32::System::LibraryLoader::GetModuleHandleW;
    use windows::Win32::UI::WindowsAndMessaging::*;

    pub struct GameWindow {
        hwnd: HWND,
    }

    impl GameWindow {
        pub fn set_foreground(&self) {
            unsafe {
                let _ = SetForegroundWindow(self.hwnd);
            }
        }

        pub fn is_foreground(&self) -> bool {
            unsafe { GetForegroundWindow() == self.hwnd }
        }
    }

    impl Drop for GameWindow {
        fn drop(&mut self) {
            unsafe {
                let _ = DestroyWindow(self.hwnd);
            }
        }
    }

    pub fn create(title: &str, width: i32, height: i32) -> GameWindow {
        let class_name = wide(title);
        let hinstance = unsafe { GetModuleHandleW(None).unwrap() };
        let class = WNDCLASSEXW {
            cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
            lpfnWndProc: Some(wnd_proc),
            hInstance: hinstance.into(),
            lpszClassName: PCWSTR(class_name.as_ptr()),
            ..Default::default()
        };
        unsafe { RegisterClassExW(&class) };
        let title = wide(title);
        let hwnd = unsafe {
            CreateWindowExW(
                WINDOW_EX_STYLE::default(),
                PCWSTR(class_name.as_ptr()),
                PCWSTR(title.as_ptr()),
                WS_POPUP | WS_VISIBLE,
                100,
                100,
                width,
                height,
                None,
                None,
                Some(hinstance.into()),
                None,
            )
        }
        .unwrap();
        GameWindow { hwnd }
    }

    unsafe extern "system" fn wnd_proc(
        hwnd: HWND,
        message: u32,
        wparam: WPARAM,
        lparam: LPARAM,
    ) -> LRESULT {
        match message {
            WM_DESTROY => LRESULT(0),
            _ => DefWindowProcW(hwnd, message, wparam, lparam),
        }
    }

    fn wide(value: &str) -> Vec<u16> {
        OsStr::new(value).encode_wide().chain(Some(0)).collect()
    }
}