Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idea/editor.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions .idea/omath.iml

This file was deleted.

7 changes: 5 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ option(OMATH_ENABLE_FORCE_INLINE
"Will for compiler to make some functions to be force inlined no matter what" ON)
option(OMATH_ENABLE_LUA
"omath bindings for lua" OFF)
option(OMATH_ENABLE_HOOKING "omath will HooksManager that can hook DirectX automatically" OFF)
option(OMATH_ENABLE_HOOKING "omath will HooksManager that can hook DirectX/OpenGL automatically" OFF)

if(VCPKG_MANIFEST_FEATURES)
foreach(omath_feature IN LISTS VCPKG_MANIFEST_FEATURES)
Expand Down Expand Up @@ -113,7 +113,10 @@ if (OMATH_ENABLE_HOOKING)
target_link_libraries(${PROJECT_NAME} PRIVATE safetyhook::safetyhook)

if (WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE d3d9 d3d11 d3d12 dxgi)
target_link_libraries(${PROJECT_NAME} PRIVATE d3d9 d3d11 d3d12 dxgi opengl32 gdi32)
elseif (UNIX AND NOT APPLE)
find_package(OpenGL REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL ${CMAKE_DL_LIBS})
endif ()
endif ()

Expand Down
7 changes: 4 additions & 3 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ add_subdirectory(example_signature_scan)
add_subdirectory(example_hud)

if(OMATH_ENABLE_HOOKING AND WIN32)
# Requires imgui with dx9-binding, dx11-binding, dx12-binding, win32-binding.
# Install via: vcpkg install imgui[dx9-binding,dx11-binding,dx12-binding,win32-binding]
# Requires imgui with dx9-binding, dx11-binding, dx12-binding, opengl3-binding, win32-binding.
# Install via: vcpkg install imgui[dx9-binding,dx11-binding,dx12-binding,opengl3-binding,win32-binding]
find_package(imgui CONFIG QUIET)
if(imgui_FOUND)
add_subdirectory(example_dx9_hook)
add_subdirectory(example_dx11_hook)
add_subdirectory(example_dx12_hook)
add_subdirectory(example_opengl_hook)
else()
message(STATUS "[omath] imgui not found — DX hook examples skipped")
message(STATUS "[omath] imgui not found - hook examples skipped")
endif()
endif()

Expand Down
13 changes: 13 additions & 0 deletions examples/example_opengl_hook/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
project(example_opengl_hook)

add_library(${PROJECT_NAME} MODULE dllmain.cpp)

set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 23
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")

find_package(imgui CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath imgui::imgui opengl32 gdi32)
116 changes: 116 additions & 0 deletions examples/example_opengl_hook/dllmain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include "omath/hooks/hooks_manager.hpp"
#include <Windows.h>
#include <chrono>
#include <imgui.h>
#include <imgui_impl_opengl3.h>
#include <imgui_impl_win32.h>
#include <optional>
#include <thread>

extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);

namespace
{
bool g_initialized = false;
bool g_init_attempted = false;
bool g_show_menu = true;

constexpr auto g_module_wait_delay = std::chrono::milliseconds{100};

void init(HDC hdc)
{
g_init_attempted = true;

const HWND hwnd = WindowFromDC(hdc);
if (!hwnd)
return;

ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().LogFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;

ImGui_ImplWin32_Init(hwnd);
ImGui_ImplOpenGL3_Init();

auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_wnd_proc(
[](HWND h, UINT msg, WPARAM wp, LPARAM lp) -> std::optional<LRESULT>
{
if (!g_show_menu)
return std::nullopt;

if (ImGui_ImplWin32_WndProcHandler(h, msg, wp, lp))
return 0;
return std::nullopt;
});
(void)mgr.hook_wnd_proc(hwnd);

g_initialized = true;
}

void on_swap_buffers(HDC hdc)
{
if (!g_initialized)
{
if (!g_init_attempted)
init(hdc);
return;
}

if (GetAsyncKeyState(VK_INSERT) & 1)
g_show_menu = !g_show_menu;

ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();

if (g_show_menu)
{
ImGui::SetNextWindowSize({300.f, 100.f}, ImGuiCond_Once);
ImGui::SetNextWindowPos({10.f, 10.f}, ImGuiCond_Once);
ImGui::Begin("omath | OpenGL hook");
ImGui::Text("Hook active");
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
ImGui::Text("INSERT toggles this window");
ImGui::End();
}

ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}

void hook_when_opengl_is_loaded()
{
while (!GetModuleHandle("opengl32.dll"))
std::this_thread::sleep_for(g_module_wait_delay);

auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_opengl_swap_buffers(on_swap_buffers);
(void)mgr.hook_opengl();
}
} // namespace

BOOL WINAPI DllMain(HINSTANCE h_instance, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(h_instance);
std::thread{hook_when_opengl_is_loaded}.detach();
}
else if (reason == DLL_PROCESS_DETACH)
{
auto& mgr = omath::hooks::HooksManager::get();
mgr.unhook_wnd_proc();
mgr.unhook_opengl();

if (g_initialized)
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
}
return true;
}
74 changes: 67 additions & 7 deletions include/omath/hooks/hooks_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
#ifdef OMATH_ENABLE_HOOKING
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <shared_mutex>

#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
Expand All @@ -16,6 +18,12 @@
#include <d3d12.h>
#include <d3d9.h>
#include <dxgi.h>
#endif // _WIN32

#ifdef __linux__
#include <GL/glx.h>
#endif // __linux__

#include <safetyhook.hpp>

namespace omath::hooks
Expand All @@ -25,6 +33,7 @@ namespace omath::hooks
HooksManager() = default;

public:
#ifdef _WIN32
// IDXGISwapChain callbacks — shared between DX11 and DX12 (same interface, same signature).
using present_callback = std::function<void(IDXGISwapChain*, UINT, UINT)>;
using resize_buffers_callback = std::function<void(IDXGISwapChain*, UINT, UINT, UINT, DXGI_FORMAT, UINT)>;
Expand All @@ -37,14 +46,27 @@ namespace omath::hooks
using dx9_reset_callback = std::function<void(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*)>;
using dx9_end_scene_callback = std::function<void(IDirect3DDevice9*)>;

// OpenGL callback — Windows. Fires before the hooked buffer swap function calls the original.
using opengl_swap_buffers_callback = std::function<void(HDC)>;

// Return nullopt to pass the message to the original WndProc; return a value to intercept it.
using wnd_proc_callback = std::function<std::optional<LRESULT>(HWND, UINT, WPARAM, LPARAM)>;
#endif // _WIN32

#ifdef __linux__
// OpenGL/GLX callback — Linux. Fires before glXSwapBuffers calls the original.
using opengl_swap_buffers_callback = std::function<void(Display*, GLXDrawable)>;
#endif // __linux__

template<typename Callback>
using callback_ptr = std::shared_ptr<const Callback>;

[[nodiscard]] static HooksManager& get();
HooksManager(const HooksManager&) = delete;
HooksManager& operator=(const HooksManager&) = delete;
~HooksManager();

#ifdef _WIN32
[[nodiscard]] bool hook_dx9();
void unhook_dx9();
void set_on_dx9_present(dx9_present_callback callback);
Expand All @@ -56,7 +78,13 @@ namespace omath::hooks

[[nodiscard]] bool hook_dx12();
void unhook_dx12();
#endif // _WIN32

[[nodiscard]] bool hook_opengl();
void unhook_opengl();
void set_on_opengl_swap_buffers(opengl_swap_buffers_callback callback);

#ifdef _WIN32
// Present and ResizeBuffers callbacks fire for whichever of DX11/DX12 is hooked.
void set_on_present(present_callback callback);
void set_on_resize_buffers(resize_buffers_callback callback);
Expand All @@ -65,8 +93,10 @@ namespace omath::hooks
[[nodiscard]] bool hook_wnd_proc(HWND hwnd);
void unhook_wnd_proc();
void set_on_wnd_proc(wnd_proc_callback callback);
#endif // _WIN32

private:
#ifdef _WIN32
[[nodiscard]]
static HRESULT __stdcall dx9_present_detour(IDirect3DDevice9* p_device, const RECT* p_source_rect,
const RECT* p_dest_rect, HWND h_dest_window_override,
Expand All @@ -91,11 +121,22 @@ namespace omath::hooks
UINT num_command_lists,
ID3D12CommandList* const* pp_command_lists);

[[nodiscard]]
static BOOL __stdcall opengl_wgl_swap_buffers_detour(HDC hdc);
[[nodiscard]]
static BOOL __stdcall opengl_swap_buffers_detour(HDC hdc);

[[nodiscard]]
static LRESULT __stdcall wnd_proc_detour(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param);
#endif // _WIN32

#ifdef __linux__
static void opengl_glx_swap_buffers_detour(Display* display, GLXDrawable drawable);
#endif // __linux__

mutable std::shared_mutex m_hook_state_mutex;

#ifdef _WIN32
mutable std::shared_mutex m_dx9_present_mutex;
mutable std::shared_mutex m_dx9_reset_mutex;
mutable std::shared_mutex m_dx9_end_scene_mutex;
Expand All @@ -105,7 +146,11 @@ namespace omath::hooks
mutable std::shared_mutex m_execute_command_lists_mutex;

mutable std::shared_mutex m_wnd_proc_mutex;
#endif // _WIN32

mutable std::shared_mutex m_opengl_swap_buffers_mutex;

#ifdef _WIN32
bool m_is_dx9_hooked = false;
bool m_is_dx11_hooked = false;
bool m_is_dx12_hooked = false;
Expand All @@ -125,14 +170,29 @@ namespace omath::hooks
safetyhook::InlineHook m_dx12_resize_buffers_hook;
safetyhook::InlineHook m_dx12_execute_command_lists_hook;

dx9_present_callback m_dx9_present_cb;
dx9_reset_callback m_dx9_reset_cb;
dx9_end_scene_callback m_dx9_end_scene_cb;
safetyhook::InlineHook m_opengl_wgl_swap_buffers_hook;
safetyhook::InlineHook m_opengl_swap_buffers_hook;
#endif // _WIN32

#ifdef __linux__
safetyhook::InlineHook m_opengl_glx_swap_buffers_hook;
#endif // __linux__

bool m_is_opengl_hooked = false;

#ifdef _WIN32
callback_ptr<dx9_present_callback> m_dx9_present_cb;
callback_ptr<dx9_reset_callback> m_dx9_reset_cb;
callback_ptr<dx9_end_scene_callback> m_dx9_end_scene_cb;

callback_ptr<present_callback> m_present_cb;
callback_ptr<resize_buffers_callback> m_resize_buffers_cb;
callback_ptr<execute_command_lists_callback> m_execute_command_lists_cb;

callback_ptr<wnd_proc_callback> m_wnd_proc_cb;
#endif // _WIN32

present_callback m_present_cb;
resize_buffers_callback m_resize_buffers_cb;
execute_command_lists_callback m_execute_command_lists_cb;
wnd_proc_callback m_wnd_proc_cb;
callback_ptr<opengl_swap_buffers_callback> m_opengl_swap_buffers_cb;
};
} // namespace omath::hooks

Expand Down
Loading
Loading