Skip to content

Commit

Permalink
Expose hooks::common (#122)
Browse files Browse the repository at this point in the history
* Make DummyHwnd public and move Fence to DX12

* Move hook-implementor traits to `common` and client traits to `hooks`

* Make `mh` module public

* Appease Clippy
  • Loading branch information
veeenu authored Aug 19, 2023
1 parent 8f12735 commit a4c9dc2
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 131 deletions.
2 changes: 1 addition & 1 deletion examples/dx11_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub fn main(_argc: i32, _argv: *const *const u8) {
println!(
"{}",
String::from_utf8_lossy(std::slice::from_raw_parts(
diqm.pDescription as *const u8,
diqm.pDescription,
diqm.DescriptionByteLength
))
);
Expand Down
2 changes: 1 addition & 1 deletion examples/renderers/dx11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub fn main(_argc: i32, _argv: *const *const u8) {
eprintln!(
"{}",
String::from_utf8_lossy(std::slice::from_raw_parts(
diqm.pDescription as *const u8,
diqm.pDescription,
diqm.DescriptionByteLength
))
);
Expand Down
2 changes: 1 addition & 1 deletion examples/renderers/dx12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ pub fn main(_argc: i32, _argv: *const *const u8) {
println!(
"{}",
String::from_utf8_lossy(std::slice::from_raw_parts(
diqm.pDescription as *const u8,
diqm.pDescription,
diqm.DescriptionByteLength
))
);
Expand Down
2 changes: 1 addition & 1 deletion examples/renderers/dx9.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ pub fn main(_argc: i32, _argv: *const *const u8) {
eprintln!(
"{}",
String::from_utf8_lossy(std::slice::from_raw_parts(
diqm.pDescription as *const u8,
diqm.pDescription,
diqm.DescriptionByteLength
))
);
Expand Down
136 changes: 54 additions & 82 deletions src/hooks/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::mem;
use std::ptr::null;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{hint, mem};

use imgui::{Context, Io, Key, Ui};
use imgui::Key;
use tracing::debug;
use windows::core::PCWSTR;
use windows::w;
Expand All @@ -15,49 +14,49 @@ use windows::Win32::UI::WindowsAndMessaging::{
CS_VREDRAW, HCURSOR, HICON, HWND_MESSAGE, WNDCLASSEXW, WS_OVERLAPPEDWINDOW,
};

pub(crate) use self::wnd_proc::*;
use crate::hooks::Hooks;
pub use crate::hooks::common::wnd_proc::*;
use crate::hooks::ImguiRenderLoop;

mod wnd_proc;
pub mod wnd_proc;

/// Holds information useful to the render loop which can't be retrieved from
/// `imgui::Ui`.
pub struct ImguiRenderLoopFlags {
/// Whether the hooked program's window is currently focused.
pub focused: bool,
}

/// Implement your `imgui` rendering logic via this trait.
pub trait ImguiRenderLoop {
/// Called once at the first occurrence of the hook. Implement this to
/// initialize your data.
fn initialize(&mut self, _ctx: &mut Context) {}

/// Called every frame. Use the provided `ui` object to build your UI.
fn render(&mut self, ui: &mut Ui, flags: &ImguiRenderLoopFlags);

/// Called during the window procedure.
fn on_wnd_proc(&self, _hwnd: HWND, _umsg: u32, _wparam: WPARAM, _lparam: LPARAM) {}

/// If this function returns true, the WndProc function will not call the
/// procedure of the parent window.
fn should_block_messages(&self, _io: &Io) -> bool {
false
}
pub type WndProcType =
unsafe extern "system" fn(hwnd: HWND, umsg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT;

fn into_hook<T>(self) -> Box<T>
/// Generic trait for platform-specific hooks.
///
/// Implement this if you are building a custom renderer.
///
/// Check out first party implementations ([`crate::hooks::dx9`],
/// [`crate::hooks::dx11`], [`crate::hooks::dx12`], [`crate::hooks::opengl3`])
/// for guidance on how to implement the methods.
pub trait Hooks {
fn from_render_loop<T>(t: T) -> Box<Self>
where
T: Hooks,
Self: Send + Sync + Sized + 'static,
{
T::from_render_loop(self)
}
Self: Sized,
T: ImguiRenderLoop + Send + Sync + 'static;

/// Find the hook target functions addresses, initialize the data, create
/// and enable the hooks.
///
/// # Safety
///
/// Is most definitely UB.
unsafe fn hook(&self);

/// Cleanup global data and disable the hooks.
///
/// # Safety
///
/// Is most definitely UB.
unsafe fn unhook(&mut self);
}

pub(crate) type WndProcType =
unsafe extern "system" fn(hwnd: HWND, umsg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT;

pub(crate) trait ImguiWindowsEventHandler {
/// Implement this if you are building a custom renderer.
///
/// Check out first party implementations ([`crate::hooks::dx9`],
/// [`crate::hooks::dx11`], [`crate::hooks::dx12`], [`crate::hooks::opengl3`])
/// for guidance on how to implement the methods.
pub trait ImguiWindowsEventHandler {
fn io(&self) -> &imgui::Io;
fn io_mut(&mut self) -> &mut imgui::Io;

Expand Down Expand Up @@ -97,53 +96,21 @@ pub(crate) trait ImguiWindowsEventHandler {
}
}

/// Spin-loop based synchronization struct.
/// A RAII dummy window.
///
/// Call [`Fence::lock`] in a thread to indicate some operation is in progress,
/// and [`Fence::wait`] on a different thread to create a spin-loop that waits
/// for the lock to be dropped.
pub(crate) struct Fence(AtomicBool);

impl Fence {
pub(crate) const fn new() -> Self {
Self(AtomicBool::new(false))
}
/// Registers a class and creates a window on instantiation.
/// Destroys the window and unregisters the class on drop.
pub struct DummyHwnd(HWND, WNDCLASSEXW);

/// Create a [`FenceGuard`].
pub(crate) fn lock(&self) -> FenceGuard<'_> {
FenceGuard::new(self)
}

/// Wait in a spin-loop for the [`FenceGuard`] created by [`Fence::lock`] to
/// be dropped.
pub(crate) fn wait(&self) {
while self.0.load(Ordering::SeqCst) {
hint::spin_loop();
}
}
}

/// A RAII implementation of a spin-loop for a [`Fence`]. When this is dropped,
/// the wait on a [`Fence`] will terminate.
pub(crate) struct FenceGuard<'a>(&'a Fence);

impl<'a> FenceGuard<'a> {
fn new(fence: &'a Fence) -> Self {
fence.0.store(true, Ordering::SeqCst);
Self(fence)
impl Default for DummyHwnd {
fn default() -> Self {
Self::new()
}
}

impl<'a> Drop for FenceGuard<'a> {
fn drop(&mut self) {
self.0 .0.store(false, Ordering::SeqCst);
}
}

pub(crate) struct DummyHwnd(HWND, WNDCLASSEXW);

impl DummyHwnd {
pub(crate) fn new() -> Self {
pub fn new() -> Self {
// The window procedure for the class just calls `DefWindowProcW`.
unsafe extern "system" fn wnd_proc(
hwnd: HWND,
msg: u32,
Expand All @@ -153,6 +120,7 @@ impl DummyHwnd {
DefWindowProcW(hwnd, msg, wparam, lparam)
}

// Create and register the class.
let wndclass = WNDCLASSEXW {
cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
style: CS_HREDRAW | CS_VREDRAW,
Expand All @@ -169,6 +137,8 @@ impl DummyHwnd {
};
debug!("{:?}", wndclass);
unsafe { RegisterClassExW(&wndclass) };

// Create the window.
let hwnd = unsafe {
CreateWindowExW(
Default::default(),
Expand All @@ -190,13 +160,15 @@ impl DummyHwnd {
Self(hwnd, wndclass)
}

pub(crate) fn hwnd(&self) -> HWND {
// Retrieve the window handle.
pub fn hwnd(&self) -> HWND {
self.0
}
}

impl Drop for DummyHwnd {
fn drop(&mut self) {
// Destroy the window and unregister the class.
unsafe {
DestroyWindow(self.0);
UnregisterClassW(self.1.lpszClassName, self.1.hInstance);
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/common/wnd_proc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ fn handle_input(io: &mut Io, state: u32, WPARAM(wparam): WPARAM, LPARAM(lparam):
////////////////////////////////////////////////////////////////////////////////

#[must_use]
pub(crate) fn imgui_wnd_proc_impl<T>(
pub fn imgui_wnd_proc_impl<T>(
hwnd: HWND,
umsg: u32,
WPARAM(wparam): WPARAM,
Expand Down
7 changes: 2 additions & 5 deletions src/hooks/dx11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,8 @@ use windows::Win32::UI::WindowsAndMessaging::SetWindowLongA;
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrA;
use windows::Win32::UI::WindowsAndMessaging::*;

use crate::hooks::common::{
imgui_wnd_proc_impl, DummyHwnd, ImguiRenderLoop, ImguiRenderLoopFlags,
ImguiWindowsEventHandler, WndProcType,
};
use crate::hooks::Hooks;
use crate::hooks::common::{imgui_wnd_proc_impl, DummyHwnd, ImguiWindowsEventHandler, WndProcType};
use crate::hooks::{Hooks, ImguiRenderLoop, ImguiRenderLoopFlags};
use crate::mh::{MhHook, MhHooks};
use crate::renderers::imgui_dx11;

Expand Down
62 changes: 55 additions & 7 deletions src/hooks/dx12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::ffi::c_void;
use std::mem::{self, ManuallyDrop};
use std::ptr::{null, null_mut};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::thread;
use std::time::{Duration, Instant};
use std::{hint, thread};

use imgui::Context;
use once_cell::sync::OnceCell;
Expand Down Expand Up @@ -33,14 +33,62 @@ use windows::Win32::UI::WindowsAndMessaging::SetWindowLongA;
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrA;
use windows::Win32::UI::WindowsAndMessaging::*;

use crate::hooks::common::{
imgui_wnd_proc_impl, DummyHwnd, Fence, ImguiRenderLoop, ImguiRenderLoopFlags,
ImguiWindowsEventHandler, WndProcType,
};
use crate::hooks::Hooks;
use crate::hooks::common::{imgui_wnd_proc_impl, DummyHwnd, ImguiWindowsEventHandler, WndProcType};
use crate::hooks::{Hooks, ImguiRenderLoop, ImguiRenderLoopFlags};
use crate::mh::{MhHook, MhHooks};
use crate::renderers::imgui_dx12::RenderEngine;

////////////////////////////////////////////////////////////////////////////////
// Utilities
////////////////////////////////////////////////////////////////////////////////

/// Spin-loop based synchronization struct.
///
/// Call [`Fence::lock`] in a thread to indicate some operation is in progress,
/// and [`Fence::wait`] on a different thread to create a spin-loop that waits
/// for the lock to be dropped.
struct Fence(AtomicBool);

impl Fence {
const fn new() -> Self {
Self(AtomicBool::new(false))
}

/// Create a [`FenceGuard`].
fn lock(&self) -> FenceGuard<'_> {
FenceGuard::new(self)
}

/// Wait in a spin-loop for the [`FenceGuard`] created by [`Fence::lock`] to
/// be dropped.
fn wait(&self) {
while self.0.load(Ordering::SeqCst) {
hint::spin_loop();
}
}
}

/// A RAII implementation of a spin-loop for a [`Fence`]. When this is dropped,
/// the wait on a [`Fence`] will terminate.
struct FenceGuard<'a>(&'a Fence);

impl<'a> FenceGuard<'a> {
fn new(fence: &'a Fence) -> Self {
fence.0.store(true, Ordering::SeqCst);
Self(fence)
}
}

impl<'a> Drop for FenceGuard<'a> {
fn drop(&mut self) {
self.0 .0.store(false, Ordering::SeqCst);
}
}

////////////////////////////////////////////////////////////////////////////////
// Type aliases
////////////////////////////////////////////////////////////////////////////////

type DXGISwapChainPresentType =
unsafe extern "system" fn(This: IDXGISwapChain3, SyncInterval: u32, Flags: u32) -> HRESULT;

Expand Down Expand Up @@ -106,7 +154,7 @@ unsafe fn print_dxgi_debug_messages() {
debug!(
"[DIQ] {}",
String::from_utf8_lossy(std::slice::from_raw_parts(
diqm.pDescription as *const u8,
diqm.pDescription,
diqm.DescriptionByteLength - 1
))
);
Expand Down
Loading

0 comments on commit a4c9dc2

Please sign in to comment.