whatawhat 0.1.1

Application for monitoring user activity
Documentation
use std::fmt::{Debug, Display};

use anyhow::{anyhow, Result};
use windows::Win32::{
    Foundation::{CloseHandle, BOOL, HANDLE, HWND},
    System::{
        SystemInformation::GetTickCount64,
        Threading::{
            OpenProcess, QueryFullProcessImageNameW, PROCESS_NAME_WIN32, PROCESS_QUERY_INFORMATION,
            PROCESS_VM_READ,
        },
    },
    UI::{
        Input::KeyboardAndMouse::{GetLastInputInfo, LASTINPUTINFO},
        WindowsAndMessaging::{GetForegroundWindow, GetWindowTextW, GetWindowThreadProcessId},
    },
};

use super::{ActiveWindowData, WindowManager};

#[derive(Debug)]
struct ActiveWindowError {}

impl ActiveWindowError {
    fn new() -> Self {
        Self {}
    }
}

impl Display for ActiveWindowError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Debug::fmt(&self, f)
    }
}

impl std::error::Error for ActiveWindowError {}

pub fn get_active() -> Result<ActiveWindowData> {
    let window = unsafe { GetForegroundWindow() };

    let mut id = 0u32;
    unsafe { GetWindowThreadProcessId(window, Some(&mut id)) };
    if id == 0 {
        return Err(ActiveWindowError::new().into());
    }
    let process_handle = unsafe {
        OpenProcess(
            PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
            BOOL::from(false),
            id,
        )
    }?;

    let mut text: [u16; 4096] = [0; 4096];
    let process_name = unsafe { get_window_process_path(process_handle, &mut text)? };
    let title = unsafe { get_window_title(window, &mut text) };

    unsafe { CloseHandle(process_handle).unwrap() };

    Ok(ActiveWindowData {
        process_name: process_name.into(),
        window_title: title.into(),
    })
}

unsafe fn get_window_process_path(window_handle: HANDLE, text: &mut [u16]) -> Result<String> { unsafe {
    let mut length = text.len() as u32;
    QueryFullProcessImageNameW(
        window_handle,
        PROCESS_NAME_WIN32,
        windows::core::PWSTR(text.as_mut_ptr()),
        &mut length,
    )?;
    Ok(String::from_utf16_lossy(&text[..length as usize]))
}}

unsafe fn get_window_title(window_handle: HWND, text: &mut [u16]) -> String {
    let len = unsafe { GetWindowTextW(window_handle, text) };
    String::from_utf16_lossy(&text[..len as usize])
}

pub fn get_idle_time() -> Result<u32> {
    let mut last: LASTINPUTINFO = LASTINPUTINFO {
        cbSize: size_of::<LASTINPUTINFO>() as u32,
        dwTime: 0,
    };
    let is_success = unsafe { GetLastInputInfo(&mut last) };
    if !is_success.as_bool() {
        return Err(anyhow!("Failed to retreive user idle time"));
    }

    let tick_count = unsafe { GetTickCount64() };
    let duration = tick_count - last.dwTime as u64;
    if duration > u32::MAX as u64 {
        Ok(u32::MAX)
    } else {
        Ok(duration as u32)
    }
}

pub struct WindowsWindowManager {}

impl WindowsWindowManager {
    pub fn new() -> Self {
        Self {}
    }
}

impl Default for WindowsWindowManager {
    fn default() -> Self {
        Self::new()
    }
}

impl WindowManager for WindowsWindowManager {
    fn get_active_window_data(&mut self) -> Result<ActiveWindowData> {
        get_active()
    }

    fn get_idle_time(&mut self) -> Result<u32> {
        get_idle_time()
    }
}