Merge pull request #188 from orange-cpp/feature/opengl_hook

Feature/opengl hook
This commit is contained in:
2026-05-06 23:41:00 +03:00
committed by GitHub
8 changed files with 388 additions and 41 deletions
+1
View File
@@ -139,6 +139,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNotAllPathsReturnValue/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNotAllPathsReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppObjectMemberMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppObjectMemberMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOutParameterMustBeWritten/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOutParameterMustBeWritten/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOverrideWithDifferentVisibility/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConst/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConstPtrOrRef/@EntryIndexedValue" value="SUGGESTION" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConstPtrOrRef/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNamesMismatch/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNamesMismatch/@EntryIndexedValue" value="HINT" type="string" />
-2
View File
@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CIDR" type="CPP_MODULE" version="4" />
+5 -2
View File
@@ -32,7 +32,7 @@ option(OMATH_ENABLE_FORCE_INLINE
"Will for compiler to make some functions to be force inlined no matter what" ON) "Will for compiler to make some functions to be force inlined no matter what" ON)
option(OMATH_ENABLE_LUA option(OMATH_ENABLE_LUA
"omath bindings for lua" OFF) "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) if(VCPKG_MANIFEST_FEATURES)
foreach(omath_feature IN LISTS VCPKG_MANIFEST_FEATURES) foreach(omath_feature IN LISTS VCPKG_MANIFEST_FEATURES)
@@ -113,7 +113,10 @@ if (OMATH_ENABLE_HOOKING)
target_link_libraries(${PROJECT_NAME} PRIVATE safetyhook::safetyhook) target_link_libraries(${PROJECT_NAME} PRIVATE safetyhook::safetyhook)
if (WIN32) 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 ()
endif () endif ()
+4 -3
View File
@@ -5,15 +5,16 @@ add_subdirectory(example_signature_scan)
add_subdirectory(example_hud) add_subdirectory(example_hud)
if(OMATH_ENABLE_HOOKING AND WIN32) if(OMATH_ENABLE_HOOKING AND WIN32)
# Requires imgui with 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,win32-binding] # Install via: vcpkg install imgui[dx9-binding,dx11-binding,dx12-binding,opengl3-binding,win32-binding]
find_package(imgui CONFIG QUIET) find_package(imgui CONFIG QUIET)
if(imgui_FOUND) if(imgui_FOUND)
add_subdirectory(example_dx9_hook) add_subdirectory(example_dx9_hook)
add_subdirectory(example_dx11_hook) add_subdirectory(example_dx11_hook)
add_subdirectory(example_dx12_hook) add_subdirectory(example_dx12_hook)
add_subdirectory(example_opengl_hook)
else() else()
message(STATUS "[omath] imgui not found — DX hook examples skipped") message(STATUS "[omath] imgui not found - hook examples skipped")
endif() endif()
endif() endif()
@@ -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
View File
@@ -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;
}
+67 -7
View File
@@ -3,9 +3,11 @@
#ifdef OMATH_ENABLE_HOOKING #ifdef OMATH_ENABLE_HOOKING
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <memory>
#include <optional> #include <optional>
#include <shared_mutex> #include <shared_mutex>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
@@ -16,6 +18,12 @@
#include <d3d12.h> #include <d3d12.h>
#include <d3d9.h> #include <d3d9.h>
#include <dxgi.h> #include <dxgi.h>
#endif // _WIN32
#ifdef __linux__
#include <GL/glx.h>
#endif // __linux__
#include <safetyhook.hpp> #include <safetyhook.hpp>
namespace omath::hooks namespace omath::hooks
@@ -25,6 +33,7 @@ namespace omath::hooks
HooksManager() = default; HooksManager() = default;
public: public:
#ifdef _WIN32
// IDXGISwapChain callbacks — shared between DX11 and DX12 (same interface, same signature). // IDXGISwapChain callbacks — shared between DX11 and DX12 (same interface, same signature).
using present_callback = std::function<void(IDXGISwapChain*, UINT, UINT)>; using present_callback = std::function<void(IDXGISwapChain*, UINT, UINT)>;
using resize_buffers_callback = std::function<void(IDXGISwapChain*, UINT, UINT, UINT, DXGI_FORMAT, UINT)>; using resize_buffers_callback = std::function<void(IDXGISwapChain*, UINT, UINT, UINT, DXGI_FORMAT, UINT)>;
@@ -37,14 +46,27 @@ namespace omath::hooks
using dx9_reset_callback = std::function<void(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*)>; using dx9_reset_callback = std::function<void(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*)>;
using dx9_end_scene_callback = std::function<void(IDirect3DDevice9*)>; 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. // 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)>; 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(); [[nodiscard]] static HooksManager& get();
HooksManager(const HooksManager&) = delete; HooksManager(const HooksManager&) = delete;
HooksManager& operator=(const HooksManager&) = delete; HooksManager& operator=(const HooksManager&) = delete;
~HooksManager(); ~HooksManager();
#ifdef _WIN32
[[nodiscard]] bool hook_dx9(); [[nodiscard]] bool hook_dx9();
void unhook_dx9(); void unhook_dx9();
void set_on_dx9_present(dx9_present_callback callback); void set_on_dx9_present(dx9_present_callback callback);
@@ -56,7 +78,13 @@ namespace omath::hooks
[[nodiscard]] bool hook_dx12(); [[nodiscard]] bool hook_dx12();
void unhook_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. // Present and ResizeBuffers callbacks fire for whichever of DX11/DX12 is hooked.
void set_on_present(present_callback callback); void set_on_present(present_callback callback);
void set_on_resize_buffers(resize_buffers_callback callback); void set_on_resize_buffers(resize_buffers_callback callback);
@@ -65,8 +93,10 @@ namespace omath::hooks
[[nodiscard]] bool hook_wnd_proc(HWND hwnd); [[nodiscard]] bool hook_wnd_proc(HWND hwnd);
void unhook_wnd_proc(); void unhook_wnd_proc();
void set_on_wnd_proc(wnd_proc_callback callback); void set_on_wnd_proc(wnd_proc_callback callback);
#endif // _WIN32
private: private:
#ifdef _WIN32
[[nodiscard]] [[nodiscard]]
static HRESULT __stdcall dx9_present_detour(IDirect3DDevice9* p_device, const RECT* p_source_rect, static HRESULT __stdcall dx9_present_detour(IDirect3DDevice9* p_device, const RECT* p_source_rect,
const RECT* p_dest_rect, HWND h_dest_window_override, const RECT* p_dest_rect, HWND h_dest_window_override,
@@ -91,11 +121,22 @@ namespace omath::hooks
UINT num_command_lists, UINT num_command_lists,
ID3D12CommandList* const* pp_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]] [[nodiscard]]
static LRESULT __stdcall wnd_proc_detour(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param); 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; mutable std::shared_mutex m_hook_state_mutex;
#ifdef _WIN32
mutable std::shared_mutex m_dx9_present_mutex; mutable std::shared_mutex m_dx9_present_mutex;
mutable std::shared_mutex m_dx9_reset_mutex; mutable std::shared_mutex m_dx9_reset_mutex;
mutable std::shared_mutex m_dx9_end_scene_mutex; mutable std::shared_mutex m_dx9_end_scene_mutex;
@@ -105,7 +146,11 @@ namespace omath::hooks
mutable std::shared_mutex m_execute_command_lists_mutex; mutable std::shared_mutex m_execute_command_lists_mutex;
mutable std::shared_mutex m_wnd_proc_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_dx9_hooked = false;
bool m_is_dx11_hooked = false; bool m_is_dx11_hooked = false;
bool m_is_dx12_hooked = false; bool m_is_dx12_hooked = false;
@@ -125,14 +170,29 @@ namespace omath::hooks
safetyhook::InlineHook m_dx12_resize_buffers_hook; safetyhook::InlineHook m_dx12_resize_buffers_hook;
safetyhook::InlineHook m_dx12_execute_command_lists_hook; safetyhook::InlineHook m_dx12_execute_command_lists_hook;
dx9_present_callback m_dx9_present_cb; safetyhook::InlineHook m_opengl_wgl_swap_buffers_hook;
dx9_reset_callback m_dx9_reset_cb; safetyhook::InlineHook m_opengl_swap_buffers_hook;
dx9_end_scene_callback m_dx9_end_scene_cb; #endif // _WIN32
present_callback m_present_cb; #ifdef __linux__
resize_buffers_callback m_resize_buffers_cb; safetyhook::InlineHook m_opengl_glx_swap_buffers_hook;
execute_command_lists_callback m_execute_command_lists_cb; #endif // __linux__
wnd_proc_callback m_wnd_proc_cb;
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
callback_ptr<opengl_swap_buffers_callback> m_opengl_swap_buffers_cb;
}; };
} // namespace omath::hooks } // namespace omath::hooks
+182 -27
View File
@@ -1,10 +1,20 @@
#include "omath/hooks/hooks_manager.hpp" #include "omath/hooks/hooks_manager.hpp"
#ifdef OMATH_ENABLE_HOOKING #ifdef OMATH_ENABLE_HOOKING
#ifdef _WIN32
#include <d3d11.h> #include <d3d11.h>
#endif // _WIN32
#ifdef __linux__
#include <dlfcn.h>
#endif // __linux__
namespace namespace
{ {
#ifdef _WIN32
thread_local bool g_is_inside_opengl_swap_buffers = false;
class DummyWindow final class DummyWindow final
{ {
WNDCLASSEX m_window_class{}; WNDCLASSEX m_window_class{};
@@ -43,6 +53,15 @@ namespace
return (*reinterpret_cast<void***>(com_obj))[index]; return (*reinterpret_cast<void***>(com_obj))[index];
} }
void* module_proc(const char* module_name, const char* proc_name)
{
const HMODULE module = GetModuleHandle(module_name);
if (!module)
return nullptr;
return reinterpret_cast<void*>(GetProcAddress(module, proc_name));
}
struct dx12_vtable_fns struct dx12_vtable_fns
{ {
void* present; void* present;
@@ -153,6 +172,7 @@ namespace
vtable_fn(objs.command_queue, 10), // ID3D12CommandQueue::ExecuteCommandLists vtable_fn(objs.command_queue, 10), // ID3D12CommandQueue::ExecuteCommandLists
}; };
} }
#endif // _WIN32
} // namespace } // namespace
namespace omath::hooks namespace omath::hooks
@@ -165,12 +185,16 @@ namespace omath::hooks
HooksManager::~HooksManager() HooksManager::~HooksManager()
{ {
#ifdef _WIN32
unhook_wnd_proc(); unhook_wnd_proc();
unhook_dx9(); unhook_dx9();
unhook_dx11(); unhook_dx11();
unhook_dx12(); unhook_dx12();
#endif // _WIN32
unhook_opengl();
} }
#ifdef _WIN32
bool HooksManager::hook_dx9() bool HooksManager::hook_dx9()
{ {
std::unique_lock lock(m_hook_state_mutex); std::unique_lock lock(m_hook_state_mutex);
@@ -247,19 +271,19 @@ namespace omath::hooks
void HooksManager::set_on_dx9_present(dx9_present_callback callback) void HooksManager::set_on_dx9_present(dx9_present_callback callback)
{ {
std::unique_lock lock(m_dx9_present_mutex); std::unique_lock lock(m_dx9_present_mutex);
m_dx9_present_cb = std::move(callback); m_dx9_present_cb = callback ? std::make_shared<dx9_present_callback>(std::move(callback)) : nullptr;
} }
void HooksManager::set_on_dx9_reset(dx9_reset_callback callback) void HooksManager::set_on_dx9_reset(dx9_reset_callback callback)
{ {
std::unique_lock lock(m_dx9_reset_mutex); std::unique_lock lock(m_dx9_reset_mutex);
m_dx9_reset_cb = std::move(callback); m_dx9_reset_cb = callback ? std::make_shared<dx9_reset_callback>(std::move(callback)) : nullptr;
} }
void HooksManager::set_on_dx9_end_scene(dx9_end_scene_callback callback) void HooksManager::set_on_dx9_end_scene(dx9_end_scene_callback callback)
{ {
std::unique_lock lock(m_dx9_end_scene_mutex); std::unique_lock lock(m_dx9_end_scene_mutex);
m_dx9_end_scene_cb = std::move(callback); m_dx9_end_scene_cb = callback ? std::make_shared<dx9_end_scene_callback>(std::move(callback)) : nullptr;
} }
bool HooksManager::hook_dx11() bool HooksManager::hook_dx11()
@@ -381,22 +405,60 @@ namespace omath::hooks
m_is_dx12_hooked = false; m_is_dx12_hooked = false;
} }
bool HooksManager::hook_opengl()
{
std::unique_lock lock(m_hook_state_mutex);
if (m_is_opengl_hooked)
return true;
if (void* wgl_swap_buffers = module_proc("opengl32.dll", "wglSwapBuffers"))
{
m_opengl_wgl_swap_buffers_hook = safetyhook::create_inline(
wgl_swap_buffers, reinterpret_cast<void*>(&opengl_wgl_swap_buffers_detour));
}
if (void* swap_buffers = module_proc("gdi32.dll", "SwapBuffers"))
{
m_opengl_swap_buffers_hook =
safetyhook::create_inline(swap_buffers, reinterpret_cast<void*>(&opengl_swap_buffers_detour));
}
if (!m_opengl_wgl_swap_buffers_hook && !m_opengl_swap_buffers_hook)
{
m_opengl_wgl_swap_buffers_hook = {};
m_opengl_swap_buffers_hook = {};
return false;
}
m_is_opengl_hooked = true;
return true;
}
void HooksManager::unhook_opengl()
{
std::unique_lock lock(m_hook_state_mutex);
m_opengl_wgl_swap_buffers_hook = {};
m_opengl_swap_buffers_hook = {};
m_is_opengl_hooked = false;
}
void HooksManager::set_on_present(present_callback callback) void HooksManager::set_on_present(present_callback callback)
{ {
std::unique_lock lock(m_present_mutex); std::unique_lock lock(m_present_mutex);
m_present_cb = std::move(callback); m_present_cb = callback ? std::make_shared<present_callback>(std::move(callback)) : nullptr;
} }
void HooksManager::set_on_resize_buffers(resize_buffers_callback callback) void HooksManager::set_on_resize_buffers(resize_buffers_callback callback)
{ {
std::unique_lock lock(m_resize_buffers_mutex); std::unique_lock lock(m_resize_buffers_mutex);
m_resize_buffers_cb = std::move(callback); m_resize_buffers_cb = callback ? std::make_shared<resize_buffers_callback>(std::move(callback)) : nullptr;
} }
void HooksManager::set_on_execute_command_lists(execute_command_lists_callback callback) void HooksManager::set_on_execute_command_lists(execute_command_lists_callback callback)
{ {
std::unique_lock lock(m_execute_command_lists_mutex); std::unique_lock lock(m_execute_command_lists_mutex);
m_execute_command_lists_cb = std::move(callback); m_execute_command_lists_cb =
callback ? std::make_shared<execute_command_lists_callback>(std::move(callback)) : nullptr;
} }
bool HooksManager::hook_wnd_proc(HWND hwnd) bool HooksManager::hook_wnd_proc(HWND hwnd)
@@ -431,24 +493,25 @@ namespace omath::hooks
void HooksManager::set_on_wnd_proc(wnd_proc_callback callback) void HooksManager::set_on_wnd_proc(wnd_proc_callback callback)
{ {
std::unique_lock lock(m_wnd_proc_mutex); std::unique_lock lock(m_wnd_proc_mutex);
m_wnd_proc_cb = std::move(callback); m_wnd_proc_cb = callback ? std::make_shared<wnd_proc_callback>(std::move(callback)) : nullptr;
} }
// Detour implementations: copy callback under shared lock, call it unlocked, // Detour implementations: copy a shared_ptr to the callback under shared lock, call it unlocked,
// then call original. This avoids a deadlock if the callback itself calls set_on_*(). // then call original. This avoids copying captured lambda state every frame and still avoids
// a deadlock if the callback itself calls set_on_*().
HRESULT __stdcall HooksManager::dx9_present_detour(IDirect3DDevice9* p_device, const RECT* p_source_rect, HRESULT __stdcall HooksManager::dx9_present_detour(IDirect3DDevice9* p_device, const RECT* p_source_rect,
const RECT* p_dest_rect, HWND h_dest_window_override, const RECT* p_dest_rect, HWND h_dest_window_override,
const RGNDATA* p_dirty_region) const RGNDATA* p_dirty_region)
{ {
auto& mgr = get(); auto& mgr = get();
dx9_present_callback cb; callback_ptr<dx9_present_callback> cb;
{ {
std::shared_lock lock(mgr.m_dx9_present_mutex); std::shared_lock lock(mgr.m_dx9_present_mutex);
cb = mgr.m_dx9_present_cb; cb = mgr.m_dx9_present_cb;
} }
if (cb) if (cb)
cb(p_device, p_source_rect, p_dest_rect, h_dest_window_override, p_dirty_region); (*cb)(p_device, p_source_rect, p_dest_rect, h_dest_window_override, p_dirty_region);
return mgr.m_dx9_present_hook.call<HRESULT>(p_device, p_source_rect, p_dest_rect, h_dest_window_override, return mgr.m_dx9_present_hook.call<HRESULT>(p_device, p_source_rect, p_dest_rect, h_dest_window_override,
p_dirty_region); p_dirty_region);
} }
@@ -457,39 +520,39 @@ namespace omath::hooks
D3DPRESENT_PARAMETERS* p_presentation_parameters) D3DPRESENT_PARAMETERS* p_presentation_parameters)
{ {
auto& mgr = get(); auto& mgr = get();
dx9_reset_callback cb; callback_ptr<dx9_reset_callback> cb;
{ {
std::shared_lock lock(mgr.m_dx9_reset_mutex); std::shared_lock lock(mgr.m_dx9_reset_mutex);
cb = mgr.m_dx9_reset_cb; cb = mgr.m_dx9_reset_cb;
} }
if (cb) if (cb)
cb(p_device, p_presentation_parameters); (*cb)(p_device, p_presentation_parameters);
return mgr.m_dx9_reset_hook.call<HRESULT>(p_device, p_presentation_parameters); return mgr.m_dx9_reset_hook.call<HRESULT>(p_device, p_presentation_parameters);
} }
HRESULT __stdcall HooksManager::dx9_end_scene_detour(IDirect3DDevice9* p_device) HRESULT __stdcall HooksManager::dx9_end_scene_detour(IDirect3DDevice9* p_device)
{ {
auto& mgr = get(); auto& mgr = get();
dx9_end_scene_callback cb; callback_ptr<dx9_end_scene_callback> cb;
{ {
std::shared_lock lock(mgr.m_dx9_end_scene_mutex); std::shared_lock lock(mgr.m_dx9_end_scene_mutex);
cb = mgr.m_dx9_end_scene_cb; cb = mgr.m_dx9_end_scene_cb;
} }
if (cb) if (cb)
cb(p_device); (*cb)(p_device);
return mgr.m_dx9_end_scene_hook.call<HRESULT>(p_device); return mgr.m_dx9_end_scene_hook.call<HRESULT>(p_device);
} }
HRESULT __stdcall HooksManager::dx11_present_detour(IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags) HRESULT __stdcall HooksManager::dx11_present_detour(IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags)
{ {
auto& mgr = get(); auto& mgr = get();
present_callback cb; callback_ptr<present_callback> cb;
{ {
std::shared_lock lock(mgr.m_present_mutex); std::shared_lock lock(mgr.m_present_mutex);
cb = mgr.m_present_cb; cb = mgr.m_present_cb;
} }
if (cb) if (cb)
cb(p_swap_chain, sync_interval, flags); (*cb)(p_swap_chain, sync_interval, flags);
return mgr.m_dx11_present_hook.call<HRESULT>(p_swap_chain, sync_interval, flags); return mgr.m_dx11_present_hook.call<HRESULT>(p_swap_chain, sync_interval, flags);
} }
@@ -498,13 +561,13 @@ namespace omath::hooks
UINT swap_chain_flags) UINT swap_chain_flags)
{ {
auto& mgr = get(); auto& mgr = get();
resize_buffers_callback cb; callback_ptr<resize_buffers_callback> cb;
{ {
std::shared_lock lock(mgr.m_resize_buffers_mutex); std::shared_lock lock(mgr.m_resize_buffers_mutex);
cb = mgr.m_resize_buffers_cb; cb = mgr.m_resize_buffers_cb;
} }
if (cb) if (cb)
cb(p_swap_chain, buffer_count, width, height, new_format, swap_chain_flags); (*cb)(p_swap_chain, buffer_count, width, height, new_format, swap_chain_flags);
return mgr.m_dx11_resize_buffers_hook.call<HRESULT>(p_swap_chain, buffer_count, width, height, new_format, return mgr.m_dx11_resize_buffers_hook.call<HRESULT>(p_swap_chain, buffer_count, width, height, new_format,
swap_chain_flags); swap_chain_flags);
} }
@@ -512,13 +575,13 @@ namespace omath::hooks
HRESULT __stdcall HooksManager::dx12_present_detour(IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags) HRESULT __stdcall HooksManager::dx12_present_detour(IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags)
{ {
auto& mgr = get(); auto& mgr = get();
present_callback cb; callback_ptr<present_callback> cb;
{ {
std::shared_lock lock(mgr.m_present_mutex); std::shared_lock lock(mgr.m_present_mutex);
cb = mgr.m_present_cb; cb = mgr.m_present_cb;
} }
if (cb) if (cb)
cb(p_swap_chain, sync_interval, flags); (*cb)(p_swap_chain, sync_interval, flags);
return mgr.m_dx12_present_hook.call<HRESULT>(p_swap_chain, sync_interval, flags); return mgr.m_dx12_present_hook.call<HRESULT>(p_swap_chain, sync_interval, flags);
} }
@@ -527,13 +590,13 @@ namespace omath::hooks
UINT swap_chain_flags) UINT swap_chain_flags)
{ {
auto& mgr = get(); auto& mgr = get();
resize_buffers_callback cb; callback_ptr<resize_buffers_callback> cb;
{ {
std::shared_lock lock(mgr.m_resize_buffers_mutex); std::shared_lock lock(mgr.m_resize_buffers_mutex);
cb = mgr.m_resize_buffers_cb; cb = mgr.m_resize_buffers_cb;
} }
if (cb) if (cb)
cb(p_swap_chain, buffer_count, width, height, new_format, swap_chain_flags); (*cb)(p_swap_chain, buffer_count, width, height, new_format, swap_chain_flags);
return mgr.m_dx12_resize_buffers_hook.call<HRESULT>(p_swap_chain, buffer_count, width, height, new_format, return mgr.m_dx12_resize_buffers_hook.call<HRESULT>(p_swap_chain, buffer_count, width, height, new_format,
swap_chain_flags); swap_chain_flags);
} }
@@ -543,20 +606,62 @@ namespace omath::hooks
ID3D12CommandList* const* pp_command_lists) ID3D12CommandList* const* pp_command_lists)
{ {
auto& mgr = get(); auto& mgr = get();
execute_command_lists_callback cb; callback_ptr<execute_command_lists_callback> cb;
{ {
std::shared_lock lock(mgr.m_execute_command_lists_mutex); std::shared_lock lock(mgr.m_execute_command_lists_mutex);
cb = mgr.m_execute_command_lists_cb; cb = mgr.m_execute_command_lists_cb;
} }
if (cb) if (cb)
cb(p_command_queue, num_command_lists, pp_command_lists); (*cb)(p_command_queue, num_command_lists, pp_command_lists);
mgr.m_dx12_execute_command_lists_hook.call<void>(p_command_queue, num_command_lists, pp_command_lists); mgr.m_dx12_execute_command_lists_hook.call<void>(p_command_queue, num_command_lists, pp_command_lists);
} }
BOOL __stdcall HooksManager::opengl_wgl_swap_buffers_detour(HDC hdc)
{
auto& mgr = get();
if (!g_is_inside_opengl_swap_buffers)
return mgr.m_opengl_wgl_swap_buffers_hook.call<BOOL>(hdc);
g_is_inside_opengl_swap_buffers = true;
callback_ptr<opengl_swap_buffers_callback> cb;
{
std::shared_lock lock(mgr.m_opengl_swap_buffers_mutex);
cb = mgr.m_opengl_swap_buffers_cb;
}
if (cb)
(*cb)(hdc);
const BOOL result = mgr.m_opengl_wgl_swap_buffers_hook.call<BOOL>(hdc);
g_is_inside_opengl_swap_buffers = false;
return result;
}
BOOL __stdcall HooksManager::opengl_swap_buffers_detour(HDC hdc)
{
auto& mgr = get();
if (g_is_inside_opengl_swap_buffers)
return mgr.m_opengl_swap_buffers_hook.call<BOOL>(hdc);
g_is_inside_opengl_swap_buffers = true;
callback_ptr<opengl_swap_buffers_callback> cb;
{
std::shared_lock lock(mgr.m_opengl_swap_buffers_mutex);
cb = mgr.m_opengl_swap_buffers_cb;
}
if (cb)
(*cb)(hdc);
const BOOL result = mgr.m_opengl_swap_buffers_hook.call<BOOL>(hdc);
g_is_inside_opengl_swap_buffers = false;
return result;
}
LRESULT __stdcall HooksManager::wnd_proc_detour(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) LRESULT __stdcall HooksManager::wnd_proc_detour(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param)
{ {
auto& mgr = get(); auto& mgr = get();
wnd_proc_callback cb; callback_ptr<wnd_proc_callback> cb;
WNDPROC original; WNDPROC original;
{ {
std::shared_lock lock(mgr.m_wnd_proc_mutex); std::shared_lock lock(mgr.m_wnd_proc_mutex);
@@ -565,11 +670,61 @@ namespace omath::hooks
} }
if (cb) if (cb)
{ {
if (const auto result = cb(hwnd, msg, w_param, l_param)) if (const auto result = (*cb)(hwnd, msg, w_param, l_param))
return *result; return *result;
} }
return CallWindowProc(original, hwnd, msg, w_param, l_param); return CallWindowProc(original, hwnd, msg, w_param, l_param);
} }
#endif // _WIN32
#ifdef __linux__
bool HooksManager::hook_opengl()
{
std::unique_lock lock(m_hook_state_mutex);
if (m_is_opengl_hooked)
return true;
void* glx_swap_buffers = dlsym(RTLD_DEFAULT, "glXSwapBuffers");
if (!glx_swap_buffers)
return false;
m_opengl_glx_swap_buffers_hook = safetyhook::create_inline(
glx_swap_buffers, reinterpret_cast<void*>(&opengl_glx_swap_buffers_detour));
if (!m_opengl_glx_swap_buffers_hook)
return false;
m_is_opengl_hooked = true;
return true;
}
void HooksManager::unhook_opengl()
{
std::unique_lock lock(m_hook_state_mutex);
m_opengl_glx_swap_buffers_hook = {};
m_is_opengl_hooked = false;
}
void HooksManager::opengl_glx_swap_buffers_detour(Display* display, GLXDrawable drawable)
{
auto& mgr = get();
callback_ptr<opengl_swap_buffers_callback> cb;
{
std::shared_lock lock(mgr.m_opengl_swap_buffers_mutex);
cb = mgr.m_opengl_swap_buffers_cb;
}
if (cb)
(*cb)(display, drawable);
mgr.m_opengl_glx_swap_buffers_hook.call<void>(display, drawable);
}
#endif // __linux__
void HooksManager::set_on_opengl_swap_buffers(opengl_swap_buffers_callback callback)
{
std::unique_lock lock(m_opengl_swap_buffers_mutex);
m_opengl_swap_buffers_cb =
callback ? std::make_shared<opengl_swap_buffers_callback>(std::move(callback)) : nullptr;
}
} // namespace omath::hooks } // namespace omath::hooks
#else // !OMATH_ENABLE_HOOKING #else // !OMATH_ENABLE_HOOKING