shroud 0.2.3

Universal library for discovering rendering functions of render engines: DirectX9, DirectX10, DirectX11, DirectX12
Documentation
use std::ffi::{IntoStringError, NulError};

use strum::IntoEnumIterator;
use strum_macros::EnumIter;
use thiserror::Error;
use windows::{
    core::{HRESULT, PCSTR},
    Win32::{
        Foundation::{BOOL, HMODULE, HWND, LPARAM},
        System::LibraryLoader::GetModuleHandleA,
        UI::WindowsAndMessaging::{EnumWindows, GetWindowThreadProcessId},
    },
};

#[cfg(not(target_os = "windows"))]
compile_error!("only windows is supported");

#[cfg(feature = "directx9")]
pub mod directx9;

#[cfg(feature = "directx10")]
pub mod directx10;

#[cfg(feature = "directx11")]
pub mod directx11;

#[cfg(feature = "directx12")]
pub mod directx12;

#[cfg(any(feature = "directx11", feature = "directx12"))]
pub mod swapchain_util;

pub(crate) fn get_process_window() -> Option<HWND> {
    extern "system" fn enum_windows_callback(hwnd: HWND, l_param: LPARAM) -> BOOL {
        let mut wnd_proc_id: u32 = 0;
        unsafe {
            GetWindowThreadProcessId(hwnd, Some(&mut wnd_proc_id));
            if std::process::id() != wnd_proc_id {
                return true.into();
            }

            *(l_param.0 as *mut HWND) = hwnd;
        }
        false.into()
    }

    let mut output: HWND = HWND(0);
    unsafe {
        EnumWindows(
            Some(enum_windows_callback),
            std::mem::transmute::<_, LPARAM>(&mut output as *mut HWND),
        )
        .ok()?
    };

    match output.0 == 0 {
        true => None,
        false => Some(output),
    }
}

static DIRECTX_9_DLL_NAME: &str = concat!("d3d9.dll", "\0");
static DIRECTX_10_DLL_NAME: &str = concat!("d3d10.dll", "\0");
static DIRECTX_11_DLL_NAME: &str = concat!("d3d11.dll", "\0");
static DIRECTX_12_DLL_NAME: &str = concat!("d3d12.dll", "\0");

#[derive(Debug, EnumIter)]
pub enum RenderEngine {
    DirectX9,
    DirectX10,
    DirectX11,
    DirectX12,
}

pub fn detect_render_engine() -> Option<RenderEngine> {
    RenderEngine::iter()
        .find(|render_engine| RenderEngine::get_render_engine_handle(render_engine).is_ok())
}

impl RenderEngine {
    pub(crate) fn get_render_engine_handle(render_engine: &RenderEngine) -> ShroudResult<HMODULE> {
        let handle_name = RenderEngine::dll_name(render_engine);
        let handle = unsafe {
            GetModuleHandleA(PCSTR::from_raw(handle_name.as_ptr()))
                .map_err(|_| ShroudError::OpenHandleError(handle_name.to_owned()))?
        };
        Ok(handle)
    }

    pub fn dll_name(entry: &RenderEngine) -> &str {
        match entry {
            RenderEngine::DirectX9 => DIRECTX_9_DLL_NAME,
            RenderEngine::DirectX10 => DIRECTX_10_DLL_NAME,
            RenderEngine::DirectX11 => DIRECTX_11_DLL_NAME,
            RenderEngine::DirectX12 => DIRECTX_12_DLL_NAME,
        }
    }
}

#[derive(Error, Debug)]
pub enum ShroudError {
    #[error("IntoString Error `{0:#?}`")]
    IntoStringError(#[from] IntoStringError),

    #[error("Std Nul Error `{0:#?}`")]
    StdNulError(#[from] NulError),

    #[error("Error opening handle for dll: `{0:#?}`")]
    OpenHandleError(String),

    #[error("Error finding main process window!")]
    Window,

    #[error("General expectation failed `{0}`")]
    Expectation(&'static str),

    #[cfg(feature = "directx9")]
    #[error("Error creating directx9 instance `{0:#?}`")]
    DirectX9Create(HRESULT),
    #[cfg(feature = "directx9")]
    #[error("Error creating directx9 device `{0:#?}`")]
    DirectX9CreateDevice(HRESULT),

    #[cfg(feature = "directx11")]
    #[error("Error creating directx11 device `{0:#?}`")]
    DirectX11CreateDeviceAndSwapchain(HRESULT),

    #[cfg(feature = "directx12")]
    #[error("Error creating directx12 factory `{0:#?}`")]
    DirectX12CreateFactory(HRESULT),
    #[cfg(feature = "directx12")]
    #[error("Error enumerating directx12 adapters `{0:#?}`")]
    DirectX12EnumAdapters(HRESULT),
    #[cfg(feature = "directx12")]
    #[error("Error creating directx12 device `{0:#?}`")]
    DirectX12CreateDevice(HRESULT),
    #[cfg(feature = "directx12")]
    #[error("Error creating directx12 command queue `{0:#?}`")]
    DirectX12CreateCommandQueue(HRESULT),
    #[cfg(feature = "directx12")]
    #[error("Error creating directx12 command allocator `{0:#?}`")]
    DirectX12CreateCommandAllocator(HRESULT),
    #[cfg(feature = "directx12")]
    #[error("Error creating directx12 command list `{0:#?}`")]
    DirectX12CreateCommandList(HRESULT),
    #[cfg(feature = "directx12")]
    #[error("Error creating directx12 swapchain `{0:#?}`")]
    DirectX12CreateSwapchain(HRESULT),
}

pub type ShroudResult<T> = std::result::Result<T, ShroudError>;