# Conflicts:
#	build_steamos.sh
#	dll/dll.cpp
This commit is contained in:
Mr_Goldberg 2020-01-19 12:50:01 -05:00
commit ffdaf72597
No known key found for this signature in database
GPG Key ID: 8597D87419DEF278
74 changed files with 49147 additions and 852 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ rtlgenrandom*
steamclient.exp
steamclient.lib
out/*
glew

View File

@ -59,7 +59,8 @@ build_windows:
image: fedora:29
script:
- dnf -y install wine wget p7zip sed dos2unix
- dnf -y install wine wget p7zip sed dos2unix unzip
- ./download_glew.sh
- unix2dos *.txt
- unix2dos files_example/*.txt files_example/*/*.txt
- sed -i 's/..\\vcpkg\\packages\\/.\\/g' build_set_protobuf_directories.bat
@ -71,6 +72,11 @@ build_windows:
- 7za x sdk_standalone.7z -osdk_standalone
- DLL_FILES="$(ls dll/*.cpp | tr "\n" " ")"; sed "s|dll/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls detours/*.cpp | tr "\n" " ")"; sed "s|detours/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls overlay_experimental/*.cpp | tr "\n" " ")"; sed "s|overlay_experimental/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls overlay_experimental/windows/*.cpp | tr "\n" " ")"; sed "s|overlay_experimental/windows/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls ImGui/*.cpp | tr "\n" " ")"; sed "s|ImGui/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls ImGui/impls/*.cpp | tr "\n" " ")"; sed "s|ImGui/impls/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls ImGui/impls/windows/*.cpp | tr "\n" " ")"; sed "s|ImGui/impls/windows/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls dll/*.proto | tr "\n" " " | sed "s/.proto/.pb.cc/g")"; sed "s|dll/\*.cc|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls steamclient_loader/*.cpp | tr "\n" " ")"; sed "s|steamclient_loader/\*.cpp|$DLL_FILES|g" -i *.bat
- export WINEDEBUG=-all
@ -98,7 +104,7 @@ build_cmake_linux:
script:
- mkdir cmake-builds && cd cmake-builds
- mkdir x64-release && cd x64-release
- cmake ../../ -G "Ninja" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" && ninja
- cmake ../../ -G "Ninja" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DNO_OVERLAY=ON && ninja
- cd ..
# - mkdir x64-experimental-release && cd x64-experimental-release
# - cmake ../../ -G "Ninja" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DEMU_EXPERIMENTAL_BUILD:BOOL=ON && ninja
@ -114,13 +120,14 @@ build_cmake_windows:
image: fedora:29
before_script:
- dnf -y install wine wget p7zip sed dos2unix
- dnf -y install wine wget p7zip sed dos2unix unzip
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/48db8f434a193aae872279dc4f5dde6a/sdk_standalone.7z'
- 7za x sdk_standalone.7z -osdk_standalone
- wget 'https://github.com/Kitware/CMake/releases/download/v3.15.0-rc1/cmake-3.15.0-rc1-win64-x64.zip'
- 7za x cmake-3.15.0-rc1-win64-x64.zip
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/0119304e030098b4821d73170fe52084/protobuf_x64-windows-static.7z'
- 7za x protobuf_x64-windows-static.7z -oprotobuf_x64-windows-static
- ./download_glew.sh
script:
- export WINEDEBUG=-all
@ -128,13 +135,13 @@ build_cmake_windows:
- mkdir cmake-builds && cd cmake-builds
- mkdir x64-release && cd x64-release
- echo call .\\..\\..\\sdk_standalone\\set_vars64.bat >> cmake-build.bat
- echo .\\..\\..\\cmake-3.15.0-rc1-win64-x64\\bin\\cmake.exe ..\\.. -G \"NMake Makefiles\" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DCMAKE_PREFIX_PATH="protobuf_x64-windows-static" -DProtobuf_PROTOC_EXECUTABLE:STRING="./../../protobuf_x64-windows-static/tools/protobuf/protoc.exe" >> cmake-build.bat
- echo .\\..\\..\\cmake-3.15.0-rc1-win64-x64\\bin\\cmake.exe ..\\.. -G \"NMake Makefiles\" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DNO_OVERLAY=ON -DCMAKE_PREFIX_PATH="protobuf_x64-windows-static" -DProtobuf_PROTOC_EXECUTABLE:STRING="./../../protobuf_x64-windows-static/tools/protobuf/protoc.exe" >> cmake-build.bat
- echo nmake.exe >> cmake-build.bat
- wine cmd /c cmake-build.bat
- cd ..
- mkdir x64-experimental-release && cd x64-experimental-release
- echo call .\\..\\..\\sdk_standalone\\set_vars64.bat >> cmake-build.bat
- echo .\\..\\..\\cmake-3.15.0-rc1-win64-x64\\bin\\cmake.exe ..\\.. -G \"NMake Makefiles\" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DCMAKE_PREFIX_PATH="protobuf_x64-windows-static" -DProtobuf_PROTOC_EXECUTABLE:STRING="./../../protobuf_x64-windows-static/tools/protobuf/protoc.exe" >> cmake-build.bat
- echo .\\..\\..\\cmake-3.15.0-rc1-win64-x64\\bin\\cmake.exe ..\\.. -G \"NMake Makefiles\" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DEMU_EXPERIMENTAL_BUILD=ON -DCMAKE_PREFIX_PATH="protobuf_x64-windows-static" -DProtobuf_PROTOC_EXECUTABLE:STRING="./../../protobuf_x64-windows-static/tools/protobuf/protoc.exe" >> cmake-build.bat
- echo nmake.exe >> cmake-build.bat
- wine cmd /c cmake-build.bat
- cd ..

View File

@ -50,12 +50,14 @@ if(WIN32)
set(LIB_STEAMNETWORKINGSOCKETS steamnetworkingsockets64)
set(BIN_LOBBY_CONNECT lobby_connect64)
set(BIN_GENERATE_INTERFACES_FILE generate_interfaces_file64)
link_directories(glew/lib/Release/x64)
else()
set(LIB_STEAM_API steam_api)
set(LIB_STEAMCLIENT steamclient)
set(LIB_STEAMNETWORKINGSOCKETS steamnetworkingsockets)
set(BIN_LOBBY_CONNECT lobby_connect)
set(BIN_GENERATE_INTERFACES_FILE generate_interfaces_file)
link_directories(glew/lib/Release/Win32)
endif()
elseif(UNIX AND NOT APPLE)
set(LIB_STEAM_API steam_api)
@ -77,6 +79,24 @@ file(GLOB DETOURS_SRC_SHARED
detours/*.cpp
)
if(WIN32)
file(GLOB OVERLAY_EXPERIMENTAL_SRC_SHARED
overlay_experimental/*.cpp
overlay_experimental/windows/*.cpp
ImGui/*.cpp
ImGui/impls/*.cpp
ImGui/impls/windows/*.cpp
)
elseif(UNIX)
file(GLOB OVERLAY_EXPERIMENTAL_SRC_SHARED
overlay_experimental/*.cpp
overlay_experimental/linux/*.cpp
ImGui/*.cpp
ImGui/impls/*.cpp
ImGui/impls/linux/*.cpp
)
endif()
###################################################
# Setup for the steam_api(64).dll / libsteam_api.so
###################################################
@ -85,6 +105,7 @@ file(GLOB DETOURS_SRC_SHARED
add_library(${LIB_STEAM_API}
SHARED
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:${DETOURS_SRC_SHARED}>
$<$<AND:$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<NOT:$<BOOL:${NO_OVERLAY}>>>:${OVERLAY_EXPERIMENTAL_SRC_SHARED}>
${DLL_SRC_SHARED}
${PROTO_SRCS}
${PROTO_HDRS}
@ -95,6 +116,8 @@ target_include_directories(${LIB_STEAM_API}
PRIVATE
${PROTOBUF_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/ImGui
${CMAKE_CURRENT_SOURCE_DIR}/glew/include
)
# Link the required libraries
@ -103,6 +126,9 @@ target_link_libraries(${LIB_STEAM_API}
protobuf::libprotobuf
$<$<BOOL:${WIN32}>:ws2_32>
$<$<BOOL:${WIN32}>:iphlpapi>
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<NOT:$<BOOL:${NO_OVERLAY}>>>:glew32s.lib>
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<NOT:$<BOOL:${NO_OVERLAY}>>>:opengl32.lib>
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<NOT:$<BOOL:${NO_OVERLAY}>>>:Winmm.lib>
)
# Add target compile definitions
@ -113,6 +139,8 @@ target_compile_definitions(${LIB_STEAM_API}
$<$<CONFIG:RelWithDebInfo>:EMU_RELEASE_BUILD>
$<$<CONFIG:MinSizeRel>:EMU_RELEASE_BUILD>
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:EMU_EXPERIMENTAL_BUILD>
$<$<BOOL:${NO_OVERLAY}>:NO_OVERLAY>
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<NOT:$<BOOL:${NO_OVERLAY}>>>:GLEW_STATIC>
)
# Install the target
@ -237,6 +265,7 @@ target_link_libraries(${BIN_LOBBY_CONNECT}
target_compile_definitions(${BIN_LOBBY_CONNECT}
PRIVATE
NO_DISK_WRITES
NO_OVERLAY
LOBBY_CONNECT
$<$<CONFIG:>:EMU_RELEASE_BUILD>
$<$<CONFIG:Release>:EMU_RELEASE_BUILD>

97
ImGui/imconfig.h Normal file
View File

@ -0,0 +1,97 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h)
// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h"
// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include
// the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
//---- Define assertion handler. Defaults to calling assert().
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty)
// It is very strongly recommended to NOT disable the demo windows during development. Please read the comments in imgui_demo.cpp.
#define IMGUI_DISABLE_DEMO_WINDOWS
#define IMGUI_DISABLE_METRICS_WINDOW
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_DISABLE_OSX_FUNCTIONS // [OSX] Won't use and link with any OSX function (clipboard).
//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf.
//#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of imgui cpp files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
#define IM_VEC2_CLASS_EXTRA \
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
operator MyVec2() const { return MyVec2(x,y); }
#define IM_VEC4_CLASS_EXTRA \
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- Using 32-bits vertex indices (default is 16-bits) is one way to allow large meshes with more than 64K vertices.
// Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bits indices).
// Another way to allow large meshes while keeping 16-bits indices is to handle ImDrawCmd::VtxOffset in your renderer.
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
//#define ImDrawIdx unsigned int
//---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly)
//struct ImDrawList;
//struct ImDrawCmd;
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools
// Use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
// Have the Item Picker break in the ItemAdd() function instead of ItemHoverable() - which is earlier in the code, will catch a few extra items, allow picking items other than Hovered one.
// This adds a small runtime cost which is why it is not enabled by default.
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
}
*/
#define IMGUI_INCLUDE_IMGUI_USER_H
#define IMGUI_IMPL_OPENGL_LOADER_GLEW

9899
ImGui/imgui.cpp Normal file

File diff suppressed because it is too large Load Diff

2224
ImGui/imgui.h Normal file

File diff suppressed because it is too large Load Diff

3386
ImGui/imgui_draw.cpp Normal file

File diff suppressed because it is too large Load Diff

1714
ImGui/imgui_internal.h Normal file

File diff suppressed because it is too large Load Diff

5
ImGui/imgui_user.h Normal file
View File

@ -0,0 +1,5 @@
namespace ImGui
{
IMGUI_API bool ColoredInputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool ColoredInputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
}

8340
ImGui/imgui_widgets.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,634 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
// 2019-03-15: OpenGL: Added a dummy GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
//----------------------------------------
// OpenGL GLSL GLSL
// version version string
//----------------------------------------
// 2.0 110 "#version 110"
// 2.1 120 "#version 120"
// 3.0 130 "#version 130"
// 3.1 140 "#version 140"
// 3.2 150 "#version 150"
// 3.3 330 "#version 330 core"
// 4.0 400 "#version 400 core"
// 4.1 410 "#version 410 core"
// 4.2 420 "#version 410 core"
// 4.3 430 "#version 430 core"
// ES 2.0 100 "#version 100" = WebGL 1.0
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "../imgui.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
#include <stdint.h> // intptr_t
#endif
#if defined(__APPLE__)
#include "TargetConditionals.h"
#endif
// Auto-detect GL version
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
#elif defined(__EMSCRIPTEN__)
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
#endif
#endif
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <GLES2/gl2.h>
#elif defined(IMGUI_IMPL_OPENGL_ES3)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
#else
#include <GLES3/gl3.h> // Use GL ES 3
#endif
#else
// About Desktop OpenGL function loaders:
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
#include <GL/gl3w.h> // Needs to be initialized with gl3wInit() in user's code
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code
#else
#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#endif
#endif
// Desktop GL has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3)
#define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 0
#else
#define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 1
#endif
// OpenGL Data
static char g_GlslVersionString[32] = "";
static GLuint g_FontTexture = 0;
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
static int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
#if IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif
// Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
#if defined(IMGUI_IMPL_OPENGL_ES2)
if (glsl_version == NULL)
glsl_version = "#version 100";
#elif defined(IMGUI_IMPL_OPENGL_ES3)
if (glsl_version == NULL)
glsl_version = "#version 300 es";
#else
if (glsl_version == NULL)
glsl_version = "#version 130";
#endif
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n");
// Make a dummy GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
return true;
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
bool ImGui_ImplOpenGL3_NewFrame()
{
if (!g_FontTexture)
return ImGui_ImplOpenGL3_CreateDeviceObjects();
return true;
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif
(void)vertex_array_object;
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(vertex_array_object);
#endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
}
// OpenGL3 Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
#ifdef GL_SAMPLER_BINDING
GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
#endif
GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object);
#endif
#ifdef GL_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
bool clip_origin_lower_left = true;
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
if (last_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
#endif
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
#ifndef IMGUI_IMPL_OPENGL_ES2
glGenVertexArrays(1, &vertex_array_object);
#endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec4 clip_rect;
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
if (clip_origin_lower_left)
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
else
glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
#if IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
#else
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
#endif
}
}
}
}
// Destroy the temporary VAO
#ifndef IMGUI_IMPL_OPENGL_ES2
glDeleteVertexArrays(1, &vertex_array_object);
#endif
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, last_sampler);
#endif
glActiveTexture(last_active_texture);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array_object);
#endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
#endif
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_UNPACK_ROW_LENGTH
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO& io = ImGui::GetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts->TexID = 0;
g_FontTexture = 0;
}
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#endif
// Parse GLSL version string
int glsl_version = 130;
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n"
"attribute vec2 Position;\n"
"attribute vec2 UV;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_300_es =
"precision mediump float;\n"
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader_glsl_120 =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D Texture;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"uniform sampler2D Texture;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
// Select shaders matching our GLSL versions
const GLchar* vertex_shader = NULL;
const GLchar* fragment_shader = NULL;
if (glsl_version < 130)
{
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
}
else if (glsl_version >= 410)
{
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
}
else if (glsl_version == 300)
{
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
}
else
{
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
glCompileShader(g_VertHandle);
CheckShader(g_VertHandle, "vertex shader");
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
glCompileShader(g_FragHandle);
CheckShader(g_FragHandle, "fragment shader");
g_ShaderHandle = glCreateProgram();
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
CheckProgram(g_ShaderHandle, "shader program");
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationVtxPos = glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationVtxUV = glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationVtxColor = glGetAttribLocation(g_ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array);
#endif
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle);
if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle);
g_VboHandle = g_ElementsHandle = 0;
if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle);
if (g_VertHandle) glDeleteShader(g_VertHandle);
g_VertHandle = 0;
if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle);
if (g_FragHandle) glDeleteShader(g_FragHandle);
g_FragHandle = 0;
if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle);
g_ShaderHandle = 0;
ImGui_ImplOpenGL3_DestroyFontsTexture();
}

View File

@ -0,0 +1,47 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// About Desktop OpenGL function loaders:
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
// About GLSL version:
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
// Specific OpenGL versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
// Set default OpenGL3 loader to be gl3w
#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#endif
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();

View File

@ -0,0 +1,338 @@
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// Implemented features:
// [?] Platform: Clipboard support
// [?] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [?] Platform: Keyboard arrays indexed using
// [?] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
#include "imgui.h"
#include "imgui_impl_x11.h"
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <GL/glew.h>
#include <ctime>
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-08-31: Initial X11 implementation
// X11 Data
static Display* g_Display = nullptr;
static Window g_Window = 0;
static uint64_t g_Time = 0;
static uint64_t g_TicksPerSecond = 0;
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
static bool g_HasGamepad = false;
static bool g_WantUpdateHasGamepad = true;
bool GetKeyState(int keysym, char keys[32])
{
int keycode = XKeysymToKeycode(g_Display, keysym);
return keys[keycode/8] & (1<<keycode%8);
}
bool IsKeySys(int key)
{
switch(key)
{
case XK_Shift_L : case XK_Shift_R :
case XK_Control_L: case XK_Control_R :
case XK_Alt_L : case XK_Alt_R :
case XK_Super_L : case XK_Super_R :
case XK_Caps_Lock: case XK_Shift_Lock:
case XK_BackSpace: case XK_Delete :
case XK_Left : case XK_Right :
case XK_Up : case XK_Down :
case XK_Prior : case XK_Next :
case XK_Home : case XK_End :
case XK_Insert : case XK_Return :
return true;
}
return false;
}
// Functions
bool ImGui_ImplX11_Init(void *display, void *window)
{
timespec ts, tsres;
clock_getres(CLOCK_MONOTONIC_RAW, &tsres);
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
g_TicksPerSecond = 1000000000.0f / (static_cast<uint64_t>(tsres.tv_nsec) + static_cast<uint64_t>(tsres.tv_sec)*1000000000);
g_Time = static_cast<uint64_t>(ts.tv_nsec) + static_cast<uint64_t>(ts.tv_sec)*1000000000;
//if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
// return false;
//if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
// return false;
// Setup back-end capabilities flags
g_Display = reinterpret_cast<Display*>(display);
g_Window = reinterpret_cast<Window>(window);
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_x11";
io.ImeWindowHandle = nullptr;
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
io.KeyMap[ImGuiKey_Tab] = XKeysymToKeycode(g_Display, XK_Tab);
io.KeyMap[ImGuiKey_LeftArrow] = XKeysymToKeycode(g_Display, XK_Left);
io.KeyMap[ImGuiKey_RightArrow] = XKeysymToKeycode(g_Display, XK_Right);
io.KeyMap[ImGuiKey_UpArrow] = XKeysymToKeycode(g_Display, XK_Up);
io.KeyMap[ImGuiKey_DownArrow] = XKeysymToKeycode(g_Display, XK_Down);
io.KeyMap[ImGuiKey_PageUp] = XKeysymToKeycode(g_Display, XK_Prior);
io.KeyMap[ImGuiKey_PageDown] = XKeysymToKeycode(g_Display, XK_Next);
io.KeyMap[ImGuiKey_Home] = XKeysymToKeycode(g_Display, XK_Home);
io.KeyMap[ImGuiKey_End] = XKeysymToKeycode(g_Display, XK_End);
io.KeyMap[ImGuiKey_Insert] = XKeysymToKeycode(g_Display, XK_Insert);
io.KeyMap[ImGuiKey_Delete] = XKeysymToKeycode(g_Display, XK_Delete);
io.KeyMap[ImGuiKey_Backspace] = XKeysymToKeycode(g_Display, XK_BackSpace);
io.KeyMap[ImGuiKey_Space] = XKeysymToKeycode(g_Display, XK_space);
io.KeyMap[ImGuiKey_Enter] = XKeysymToKeycode(g_Display, XK_Return);
io.KeyMap[ImGuiKey_Escape] = XKeysymToKeycode(g_Display, XK_Escape);
io.KeyMap[ImGuiKey_A] = XKeysymToKeycode(g_Display, XK_A);
io.KeyMap[ImGuiKey_C] = XKeysymToKeycode(g_Display, XK_C);
io.KeyMap[ImGuiKey_V] = XKeysymToKeycode(g_Display, XK_V);
io.KeyMap[ImGuiKey_X] = XKeysymToKeycode(g_Display, XK_X);
io.KeyMap[ImGuiKey_Y] = XKeysymToKeycode(g_Display, XK_Y);
io.KeyMap[ImGuiKey_Z] = XKeysymToKeycode(g_Display, XK_Z);
return true;
}
void ImGui_ImplX11_Shutdown()
{
g_Display = nullptr;
g_Window = 0;
}
static bool ImGui_ImplX11_UpdateMouseCursor()
{
/*
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return false;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
::SetCursor(NULL);
}
else
{
// Show OS mouse cursor
LPTSTR win32_cursor = IDC_ARROW;
switch (imgui_cursor)
{
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
}
::SetCursor(::LoadCursor(NULL, win32_cursor));
}
*/
return true;
}
static void ImGui_ImplX11_UpdateMousePos()
{
ImGuiIO& io = ImGui::GetIO();
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
{
// POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
// ::ClientToScreen(g_hWnd, &pos);
// ::SetCursorPos(pos.x, pos.y);
}
// Set mouse position
Window unused_window;
int rx, ry, x, y;
unsigned int mask;
XQueryPointer(g_Display, g_Window, &unused_window, &unused_window, &rx, &ry, &x, &y, &mask);
io.MousePos = ImVec2((float)x, (float)y);
}
// Gamepad navigation mapping
static void ImGui_ImplX11_UpdateGamepads()
{
/* TODO: support linux gamepad ?
ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
if (g_WantUpdateHasGamepad)
{
XINPUT_CAPABILITIES caps;
g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS);
g_WantUpdateHasGamepad = false;
}
XINPUT_STATE xinput_state;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
{
const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
#define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
#define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A
MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B
MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X
MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y
MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down
MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767);
#undef MAP_BUTTON
#undef MAP_ANALOG
}
*/
}
void ImGui_ImplX11_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
unsigned int width, height;
Window unused_window;
int unused_int;
unsigned int unused_unsigned_int;
XGetGeometry(g_Display, (Window)g_Window, &unused_window, &unused_int, &unused_int, &width, &height, &unused_unsigned_int, &unused_unsigned_int);
io.DisplaySize.x = width;
io.DisplaySize.y = height;
timespec ts, tsres;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
uint64_t current_time = static_cast<uint64_t>(ts.tv_nsec) + static_cast<uint64_t>(ts.tv_sec)*1000000000;
io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
g_Time = current_time;
// Read keyboard modifiers inputs
char keys[32];
XQueryKeymap(g_Display, keys);
io.KeyCtrl = GetKeyState(XK_Control_L, keys);
io.KeyShift = GetKeyState(XK_Shift_L, keys);
io.KeyAlt = GetKeyState(XK_Alt_L, keys);
io.KeySuper = false;
// io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
// Update OS mouse position
ImGui_ImplX11_UpdateMousePos();
/*
// Update OS mouse cursor with the cursor requested by imgui
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
if (g_LastMouseCursor != mouse_cursor)
{
g_LastMouseCursor = mouse_cursor;
ImGui_ImplX11_UpdateMouseCursor();
}
*/
// Update game controllers (if enabled and available)
ImGui_ImplX11_UpdateGamepads();
}
// Process X11 mouse/keyboard inputs.
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
IMGUI_IMPL_API int ImGui_ImplX11_EventHandler(XEvent &event)
{
if (ImGui::GetCurrentContext() == NULL)
return 0;
ImGuiIO& io = ImGui::GetIO();
switch (event.type)
{
case ButtonPress:
case ButtonRelease:
switch(event.xbutton.button)
{
case Button1:
io.MouseDown[0] = event.type == ButtonPress;
break;
case Button2:
io.MouseDown[2] = event.type == ButtonPress;
break;
case Button3:
io.MouseDown[1] = event.type == ButtonPress;
break;
case Button4: // Mouse wheel up
if( event.type == ButtonPress )
io.MouseWheel += 1;
return 0;
case Button5: // Mouse wheel down
if( event.type == ButtonPress )
io.MouseWheel -= 1;
return 0;
}
break;
case KeyPress:
{
int key = XKeycodeToKeysym(g_Display, event.xkey.keycode, event.xkey.state & ShiftMask ? 1 : 0);
if( IsKeySys(key) )
io.KeysDown[event.xkey.keycode] = true;
else
io.AddInputCharacter(key);
return 0;
}
case KeyRelease:
{
int key = XKeycodeToKeysym(g_Display, event.xkey.keycode, event.xkey.state & ShiftMask ? 1 : 0);
if( IsKeySys(key) )
io.KeysDown[event.xkey.keycode] = false;
return 0;
}
}
/*
case WM_DEVICECHANGE:
if ((UINT)wParam == DBT_DEVNODES_CHANGED)
g_WantUpdateHasGamepad = true;
return 0;
}
*/
return 0;
}

View File

@ -0,0 +1,21 @@
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// Implemented features:
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
#pragma once
IMGUI_IMPL_API bool ImGui_ImplX11_Init(void* display, void* window);
IMGUI_IMPL_API void ImGui_ImplX11_Shutdown();
IMGUI_IMPL_API void ImGui_ImplX11_NewFrame();
// Handler for Win32 messages, update mouse/keyboard data.
// You may or not need this for your implementation, but it can serve as reference for handling inputs.
// Intentionally commented out to avoid dragging dependencies on <windows.h> types. You can COPY this line into your .cpp code instead.
/*
IMGUI_IMPL_API int ImGui_ImplX11_EventHandler(XEvent *event);
*/

View File

@ -0,0 +1,557 @@
// dear imgui: Renderer for DirectX10
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: DirectX10: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX10: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-07-13: DirectX10: Fixed unreleased resources in Init and Shutdown functions.
// 2018-06-08: Misc: Extracted imgui_impl_dx10.cpp/.h away from the old combined DX10+Win32 example.
// 2018-06-08: DirectX10: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-04-09: Misc: Fixed erroneous call to io.Fonts->ClearInputData() + ClearTexData() that was left in DX10 example but removed in 1.47 (Nov 2015) on other back-ends.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX10_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2016-05-07: DirectX10: Disabling depth-write.
#include "../../imgui.h"
#include "imgui_impl_dx10.h"
// DirectX
#include <stdio.h>
#include <d3d10_1.h>
#include <d3d10.h>
#include "../../../overlay_experimental/windows/ImGui_ShaderBlobs.h"
#ifdef USE_D3DCOMPILE
static ID3DBlob* g_pVertexShaderBlob = NULL;
static ID3DBlob* g_pPixelShaderBlob = NULL;
#endif
// DirectX data
static ID3D10Device* g_pd3dDevice = NULL;
static IDXGIFactory* g_pFactory = NULL;
static ID3D10Buffer* g_pVB = NULL;
static ID3D10Buffer* g_pIB = NULL;
static ID3D10VertexShader* g_pVertexShader = NULL;
static ID3D10InputLayout* g_pInputLayout = NULL;
static ID3D10Buffer* g_pVertexConstantBuffer = NULL;
static ID3D10PixelShader* g_pPixelShader = NULL;
static ID3D10SamplerState* g_pFontSampler = NULL;
static ID3D10ShaderResourceView*g_pFontTextureView = NULL;
static ID3D10RasterizerState* g_pRasterizerState = NULL;
static ID3D10BlendState* g_pBlendState = NULL;
static ID3D10DepthStencilState* g_pDepthStencilState = NULL;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
struct VERTEX_CONSTANT_BUFFER
{
float mvp[4][4];
};
static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* ctx)
{
// Setup viewport
D3D10_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D10_VIEWPORT));
vp.Width = (UINT)draw_data->DisplaySize.x;
vp.Height = (UINT)draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0;
ctx->RSSetViewports(1, &vp);
// Bind shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0;
ctx->IASetInputLayout(g_pInputLayout);
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset);
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->VSSetShader(g_pVertexShader);
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer);
ctx->PSSetShader(g_pPixelShader);
ctx->PSSetSamplers(0, 1, &g_pFontSampler);
// Setup render state
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff);
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0);
ctx->RSSetState(g_pRasterizerState);
}
// Render function
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
ID3D10Device* ctx = g_pd3dDevice;
// Create and grow vertex/index buffers if needed
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
{
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D10_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
if (ctx->CreateBuffer(&desc, NULL, &g_pVB) < 0)
return;
}
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
{
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D10_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
if (ctx->CreateBuffer(&desc, NULL, &g_pIB) < 0)
return;
}
// Copy and convert all vertices into a single contiguous buffer
ImDrawVert* vtx_dst = NULL;
ImDrawIdx* idx_dst = NULL;
g_pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst);
g_pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst);
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
g_pVB->Unmap();
g_pIB->Unmap();
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
{
void* mapped_resource;
if (g_pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
return;
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource;
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
g_pVertexConstantBuffer->Unmap();
}
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
struct BACKUP_DX10_STATE
{
UINT ScissorRectsCount, ViewportsCount;
D3D10_RECT ScissorRects[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
D3D10_VIEWPORT Viewports[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
ID3D10RasterizerState* RS;
ID3D10BlendState* BlendState;
FLOAT BlendFactor[4];
UINT SampleMask;
UINT StencilRef;
ID3D10DepthStencilState* DepthStencilState;
ID3D10ShaderResourceView* PSShaderResource;
ID3D10SamplerState* PSSampler;
ID3D10PixelShader* PS;
ID3D10VertexShader* VS;
D3D10_PRIMITIVE_TOPOLOGY PrimitiveTopology;
ID3D10Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
DXGI_FORMAT IndexBufferFormat;
ID3D10InputLayout* InputLayout;
};
BACKUP_DX10_STATE old;
old.ScissorRectsCount = old.ViewportsCount = D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
ctx->RSGetState(&old.RS);
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
ctx->PSGetSamplers(0, 1, &old.PSSampler);
ctx->PSGetShader(&old.PS);
ctx->VSGetShader(&old.VS);
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
ctx->IAGetInputLayout(&old.InputLayout);
// Setup desired DX state
ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Apply scissor/clipping rectangle
const D3D10_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y)};
ctx->RSSetScissorRects(1, &r);
// Bind texture, Draw
ID3D10ShaderResourceView* texture_srv = (ID3D10ShaderResourceView*)pcmd->TextureId;
ctx->PSSetShaderResources(0, 1, &texture_srv);
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore modified DX state
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
ctx->PSSetShader(old.PS); if (old.PS) old.PS->Release();
ctx->VSSetShader(old.VS); if (old.VS) old.VS->Release();
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
}
static void ImGui_ImplDX10_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
{
D3D10_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
ID3D10Texture2D *pTexture = NULL;
D3D10_SUBRESOURCE_DATA subResource;
subResource.pSysMem = pixels;
subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0;
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
// Create texture view
D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc;
ZeroMemory(&srv_desc, sizeof(srv_desc));
srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MipLevels = desc.MipLevels;
srv_desc.Texture2D.MostDetailedMip = 0;
g_pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &g_pFontTextureView);
pTexture->Release();
}
// Store our identifier
io.Fonts->TexID = (ImTextureID)g_pFontTextureView;
// Create texture sampler
{
D3D10_SAMPLER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
desc.AddressU = D3D10_TEXTURE_ADDRESS_WRAP;
desc.AddressV = D3D10_TEXTURE_ADDRESS_WRAP;
desc.AddressW = D3D10_TEXTURE_ADDRESS_WRAP;
desc.MipLODBias = 0.f;
desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
desc.MinLOD = 0.f;
desc.MaxLOD = 0.f;
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler);
}
}
bool ImGui_ImplDX10_CreateDeviceObjects()
{
if (!g_pd3dDevice)
return false;
if (g_pFontSampler)
ImGui_ImplDX10_InvalidateDeviceObjects();
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX10 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
#ifdef USE_D3DCOMPILE
decltype(D3DCompile)* D3DCompile = load_d3dcompile();
if (D3DCompile == nullptr)
return false;
#endif
// Create the vertex shader
{
#ifdef USE_D3DCOMPILE
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, NULL);
if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pVertexShader) != S_OK)
return false;
#else
if (g_pd3dDevice->CreateVertexShader(ImGui_vertexShaderDX10, ImGui_vertexShaderDX10_len, &g_pVertexShader) != S_OK)
return false;
#endif
// Create the input layout
D3D10_INPUT_ELEMENT_DESC local_layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->pos), D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->uv), D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)(&((ImDrawVert*)0)->col), D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
#ifdef USE_D3DCOMPILE
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK)
return false;
#else
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, ImGui_vertexShaderDX10, ImGui_vertexShaderDX10_len, &g_pInputLayout) != S_OK)
return false;
#endif
// Create the constant buffer
{
D3D10_BUFFER_DESC desc;
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer);
}
}
// Create the pixel shader
{
#ifdef USE_D3DCOMPILE
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
sampler sampler0;\
Texture2D texture0;\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, NULL);
if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), &g_pPixelShader) != S_OK)
return false;
#else
if (g_pd3dDevice->CreatePixelShader(ImGui_pixelShaderDX10, ImGui_pixelShaderDX10_len, &g_pPixelShader) != S_OK)
return false;
#endif
}
#ifdef USE_D3DCOMPILE
unload_d3dcompile();
#endif
// Create the blending setup
{
D3D10_BLEND_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.AlphaToCoverageEnable = false;
desc.BlendEnable[0] = true;
desc.SrcBlend = D3D10_BLEND_SRC_ALPHA;
desc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA;
desc.BlendOp = D3D10_BLEND_OP_ADD;
desc.SrcBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
desc.DestBlendAlpha = D3D10_BLEND_ZERO;
desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState);
}
// Create the rasterizer state
{
D3D10_RASTERIZER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.FillMode = D3D10_FILL_SOLID;
desc.CullMode = D3D10_CULL_NONE;
desc.ScissorEnable = true;
desc.DepthClipEnable = true;
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState);
}
// Create depth-stencil State
{
D3D10_DEPTH_STENCIL_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.DepthEnable = false;
desc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D10_COMPARISON_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState);
}
ImGui_ImplDX10_CreateFontsTexture();
return true;
}
void ImGui_ImplDX10_InvalidateDeviceObjects()
{
if (!g_pd3dDevice)
return;
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; }
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; }
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; }
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; }
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; }
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; }
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
#ifdef USE_D3DCOMPILE
if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
#endif
}
bool ImGui_ImplDX10_Init(ID3D10Device* device)
{
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_dx10";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
// Get factory from device
IDXGIDevice* pDXGIDevice = NULL;
IDXGIAdapter* pDXGIAdapter = NULL;
IDXGIFactory* pFactory = NULL;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
{
g_pd3dDevice = device;
g_pFactory = pFactory;
}
if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release();
g_pd3dDevice->AddRef();
return true;
}
void ImGui_ImplDX10_Shutdown()
{
ImGui_ImplDX10_InvalidateDeviceObjects();
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; }
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
}
bool ImGui_ImplDX10_NewFrame()
{
if (!g_pFontSampler)
return ImGui_ImplDX10_CreateDeviceObjects();
return true;
}

View File

@ -0,0 +1,23 @@
// dear imgui: Renderer for DirectX10
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
struct ID3D10Device;
IMGUI_IMPL_API bool ImGui_ImplDX10_Init(ID3D10Device* device);
IMGUI_IMPL_API void ImGui_ImplDX10_Shutdown();
IMGUI_IMPL_API bool ImGui_ImplDX10_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing ImGui state.
IMGUI_IMPL_API void ImGui_ImplDX10_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects();

View File

@ -0,0 +1,684 @@
// dear imgui: Renderer for DirectX11
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility.
// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions.
// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example.
// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2016-05-07: DirectX11: Disabling depth-write.
#include "../../imgui.h"
#include "imgui_impl_dx11.h"
// DirectX
#include <stdio.h>
#include <d3d11.h>
#include "../../../overlay_experimental/windows/ImGui_ShaderBlobs.h"
#ifdef USE_D3DCOMPILE
static ID3DBlob* g_pVertexShaderBlob = NULL;
static ID3DBlob* g_pPixelShaderBlob = NULL;
#endif
// DirectX data
static ID3D11Device* g_pd3dDevice = NULL;
static ID3D11DeviceContext* g_pd3dDeviceContext = NULL;
static IDXGIFactory* g_pFactory = NULL;
static ID3D11Buffer* g_pVB = NULL;
static ID3D11Buffer* g_pIB = NULL;
static ID3D11VertexShader* g_pVertexShader = NULL;
static ID3D11InputLayout* g_pInputLayout = NULL;
static ID3D11Buffer* g_pVertexConstantBuffer = NULL;
static ID3D11PixelShader* g_pPixelShader = NULL;
static ID3D11SamplerState* g_pFontSampler = NULL;
static ID3D11ShaderResourceView*g_pFontTextureView = NULL;
static ID3D11RasterizerState* g_pRasterizerState = NULL;
static ID3D11BlendState* g_pBlendState = NULL;
static ID3D11DepthStencilState* g_pDepthStencilState = NULL;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
struct VERTEX_CONSTANT_BUFFER
{
float mvp[4][4];
};
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
{
// Setup viewport
D3D11_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
vp.Width = draw_data->DisplaySize.x;
vp.Height = draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0;
ctx->RSSetViewports(1, &vp);
// Setup shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0;
ctx->IASetInputLayout(g_pInputLayout);
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset);
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->VSSetShader(g_pVertexShader, NULL, 0);
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer);
ctx->PSSetShader(g_pPixelShader, NULL, 0);
ctx->PSSetSamplers(0, 1, &g_pFontSampler);
// Setup blend state
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff);
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0);
ctx->RSSetState(g_pRasterizerState);
}
// Render function
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
ID3D11DeviceContext* ctx = g_pd3dDeviceContext;
// Create and grow vertex/index buffers if needed
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
{
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0)
return;
}
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
{
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0)
return;
}
// Upload vertex/index data into a single contiguous GPU buffer
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
return;
if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
return;
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
ctx->Unmap(g_pVB, 0);
ctx->Unmap(g_pIB, 0);
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
{
D3D11_MAPPED_SUBRESOURCE mapped_resource;
if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
return;
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData;
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
ctx->Unmap(g_pVertexConstantBuffer, 0);
}
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
struct BACKUP_DX11_STATE
{
UINT ScissorRectsCount, ViewportsCount;
D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
ID3D11RasterizerState* RS;
ID3D11BlendState* BlendState;
FLOAT BlendFactor[4];
UINT SampleMask;
UINT StencilRef;
ID3D11DepthStencilState* DepthStencilState;
ID3D11ShaderResourceView* PSShaderResource;
ID3D11SamplerState* PSSampler;
ID3D11PixelShader* PS;
ID3D11VertexShader* VS;
UINT PSInstancesCount, VSInstancesCount;
ID3D11ClassInstance* PSInstances[256], *VSInstances[256]; // 256 is max according to PSSetShader documentation
D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology;
ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
DXGI_FORMAT IndexBufferFormat;
ID3D11InputLayout* InputLayout;
};
BACKUP_DX11_STATE old;
old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
ctx->RSGetState(&old.RS);
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
ctx->PSGetSamplers(0, 1, &old.PSSampler);
old.PSInstancesCount = old.VSInstancesCount = 256;
ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
ctx->IAGetInputLayout(&old.InputLayout);
// Setup desired DX state
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_idx_offset = 0;
int global_vtx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Apply scissor/clipping rectangle
const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
ctx->RSSetScissorRects(1, &r);
// Bind texture, Draw
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId;
ctx->PSSetShaderResources(0, 1, &texture_srv);
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore modified DX state
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
}
static void ImGui_ImplDX11_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
{
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
ID3D11Texture2D *pTexture = NULL;
D3D11_SUBRESOURCE_DATA subResource;
subResource.pSysMem = pixels;
subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0;
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
// Create texture view
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView);
pTexture->Release();
}
// Store our identifier
io.Fonts->TexID = (ImTextureID)g_pFontTextureView;
// Create texture sampler
{
D3D11_SAMPLER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
desc.MipLODBias = 0.f;
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
desc.MinLOD = 0.f;
desc.MaxLOD = 0.f;
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler);
}
}
bool ImGui_ImplDX11_CreateDeviceObjects()
{
if (!g_pd3dDevice)
return false;
if (g_pFontSampler)
ImGui_ImplDX11_InvalidateDeviceObjects();
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX11 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
#ifdef USE_D3DCOMPILE
decltype(D3DCompile)* D3DCompile = load_d3dcompile();
if (D3DCompile == nullptr)
return false;
#endif
// Create the vertex shader
{
#ifdef USE_D3DCOMPILE
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
const char* target;
switch (g_pd3dDevice->GetFeatureLevel())
{
case D3D_FEATURE_LEVEL_9_1: target = "vs_4_0_level_9_1"; break;
case D3D_FEATURE_LEVEL_9_2: target = "vs_4_0_level_9_2"; break;
case D3D_FEATURE_LEVEL_9_3: target = "vs_4_0_level_9_3"; break;
case D3D_FEATURE_LEVEL_10_0: target = "vs_4_0"; break;
case D3D_FEATURE_LEVEL_10_1: target = "vs_4_1"; break;
case D3D_FEATURE_LEVEL_11_0: target = "vs_5_0"; break;
case D3D_FEATURE_LEVEL_11_1: target = "vs_5_0"; break;
default: target = "vs_4_0";
}
D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", target, 0, 0, &g_pVertexShaderBlob, NULL);
if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK)
return false;
#else
unsigned char* byteCode;
SIZE_T byteCodeSize;
switch (g_pd3dDevice->GetFeatureLevel())
{
case D3D_FEATURE_LEVEL_9_1:
byteCode = ImGui_vertexShaderDX11_9_1;
byteCodeSize = ImGui_vertexShaderDX11_9_1_len;
break;
case D3D_FEATURE_LEVEL_9_2:
byteCode = ImGui_vertexShaderDX11_9_2;
byteCodeSize = ImGui_vertexShaderDX11_9_2_len;
break;
case D3D_FEATURE_LEVEL_9_3:
byteCode = ImGui_vertexShaderDX11_9_3;
byteCodeSize = ImGui_vertexShaderDX11_9_3_len;
break;
case D3D_FEATURE_LEVEL_10_0:
byteCode = ImGui_vertexShaderDX11_10_0;
byteCodeSize = ImGui_vertexShaderDX11_10_0_len;
break;
case D3D_FEATURE_LEVEL_10_1:
byteCode = ImGui_vertexShaderDX11_10_1;
byteCodeSize = ImGui_vertexShaderDX11_10_1_len;
break;
case D3D_FEATURE_LEVEL_11_0:
byteCode = ImGui_vertexShaderDX11_11_0;
byteCodeSize = ImGui_vertexShaderDX11_11_0_len;
break;
case D3D_FEATURE_LEVEL_11_1:
byteCode = ImGui_vertexShaderDX11_11_1;
byteCodeSize = ImGui_vertexShaderDX11_11_1_len;
break;
default:
byteCode = ImGui_vertexShaderDX11;
byteCodeSize = ImGui_vertexShaderDX11_len;
}
auto x = g_pd3dDevice->CreateVertexShader(byteCode, byteCodeSize, NULL, &g_pVertexShader);
if (x != S_OK)
return false;
#endif
// Create the input layout
D3D11_INPUT_ELEMENT_DESC local_layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->pos), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->uv), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)(&((ImDrawVert*)0)->col), D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
#ifdef USE_D3DCOMPILE
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK)
return false;
#else
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, ImGui_vertexShaderDX11, ImGui_vertexShaderDX11_len, &g_pInputLayout) != S_OK)
return false;
#endif
// Create the constant buffer
{
D3D11_BUFFER_DESC desc;
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer);
}
}
// Create the pixel shader
{
#ifdef USE_D3DCOMPILE
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
sampler sampler0;\
Texture2D texture0;\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
const char* target;
switch (g_pd3dDevice->GetFeatureLevel())
{
case D3D_FEATURE_LEVEL_9_1: target = "ps_4_0_level_9_1"; break;
case D3D_FEATURE_LEVEL_9_2: target = "ps_4_0_level_9_2"; break;
case D3D_FEATURE_LEVEL_9_3: target = "ps_4_0_level_9_3"; break;
case D3D_FEATURE_LEVEL_10_0: target = "ps_4_0"; break;
case D3D_FEATURE_LEVEL_10_1: target = "ps_4_1"; break;
case D3D_FEATURE_LEVEL_11_0: target = "ps_5_0"; break;
case D3D_FEATURE_LEVEL_11_1: target = "ps_5_0"; break;
default: target = "ps_4_0";
}
D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", target, 0, 0, &g_pPixelShaderBlob, NULL);
if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK)
return false;
#else
unsigned char* byteCode;
SIZE_T byteCodeSize;
switch (g_pd3dDevice->GetFeatureLevel())
{
case D3D_FEATURE_LEVEL_9_1:
byteCode = ImGui_pixelShaderDX11_9_1;
byteCodeSize = ImGui_pixelShaderDX11_9_1_len;
break;
case D3D_FEATURE_LEVEL_9_2:
byteCode = ImGui_pixelShaderDX11_9_2;
byteCodeSize = ImGui_pixelShaderDX11_9_2_len;
break;
case D3D_FEATURE_LEVEL_9_3:
byteCode = ImGui_pixelShaderDX11_9_3;
byteCodeSize = ImGui_pixelShaderDX11_9_3_len;
break;
case D3D_FEATURE_LEVEL_10_0:
byteCode = ImGui_pixelShaderDX11_10_0;
byteCodeSize = ImGui_pixelShaderDX11_10_0_len;
break;
case D3D_FEATURE_LEVEL_10_1:
byteCode = ImGui_pixelShaderDX11_10_1;
byteCodeSize = ImGui_pixelShaderDX11_10_1_len;
break;
case D3D_FEATURE_LEVEL_11_0:
byteCode = ImGui_pixelShaderDX11_11_0;
byteCodeSize = ImGui_pixelShaderDX11_11_0_len;
break;
case D3D_FEATURE_LEVEL_11_1:
byteCode = ImGui_pixelShaderDX11_11_1;
byteCodeSize = ImGui_pixelShaderDX11_11_1_len;
break;
default:
byteCode = ImGui_pixelShaderDX11;
byteCodeSize = ImGui_pixelShaderDX11_len;
}
if (g_pd3dDevice->CreatePixelShader(byteCode, byteCodeSize, NULL, &g_pPixelShader) != S_OK)
return false;
#endif
}
#ifdef USE_D3DCOMPILE
unload_d3dcompile();
#endif
// Create the blending setup
{
D3D11_BLEND_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.AlphaToCoverageEnable = false;
desc.RenderTarget[0].BlendEnable = true;
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState);
}
// Create the rasterizer state
{
D3D11_RASTERIZER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.FillMode = D3D11_FILL_SOLID;
desc.CullMode = D3D11_CULL_NONE;
desc.ScissorEnable = true;
desc.DepthClipEnable = true;
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState);
}
// Create depth-stencil State
{
D3D11_DEPTH_STENCIL_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.DepthEnable = false;
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState);
}
ImGui_ImplDX11_CreateFontsTexture();
return true;
}
void ImGui_ImplDX11_InvalidateDeviceObjects()
{
if (!g_pd3dDevice)
return;
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; }
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; }
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; }
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; }
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; }
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; }
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
#ifdef USE_D3DCOMPILE
if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
#endif
}
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
{
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_dx11";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
// Get factory from device
IDXGIDevice* pDXGIDevice = NULL;
IDXGIAdapter* pDXGIAdapter = NULL;
IDXGIFactory* pFactory = NULL;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
{
g_pd3dDevice = device;
g_pd3dDeviceContext = device_context;
g_pFactory = pFactory;
}
if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release();
g_pd3dDevice->AddRef();
g_pd3dDeviceContext->AddRef();
return true;
}
void ImGui_ImplDX11_Shutdown()
{
ImGui_ImplDX11_InvalidateDeviceObjects();
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; }
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; }
}
bool ImGui_ImplDX11_NewFrame()
{
if (!g_pFontSampler)
return ImGui_ImplDX11_CreateDeviceObjects();
return true;
}

View File

@ -0,0 +1,24 @@
// dear imgui: Renderer for DirectX11
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
struct ID3D11Device;
struct ID3D11DeviceContext;
IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown();
IMGUI_IMPL_API bool ImGui_ImplDX11_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing ImGui state.
IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects();

View File

@ -0,0 +1,677 @@
// dear imgui: Renderer for DirectX12
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// Issues:
// [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: Misc: Various minor tidying up.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData().
// 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example.
// 2018-06-08: DirectX12: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle (to ease support for future multi-viewport).
// 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
#include "../../imgui.h"
#include "imgui_impl_dx12.h"
// DirectX
#include <d3d12.h>
#include <dxgi1_4.h>
#include "../../../overlay_experimental/windows/ImGui_ShaderBlobs.h"
#ifdef USE_D3DCOMPILE
static ID3DBlob* g_pVertexShaderBlob = NULL;
static ID3DBlob* g_pPixelShaderBlob = NULL;
#endif
// DirectX data
static ID3D12Device* g_pd3dDevice = NULL;
static ID3D12RootSignature* g_pRootSignature = NULL;
static ID3D12PipelineState* g_pPipelineState = NULL;
static DXGI_FORMAT g_RTVFormat = DXGI_FORMAT_UNKNOWN;
static ID3D12Resource* g_pFontTextureResource = NULL;
static D3D12_CPU_DESCRIPTOR_HANDLE g_hFontSrvCpuDescHandle = {};
static D3D12_GPU_DESCRIPTOR_HANDLE g_hFontSrvGpuDescHandle = {};
struct FrameResources
{
ID3D12Resource* IndexBuffer;
ID3D12Resource* VertexBuffer;
int IndexBufferSize;
int VertexBufferSize;
};
static FrameResources* g_pFrameResources = NULL;
static UINT g_numFramesInFlight = 0;
static UINT g_frameIndex = UINT_MAX;
struct VERTEX_CONSTANT_BUFFER
{
float mvp[4][4];
};
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, FrameResources* fr)
{
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
VERTEX_CONSTANT_BUFFER vertex_constant_buffer;
{
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&vertex_constant_buffer.mvp, mvp, sizeof(mvp));
}
// Setup viewport
D3D12_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D12_VIEWPORT));
vp.Width = draw_data->DisplaySize.x;
vp.Height = draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0.0f;
ctx->RSSetViewports(1, &vp);
// Bind shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0;
D3D12_VERTEX_BUFFER_VIEW vbv;
memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW));
vbv.BufferLocation = fr->VertexBuffer->GetGPUVirtualAddress() + offset;
vbv.SizeInBytes = fr->VertexBufferSize * stride;
vbv.StrideInBytes = stride;
ctx->IASetVertexBuffers(0, 1, &vbv);
D3D12_INDEX_BUFFER_VIEW ibv;
memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW));
ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress();
ibv.SizeInBytes = fr->IndexBufferSize * sizeof(ImDrawIdx);
ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
ctx->IASetIndexBuffer(&ibv);
ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->SetPipelineState(g_pPipelineState);
ctx->SetGraphicsRootSignature(g_pRootSignature);
ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
// Setup blend factor
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendFactor(blend_factor);
}
// Render function
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
// FIXME: I'm assuming that this only gets called once per frame!
// If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
g_frameIndex = g_frameIndex + 1;
FrameResources* fr = &g_pFrameResources[g_frameIndex % g_numFramesInFlight];
// Create and grow vertex/index buffers if needed
if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount)
{
if (fr->VertexBuffer != NULL) { fr->VertexBuffer->Release(); fr->VertexBuffer = NULL; }
fr->VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D12_HEAP_PROPERTIES props;
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
props.Type = D3D12_HEAP_TYPE_UPLOAD;
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_RESOURCE_DESC desc;
memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC));
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = fr->VertexBufferSize * sizeof(ImDrawVert);
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
if (g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->VertexBuffer)) < 0)
return;
}
if (fr->IndexBuffer == NULL || fr->IndexBufferSize < draw_data->TotalIdxCount)
{
if (fr->IndexBuffer != NULL) { fr->IndexBuffer->Release(); fr->IndexBuffer = NULL; }
fr->IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D12_HEAP_PROPERTIES props;
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
props.Type = D3D12_HEAP_TYPE_UPLOAD;
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_RESOURCE_DESC desc;
memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC));
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = fr->IndexBufferSize * sizeof(ImDrawIdx);
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
if (g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->IndexBuffer)) < 0)
return;
}
// Upload vertex/index data into a single contiguous GPU buffer
void* vtx_resource, *idx_resource;
D3D12_RANGE range;
memset(&range, 0, sizeof(D3D12_RANGE));
if (fr->VertexBuffer->Map(0, &range, &vtx_resource) != S_OK)
return;
if (fr->IndexBuffer->Map(0, &range, &idx_resource) != S_OK)
return;
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource;
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
fr->VertexBuffer->Unmap(0, &range);
fr->IndexBuffer->Unmap(0, &range);
// Setup desired DX state
ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Apply Scissor, Bind texture, Draw
const D3D12_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
ctx->SetGraphicsRootDescriptorTable(1, *(D3D12_GPU_DESCRIPTOR_HANDLE*)&pcmd->TextureId);
ctx->RSSetScissorRects(1, &r);
ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
}
static void ImGui_ImplDX12_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
{
D3D12_HEAP_PROPERTIES props;
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
props.Type = D3D12_HEAP_TYPE_DEFAULT;
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_RESOURCE_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.Alignment = 0;
desc.Width = width;
desc.Height = height;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
ID3D12Resource* pTexture = NULL;
g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&pTexture));
UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
UINT uploadSize = height * uploadPitch;
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Alignment = 0;
desc.Width = uploadSize;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
props.Type = D3D12_HEAP_TYPE_UPLOAD;
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
ID3D12Resource* uploadBuffer = NULL;
HRESULT hr = g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&uploadBuffer));
IM_ASSERT(SUCCEEDED(hr));
void* mapped = NULL;
D3D12_RANGE range = { 0, uploadSize };
hr = uploadBuffer->Map(0, &range, &mapped);
IM_ASSERT(SUCCEEDED(hr));
for (int y = 0; y < height; y++)
memcpy((void*) ((uintptr_t) mapped + y * uploadPitch), pixels + y * width * 4, width * 4);
uploadBuffer->Unmap(0, &range);
D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
srcLocation.pResource = uploadBuffer;
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srcLocation.PlacedFootprint.Footprint.Width = width;
srcLocation.PlacedFootprint.Footprint.Height = height;
srcLocation.PlacedFootprint.Footprint.Depth = 1;
srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch;
D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
dstLocation.pResource = pTexture;
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstLocation.SubresourceIndex = 0;
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = pTexture;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
ID3D12Fence* fence = NULL;
hr = g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
IM_ASSERT(SUCCEEDED(hr));
HANDLE event = CreateEvent(0, 0, 0, 0);
IM_ASSERT(event != NULL);
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.NodeMask = 1;
ID3D12CommandQueue* cmdQueue = NULL;
hr = g_pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue));
IM_ASSERT(SUCCEEDED(hr));
ID3D12CommandAllocator* cmdAlloc = NULL;
hr = g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
IM_ASSERT(SUCCEEDED(hr));
ID3D12GraphicsCommandList* cmdList = NULL;
hr = g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, NULL, IID_PPV_ARGS(&cmdList));
IM_ASSERT(SUCCEEDED(hr));
cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, NULL);
cmdList->ResourceBarrier(1, &barrier);
hr = cmdList->Close();
IM_ASSERT(SUCCEEDED(hr));
cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*) &cmdList);
hr = cmdQueue->Signal(fence, 1);
IM_ASSERT(SUCCEEDED(hr));
fence->SetEventOnCompletion(1, event);
WaitForSingleObject(event, INFINITE);
cmdList->Release();
cmdAlloc->Release();
cmdQueue->Release();
CloseHandle(event);
fence->Release();
uploadBuffer->Release();
// Create texture view
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, g_hFontSrvCpuDescHandle);
if (g_pFontTextureResource != NULL)
g_pFontTextureResource->Release();
g_pFontTextureResource = pTexture;
}
// Store our identifier
static_assert(sizeof(ImTextureID) >= sizeof(g_hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet.");
io.Fonts->TexID = (ImTextureID)g_hFontSrvGpuDescHandle.ptr;
}
bool ImGui_ImplDX12_CreateDeviceObjects()
{
if (!g_pd3dDevice)
return false;
if (g_pPipelineState)
ImGui_ImplDX12_InvalidateDeviceObjects();
// Create the root signature
{
D3D12_DESCRIPTOR_RANGE descRange = {};
descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
descRange.NumDescriptors = 1;
descRange.BaseShaderRegister = 0;
descRange.RegisterSpace = 0;
descRange.OffsetInDescriptorsFromTableStart = 0;
D3D12_ROOT_PARAMETER param[2] = {};
param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
param[0].Constants.ShaderRegister = 0;
param[0].Constants.RegisterSpace = 0;
param[0].Constants.Num32BitValues = 16;
param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
param[1].DescriptorTable.NumDescriptorRanges = 1;
param[1].DescriptorTable.pDescriptorRanges = &descRange;
param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_STATIC_SAMPLER_DESC staticSampler = {};
staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSampler.MipLODBias = 0.f;
staticSampler.MaxAnisotropy = 0;
staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
staticSampler.MinLOD = 0.f;
staticSampler.MaxLOD = 0.f;
staticSampler.ShaderRegister = 0;
staticSampler.RegisterSpace = 0;
staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc = {};
desc.NumParameters = _countof(param);
desc.pParameters = param;
desc.NumStaticSamplers = 1;
desc.pStaticSamplers = &staticSampler;
desc.Flags =
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
ID3DBlob* blob = NULL;
static decltype(D3D12SerializeRootSignature)* D3D12SerializeRootSignature = (decltype(D3D12SerializeRootSignature))GetProcAddress(GetModuleHandle("d3d12.dll"), "D3D12SerializeRootSignature");
if (D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, NULL) != S_OK)
return false;
g_pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&g_pRootSignature));
blob->Release();
}
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX12 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
psoDesc.NodeMask = 1;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.pRootSignature = g_pRootSignature;
psoDesc.SampleMask = UINT_MAX;
psoDesc.NumRenderTargets = 1;
psoDesc.RTVFormats[0] = g_RTVFormat;
psoDesc.SampleDesc.Count = 1;
psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
#ifdef USE_D3DCOMPILE
decltype(D3DCompile)* D3DCompile = load_d3dcompile();
if (D3DCompile == nullptr)
return false;
#endif
// Create the vertex shader
{
#ifdef USE_D3DCOMPILE
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &g_pVertexShaderBlob, NULL);
if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
psoDesc.VS = { g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize() };
#else
psoDesc.VS = { ImGui_vertexShaderDX12, ImGui_vertexShaderDX12_len };
#endif
// Create the input layout
static D3D12_INPUT_ELEMENT_DESC local_layout[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
psoDesc.InputLayout = { local_layout, 3 };
}
// Create the pixel shader
{
#ifdef USE_D3DCOMPILE
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
SamplerState sampler0 : register(s0);\
Texture2D texture0 : register(t0);\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &g_pPixelShaderBlob, NULL);
if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
psoDesc.PS = { g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize() };
#else
psoDesc.PS = { ImGui_pixelShaderDX12, ImGui_pixelShaderDX12_len };
#endif
}
#ifdef USE_D3DCOMPILE
unload_d3dcompile();
#endif
// Create the blending setup
{
D3D12_BLEND_DESC& desc = psoDesc.BlendState;
desc.AlphaToCoverageEnable = false;
desc.RenderTarget[0].BlendEnable = true;
desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO;
desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
}
// Create the rasterizer state
{
D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState;
desc.FillMode = D3D12_FILL_MODE_SOLID;
desc.CullMode = D3D12_CULL_MODE_NONE;
desc.FrontCounterClockwise = FALSE;
desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
desc.DepthClipEnable = true;
desc.MultisampleEnable = FALSE;
desc.AntialiasedLineEnable = FALSE;
desc.ForcedSampleCount = 0;
desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
}
// Create depth-stencil State
{
D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState;
desc.DepthEnable = false;
desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc.BackFace = desc.FrontFace;
}
if (g_pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&g_pPipelineState)) != S_OK)
return false;
ImGui_ImplDX12_CreateFontsTexture();
return true;
}
void ImGui_ImplDX12_InvalidateDeviceObjects()
{
if (!g_pd3dDevice)
return;
ImGuiIO& io = ImGui::GetIO();
#ifdef USE_D3DCOMPILE
if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
#endif
if (g_pRootSignature) { g_pRootSignature->Release(); g_pRootSignature = NULL; }
if (g_pPipelineState) { g_pPipelineState->Release(); g_pPipelineState = NULL; }
if (g_pFontTextureResource) { g_pFontTextureResource->Release(); g_pFontTextureResource = NULL; io.Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
for (UINT i = 0; i < g_numFramesInFlight; i++)
{
FrameResources* fr = &g_pFrameResources[i];
if (fr->IndexBuffer) { fr->IndexBuffer->Release(); fr->IndexBuffer = NULL; }
if (fr->VertexBuffer) { fr->VertexBuffer->Release(); fr->VertexBuffer = NULL; }
}
}
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format,
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
{
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_dx12";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
g_pd3dDevice = device;
g_RTVFormat = rtv_format;
g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
g_hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
g_pFrameResources = new FrameResources[num_frames_in_flight];
g_numFramesInFlight = num_frames_in_flight;
g_frameIndex = UINT_MAX;
// Create buffers with a default size (they will later be grown as needed)
for (int i = 0; i < num_frames_in_flight; i++)
{
FrameResources* fr = &g_pFrameResources[i];
fr->IndexBuffer = NULL;
fr->VertexBuffer = NULL;
fr->IndexBufferSize = 10000;
fr->VertexBufferSize = 5000;
}
return true;
}
void ImGui_ImplDX12_Shutdown()
{
ImGui_ImplDX12_InvalidateDeviceObjects();
delete[] g_pFrameResources;
g_pFrameResources = NULL;
g_pd3dDevice = NULL;
g_hFontSrvCpuDescHandle.ptr = 0;
g_hFontSrvGpuDescHandle.ptr = 0;
g_numFramesInFlight = 0;
g_frameIndex = UINT_MAX;
}
bool ImGui_ImplDX12_NewFrame()
{
if (!g_pPipelineState)
return ImGui_ImplDX12_CreateDeviceObjects();
return true;
}

View File

@ -0,0 +1,34 @@
// dear imgui: Renderer for DirectX12
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// Issues:
// [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
enum DXGI_FORMAT;
struct ID3D12Device;
struct ID3D12GraphicsCommandList;
struct D3D12_CPU_DESCRIPTOR_HANDLE;
struct D3D12_GPU_DESCRIPTOR_HANDLE;
// cmd_list is the command list that the implementation will use to render imgui draw lists.
// Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate
// render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle.
// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture.
IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format,
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle);
IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown();
IMGUI_IMPL_API bool ImGui_ImplDX12_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list);
// Use if you want to reset your rendering device without losing ImGui state.
IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects();

View File

@ -0,0 +1,286 @@
// dear imgui: Renderer for DirectX9
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
// 2019-01-16: Misc: Disabled fog before drawing UI's. Fixes issue #2288.
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-06-08: Misc: Extracted imgui_impl_dx9.cpp/.h away from the old combined DX9+Win32 example.
// 2018-06-08: DirectX9: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-07: Render: Saving/restoring Transform because they don't seem to be included in the StateBlock. Setting shading mode to Gouraud.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
#include "../../imgui.h"
#include "imgui_impl_dx9.h"
// DirectX
#include <d3d9.h>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
// DirectX data
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
static LPDIRECT3DTEXTURE9 g_FontTexture = NULL;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
struct CUSTOMVERTEX
{
float pos[3];
D3DCOLOR col;
float uv[2];
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data)
{
// Setup viewport
D3DVIEWPORT9 vp;
vp.X = vp.Y = 0;
vp.Width = (DWORD)draw_data->DisplaySize.x;
vp.Height = (DWORD)draw_data->DisplaySize.y;
vp.MinZ = 0.0f;
vp.MaxZ = 1.0f;
g_pd3dDevice->SetViewport(&vp);
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient)
g_pd3dDevice->SetPixelShader(NULL);
g_pd3dDevice->SetVertexShader(NULL);
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, true);
g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, false);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// Setup orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
// Being agnostic of whether <d3dx9.h> or <DirectXMath.h> can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH()
{
float L = draw_data->DisplayPos.x + 0.5f;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f;
float T = draw_data->DisplayPos.y + 0.5f;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f;
D3DMATRIX mat_identity = { { { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } } };
D3DMATRIX mat_projection =
{ { {
2.0f/(R-L), 0.0f, 0.0f, 0.0f,
0.0f, 2.0f/(T-B), 0.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f,
(L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f
} } };
g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
}
}
// Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
// Create and grow buffers if needed
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
{
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
if (g_pd3dDevice->CreateVertexBuffer(g_VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL) < 0)
return;
}
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
{
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
if (g_pd3dDevice->CreateIndexBuffer(g_IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &g_pIB, NULL) < 0)
return;
}
// Backup the DX9 state
IDirect3DStateBlock9* d3d9_state_block = NULL;
if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
return;
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
D3DMATRIX last_world, last_view, last_projection;
g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
// Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format.
// FIXME-OPT: This is a waste of resource, the ideal is to use imconfig.h and
// 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR
// 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
CUSTOMVERTEX* vtx_dst;
ImDrawIdx* idx_dst;
if (g_pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0)
return;
if (g_pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0)
return;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
const ImDrawVert* vtx_src = cmd_list->VtxBuffer.Data;
for (int i = 0; i < cmd_list->VtxBuffer.Size; i++)
{
vtx_dst->pos[0] = vtx_src->pos.x;
vtx_dst->pos[1] = vtx_src->pos.y;
vtx_dst->pos[2] = 0.0f;
vtx_dst->col = (vtx_src->col & 0xFF00FF00) | ((vtx_src->col & 0xFF0000) >> 16) | ((vtx_src->col & 0xFF) << 16); // RGBA --> ARGB for DirectX9
vtx_dst->uv[0] = vtx_src->uv.x;
vtx_dst->uv[1] = vtx_src->uv.y;
vtx_dst++;
vtx_src++;
}
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
idx_dst += cmd_list->IdxBuffer.Size;
}
g_pVB->Unlock();
g_pIB->Unlock();
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetIndices(g_pIB);
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
// Setup desired DX state
ImGui_ImplDX9_SetupRenderState(draw_data);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX9_SetupRenderState(draw_data);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
const RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->TextureId;
g_pd3dDevice->SetTexture(0, texture);
g_pd3dDevice->SetScissorRect(&r);
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount/3);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore the DX9 transform
g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
// Restore the DX9 state
d3d9_state_block->Apply();
d3d9_state_block->Release();
}
bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
{
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_dx9";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
g_pd3dDevice = device;
g_pd3dDevice->AddRef();
return true;
}
void ImGui_ImplDX9_Shutdown()
{
ImGui_ImplDX9_InvalidateDeviceObjects();
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
}
static bool ImGui_ImplDX9_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height, bytes_per_pixel;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
// Upload texture to graphics system
g_FontTexture = NULL;
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0)
return false;
D3DLOCKED_RECT tex_locked_rect;
if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
return false;
for (int y = 0; y < height; y++)
memcpy((unsigned char *)tex_locked_rect.pBits + tex_locked_rect.Pitch * y, pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel));
g_FontTexture->UnlockRect(0);
// Store our identifier
io.Fonts->TexID = (ImTextureID)g_FontTexture;
return true;
}
bool ImGui_ImplDX9_CreateDeviceObjects()
{
if (!g_pd3dDevice)
return false;
if (!ImGui_ImplDX9_CreateFontsTexture())
return false;
return true;
}
void ImGui_ImplDX9_InvalidateDeviceObjects()
{
if (!g_pd3dDevice)
return;
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
if (g_FontTexture) { g_FontTexture->Release(); g_FontTexture = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
}
bool ImGui_ImplDX9_NewFrame()
{
if (!g_FontTexture)
return ImGui_ImplDX9_CreateDeviceObjects();
return true;
}

View File

@ -0,0 +1,23 @@
// dear imgui: Renderer for DirectX9
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
struct IDirect3DDevice9;
IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device);
IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown();
IMGUI_IMPL_API bool ImGui_ImplDX9_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing ImGui state.
IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects();

View File

@ -0,0 +1,324 @@
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// Implemented features:
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
#include "../../imgui.h"
#include "imgui_impl_win32.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <XInput.h>
#include <tchar.h>
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter().
// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent.
// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages.
// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set.
// Win32 Data
static HWND g_hWnd = 0;
static INT64 g_Time = 0;
static INT64 g_TicksPerSecond = 0;
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
static bool g_HasGamepad = false;
static bool g_WantUpdateHasGamepad = true;
// Functions
bool ImGui_ImplWin32_Init(void* hwnd)
{
if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
return false;
if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
return false;
// Setup back-end capabilities flags
g_hWnd = (HWND)hwnd;
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_win32";
io.ImeWindowHandle = hwnd;
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
io.KeyMap[ImGuiKey_Tab] = VK_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
io.KeyMap[ImGuiKey_Home] = VK_HOME;
io.KeyMap[ImGuiKey_End] = VK_END;
io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
io.KeyMap[ImGuiKey_Space] = VK_SPACE;
io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
io.KeyMap[ImGuiKey_A] = 'A';
io.KeyMap[ImGuiKey_C] = 'C';
io.KeyMap[ImGuiKey_V] = 'V';
io.KeyMap[ImGuiKey_X] = 'X';
io.KeyMap[ImGuiKey_Y] = 'Y';
io.KeyMap[ImGuiKey_Z] = 'Z';
return true;
}
void ImGui_ImplWin32_Shutdown()
{
g_hWnd = (HWND)0;
}
static bool ImGui_ImplWin32_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return false;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
::SetCursor(NULL);
}
else
{
// Show OS mouse cursor
LPTSTR win32_cursor = IDC_ARROW;
switch (imgui_cursor)
{
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
}
::SetCursor(::LoadCursor(NULL, win32_cursor));
}
return true;
}
static void ImGui_ImplWin32_UpdateMousePos()
{
ImGuiIO& io = ImGui::GetIO();
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
{
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
::ClientToScreen(g_hWnd, &pos);
::SetCursorPos(pos.x, pos.y);
}
// Set mouse position
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
POINT pos;
if (HWND active_window = ::GetForegroundWindow())
if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd))
if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos))
io.MousePos = ImVec2((float)pos.x, (float)pos.y);
}
/*
#ifdef _MSC_VER
#pragma comment(lib, "xinput")
#endif
// Gamepad navigation mapping
static void ImGui_ImplWin32_UpdateGamepads()
{
ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
if (g_WantUpdateHasGamepad)
{
XINPUT_CAPABILITIES caps;
g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS);
g_WantUpdateHasGamepad = false;
}
XINPUT_STATE xinput_state;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
{
const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
#define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
#define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A
MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B
MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X
MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y
MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down
MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767);
#undef MAP_BUTTON
#undef MAP_ANALOG
}
}
*/
void ImGui_ImplWin32_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
// Setup display size (every frame to accommodate for window resizing)
RECT rect;
::GetClientRect(g_hWnd, &rect);
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
// Setup time step
INT64 current_time;
::QueryPerformanceCounter((LARGE_INTEGER *)&current_time);
io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
g_Time = current_time;
// Read keyboard modifiers inputs
io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0;
io.KeySuper = false;
// io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
// Update OS mouse position
ImGui_ImplWin32_UpdateMousePos();
// Update OS mouse cursor with the cursor requested by imgui
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
if (g_LastMouseCursor != mouse_cursor)
{
g_LastMouseCursor = mouse_cursor;
ImGui_ImplWin32_UpdateMouseCursor();
}
// Update game controllers (if enabled and available)
//ImGui_ImplWin32_UpdateGamepads();
}
// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
#ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x020E
#endif
#ifndef DBT_DEVNODES_CHANGED
#define DBT_DEVNODES_CHANGED 0x0007
#endif
// Process Win32 mouse/keyboard inputs.
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ImGui::GetCurrentContext() == NULL)
return 0;
ImGuiIO& io = ImGui::GetIO();
switch (msg)
{
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
{
int button = 0;
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; }
if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL)
::SetCapture(hwnd);
io.MouseDown[button] = true;
return 0;
}
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_XBUTTONUP:
{
int button = 0;
if (msg == WM_LBUTTONUP) { button = 0; }
if (msg == WM_RBUTTONUP) { button = 1; }
if (msg == WM_MBUTTONUP) { button = 2; }
if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
io.MouseDown[button] = false;
if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd)
::ReleaseCapture();
return 0;
}
case WM_MOUSEWHEEL:
io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
return 0;
case WM_MOUSEHWHEEL:
io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
return 0;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (wParam < 256)
io.KeysDown[wParam] = 1;
return 0;
case WM_KEYUP:
case WM_SYSKEYUP:
if (wParam < 256)
io.KeysDown[wParam] = 0;
return 0;
case WM_CHAR:
// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
io.AddInputCharacter((unsigned int)wParam);
return 0;
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
return 1;
return 0;
case WM_DEVICECHANGE:
if ((UINT)wParam == DBT_DEVNODES_CHANGED)
g_WantUpdateHasGamepad = true;
return 0;
}
return 0;
}

View File

@ -0,0 +1,21 @@
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// Implemented features:
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
#pragma once
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
// Handler for Win32 messages, update mouse/keyboard data.
// You may or not need this for your implementation, but it can serve as reference for handling inputs.
// Intentionally commented out to avoid dragging dependencies on <windows.h> types. You can COPY this line into your .cpp code instead.
/*
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
*/

630
ImGui/imstb_rectpack.h Normal file
View File

@ -0,0 +1,630 @@
// [DEAR IMGUI]
// This is a slightly modified version of stb_rect_pack.h 0.99.
// Those changes would need to be pushed into nothings/stb:
// - Added STBRP__CDECL
// Grep for [DEAR IMGUI] to find the changes.
// stb_rect_pack.h - v0.99 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
// Does not do rotation.
//
// Not necessarily the awesomest packing method, but better than
// the totally naive one in stb_truetype (which is primarily what
// this is meant to replace).
//
// Has only had a few tests run, may have issues.
//
// More docs to come.
//
// No memory allocations; uses qsort() and assert() from stdlib.
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
//
// This library currently uses the Skyline Bottom-Left algorithm.
//
// Please note: better rectangle packers are welcome! Please
// implement them to the same API, but with a different init
// function.
//
// Credits
//
// Library
// Sean Barrett
// Minor features
// Martins Mozeiko
// github:IntellectualKitty
//
// Bugfixes / warning fixes
// Jeremy Jaussaud
//
// Version history:
//
// 0.99 (2019-02-07) warning fixes
// 0.11 (2017-03-03) return packing success/fail result
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
// 0.09 (2016-08-27) fix compiler warnings
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
// 0.05: added STBRP_ASSERT to allow replacing assert
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
// 0.01: initial release
//
// LICENSE
//
// See end of file for license information.
//////////////////////////////////////////////////////////////////////////////
//
// INCLUDE SECTION
//
#ifndef STB_INCLUDE_STB_RECT_PACK_H
#define STB_INCLUDE_STB_RECT_PACK_H
#define STB_RECT_PACK_VERSION 1
#ifdef STBRP_STATIC
#define STBRP_DEF static
#else
#define STBRP_DEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stbrp_context stbrp_context;
typedef struct stbrp_node stbrp_node;
typedef struct stbrp_rect stbrp_rect;
#ifdef STBRP_LARGE_RECTS
typedef int stbrp_coord;
#else
typedef unsigned short stbrp_coord;
#endif
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
// Assign packed locations to rectangles. The rectangles are of type
// 'stbrp_rect' defined below, stored in the array 'rects', and there
// are 'num_rects' many of them.
//
// Rectangles which are successfully packed have the 'was_packed' flag
// set to a non-zero value and 'x' and 'y' store the minimum location
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
// if you imagine y increasing downwards). Rectangles which do not fit
// have the 'was_packed' flag set to 0.
//
// You should not try to access the 'rects' array from another thread
// while this function is running, as the function temporarily reorders
// the array while it executes.
//
// To pack into another rectangle, you need to call stbrp_init_target
// again. To continue packing into the same rectangle, you can call
// this function again. Calling this multiple times with multiple rect
// arrays will probably produce worse packing results than calling it
// a single time with the full rectangle array, but the option is
// available.
//
// The function returns 1 if all of the rectangles were successfully
// packed and 0 otherwise.
struct stbrp_rect
{
// reserved for your use:
int id;
// input:
stbrp_coord w, h;
// output:
stbrp_coord x, y;
int was_packed; // non-zero if valid packing
}; // 16 bytes, nominally
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
// Initialize a rectangle packer to:
// pack a rectangle that is 'width' by 'height' in dimensions
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
//
// You must call this function every time you start packing into a new target.
//
// There is no "shutdown" function. The 'nodes' memory must stay valid for
// the following stbrp_pack_rects() call (or calls), but can be freed after
// the call (or calls) finish.
//
// Note: to guarantee best results, either:
// 1. make sure 'num_nodes' >= 'width'
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
//
// If you don't do either of the above things, widths will be quantized to multiples
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
//
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
// may run out of temporary storage and be unable to pack some rectangles.
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
// Optionally call this function after init but before doing any packing to
// change the handling of the out-of-temp-memory scenario, described above.
// If you call init again, this will be reset to the default (false).
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
// Optionally select which packing heuristic the library should use. Different
// heuristics will produce better/worse results for different data sets.
// If you call init again, this will be reset to the default.
enum
{
STBRP_HEURISTIC_Skyline_default=0,
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
STBRP_HEURISTIC_Skyline_BF_sortHeight
};
//////////////////////////////////////////////////////////////////////////////
//
// the details of the following structures don't matter to you, but they must
// be visible so you can handle the memory allocations for them
struct stbrp_node
{
stbrp_coord x,y;
stbrp_node *next;
};
struct stbrp_context
{
int width;
int height;
int align;
int init_mode;
int heuristic;
int num_nodes;
stbrp_node *active_head;
stbrp_node *free_head;
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
};
#ifdef __cplusplus
}
#endif
#endif
//////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION SECTION
//
#ifdef STB_RECT_PACK_IMPLEMENTATION
#ifndef STBRP_SORT
#include <stdlib.h>
#define STBRP_SORT qsort
#endif
#ifndef STBRP_ASSERT
#include <assert.h>
#define STBRP_ASSERT assert
#endif
// [DEAR IMGUI] Added STBRP__CDECL
#ifdef _MSC_VER
#define STBRP__NOTUSED(v) (void)(v)
#define STBRP__CDECL __cdecl
#else
#define STBRP__NOTUSED(v) (void)sizeof(v)
#define STBRP__CDECL
#endif
enum
{
STBRP__INIT_skyline = 1
};
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
{
switch (context->init_mode) {
case STBRP__INIT_skyline:
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
context->heuristic = heuristic;
break;
default:
STBRP_ASSERT(0);
}
}
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
{
if (allow_out_of_mem)
// if it's ok to run out of memory, then don't bother aligning them;
// this gives better packing, but may fail due to OOM (even though
// the rectangles easily fit). @TODO a smarter approach would be to only
// quantize once we've hit OOM, then we could get rid of this parameter.
context->align = 1;
else {
// if it's not ok to run out of memory, then quantize the widths
// so that num_nodes is always enough nodes.
//
// I.e. num_nodes * align >= width
// align >= width / num_nodes
// align = ceil(width/num_nodes)
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
}
}
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
{
int i;
#ifndef STBRP_LARGE_RECTS
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
#endif
for (i=0; i < num_nodes-1; ++i)
nodes[i].next = &nodes[i+1];
nodes[i].next = NULL;
context->init_mode = STBRP__INIT_skyline;
context->heuristic = STBRP_HEURISTIC_Skyline_default;
context->free_head = &nodes[0];
context->active_head = &context->extra[0];
context->width = width;
context->height = height;
context->num_nodes = num_nodes;
stbrp_setup_allow_out_of_mem(context, 0);
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
context->extra[0].x = 0;
context->extra[0].y = 0;
context->extra[0].next = &context->extra[1];
context->extra[1].x = (stbrp_coord) width;
#ifdef STBRP_LARGE_RECTS
context->extra[1].y = (1<<30);
#else
context->extra[1].y = 65535;
#endif
context->extra[1].next = NULL;
}
// find minimum y position if it starts at x1
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
{
stbrp_node *node = first;
int x1 = x0 + width;
int min_y, visited_width, waste_area;
STBRP__NOTUSED(c);
STBRP_ASSERT(first->x <= x0);
#if 0
// skip in case we're past the node
while (node->next->x <= x0)
++node;
#else
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
#endif
STBRP_ASSERT(node->x <= x0);
min_y = 0;
waste_area = 0;
visited_width = 0;
while (node->x < x1) {
if (node->y > min_y) {
// raise min_y higher.
// we've accounted for all waste up to min_y,
// but we'll now add more waste for everything we've visted
waste_area += visited_width * (node->y - min_y);
min_y = node->y;
// the first time through, visited_width might be reduced
if (node->x < x0)
visited_width += node->next->x - x0;
else
visited_width += node->next->x - node->x;
} else {
// add waste area
int under_width = node->next->x - node->x;
if (under_width + visited_width > width)
under_width = width - visited_width;
waste_area += under_width * (min_y - node->y);
visited_width += under_width;
}
node = node->next;
}
*pwaste = waste_area;
return min_y;
}
typedef struct
{
int x,y;
stbrp_node **prev_link;
} stbrp__findresult;
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
{
int best_waste = (1<<30), best_x, best_y = (1 << 30);
stbrp__findresult fr;
stbrp_node **prev, *node, *tail, **best = NULL;
// align to multiple of c->align
width = (width + c->align - 1);
width -= width % c->align;
STBRP_ASSERT(width % c->align == 0);
node = c->active_head;
prev = &c->active_head;
while (node->x + width <= c->width) {
int y,waste;
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
// bottom left
if (y < best_y) {
best_y = y;
best = prev;
}
} else {
// best-fit
if (y + height <= c->height) {
// can only use it if it first vertically
if (y < best_y || (y == best_y && waste < best_waste)) {
best_y = y;
best_waste = waste;
best = prev;
}
}
}
prev = &node->next;
node = node->next;
}
best_x = (best == NULL) ? 0 : (*best)->x;
// if doing best-fit (BF), we also have to try aligning right edge to each node position
//
// e.g, if fitting
//
// ____________________
// |____________________|
//
// into
//
// | |
// | ____________|
// |____________|
//
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
//
// This makes BF take about 2x the time
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
tail = c->active_head;
node = c->active_head;
prev = &c->active_head;
// find first node that's admissible
while (tail->x < width)
tail = tail->next;
while (tail) {
int xpos = tail->x - width;
int y,waste;
STBRP_ASSERT(xpos >= 0);
// find the left position that matches this
while (node->next->x <= xpos) {
prev = &node->next;
node = node->next;
}
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
if (y + height < c->height) {
if (y <= best_y) {
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
best_x = xpos;
STBRP_ASSERT(y <= best_y);
best_y = y;
best_waste = waste;
best = prev;
}
}
}
tail = tail->next;
}
}
fr.prev_link = best;
fr.x = best_x;
fr.y = best_y;
return fr;
}
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
{
// find best position according to heuristic
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
stbrp_node *node, *cur;
// bail if:
// 1. it failed
// 2. the best node doesn't fit (we don't always check this)
// 3. we're out of memory
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
res.prev_link = NULL;
return res;
}
// on success, create new node
node = context->free_head;
node->x = (stbrp_coord) res.x;
node->y = (stbrp_coord) (res.y + height);
context->free_head = node->next;
// insert the new node into the right starting point, and
// let 'cur' point to the remaining nodes needing to be
// stiched back in
cur = *res.prev_link;
if (cur->x < res.x) {
// preserve the existing one, so start testing with the next one
stbrp_node *next = cur->next;
cur->next = node;
cur = next;
} else {
*res.prev_link = node;
}
// from here, traverse cur and free the nodes, until we get to one
// that shouldn't be freed
while (cur->next && cur->next->x <= res.x + width) {
stbrp_node *next = cur->next;
// move the current node to the free list
cur->next = context->free_head;
context->free_head = cur;
cur = next;
}
// stitch the list back in
node->next = cur;
if (cur->x < res.x + width)
cur->x = (stbrp_coord) (res.x + width);
#ifdef _DEBUG
cur = context->active_head;
while (cur->x < context->width) {
STBRP_ASSERT(cur->x < cur->next->x);
cur = cur->next;
}
STBRP_ASSERT(cur->next == NULL);
{
int count=0;
cur = context->active_head;
while (cur) {
cur = cur->next;
++count;
}
cur = context->free_head;
while (cur) {
cur = cur->next;
++count;
}
STBRP_ASSERT(count == context->num_nodes+2);
}
#endif
return res;
}
// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
if (p->h > q->h)
return -1;
if (p->h < q->h)
return 1;
return (p->w > q->w) ? -1 : (p->w < q->w);
}
// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
}
#ifdef STBRP_LARGE_RECTS
#define STBRP__MAXVAL 0xffffffff
#else
#define STBRP__MAXVAL 0xffff
#endif
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
{
int i, all_rects_packed = 1;
// we use the 'was_packed' field internally to allow sorting/unsorting
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = i;
}
// sort according to heuristic
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
for (i=0; i < num_rects; ++i) {
if (rects[i].w == 0 || rects[i].h == 0) {
rects[i].x = rects[i].y = 0; // empty rect needs no space
} else {
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
if (fr.prev_link) {
rects[i].x = (stbrp_coord) fr.x;
rects[i].y = (stbrp_coord) fr.y;
} else {
rects[i].x = rects[i].y = STBRP__MAXVAL;
}
}
}
// unsort
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
// set was_packed flags and all_rects_packed status
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
if (!rects[i].was_packed)
all_rects_packed = 0;
}
// return the all_rects_packed status
return all_rects_packed;
}
#endif
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

1417
ImGui/imstb_textedit.h Normal file

File diff suppressed because it is too large Load Diff

4903
ImGui/imstb_truetype.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,10 +7,10 @@ mkdir -p linux/tools
cp scripts/find_interfaces.sh linux/tools/
cp scripts/steamclient_loader.sh linux/tools/
../protobuf/prefix_x86/bin/protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86/libsteam_api.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -std=c++11 && echo built32
g++ -m32 -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x86 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -lpthread -std=c++11 && echo built_lobby_connect32
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DSTEAMCLIENT_DLL -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86/steamclient.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -std=c++11 && echo built32_steamclient
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DNO_OVERLAY -DEMU_RELEASE_BUILD -DNDEBUG -s -o linux/x86/libsteam_api.so dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -std=c++11 && echo built32
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DNO_OVERLAY -DEMU_RELEASE_BUILD -DSTEAMCLIENT_DLL -DNDEBUG -s -o linux/x86/steamclient.so dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -std=c++11 && echo built32_steamclient
g++ -m32 -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DNO_OVERLAY -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x86 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -lpthread -std=c++11 && echo built_lobby_connect32
../protobuf/prefix/bin/protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86_64/libsteam_api.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -std=c++11 && echo built64
g++ -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x64 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -lpthread -std=c++11 && echo built_lobby_connect64
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DSTEAMCLIENT_DLL -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86_64/steamclient.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -std=c++11 && echo built64_steamclient
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DNO_OVERLAY -DEMU_RELEASE_BUILD -DNDEBUG -s -o linux/x86_64/libsteam_api.so dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -std=c++11 && echo built64
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DNO_OVERLAY -DEMU_RELEASE_BUILD -DSTEAMCLIENT_DLL -DNDEBUG -s -o linux/x86_64/steamclient.so dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -std=c++11 && echo built64_steamclient
g++ -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DNO_OVERLAY -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x64 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -lpthread -std=c++11 && echo built_lobby_connect64

View File

@ -4,13 +4,13 @@ call build_set_protobuf_directories.bat
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /I%PROTOBUF_X86_DIRECTORY%\include\ /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /link /OUT:steam_api.dll
cl /LD /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ /DGLEW_STATIC /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\lib\Release\Win32\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api.dll
cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient.dll
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets.dll
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x64.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /I%PROTOBUF_X64_DIRECTORY%\include\ /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c "%PROTOBUF_X64_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /link /OUT:steam_api64.dll
cl /LD /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ /DGLEW_STATIC /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\lib\Release\x64\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api64.dll
cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient64.dll
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets64.dll

View File

@ -6,7 +6,7 @@ call build_set_protobuf_directories.bat
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /DNO_DISK_WRITES /DLOBBY_CONNECT /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ lobby_connect.cpp dll/*.cpp dll/*.cc "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Comdlg32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\lobby_connect\lobby_connect.exe
cl /DNO_OVERLAY /DNO_DISK_WRITES /DLOBBY_CONNECT /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ lobby_connect.cpp dll/*.cpp dll/*.cc "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Comdlg32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\lobby_connect\lobby_connect.exe
del /Q /S release\lobby_connect\*.lib
del /Q /S release\lobby_connect\*.exp
copy Readme_lobby_connect.txt release\lobby_connect\Readme.txt

View File

@ -10,12 +10,12 @@ call build_set_protobuf_directories.bat
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api.dll
call build_set_protobuf_directories.bat
cl /LD /DNO_OVERLAY /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api.dll
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x64.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc "%PROTOBUF_X64_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api64.dll
cl /LD /DNO_OVERLAY /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc "%PROTOBUF_X64_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api64.dll
copy Readme_release.txt release\Readme.txt
xcopy /s files_example\* release\
call build_win_release_experimental.bat

View File

@ -6,11 +6,11 @@ call build_set_protobuf_directories.bat
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api.dll
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DCONTROLLER_SUPPORT /DNDEBUG /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\lib\Release\Win32\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api.dll
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG steamclient.cpp /EHsc /MP12 /Ox /link /OUT:release\experimental\steamclient.dll
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x64.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c "%PROTOBUF_X64_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api64.dll
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DCONTROLLER_SUPPORT /DNDEBUG /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\lib\Release\x64\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api64.dll
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG steamclient.cpp /EHsc /MP12 /Ox /link /OUT:release\experimental\steamclient64.dll
copy Readme_experimental.txt release\experimental\Readme.txt

View File

@ -18,7 +18,29 @@
#ifndef BASE_INCLUDE
#define BASE_INCLUDE
#if defined(WIN32) || defined(_WIN32)
#if defined(WIN64) || defined(_WIN64) || defined(__MINGW64__)
#define __WINDOWS_64__
#elif defined(WIN32) || defined(_WIN32) || defined(__MINGW32__)
#define __WINDOWS_32__
#endif
#if defined(__WINDOWS_32__) || defined(__WINDOWS_64__)
#define __WINDOWS__
#endif
#if defined(__linux__) || defined(linux)
#if defined(__x86_64__)
#define __LINUX_64__
#else
#define __LINUX_32__
#endif
#endif
#if defined(__LINUX_32__) || defined(__LINUX_64__)
#define __LINUX__
#endif
#if defined(__WINDOWS__)
#define STEAM_WIN32
#pragma warning( disable : 4716)
#ifndef NOMINMAX

View File

@ -223,6 +223,12 @@ STEAMAPI_API bool S_CALLTYPE SteamAPI_Init()
load_old_interface_versions();
user_steam_pipe = get_steam_client()->CreateSteamPipe();
get_steam_client()->ConnectToGlobalUser(user_steam_pipe);
Steam_Client* client = get_steam_client();
#ifndef NO_OVERLAY
if( client->enable_overlay )
client->steam_overlay->SetupOverlay();
#endif
return true;
}

View File

@ -185,6 +185,17 @@ message Friend_Messages {
}
}
message Steam_Messages {
enum Types {
FRIEND_CHAT = 0;
}
Types type = 1;
oneof message_data {
bytes message = 2;
}
}
message Common_Message {
uint64 source_id = 1;
uint64 dest_id = 2;
@ -200,6 +211,7 @@ message Common_Message {
Friend_Messages friend_messages = 11;
Network_Old network_old = 12;
Networking_Sockets networking_sockets = 13;
Steam_Messages steam_messages = 14;
}
uint32 source_ip = 128;

View File

@ -574,6 +574,11 @@ void Networking::do_callbacks_message(Common_Message *msg)
PRINT_DEBUG("has_networking_sockets\n");
run_callbacks(CALLBACK_ID_NETWORKING_SOCKETS, msg);
}
if (msg->has_steam_messages()) {
PRINT_DEBUG("has_steam_messages\n");
run_callbacks(CALLBACK_ID_STEAM_MESSAGES, msg);
}
}
bool Networking::handle_tcp(Common_Message *msg, struct TCP_Socket &socket)

View File

@ -60,6 +60,7 @@ enum Callback_Ids {
CALLBACK_ID_AUTH_TICKET,
CALLBACK_ID_FRIEND_MESSAGES,
CALLBACK_ID_NETWORKING_SOCKETS,
CALLBACK_ID_STEAM_MESSAGES,
CALLBACK_IDS_MAX
};

View File

@ -46,9 +46,13 @@ static void background_thread(Steam_Client *client)
Steam_Client::Steam_Client()
{
uint32 appid = create_localstorage_settings(&settings_client, &settings_server, &local_storage);
{
std::ifstream chk_ovlay(Local_Storage::get_program_path() + PATH_SEPARATOR + "disable_overlay.txt", std::ios::in);
if (chk_ovlay)
enable_overlay = false;
}
network = new Networking(settings_server->get_local_steam_id(), appid, settings_server->get_port(), &(settings_server->custom_broadcasts), settings_server->disable_networking);
callback_results_client = new SteamCallResults();
@ -59,9 +63,12 @@ Steam_Client::Steam_Client()
PRINT_DEBUG("steam client init: id: %llu server id: %llu appid: %u port: %u \n", settings_client->get_local_steam_id().ConvertToUint64(), settings_server->get_local_steam_id().ConvertToUint64(), appid, settings_server->get_port());
steam_overlay = new Steam_Overlay(settings_client, callback_results_client, callbacks_client, run_every_runcb, network);
steam_user = new Steam_User(settings_client, local_storage, network, callback_results_client, callbacks_client);
steam_friends = new Steam_Friends(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_utils = new Steam_Utils(settings_client, callback_results_client);
steam_friends = new Steam_Friends(settings_client, network, callback_results_client, callbacks_client, run_every_runcb, steam_overlay);
steam_utils = new Steam_Utils(settings_client, callback_results_client, steam_overlay);
steam_matchmaking = new Steam_Matchmaking(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_matchmaking_servers = new Steam_Matchmaking_Servers(settings_client, network);
steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client);
@ -90,7 +97,7 @@ Steam_Client::Steam_Client()
PRINT_DEBUG("client init gameserver\n");
steam_gameserver = new Steam_GameServer(settings_server, network, callbacks_server);
steam_gameserver_utils = new Steam_Utils(settings_server, callback_results_server);
steam_gameserver_utils = new Steam_Utils(settings_server, callback_results_server, steam_overlay);
steam_gameserverstats = new Steam_GameServerStats(settings_server, network, callback_results_server, callbacks_server);
steam_gameserver_networking = new Steam_Networking(settings_server, network, callbacks_server, run_every_runcb);
steam_gameserver_http = new Steam_HTTP(settings_server, network, callback_results_server, callbacks_server);

View File

@ -53,6 +53,8 @@
#include "steam_gameserverstats.h"
#include "steam_masterserver_updater.h"
#include "../overlay_experimental/steam_overlay.h"
#include <thread>
enum Steam_Pipe {
@ -127,6 +129,9 @@ public:
Steam_Game_Coordinator *steam_gameserver_game_coordinator;
Steam_Masterserver_Updater *steam_masterserver_updater;
Steam_Overlay* steam_overlay;
bool enable_overlay = true;
bool user_logged_in = false;
bool server_init = false;
std::thread background_keepalive;

View File

@ -15,7 +15,11 @@
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_FRIENDS_H__
#define __INCLUDED_STEAM_FRIENDS_H__
#include "base.h"
#include "../overlay_experimental/steam_overlay.h"
#define SEND_FRIEND_RATE 4.0
@ -46,6 +50,7 @@ public ISteamFriends
class SteamCallBacks *callbacks;
class SteamCallResults *callback_results;
class RunEveryRunCB *run_every_runcb;
class Steam_Overlay* overlay;
Friend us;
bool modified;
@ -128,13 +133,14 @@ static void steam_friends_run_every_runcb(void *object)
steam_friends->RunCallbacks();
}
Steam_Friends(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
Steam_Friends(Settings* settings, Networking* network, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Steam_Overlay* overlay):
settings(settings),
network(network),
callbacks(callbacks),
callback_results(callback_results),
run_every_runcb(run_every_runcb),
overlay(overlay)
{
this->settings = settings;
this->network = network;
this->callbacks = callbacks;
this->callback_results = callback_results;
this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_FRIEND, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
this->network->setCallback(CALLBACK_ID_FRIEND_MESSAGES, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
@ -518,6 +524,7 @@ void SetInGameVoiceSpeaking( CSteamID steamIDUser, bool bSpeaking )
void ActivateGameOverlay( const char *pchDialog )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlay %s\n", pchDialog);
overlay->OpenOverlay(pchDialog);
}
@ -574,7 +581,7 @@ void SetPlayedWith( CSteamID steamIDUserPlayedWith )
void ActivateGameOverlayInviteDialog( CSteamID steamIDLobby )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayInviteDialog\n");
// TODO: Here open the overlay
overlay->OpenOverlayInvite(steamIDLobby);
}
// gets the small (32x32) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set
@ -1014,6 +1021,7 @@ void Callback(Common_Message *msg)
auto f = std::find_if(friends.begin(), friends.end(), [&id](Friend const& item) { return item.id() == id; });
if (friends.end() != f) {
persona_change((uint64)f->id(), k_EPersonaChangeStatus);
overlay->FriendDisconnect(*f);
friends.erase(f);
}
}
@ -1039,6 +1047,7 @@ void Callback(Common_Message *msg)
if (!f) {
if (msg->friend_().id() != settings->get_local_steam_id().ConvertToUint64()) {
friends.push_back(msg->friend_());
overlay->FriendConnect(msg->friend_());
persona_change((uint64)msg->friend_().id(), k_EPersonaChangeName);
}
} else {
@ -1059,16 +1068,31 @@ void Callback(Common_Message *msg)
if (msg->has_friend_messages()) {
if (msg->friend_messages().type() == Friend_Messages::LOBBY_INVITE) {
PRINT_DEBUG("Steam_Friends Got Lobby Invite\n");
if (overlay->Ready())
{
//TODO: the user should accept the invite first but we auto accept it because there's no gui yet
// Then we will handle it !
overlay->SetLobbyInvite(*find_friend(static_cast<uint64>(msg->source_id())), msg->friend_messages().lobby_id());
}
else
{
GameLobbyJoinRequested_t data;
data.m_steamIDLobby = CSteamID((uint64)msg->friend_messages().lobby_id());
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
if (msg->friend_messages().type() == Friend_Messages::GAME_INVITE) {
PRINT_DEBUG("Steam_Friends Got Game Invite\n");
//TODO: I'm pretty sure that the user should accept the invite before this is posted but we do like above
if (overlay->Ready())
{
// Then we will handle it !
overlay->SetRichInvite(*find_friend(static_cast<uint64>(msg->source_id())), msg->friend_messages().connect_str().c_str());
}
else
{
std::string const& connect_str = msg->friend_messages().connect_str();
GameRichPresenceJoinRequested_t data = {};
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
@ -1077,5 +1101,8 @@ void Callback(Common_Message *msg)
}
}
}
}
};
#endif//__INCLUDED_STEAM_FRIENDS_H__

View File

@ -1268,7 +1268,7 @@ void Callback(Common_Message *msg)
if (msg->lobby().owner() != settings->get_local_steam_id().ConvertToUint64() && msg->lobby().appid() == settings->get_local_game_id().AppID()) {
Lobby *lobby = get_lobby((uint64)msg->lobby().room_id());
if (!lobby) {
unsigned int old_size = lobbies.size();
size_t old_size = lobbies.size();
lobbies.resize(old_size + 1);
lobbies[old_size].set_room_id(msg->lobby().room_id());
lobby = &(lobbies[old_size]);

829
dll/steam_user_stats.cpp Normal file
View File

@ -0,0 +1,829 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_user_stats.h"
#include "dll.h"
unsigned int Steam_User_Stats::find_leaderboard(std::string name)
{
unsigned index = 1;
for (auto& leaderboard : leaderboards) {
if (leaderboard.name == name) return index;
++index;
}
return 0;
}
void Steam_User_Stats::load_achievements_db()
{
std::string file_path = Local_Storage::get_game_settings_path() + achievements_user_file;
local_storage->load_json(file_path, defined_achievements);
}
void Steam_User_Stats::load_achievements()
{
local_storage->load_json_file("", achievements_user_file, user_achievements);
}
void Steam_User_Stats::save_achievements()
{
local_storage->write_json_file("", achievements_user_file, user_achievements);
}
Steam_User_Stats::Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks):
settings(settings),
local_storage(local_storage),
callback_results(callback_results),
callbacks(callbacks),
defined_achievements(nlohmann::json::object()),
user_achievements(nlohmann::json::object())
{
load_achievements_db(); // achievements db
load_achievements(); // achievements per user
}
nlohmann::json const& Steam_User_Stats::GetAchievementsDb() const
{
return defined_achievements;
}
// Ask the server to send down this user's data and achievements for this game
STEAM_CALL_BACK(UserStatsReceived_t)
bool Steam_User_Stats::RequestCurrentStats()
{
PRINT_DEBUG("Steam_User_Stats::RequestCurrentStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
UserStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
data.m_steamIDUser = settings->get_local_steam_id();
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
return true;
}
// Data accessors
bool Steam_User_Stats::GetStat(const char* pchName, int32* pData)
{
PRINT_DEBUG("GetStat int32 %s\n", pchName);
if (!pchName || !pData) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto stats_config = settings->getStats();
auto stats_data = stats_config.find(pchName);
if (stats_data != stats_config.end()) {
if (stats_data->second.type != Stat_Type::STAT_TYPE_INT) return false;
}
int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char*)pData, sizeof(*pData));
if (read_data == sizeof(int32))
return true;
if (stats_data != stats_config.end()) {
*pData = stats_data->second.default_value_int;
return true;
}
return false;
}
bool Steam_User_Stats::GetStat(const char* pchName, float* pData)
{
PRINT_DEBUG("GetStat float %s\n", pchName);
if (!pchName || !pData) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto stats_config = settings->getStats();
auto stats_data = stats_config.find(pchName);
if (stats_data != stats_config.end()) {
if (stats_data->second.type == Stat_Type::STAT_TYPE_INT) return false;
}
int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char*)pData, sizeof(*pData));
if (read_data == sizeof(float))
return true;
if (stats_data != stats_config.end()) {
*pData = stats_data->second.default_value_float;
return true;
}
return false;
}
// Set / update data
bool Steam_User_Stats::SetStat(const char* pchName, int32 nData)
{
PRINT_DEBUG("SetStat int32 %s\n", pchName);
if (!pchName) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, (char*)&nData, sizeof(nData)) == sizeof(nData);
}
bool Steam_User_Stats::SetStat(const char* pchName, float fData)
{
PRINT_DEBUG("SetStat float %s\n", pchName);
if (!pchName) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, (char*)&fData, sizeof(fData)) == sizeof(fData);
}
bool Steam_User_Stats::UpdateAvgRateStat(const char* pchName, float flCountThisSession, double dSessionLength)
{
PRINT_DEBUG("UpdateAvgRateStat %s\n", pchName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
char data[sizeof(float) + sizeof(float) + sizeof(double)];
int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char*)data, sizeof(*data));
float oldcount = 0;
double oldsessionlength = 0;
if (read_data == sizeof(data)) {
memcpy(&oldcount, data + sizeof(float), sizeof(oldcount));
memcpy(&oldsessionlength, data + sizeof(float) + sizeof(double), sizeof(oldsessionlength));
}
oldcount += flCountThisSession;
oldsessionlength += dSessionLength;
float average = oldcount / oldsessionlength;
memcpy(data, &average, sizeof(average));
memcpy(data + sizeof(float), &oldcount, sizeof(oldcount));
memcpy(data + sizeof(float) * 2, &oldsessionlength, sizeof(oldsessionlength));
return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, data, sizeof(data)) == sizeof(data);
}
// Achievement flag accessors
bool Steam_User_Stats::GetAchievement(const char* pchName, bool* pbAchieved)
{
PRINT_DEBUG("GetAchievement %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return item["name"].get<std::string>() == pchName;
});
auto ach = user_achievements.find(pchName);
if (it != defined_achievements.end() && ach != user_achievements.end()) {
if (pbAchieved != nullptr) *pbAchieved = (*ach)["earned"];
return true;
}
}
catch (...) {}
if (pbAchieved != nullptr)*pbAchieved = false;
return false;
}
bool Steam_User_Stats::SetAchievement(const char* pchName)
{
PRINT_DEBUG("SetAchievement %s\n", pchName);
if (pchName == nullptr) return false;
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return item["name"].get<std::string>() == pchName;
});
if (it != defined_achievements.end()) {
if (user_achievements.find(pchName) == user_achievements.end() || user_achievements[pchName].value("earned", false) == false) {
user_achievements[pchName]["earned"] = true;
user_achievements[pchName]["earned_time"] = std::chrono::duration_cast<std::chrono::duration<uint32>>(std::chrono::system_clock::now().time_since_epoch()).count();
#ifndef NO_OVERLAY
get_steam_client()->steam_overlay->AddAchievementNotification(it.value());
#endif
}
return true;
}
}
catch (...) {}
return false;
}
bool Steam_User_Stats::ClearAchievement(const char* pchName)
{
PRINT_DEBUG("ClearAchievement %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
user_achievements[pchName]["earned"] = false;
user_achievements[pchName]["earned_time"] = static_cast<uint32>(0);
return true;
}
}
catch (...) {}
return false;
}
// Get the achievement status, and the time it was unlocked if unlocked.
// If the return value is true, but the unlock time is zero, that means it was unlocked before Steam
// began tracking achievement unlock times (December 2009). Time is seconds since January 1, 1970.
bool Steam_User_Stats::GetAchievementAndUnlockTime(const char* pchName, bool* pbAchieved, uint32* punUnlockTime)
{
PRINT_DEBUG("GetAchievementAndUnlockTime\n");
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
auto ach = user_achievements.find(pchName);
if (it != defined_achievements.end() && ach != user_achievements.end()) {
if (pbAchieved != nullptr) *pbAchieved = (*ach)["earned"];
if (punUnlockTime != nullptr) *punUnlockTime = (*ach)["earned_time"];
return true;
}
}
catch (...) {}
if (pbAchieved != nullptr) *pbAchieved = false;
if (punUnlockTime != nullptr) *punUnlockTime = 0;
return true;
}
// Store the current data on the server, will get a callback when set
// And one callback for every new achievement
//
// If the callback has a result of k_EResultInvalidParam, one or more stats
// uploaded has been rejected, either because they broke constraints
// or were out of date. In this case the server sends back updated values.
// The stats should be re-iterated to keep in sync.
bool Steam_User_Stats::StoreStats()
{
PRINT_DEBUG("StoreStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
save_achievements();
UserStatsStored_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return true;
}
// Achievement / GroupAchievement metadata
// Gets the icon of the achievement, which is a handle to be used in ISteamUtils::GetImageRGBA(), or 0 if none set.
// A return value of 0 may indicate we are still fetching data, and you can wait for the UserAchievementIconFetched_t callback
// which will notify you when the bits are ready. If the callback still returns zero, then there is no image set for the
// specified achievement.
int Steam_User_Stats::GetAchievementIcon(const char* pchName)
{
PRINT_DEBUG("GetAchievementIcon\n");
if (pchName == nullptr) return 0;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Get general attributes for an achievement. Accepts the following keys:
// - "name" and "desc" for retrieving the localized achievement name and description (returned in UTF8)
// - "hidden" for retrieving if an achievement is hidden (returns "0" when not hidden, "1" when hidden)
const char* Steam_User_Stats::GetAchievementDisplayAttribute(const char* pchName, const char* pchKey)
{
PRINT_DEBUG("GetAchievementDisplayAttribute %s %s\n", pchName, pchKey);
if (pchName == nullptr) return "";
if (pchKey == nullptr) return "";
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (strcmp (pchKey, "name") == 0) {
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["displayName"].get_ptr<std::string*>()->c_str();
}
}
catch (...) {}
}
if (strcmp(pchKey, "desc") == 0) {
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["description"].get_ptr<std::string*>()->c_str();
}
}
catch (...) {}
}
if (strcmp(pchKey, "hidden") == 0) {
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["hidden"].get_ptr<std::string*>()->c_str();
}
}
catch (...) {}
}
return "";
}
// Achievement progress - triggers an AchievementProgress callback, that is all.
// Calling this w/ N out of N progress will NOT set the achievement, the game must still do that.
bool Steam_User_Stats::IndicateAchievementProgress(const char* pchName, uint32 nCurProgress, uint32 nMaxProgress)
{
PRINT_DEBUG("IndicateAchievementProgress\n");
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
auto ach = user_achievements.find(pchName);
if (it != defined_achievements.end()) {
bool achieved = false;
if (ach != user_achievements.end()) {
bool achieved = ach->value("earned", false);
}
UserAchievementStored_t data = {};
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_bGroupAchievement = false;
strncpy(data.m_rgchAchievementName, pchName, k_cchStatNameMax);
if (achieved) {
data.m_nCurProgress = 0;
data.m_nMaxProgress = 0;
}
else {
user_achievements[pchName]["progress"] = nCurProgress;
user_achievements[pchName]["max_progress"] = nMaxProgress;
data.m_nCurProgress = nCurProgress;
data.m_nMaxProgress = nMaxProgress;
}
save_achievements();
callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
return true;
}
}
catch (...) {}
return false;
}
// Used for iterating achievements. In general games should not need these functions because they should have a
// list of existing achievements compiled into them
uint32 Steam_User_Stats::GetNumAchievements()
{
PRINT_DEBUG("GetNumAchievements\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return defined_achievements.size();
}
// Get achievement name iAchievement in [0,GetNumAchievements)
const char* Steam_User_Stats::GetAchievementName(uint32 iAchievement)
{
PRINT_DEBUG("GetAchievementName\n");
try {
static std::string achievement_name;
achievement_name = defined_achievements[iAchievement]["name"].get<std::string>();
return achievement_name.c_str();
}
catch (...) {}
return "";
}
// Friends stats & achievements
// downloads stats for the user
// returns a UserStatsReceived_t received when completed
// if the other user has no stats, UserStatsReceived_t.m_eResult will be set to k_EResultFail
// these stats won't be auto-updated; you'll need to call RequestUserStats() again to refresh any data
STEAM_CALL_RESULT(UserStatsReceived_t)
SteamAPICall_t Steam_User_Stats::RequestUserStats(CSteamID steamIDUser)
{
PRINT_DEBUG("Steam_User_Stats::RequestUserStats %llu\n", steamIDUser.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
// Enable this to allow hot reload achievements status
//if (steamIDUser == settings->get_local_steam_id()) {
// load_achievements();
//}
UserStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
data.m_steamIDUser = steamIDUser;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1);
}
// requests stat information for a user, usable after a successful call to RequestUserStats()
bool Steam_User_Stats::GetUserStat(CSteamID steamIDUser, const char* pchName, int32* pData)
{
PRINT_DEBUG("GetUserStat %s %llu\n", pchName, steamIDUser.ConvertToUint64());
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData);
}
else {
*pData = 0;
}
return true;
}
bool Steam_User_Stats::GetUserStat(CSteamID steamIDUser, const char* pchName, float* pData)
{
PRINT_DEBUG("GetUserStat %s %llu\n", pchName, steamIDUser.ConvertToUint64());
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData);
}
else {
*pData = 0;
}
return true;
}
bool Steam_User_Stats::GetUserAchievement(CSteamID steamIDUser, const char* pchName, bool* pbAchieved)
{
PRINT_DEBUG("GetUserAchievement %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
return GetAchievement(pchName, pbAchieved);
}
return false;
}
// See notes for GetAchievementAndUnlockTime above
bool Steam_User_Stats::GetUserAchievementAndUnlockTime(CSteamID steamIDUser, const char* pchName, bool* pbAchieved, uint32* punUnlockTime)
{
PRINT_DEBUG("GetUserAchievementAndUnlockTime %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
return GetAchievementAndUnlockTime(pchName, pbAchieved, punUnlockTime);
}
return false;
}
// Reset stats
bool Steam_User_Stats::ResetAllStats(bool bAchievementsToo)
{
PRINT_DEBUG("ResetAllStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO
if (bAchievementsToo) {
std::for_each(user_achievements.begin(), user_achievements.end(), [](nlohmann::json& v) {
v["earned"] = false;
v["earned_time"] = 0;
});
}
return true;
}
// Leaderboard functions
// asks the Steam back-end for a leaderboard by name, and will create it if it's not yet
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT(LeaderboardFindResult_t)
SteamAPICall_t Steam_User_Stats::FindOrCreateLeaderboard(const char* pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType)
{
PRINT_DEBUG("FindOrCreateLeaderboard %s\n", pchLeaderboardName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
unsigned int leader = find_leaderboard(pchLeaderboardName);
if (!leader) {
struct Steam_Leaderboard leaderboard;
leaderboard.name = std::string(pchLeaderboardName);
leaderboard.sort_method = eLeaderboardSortMethod;
leaderboard.display_type = eLeaderboardDisplayType;
leaderboards.push_back(leaderboard);
leader = leaderboards.size();
}
LeaderboardFindResult_t data;
data.m_hSteamLeaderboard = leader;
data.m_bLeaderboardFound = 1;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// as above, but won't create the leaderboard if it's not found
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT(LeaderboardFindResult_t)
SteamAPICall_t Steam_User_Stats::FindLeaderboard(const char* pchLeaderboardName)
{
PRINT_DEBUG("FindLeaderboard %s\n", pchLeaderboardName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto settings_Leaderboards = settings->getLeaderboards();
if (settings_Leaderboards.count(pchLeaderboardName)) {
auto config = settings_Leaderboards[pchLeaderboardName];
return FindOrCreateLeaderboard(pchLeaderboardName, config.sort_method, config.display_type);
}
else if (settings->createUnknownLeaderboards()) {
return FindOrCreateLeaderboard(pchLeaderboardName, k_ELeaderboardSortMethodDescending, k_ELeaderboardDisplayTypeNumeric);
}
else {
LeaderboardFindResult_t data;
data.m_hSteamLeaderboard = find_leaderboard(pchLeaderboardName);;
data.m_bLeaderboardFound = !!data.m_hSteamLeaderboard;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
}
// returns the name of a leaderboard
const char* Steam_User_Stats::GetLeaderboardName(SteamLeaderboard_t hSteamLeaderboard)
{
PRINT_DEBUG("GetLeaderboardName\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return "";
return leaderboards[hSteamLeaderboard - 1].name.c_str();
}
// returns the total number of entries in a leaderboard, as of the last request
int Steam_User_Stats::GetLeaderboardEntryCount(SteamLeaderboard_t hSteamLeaderboard)
{
PRINT_DEBUG("GetLeaderboardEntryCount\n");
return 0;
}
// returns the sort method of the leaderboard
ELeaderboardSortMethod Steam_User_Stats::GetLeaderboardSortMethod(SteamLeaderboard_t hSteamLeaderboard)
{
PRINT_DEBUG("GetLeaderboardSortMethod\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_ELeaderboardSortMethodNone;
return leaderboards[hSteamLeaderboard - 1].sort_method;
}
// returns the display type of the leaderboard
ELeaderboardDisplayType Steam_User_Stats::GetLeaderboardDisplayType(SteamLeaderboard_t hSteamLeaderboard)
{
PRINT_DEBUG("GetLeaderboardDisplayType\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_ELeaderboardDisplayTypeNone;
return leaderboards[hSteamLeaderboard - 1].display_type;
}
// Asks the Steam back-end for a set of rows in the leaderboard.
// This call is asynchronous, with the result returned in LeaderboardScoresDownloaded_t
// LeaderboardScoresDownloaded_t will contain a handle to pull the results from GetDownloadedLeaderboardEntries() (below)
// You can ask for more entries than exist, and it will return as many as do exist.
// k_ELeaderboardDataRequestGlobal requests rows in the leaderboard from the full table, with nRangeStart & nRangeEnd in the range [1, TotalEntries]
// k_ELeaderboardDataRequestGlobalAroundUser requests rows around the current user, nRangeStart being negate
// e.g. DownloadLeaderboardEntries( hLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -3, 3 ) will return 7 rows, 3 before the user, 3 after
// k_ELeaderboardDataRequestFriends requests all the rows for friends of the current user
STEAM_CALL_RESULT(LeaderboardScoresDownloaded_t)
SteamAPICall_t Steam_User_Stats::DownloadLeaderboardEntries(SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd)
{
PRINT_DEBUG("DownloadLeaderboardEntries %llu %i %i %i\n", hSteamLeaderboard, eLeaderboardDataRequest, nRangeStart, nRangeEnd);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoresDownloaded_t data;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_hSteamLeaderboardEntries = 123;
data.m_cEntryCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// as above, but downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers
// if a user doesn't have a leaderboard entry, they won't be included in the result
// a max of 100 users can be downloaded at a time, with only one outstanding call at a time
STEAM_METHOD_DESC(Downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers)
STEAM_CALL_RESULT(LeaderboardScoresDownloaded_t)
SteamAPICall_t Steam_User_Stats::DownloadLeaderboardEntriesForUsers(SteamLeaderboard_t hSteamLeaderboard,
STEAM_ARRAY_COUNT_D(cUsers, Array of users to retrieve) CSteamID * prgUsers, int cUsers)
{
PRINT_DEBUG("DownloadLeaderboardEntriesForUsers %i %llu\n", cUsers, cUsers > 0 ? prgUsers[0].ConvertToUint64() : 0);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoresDownloaded_t data;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_hSteamLeaderboardEntries = 123;
data.m_cEntryCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Returns data about a single leaderboard entry
// use a for loop from 0 to LeaderboardScoresDownloaded_t::m_cEntryCount to get all the downloaded entries
// e.g.
// void OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *pLeaderboardScoresDownloaded )
// {
// for ( int index = 0; index < pLeaderboardScoresDownloaded->m_cEntryCount; index++ )
// {
// LeaderboardEntry_t leaderboardEntry;
// int32 details[3]; // we know this is how many we've stored previously
// GetDownloadedLeaderboardEntry( pLeaderboardScoresDownloaded->m_hSteamLeaderboardEntries, index, &leaderboardEntry, details, 3 );
// assert( leaderboardEntry.m_cDetails == 3 );
// ...
// }
// once you've accessed all the entries, the data will be free'd, and the SteamLeaderboardEntries_t handle will become invalid
bool Steam_User_Stats::GetDownloadedLeaderboardEntry(SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t * pLeaderboardEntry, int32 * pDetails, int cDetailsMax)
{
PRINT_DEBUG("GetDownloadedLeaderboardEntry\n");
return false;
}
// Uploads a user score to the Steam back-end.
// This call is asynchronous, with the result returned in LeaderboardScoreUploaded_t
// Details are extra game-defined information regarding how the user got that score
// pScoreDetails points to an array of int32's, cScoreDetailsCount is the number of int32's in the list
STEAM_CALL_RESULT(LeaderboardScoreUploaded_t)
SteamAPICall_t Steam_User_Stats::UploadLeaderboardScore(SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 * pScoreDetails, int cScoreDetailsCount)
{
PRINT_DEBUG("UploadLeaderboardScore\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoreUploaded_t data;
data.m_bSuccess = 1; //needs to be success or DOA6 freezes when uploading score.
//data.m_bSuccess = 0;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_nScore = nScore;
//data.m_bScoreChanged = 1;
data.m_bScoreChanged = 0;
//data.m_nGlobalRankNew = 1;
data.m_nGlobalRankNew = 0;
data.m_nGlobalRankPrevious = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t Steam_User_Stats::UploadLeaderboardScore(SteamLeaderboard_t hSteamLeaderboard, int32 nScore, int32 * pScoreDetails, int cScoreDetailsCount)
{
PRINT_DEBUG("UploadLeaderboardScore old\n");
return UploadLeaderboardScore(hSteamLeaderboard, k_ELeaderboardUploadScoreMethodKeepBest, nScore, pScoreDetails, cScoreDetailsCount);
}
// Attaches a piece of user generated content the user's entry on a leaderboard.
// hContent is a handle to a piece of user generated content that was shared using ISteamUserRemoteStorage::FileShare().
// This call is asynchronous, with the result returned in LeaderboardUGCSet_t.
STEAM_CALL_RESULT(LeaderboardUGCSet_t)
SteamAPICall_t Steam_User_Stats::AttachLeaderboardUGC(SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC)
{
PRINT_DEBUG("AttachLeaderboardUGC\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardUGCSet_t data = {};
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) {
data.m_eResult = k_EResultFail;
}
else {
data.m_eResult = k_EResultOK;
}
data.m_hSteamLeaderboard = hSteamLeaderboard;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Retrieves the number of players currently playing your game (online + offline)
// This call is asynchronous, with the result returned in NumberOfCurrentPlayers_t
STEAM_CALL_RESULT(NumberOfCurrentPlayers_t)
SteamAPICall_t Steam_User_Stats::GetNumberOfCurrentPlayers()
{
PRINT_DEBUG("GetNumberOfCurrentPlayers\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
NumberOfCurrentPlayers_t data;
data.m_bSuccess = 1;
data.m_cPlayers = 69;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Requests that Steam fetch data on the percentage of players who have received each achievement
// for the game globally.
// This call is asynchronous, with the result returned in GlobalAchievementPercentagesReady_t.
STEAM_CALL_RESULT(GlobalAchievementPercentagesReady_t)
SteamAPICall_t Steam_User_Stats::RequestGlobalAchievementPercentages()
{
PRINT_DEBUG("RequestGlobalAchievementPercentages\n");
}
// Get the info on the most achieved achievement for the game, returns an iterator index you can use to fetch
// the next most achieved afterwards. Will return -1 if there is no data on achievement
// percentages (ie, you haven't called RequestGlobalAchievementPercentages and waited on the callback).
int Steam_User_Stats::GetMostAchievedAchievementInfo(char* pchName, uint32 unNameBufLen, float* pflPercent, bool* pbAchieved)
{
PRINT_DEBUG("GetMostAchievedAchievementInfo\n");
}
// Get the info on the next most achieved achievement for the game. Call this after GetMostAchievedAchievementInfo or another
// GetNextMostAchievedAchievementInfo call passing the iterator from the previous call. Returns -1 after the last
// achievement has been iterated.
int Steam_User_Stats::GetNextMostAchievedAchievementInfo(int iIteratorPrevious, char* pchName, uint32 unNameBufLen, float* pflPercent, bool* pbAchieved)
{
PRINT_DEBUG("GetNextMostAchievedAchievementInfo\n");
}
// Returns the percentage of users who have achieved the specified achievement.
bool Steam_User_Stats::GetAchievementAchievedPercent(const char* pchName, float* pflPercent)
{
PRINT_DEBUG("GetAchievementAchievedPercent\n");
}
// Requests global stats data, which is available for stats marked as "aggregated".
// This call is asynchronous, with the results returned in GlobalStatsReceived_t.
// nHistoryDays specifies how many days of day-by-day history to retrieve in addition
// to the overall totals. The limit is 60.
STEAM_CALL_RESULT(GlobalStatsReceived_t)
SteamAPICall_t Steam_User_Stats::RequestGlobalStats(int nHistoryDays)
{
PRINT_DEBUG("RequestGlobalStats %i\n", nHistoryDays);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
GlobalStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Gets the lifetime totals for an aggregated stat
bool Steam_User_Stats::GetGlobalStat(const char* pchStatName, int64 * pData)
{
PRINT_DEBUG("GetGlobalStat %s\n", pchStatName);
return false;
}
bool Steam_User_Stats::GetGlobalStat(const char* pchStatName, double* pData)
{
PRINT_DEBUG("GetGlobalStat %s\n", pchStatName);
return false;
}
// Gets history for an aggregated stat. pData will be filled with daily values, starting with today.
// So when called, pData[0] will be today, pData[1] will be yesterday, and pData[2] will be two days ago,
// etc. cubData is the size in bytes of the pubData buffer. Returns the number of
// elements actually set.
int32 Steam_User_Stats::GetGlobalStatHistory(const char* pchStatName, STEAM_ARRAY_COUNT(cubData) int64 * pData, uint32 cubData)
{
PRINT_DEBUG("GetGlobalStatHistory int64 %s\n", pchStatName);
return 0;
}
int32 Steam_User_Stats::GetGlobalStatHistory(const char* pchStatName, STEAM_ARRAY_COUNT(cubData) double* pData, uint32 cubData)
{
PRINT_DEBUG("GetGlobalStatHistory double %s\n", pchStatName);
return 0;
}

View File

@ -15,6 +15,9 @@
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_USER_STATS_H__
#define __INCLUDED_STEAM_USER_STATS_H__
#include "base.h"
#include <iomanip>
@ -52,250 +55,40 @@ private:
nlohmann::json defined_achievements;
nlohmann::json user_achievements;
unsigned int find_leaderboard(std::string name)
{
unsigned index = 1;
for (auto &leaderboard : leaderboards) {
if (leaderboard.name == name) return index;
++index;
}
unsigned int find_leaderboard(std::string name);
return 0;
}
void load_achievements_db()
{
std::string file_path = Local_Storage::get_game_settings_path() + achievements_user_file;
local_storage->load_json(file_path, defined_achievements);
}
void load_achievements()
{
local_storage->load_json_file("", achievements_user_file, user_achievements);
}
void save_achievements()
{
local_storage->write_json_file("", achievements_user_file, user_achievements);
}
void load_achievements_db();
void load_achievements();
void save_achievements();
public:
Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks):
settings(settings),
local_storage(local_storage),
callback_results(callback_results),
callbacks(callbacks),
defined_achievements(nlohmann::json::object()),
user_achievements(nlohmann::json::object())
{
load_achievements_db(); // achievements db
load_achievements(); // achievements per user
}
Steam_User_Stats(Settings* settings, Local_Storage* local_storage, class SteamCallResults* callback_results, class SteamCallBacks* callbacks);
nlohmann::json const& GetAchievementsDb() const;
// Ask the server to send down this user's data and achievements for this game
STEAM_CALL_BACK(UserStatsReceived_t)
bool RequestCurrentStats()
{
PRINT_DEBUG("Steam_User_Stats::RequestCurrentStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
UserStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
data.m_steamIDUser = settings->get_local_steam_id();
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
return true;
}
bool RequestCurrentStats();
// Data accessors
bool GetStat( const char *pchName, int32 *pData )
{
PRINT_DEBUG("GetStat int32 %s\n", pchName);
if (!pchName || !pData) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto stats_config = settings->getStats();
auto stats_data = stats_config.find(pchName);
if (stats_data != stats_config.end()) {
if (stats_data->second.type != Stat_Type::STAT_TYPE_INT) return false;
}
int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char* )pData, sizeof(*pData));
if (read_data == sizeof(int32))
return true;
if (stats_data != stats_config.end()) {
*pData = stats_data->second.default_value_int;
return true;
}
return false;
}
bool GetStat( const char *pchName, float *pData )
{
PRINT_DEBUG("GetStat float %s\n", pchName);
if (!pchName || !pData) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto stats_config = settings->getStats();
auto stats_data = stats_config.find(pchName);
if (stats_data != stats_config.end()) {
if (stats_data->second.type == Stat_Type::STAT_TYPE_INT) return false;
}
int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char* )pData, sizeof(*pData));
if (read_data == sizeof(float))
return true;
if (stats_data != stats_config.end()) {
*pData = stats_data->second.default_value_float;
return true;
}
return false;
}
bool GetStat(const char* pchName, int32* pData);
bool GetStat(const char* pchName, float* pData);
// Set / update data
bool SetStat( const char *pchName, int32 nData )
{
PRINT_DEBUG("SetStat int32 %s\n", pchName);
if (!pchName) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, (char* )&nData, sizeof(nData)) == sizeof(nData);
}
bool SetStat( const char *pchName, float fData )
{
PRINT_DEBUG("SetStat float %s\n", pchName);
if (!pchName) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, (char* )&fData, sizeof(fData)) == sizeof(fData);
}
bool UpdateAvgRateStat( const char *pchName, float flCountThisSession, double dSessionLength )
{
PRINT_DEBUG("UpdateAvgRateStat %s\n", pchName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
char data[sizeof(float) + sizeof(float) + sizeof(double)];
int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char* )data, sizeof(*data));
float oldcount = 0;
double oldsessionlength = 0;
if (read_data == sizeof(data)) {
memcpy(&oldcount, data + sizeof(float), sizeof(oldcount));
memcpy(&oldsessionlength, data + sizeof(float) + sizeof(double), sizeof(oldsessionlength));
}
oldcount += flCountThisSession;
oldsessionlength += dSessionLength;
float average = oldcount / oldsessionlength;
memcpy(data, &average, sizeof(average));
memcpy(data + sizeof(float), &oldcount, sizeof(oldcount));
memcpy(data + sizeof(float) * 2, &oldsessionlength, sizeof(oldsessionlength));
return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, data, sizeof(data)) == sizeof(data);
}
bool SetStat(const char* pchName, int32 nData);
bool SetStat(const char* pchName, float fData);
bool UpdateAvgRateStat(const char* pchName, float flCountThisSession, double dSessionLength);
// Achievement flag accessors
bool GetAchievement( const char *pchName, bool *pbAchieved )
{
PRINT_DEBUG("GetAchievement %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName]( nlohmann::json &item ) {
return item["name"].get<std::string>() == pchName;
});
auto ach = user_achievements.find(pchName);
if (it != defined_achievements.end() && ach != user_achievements.end()) {
if(pbAchieved != nullptr) *pbAchieved = (*ach)["earned"];
return true;
}
} catch (...) {}
if (pbAchieved != nullptr)* pbAchieved = false;
return false;
}
bool SetAchievement( const char *pchName )
{
PRINT_DEBUG("SetAchievement %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return item["name"].get<std::string>() == pchName;
});
if (it != defined_achievements.end()) {
if (user_achievements.find(pchName) == user_achievements.end() || user_achievements[pchName].value("earned", false) == false) {
user_achievements[pchName]["earned"] = true;
user_achievements[pchName]["earned_time"] = std::chrono::duration_cast<std::chrono::duration<uint32>>(std::chrono::system_clock::now().time_since_epoch()).count();
save_achievements();
}
return true;
}
} catch (...) {}
return false;
}
bool ClearAchievement( const char *pchName )
{
PRINT_DEBUG("ClearAchievement %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
user_achievements[pchName]["earned"] = false;
user_achievements[pchName]["earned_time"] = static_cast<uint32>(0);
save_achievements();
return true;
}
} catch (...) {}
return false;
}
bool GetAchievement(const char* pchName, bool* pbAchieved);
bool SetAchievement(const char* pchName);
bool ClearAchievement(const char* pchName);
// Get the achievement status, and the time it was unlocked if unlocked.
// If the return value is true, but the unlock time is zero, that means it was unlocked before Steam
// began tracking achievement unlock times (December 2009). Time is seconds since January 1, 1970.
bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32 *punUnlockTime )
{
PRINT_DEBUG("GetAchievementAndUnlockTime\n");
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
auto ach = user_achievements.find(pchName);
if (it != defined_achievements.end() && ach != user_achievements.end()) {
if(pbAchieved != nullptr) *pbAchieved = (*ach)["earned"];
if(punUnlockTime != nullptr) *punUnlockTime = (*ach)["earned_time"];
return true;
}
} catch (...) {}
if(pbAchieved != nullptr) *pbAchieved = false;
if(punUnlockTime != nullptr) *punUnlockTime = 0;
return true;
}
bool GetAchievementAndUnlockTime(const char* pchName, bool* pbAchieved, uint32* punUnlockTime);
// Store the current data on the server, will get a callback when set
// And one callback for every new achievement
@ -304,18 +97,7 @@ bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32
// uploaded has been rejected, either because they broke constraints
// or were out of date. In this case the server sends back updated values.
// The stats should be re-iterated to keep in sync.
bool StoreStats()
{
PRINT_DEBUG("StoreStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
UserStatsStored_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return true;
}
bool StoreStats();
// Achievement / GroupAchievement metadata
@ -323,129 +105,24 @@ bool StoreStats()
// A return value of 0 may indicate we are still fetching data, and you can wait for the UserAchievementIconFetched_t callback
// which will notify you when the bits are ready. If the callback still returns zero, then there is no image set for the
// specified achievement.
int GetAchievementIcon( const char *pchName )
{
PRINT_DEBUG("GetAchievementIcon\n");
if (pchName == nullptr) return 0;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int GetAchievementIcon(const char* pchName);
// Get general attributes for an achievement. Accepts the following keys:
// - "name" and "desc" for retrieving the localized achievement name and description (returned in UTF8)
// - "hidden" for retrieving if an achievement is hidden (returns "0" when not hidden, "1" when hidden)
const char * GetAchievementDisplayAttribute( const char *pchName, const char *pchKey )
{
PRINT_DEBUG("GetAchievementDisplayAttribute %s %s\n", pchName, pchKey);
if (pchName == nullptr) return "";
if (pchKey == nullptr) return "";
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (strcmp (pchKey, "name") == 0) {
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["displayName"].get_ptr<std::string*>()->c_str();
}
} catch (...) {}
}
if (strcmp (pchKey, "desc") == 0) {
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["description"].get_ptr<std::string*>()->c_str();
}
} catch (...) {}
}
if (strcmp (pchKey, "hidden") == 0) {
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["hidden"].get_ptr<std::string*>()->c_str();
}
} catch (...) {}
}
return "";
}
const char* GetAchievementDisplayAttribute(const char* pchName, const char* pchKey);
// Achievement progress - triggers an AchievementProgress callback, that is all.
// Calling this w/ N out of N progress will NOT set the achievement, the game must still do that.
bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint32 nMaxProgress )
{
PRINT_DEBUG("IndicateAchievementProgress\n");
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
auto ach = user_achievements.find(pchName);
if (it != defined_achievements.end()) {
bool achieved = false;
if ( ach != user_achievements.end()) {
bool achieved = ach->value("earned", false);
}
UserAchievementStored_t data = {};
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_bGroupAchievement = false;
strncpy(data.m_rgchAchievementName, pchName, k_cchStatNameMax);
if (achieved) {
data.m_nCurProgress = 0;
data.m_nMaxProgress = 0;
} else {
user_achievements[pchName]["progress"] = nCurProgress;
user_achievements[pchName]["max_progress"] = nMaxProgress;
data.m_nCurProgress = nCurProgress;
data.m_nMaxProgress = nMaxProgress;
}
save_achievements();
callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
return true;
}
} catch (...) {}
return false;
}
bool IndicateAchievementProgress(const char* pchName, uint32 nCurProgress, uint32 nMaxProgress);
// Used for iterating achievements. In general games should not need these functions because they should have a
// list of existing achievements compiled into them
uint32 GetNumAchievements()
{
PRINT_DEBUG("GetNumAchievements\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return defined_achievements.size();
}
uint32 GetNumAchievements();
// Get achievement name iAchievement in [0,GetNumAchievements)
const char * GetAchievementName( uint32 iAchievement )
{
PRINT_DEBUG("GetAchievementName\n");
try {
static std::string achievement_name;
achievement_name = defined_achievements[iAchievement]["name"].get<std::string>();
return achievement_name.c_str();
} catch (...) {}
return "";
}
const char* GetAchievementName(uint32 iAchievement);
// Friends stats & achievements
@ -454,190 +131,44 @@ const char * GetAchievementName( uint32 iAchievement )
// if the other user has no stats, UserStatsReceived_t.m_eResult will be set to k_EResultFail
// these stats won't be auto-updated; you'll need to call RequestUserStats() again to refresh any data
STEAM_CALL_RESULT(UserStatsReceived_t)
SteamAPICall_t RequestUserStats( CSteamID steamIDUser )
{
PRINT_DEBUG("Steam_User_Stats::RequestUserStats %llu\n", steamIDUser.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
// Enable this to allow hot reload achievements status
//if (steamIDUser == settings->get_local_steam_id()) {
// load_achievements();
//}
UserStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
data.m_steamIDUser = steamIDUser;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1);
}
SteamAPICall_t RequestUserStats(CSteamID steamIDUser);
// requests stat information for a user, usable after a successful call to RequestUserStats()
bool GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData )
{
PRINT_DEBUG("GetUserStat %s %llu\n", pchName, steamIDUser.ConvertToUint64());
if (pchName == nullptr) return false;
bool GetUserStat(CSteamID steamIDUser, const char* pchName, int32* pData);
bool GetUserStat(CSteamID steamIDUser, const char* pchName, float* pData);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData);
} else {
*pData = 0;
}
return true;
}
bool GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData )
{
PRINT_DEBUG("GetUserStat %s %llu\n", pchName, steamIDUser.ConvertToUint64());
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData);
} else {
*pData = 0;
}
return true;
}
bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved )
{
PRINT_DEBUG("GetUserAchievement %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
return GetAchievement(pchName, pbAchieved);
}
return false;
}
bool GetUserAchievement(CSteamID steamIDUser, const char* pchName, bool* pbAchieved);
// See notes for GetAchievementAndUnlockTime above
bool GetUserAchievementAndUnlockTime( CSteamID steamIDUser, const char *pchName, bool *pbAchieved, uint32 *punUnlockTime )
{
PRINT_DEBUG("GetUserAchievementAndUnlockTime %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
return GetAchievementAndUnlockTime(pchName, pbAchieved, punUnlockTime);
}
return false;
}
bool GetUserAchievementAndUnlockTime(CSteamID steamIDUser, const char* pchName, bool* pbAchieved, uint32* punUnlockTime);
// Reset stats
bool ResetAllStats( bool bAchievementsToo )
{
PRINT_DEBUG("ResetAllStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO
if (bAchievementsToo) {
std::for_each(user_achievements.begin(), user_achievements.end(), [](nlohmann::json& v) {
v["earned"] = false;
v["earned_time"] = 0;
});
}
return true;
}
bool ResetAllStats(bool bAchievementsToo);
// Leaderboard functions
// asks the Steam back-end for a leaderboard by name, and will create it if it's not yet
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT(LeaderboardFindResult_t)
SteamAPICall_t FindOrCreateLeaderboard( const char *pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType )
{
PRINT_DEBUG("FindOrCreateLeaderboard %s\n", pchLeaderboardName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
unsigned int leader = find_leaderboard(pchLeaderboardName);
if (!leader) {
struct Steam_Leaderboard leaderboard;
leaderboard.name = std::string(pchLeaderboardName);
leaderboard.sort_method = eLeaderboardSortMethod;
leaderboard.display_type = eLeaderboardDisplayType;
leaderboards.push_back(leaderboard);
leader = leaderboards.size();
}
LeaderboardFindResult_t data;
data.m_hSteamLeaderboard = leader;
data.m_bLeaderboardFound = 1;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t FindOrCreateLeaderboard(const char* pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType);
// as above, but won't create the leaderboard if it's not found
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT(LeaderboardFindResult_t)
SteamAPICall_t FindLeaderboard( const char *pchLeaderboardName )
{
PRINT_DEBUG("FindLeaderboard %s\n", pchLeaderboardName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto settings_Leaderboards = settings->getLeaderboards();
if (settings_Leaderboards.count(pchLeaderboardName)) {
auto config = settings_Leaderboards[pchLeaderboardName];
return FindOrCreateLeaderboard(pchLeaderboardName, config.sort_method, config.display_type);
} else if (settings->createUnknownLeaderboards()) {
return FindOrCreateLeaderboard(pchLeaderboardName, k_ELeaderboardSortMethodDescending, k_ELeaderboardDisplayTypeNumeric);
} else {
LeaderboardFindResult_t data;
data.m_hSteamLeaderboard = find_leaderboard(pchLeaderboardName);;
data.m_bLeaderboardFound = !!data.m_hSteamLeaderboard;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
}
SteamAPICall_t FindLeaderboard(const char* pchLeaderboardName);
// returns the name of a leaderboard
const char * GetLeaderboardName( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardName\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return "";
return leaderboards[hSteamLeaderboard - 1].name.c_str();
}
const char* GetLeaderboardName(SteamLeaderboard_t hSteamLeaderboard);
// returns the total number of entries in a leaderboard, as of the last request
int GetLeaderboardEntryCount( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardEntryCount\n");
return 0;
}
int GetLeaderboardEntryCount(SteamLeaderboard_t hSteamLeaderboard);
// returns the sort method of the leaderboard
ELeaderboardSortMethod GetLeaderboardSortMethod( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardSortMethod\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_ELeaderboardSortMethodNone;
return leaderboards[hSteamLeaderboard - 1].sort_method;
}
ELeaderboardSortMethod GetLeaderboardSortMethod(SteamLeaderboard_t hSteamLeaderboard);
// returns the display type of the leaderboard
ELeaderboardDisplayType GetLeaderboardDisplayType( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardDisplayType\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_ELeaderboardDisplayTypeNone;
return leaderboards[hSteamLeaderboard - 1].display_type;
}
ELeaderboardDisplayType GetLeaderboardDisplayType(SteamLeaderboard_t hSteamLeaderboard);
// Asks the Steam back-end for a set of rows in the leaderboard.
// This call is asynchronous, with the result returned in LeaderboardScoresDownloaded_t
@ -648,16 +179,7 @@ ELeaderboardDisplayType GetLeaderboardDisplayType( SteamLeaderboard_t hSteamLead
// e.g. DownloadLeaderboardEntries( hLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -3, 3 ) will return 7 rows, 3 before the user, 3 after
// k_ELeaderboardDataRequestFriends requests all the rows for friends of the current user
STEAM_CALL_RESULT(LeaderboardScoresDownloaded_t)
SteamAPICall_t DownloadLeaderboardEntries( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd )
{
PRINT_DEBUG("DownloadLeaderboardEntries %llu %i %i %i\n", hSteamLeaderboard, eLeaderboardDataRequest, nRangeStart, nRangeEnd);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoresDownloaded_t data;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_hSteamLeaderboardEntries = 123;
data.m_cEntryCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t DownloadLeaderboardEntries(SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd);
// as above, but downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers
// if a user doesn't have a leaderboard entry, they won't be included in the result
@ -665,17 +187,7 @@ SteamAPICall_t DownloadLeaderboardEntries( SteamLeaderboard_t hSteamLeaderboard,
STEAM_METHOD_DESC(Downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers)
STEAM_CALL_RESULT(LeaderboardScoresDownloaded_t)
SteamAPICall_t DownloadLeaderboardEntriesForUsers(SteamLeaderboard_t hSteamLeaderboard,
STEAM_ARRAY_COUNT_D(cUsers, Array of users to retrieve) CSteamID *prgUsers, int cUsers )
{
PRINT_DEBUG("DownloadLeaderboardEntriesForUsers %i %llu\n", cUsers, cUsers > 0 ? prgUsers[0].ConvertToUint64() : 0);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoresDownloaded_t data;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_hSteamLeaderboardEntries = 123;
data.m_cEntryCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
STEAM_ARRAY_COUNT_D(cUsers, Array of users to retrieve) CSteamID* prgUsers, int cUsers);
// Returns data about a single leaderboard entry
// use a for loop from 0 to LeaderboardScoresDownloaded_t::m_cEntryCount to get all the downloaded entries
@ -691,154 +203,64 @@ SteamAPICall_t DownloadLeaderboardEntriesForUsers( SteamLeaderboard_t hSteamLead
// ...
// }
// once you've accessed all the entries, the data will be free'd, and the SteamLeaderboardEntries_t handle will become invalid
bool GetDownloadedLeaderboardEntry( SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t *pLeaderboardEntry, int32 *pDetails, int cDetailsMax )
{
PRINT_DEBUG("GetDownloadedLeaderboardEntry\n");
return false;
}
bool GetDownloadedLeaderboardEntry(SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t* pLeaderboardEntry, int32* pDetails, int cDetailsMax);
// Uploads a user score to the Steam back-end.
// This call is asynchronous, with the result returned in LeaderboardScoreUploaded_t
// Details are extra game-defined information regarding how the user got that score
// pScoreDetails points to an array of int32's, cScoreDetailsCount is the number of int32's in the list
STEAM_CALL_RESULT(LeaderboardScoreUploaded_t)
SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 *pScoreDetails, int cScoreDetailsCount )
{
PRINT_DEBUG("UploadLeaderboardScore\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoreUploaded_t data;
data.m_bSuccess = 1; //needs to be success or DOA6 freezes when uploading score.
//data.m_bSuccess = 0;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_nScore = nScore;
//data.m_bScoreChanged = 1;
data.m_bScoreChanged = 0;
//data.m_nGlobalRankNew = 1;
data.m_nGlobalRankNew = 0;
data.m_nGlobalRankPrevious = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, int32 nScore, int32 *pScoreDetails, int cScoreDetailsCount )
{
PRINT_DEBUG("UploadLeaderboardScore old\n");
return UploadLeaderboardScore(hSteamLeaderboard, k_ELeaderboardUploadScoreMethodKeepBest, nScore, pScoreDetails, cScoreDetailsCount);
}
SteamAPICall_t UploadLeaderboardScore(SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32* pScoreDetails, int cScoreDetailsCount);
SteamAPICall_t UploadLeaderboardScore(SteamLeaderboard_t hSteamLeaderboard, int32 nScore, int32* pScoreDetails, int cScoreDetailsCount);
// Attaches a piece of user generated content the user's entry on a leaderboard.
// hContent is a handle to a piece of user generated content that was shared using ISteamUserRemoteStorage::FileShare().
// This call is asynchronous, with the result returned in LeaderboardUGCSet_t.
STEAM_CALL_RESULT(LeaderboardUGCSet_t)
SteamAPICall_t AttachLeaderboardUGC( SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC )
{
PRINT_DEBUG("AttachLeaderboardUGC\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardUGCSet_t data = {};
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) {
data.m_eResult = k_EResultFail;
} else {
data.m_eResult = k_EResultOK;
}
data.m_hSteamLeaderboard = hSteamLeaderboard;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t AttachLeaderboardUGC(SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC);
// Retrieves the number of players currently playing your game (online + offline)
// This call is asynchronous, with the result returned in NumberOfCurrentPlayers_t
STEAM_CALL_RESULT(NumberOfCurrentPlayers_t)
SteamAPICall_t GetNumberOfCurrentPlayers()
{
PRINT_DEBUG("GetNumberOfCurrentPlayers\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
NumberOfCurrentPlayers_t data;
data.m_bSuccess = 1;
data.m_cPlayers = 69;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t GetNumberOfCurrentPlayers();
// Requests that Steam fetch data on the percentage of players who have received each achievement
// for the game globally.
// This call is asynchronous, with the result returned in GlobalAchievementPercentagesReady_t.
STEAM_CALL_RESULT(GlobalAchievementPercentagesReady_t)
SteamAPICall_t RequestGlobalAchievementPercentages()
{
PRINT_DEBUG("RequestGlobalAchievementPercentages\n");
}
SteamAPICall_t RequestGlobalAchievementPercentages();
// Get the info on the most achieved achievement for the game, returns an iterator index you can use to fetch
// the next most achieved afterwards. Will return -1 if there is no data on achievement
// percentages (ie, you haven't called RequestGlobalAchievementPercentages and waited on the callback).
int GetMostAchievedAchievementInfo( char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved )
{
PRINT_DEBUG("GetMostAchievedAchievementInfo\n");
}
int GetMostAchievedAchievementInfo(char* pchName, uint32 unNameBufLen, float* pflPercent, bool* pbAchieved);
// Get the info on the next most achieved achievement for the game. Call this after GetMostAchievedAchievementInfo or another
// GetNextMostAchievedAchievementInfo call passing the iterator from the previous call. Returns -1 after the last
// achievement has been iterated.
int GetNextMostAchievedAchievementInfo( int iIteratorPrevious, char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved )
{
PRINT_DEBUG("GetNextMostAchievedAchievementInfo\n");
}
int GetNextMostAchievedAchievementInfo(int iIteratorPrevious, char* pchName, uint32 unNameBufLen, float* pflPercent, bool* pbAchieved);
// Returns the percentage of users who have achieved the specified achievement.
bool GetAchievementAchievedPercent( const char *pchName, float *pflPercent )
{
PRINT_DEBUG("GetAchievementAchievedPercent\n");
}
bool GetAchievementAchievedPercent(const char* pchName, float* pflPercent);
// Requests global stats data, which is available for stats marked as "aggregated".
// This call is asynchronous, with the results returned in GlobalStatsReceived_t.
// nHistoryDays specifies how many days of day-by-day history to retrieve in addition
// to the overall totals. The limit is 60.
STEAM_CALL_RESULT(GlobalStatsReceived_t)
SteamAPICall_t RequestGlobalStats( int nHistoryDays )
{
PRINT_DEBUG("RequestGlobalStats %i\n", nHistoryDays);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
GlobalStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t RequestGlobalStats(int nHistoryDays);
// Gets the lifetime totals for an aggregated stat
bool GetGlobalStat( const char *pchStatName, int64 *pData )
{
PRINT_DEBUG("GetGlobalStat %s\n", pchStatName);
return false;
}
bool GetGlobalStat( const char *pchStatName, double *pData )
{
PRINT_DEBUG("GetGlobalStat %s\n", pchStatName);
return false;
}
bool GetGlobalStat(const char* pchStatName, int64* pData);
bool GetGlobalStat(const char* pchStatName, double* pData);
// Gets history for an aggregated stat. pData will be filled with daily values, starting with today.
// So when called, pData[0] will be today, pData[1] will be yesterday, and pData[2] will be two days ago,
// etc. cubData is the size in bytes of the pubData buffer. Returns the number of
// elements actually set.
int32 GetGlobalStatHistory( const char *pchStatName, STEAM_ARRAY_COUNT(cubData) int64 *pData, uint32 cubData )
{
PRINT_DEBUG("GetGlobalStatHistory int64 %s\n", pchStatName);
return 0;
}
int32 GetGlobalStatHistory( const char *pchStatName, STEAM_ARRAY_COUNT(cubData) double *pData, uint32 cubData )
{
PRINT_DEBUG("GetGlobalStatHistory double %s\n", pchStatName);
return 0;
}
int32 GetGlobalStatHistory(const char* pchStatName, STEAM_ARRAY_COUNT(cubData) int64* pData, uint32 cubData);
int32 GetGlobalStatHistory(const char* pchStatName, STEAM_ARRAY_COUNT(cubData) double* pData, uint32 cubData);
};
#endif//__INCLUDED_STEAM_USER_STATS_H__

View File

@ -17,6 +17,7 @@
#include "base.h"
#include "local_storage.h"
#include "../overlay_experimental/steam_overlay.h"
static std::chrono::time_point<std::chrono::steady_clock> app_initialized_time = std::chrono::steady_clock::now();
@ -34,13 +35,14 @@ public ISteamUtils
private:
Settings *settings;
class SteamCallResults *callback_results;
Steam_Overlay* overlay;
public:
Steam_Utils(Settings *settings, class SteamCallResults *callback_results)
{
this->settings = settings;
this->callback_results = callback_results;
}
Steam_Utils(Settings *settings, class SteamCallResults *callback_results, Steam_Overlay *overlay):
settings(settings),
callback_results(callback_results),
overlay(overlay)
{}
// return the number of seconds since the user
uint32 GetSecondsSinceAppActive()
@ -144,6 +146,7 @@ uint32 GetAppID()
void SetOverlayNotificationPosition( ENotificationPosition eNotificationPosition )
{
PRINT_DEBUG("SetOverlayNotificationPosition\n");
overlay->SetNotificationPosition(eNotificationPosition);
}
@ -218,8 +221,7 @@ void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction )
bool IsOverlayEnabled()
{
PRINT_DEBUG("IsOverlayEnabled\n");
//TODO
return false;
return overlay->Ready();
}
@ -235,7 +237,7 @@ bool IsOverlayEnabled()
bool BOverlayNeedsPresent()
{
PRINT_DEBUG("BOverlayNeedsPresent\n");
return false;
return overlay->NeedPresent();
}
@ -305,6 +307,7 @@ bool IsSteamRunningInVR()
void SetOverlayNotificationInset( int nHorizontalInset, int nVerticalInset )
{
PRINT_DEBUG("SetOverlayNotificationInset\n");
overlay->SetNotificationInset(nHorizontalInset, nVerticalInset);
}

12
download_glew.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
rm -rf glew*
URL="https://downloads.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0-win32.zip?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Fglew%2Ffiles%2Fglew%2F2.1.0%2Fglew-2.1.0-win32.zip%2Fdownload&ts=1565791827"
wget "$URL" -O glew.zip
unzip glew.zip
rm glew.zip
mv glew-* glew
# Fix libraries name
sed -i "s/LIBCMT/libcmt/;s/OLDNAMES/oldnames/" glew/lib/Release/*/glew32s.lib

View File

@ -0,0 +1,88 @@
#include "Base_Hook.h"
#include "Hook_Manager.h"
#include <algorithm>
#ifndef NO_OVERLAY
#ifdef STEAM_WIN32
#include "../detours/detours.h"
Base_Hook::Base_Hook():
_library(nullptr)
{}
Base_Hook::~Base_Hook()
{
UnhookAll();
}
const char* Base_Hook::get_lib_name() const
{
return "<no_name>";
}
void Base_Hook::BeginHook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
}
void Base_Hook::EndHook()
{
DetourTransactionCommit();
}
void Base_Hook::HookFunc(std::pair<void**, void*> hook)
{
if( DetourAttach(hook.first, hook.second) == 0 )
_hooked_funcs.emplace_back(hook);
}
void Base_Hook::UnhookAll()
{
if (_hooked_funcs.size())
{
BeginHook();
std::for_each(_hooked_funcs.begin(), _hooked_funcs.end(), [](std::pair<void**, void*>& hook) {
DetourDetach(hook.first, hook.second);
});
EndHook();
_hooked_funcs.clear();
}
}
#else
Base_Hook::Base_Hook():
_library(nullptr)
{}
Base_Hook::~Base_Hook()
{
}
const char* Base_Hook::get_lib_name() const
{
return "<no_name>";
}
void Base_Hook::BeginHook()
{
}
void Base_Hook::EndHook()
{
}
void Base_Hook::HookFunc(std::pair<void**, void*> hook)
{
}
void Base_Hook::UnhookAll()
{
}
#endif
#endif//NO_OVERLAY

View File

@ -0,0 +1,50 @@
#ifndef __INCLUDED_BASE_HOOK_H__
#define __INCLUDED_BASE_HOOK_H__
#ifndef NO_OVERLAY
#include <vector>
#include <utility>
#include "../dll/base.h"
class Base_Hook
{
protected:
std::vector<std::pair<void**, void*>> _hooked_funcs;
void* _library;
Base_Hook(const Base_Hook&) = delete;
Base_Hook(Base_Hook&&) = delete;
Base_Hook& operator =(const Base_Hook&) = delete;
Base_Hook& operator =(Base_Hook&&) = delete;
public:
Base_Hook();
virtual ~Base_Hook();
void BeginHook();
void EndHook();
void UnhookAll();
virtual const char* get_lib_name() const;
void HookFunc(std::pair<void**, void*> hook);
template<typename T>
void HookFuncs(std::pair<T*, T> funcs)
{
HookFunc(funcs);
}
template<typename T, typename ...Args>
void HookFuncs(std::pair<T*, T> funcs, Args... args)
{
HookFunc(funcs);
HookFuncs(args...);
}
};
#endif//NO_OVERLAY
#endif//__INCLUDED_BASE_HOOK_H__

View File

@ -0,0 +1,36 @@
#include "../dll/dll.h"
#include "Hook_Manager.h"
#ifndef NO_OVERLAY
Hook_Manager::Hook_Manager()
{}
Hook_Manager::~Hook_Manager()
{
for (auto& i : _hooks)
delete i;
}
Hook_Manager& Hook_Manager::Inst()
{
static Hook_Manager hook;
return hook;
}
void Hook_Manager::AddHook(Base_Hook* hook)
{
_hooks.insert(hook);
}
void Hook_Manager::RemoveHook(Base_Hook* hook)
{
auto it = _hooks.find(hook);
if (it != _hooks.end())
{
delete hook;
_hooks.erase(it);
}
}
#endif//NO_OVERLAY

View File

@ -0,0 +1,29 @@
#ifndef __INCLUDED_HOOK_BASE_H__
#define __INCLUDED_HOOK_BASE_H__
#include "Base_Hook.h"
#ifndef NO_OVERLAY
#include <set>
class Hook_Manager
{
friend class Base_Hook;
protected:
std::set<Base_Hook*> _hooks;
Hook_Manager();
virtual ~Hook_Manager();
public:
static Hook_Manager& Inst();
void AddHook(Base_Hook* hook);
void RemoveHook(Base_Hook* hook);
};
#endif//NO_OVERLAY
#endif//__INCLUDED_HOOK_BASE_H__

View File

@ -0,0 +1,832 @@
#include "Renderer_Detector.h"
#include "Hook_Manager.h"
#include <algorithm>
constexpr int max_hook_retries = 500;
#ifdef STEAM_WIN32
#include "windows/DX12_Hook.h"
#include "windows/DX11_Hook.h"
#include "windows/DX10_Hook.h"
#include "windows/DX9_Hook.h"
#include "windows/OpenGL_Hook.h"
#include "windows/Windows_Hook.h"
static decltype(&IDXGISwapChain::Present) _IDXGISwapChain_Present = nullptr;
static decltype(&IDirect3DDevice9::Present) _IDirect3DDevice9_Present = nullptr;
static decltype(&IDirect3DDevice9Ex::PresentEx) _IDirect3DDevice9Ex_PresentEx = nullptr;
static decltype(wglMakeCurrent)* _wglMakeCurrent = nullptr;
static constexpr auto windowClassName = "___overlay_window_class___";
void Renderer_Detector::create_hwnd()
{
if (dummy_hWnd == nullptr)
{
HINSTANCE hInst = GetModuleHandle(nullptr);
if (atom == 0)
{
// Register a window class for creating our render window with.
WNDCLASSEX windowClass = {};
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = DefWindowProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = hInst;
windowClass.hIcon = NULL;
windowClass.hCursor = ::LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = windowClassName;
windowClass.hIconSm = NULL;
atom = ::RegisterClassEx(&windowClass);
}
if (atom > 0)
{
dummy_hWnd = ::CreateWindowEx(
NULL,
windowClassName,
"",
WS_OVERLAPPEDWINDOW,
0,
0,
1,
1,
NULL,
NULL,
hInst,
nullptr
);
assert(dummy_hWnd && "Failed to create window");
}
}
}
void Renderer_Detector::destroy_hwnd()
{
if (dummy_hWnd != nullptr)
{
DestroyWindow(dummy_hWnd);
UnregisterClass(windowClassName, GetModuleHandle(nullptr));
}
}
HRESULT STDMETHODCALLTYPE Renderer_Detector::MyIDXGISwapChain_Present(IDXGISwapChain* _this, UINT SyncInterval, UINT Flags)
{
Renderer_Detector& inst = Renderer_Detector::Inst();
Hook_Manager& hm = Hook_Manager::Inst();
auto res = (_this->*_IDXGISwapChain_Present)(SyncInterval, Flags);
if (!inst.stop_retry())
{
IUnknown* pDevice = nullptr;
if (inst._dx12_hooked)
{
_this->GetDevice(IID_PPV_ARGS(reinterpret_cast<ID3D12Device**>(&pDevice)));
}
if (pDevice)
{
DX12_Hook::Inst()->start_hook();
}
else
{
if (inst._dx11_hooked)
{
_this->GetDevice(IID_PPV_ARGS(reinterpret_cast<ID3D11Device**>(&pDevice)));
}
if (pDevice)
{
DX11_Hook::Inst()->start_hook();
}
else
{
if (inst._dx10_hooked)
{
_this->GetDevice(IID_PPV_ARGS(reinterpret_cast<ID3D10Device**>(&pDevice)));
}
if (pDevice)
{
DX10_Hook::Inst()->start_hook();
}
}
}
if (pDevice) pDevice->Release();
}
return res;
}
HRESULT STDMETHODCALLTYPE Renderer_Detector::MyPresent(IDirect3DDevice9* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion)
{
Renderer_Detector& inst = Renderer_Detector::Inst();
Hook_Manager& hm = Hook_Manager::Inst();
auto res = (_this->*_IDirect3DDevice9_Present)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
if (!inst.stop_retry())
{
DX9_Hook::Inst()->start_hook();
}
return res;
}
HRESULT STDMETHODCALLTYPE Renderer_Detector::MyPresentEx(IDirect3DDevice9Ex* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags)
{
Renderer_Detector& inst = Renderer_Detector::Inst();
Hook_Manager& hm = Hook_Manager::Inst();
auto res = (_this->*_IDirect3DDevice9Ex_PresentEx)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags);
if (!inst.stop_retry())
{
DX9_Hook::Inst()->start_hook();
}
return res;
}
BOOL WINAPI Renderer_Detector::MywglMakeCurrent(HDC hDC, HGLRC hGLRC)
{
Renderer_Detector& inst = Renderer_Detector::Inst();
Hook_Manager& hm = Hook_Manager::Inst();
auto res = _wglMakeCurrent(hDC, hGLRC);
if (!inst.stop_retry())
{
OpenGL_Hook::Inst()->start_hook();
}
return res;
}
void Renderer_Detector::HookDXGIPresent(IDXGISwapChain* pSwapChain)
{
if (!_dxgi_hooked)
{
_dxgi_hooked = true;
(void*&)_IDXGISwapChain_Present = (*reinterpret_cast<void***>(pSwapChain))[(int)IDXGISwapChainVTable::Present];
rendererdetect_hook->BeginHook();
rendererdetect_hook->HookFuncs(
std::pair<void**, void*>((PVOID*)& _IDXGISwapChain_Present, &Renderer_Detector::MyIDXGISwapChain_Present)
);
rendererdetect_hook->EndHook();
}
}
void Renderer_Detector::HookDX9Present(IDirect3DDevice9* pDevice, bool ex)
{
(void*&)_IDirect3DDevice9_Present = (*reinterpret_cast<void***>(pDevice))[(int)IDirect3DDevice9VTable::Present];
if (ex)
(void*&)_IDirect3DDevice9Ex_PresentEx = (*reinterpret_cast<void***>(pDevice))[(int)IDirect3DDevice9VTable::PresentEx];
rendererdetect_hook->BeginHook();
rendererdetect_hook->HookFuncs(
std::pair<void**, void*>((PVOID*)& _IDirect3DDevice9_Present, &Renderer_Detector::MyPresent)
);
if (ex)
{
rendererdetect_hook->HookFuncs(
std::pair<void**, void*>((PVOID*)& _IDirect3DDevice9Ex_PresentEx, &Renderer_Detector::MyPresentEx)
);
}
rendererdetect_hook->EndHook();
}
void Renderer_Detector::HookwglMakeCurrent(decltype(wglMakeCurrent)* wglMakeCurrent)
{
_wglMakeCurrent = wglMakeCurrent;
rendererdetect_hook->BeginHook();
rendererdetect_hook->HookFuncs(
std::pair<void**, void*>((PVOID*)& _wglMakeCurrent, &Renderer_Detector::MywglMakeCurrent)
);
rendererdetect_hook->EndHook();
}
void Renderer_Detector::hook_dx9()
{
if (!_dx9_hooked && !_renderer_found)
{
create_hwnd();
if (dummy_hWnd == nullptr)
return;
IDirect3D9Ex* pD3D = nullptr;
IUnknown* pDevice = nullptr;
HMODULE library = GetModuleHandle(DX9_Hook::DLL_NAME);
decltype(Direct3DCreate9Ex)* Direct3DCreate9Ex = nullptr;
if (library != nullptr)
{
Direct3DCreate9Ex = (decltype(Direct3DCreate9Ex))GetProcAddress(library, "Direct3DCreate9Ex");
D3DPRESENT_PARAMETERS params = {};
params.BackBufferWidth = 1;
params.BackBufferHeight = 1;
params.hDeviceWindow = dummy_hWnd;
params.BackBufferCount = 1;
params.Windowed = TRUE;
params.SwapEffect = D3DSWAPEFFECT_DISCARD;
if (Direct3DCreate9Ex != nullptr)
{
Direct3DCreate9Ex(D3D_SDK_VERSION, &pD3D);
pD3D->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING, &params, NULL, reinterpret_cast<IDirect3DDevice9Ex * *>(&pDevice));
}
else
{
decltype(Direct3DCreate9)* Direct3DCreate9 = (decltype(Direct3DCreate9))GetProcAddress(library, "Direct3DCreate9");
if (Direct3DCreate9 != nullptr)
{
pD3D = reinterpret_cast<IDirect3D9Ex*>(Direct3DCreate9(D3D_SDK_VERSION));
pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING, &params, reinterpret_cast<IDirect3DDevice9 * *>(&pDevice));
}
}
}
if (pDevice != nullptr)
{
PRINT_DEBUG("Hooked D3D9::Present to detect DX Version\n");
_dx9_hooked = true;
auto h = DX9_Hook::Inst();
h->loadFunctions(reinterpret_cast<IDirect3DDevice9*>(pDevice), Direct3DCreate9Ex != nullptr);
Hook_Manager::Inst().AddHook(h);
HookDX9Present(reinterpret_cast<IDirect3DDevice9*>(pDevice), Direct3DCreate9Ex != nullptr);
}
else
{
PRINT_DEBUG("Failed to hook D3D9::Present to detect DX Version\n");
}
if (pDevice) pDevice->Release();
if (pD3D) pD3D->Release();
}
}
void Renderer_Detector::hook_dx10()
{
if (!_dxgi_hooked && !_renderer_found)
{
create_hwnd();
if (dummy_hWnd == nullptr)
return;
IDXGISwapChain* pSwapChain = nullptr;
ID3D10Device* pDevice = nullptr;
HMODULE library = GetModuleHandle(DX10_Hook::DLL_NAME);
if (library != nullptr)
{
decltype(D3D10CreateDeviceAndSwapChain)* D3D10CreateDeviceAndSwapChain =
(decltype(D3D10CreateDeviceAndSwapChain))GetProcAddress(library, "D3D10CreateDeviceAndSwapChain");
if (D3D10CreateDeviceAndSwapChain != nullptr)
{
DXGI_SWAP_CHAIN_DESC SwapChainDesc = {};
SwapChainDesc.BufferCount = 1;
SwapChainDesc.BufferDesc.Width = 1;
SwapChainDesc.BufferDesc.Height = 1;
SwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
SwapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
SwapChainDesc.BufferDesc.RefreshRate.Denominator = 0;
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.OutputWindow = dummy_hWnd;
SwapChainDesc.SampleDesc.Count = 1;
SwapChainDesc.SampleDesc.Quality = 0;
SwapChainDesc.Windowed = TRUE;
D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_NULL, NULL, 0, D3D10_SDK_VERSION, &SwapChainDesc, &pSwapChain, &pDevice);
}
}
if (pSwapChain != nullptr)
{
PRINT_DEBUG("Hooked IDXGISwapChain::Present to detect DX Version\n");
_dx10_hooked = true;
auto h = DX10_Hook::Inst();
h->loadFunctions(pSwapChain);
Hook_Manager::Inst().AddHook(h);
HookDXGIPresent(pSwapChain);
}
else
{
PRINT_DEBUG("Failed to Hook IDXGISwapChain::Present to detect DX Version\n");
}
if (pDevice)pDevice->Release();
if (pSwapChain)pSwapChain->Release();
}
}
void Renderer_Detector::hook_dx11()
{
if (!_dxgi_hooked && !_renderer_found)
{
create_hwnd();
if (dummy_hWnd == nullptr)
return;
IDXGISwapChain* pSwapChain = nullptr;
ID3D11Device* pDevice = nullptr;
HMODULE library = GetModuleHandle(DX11_Hook::DLL_NAME);
if (library != nullptr)
{
decltype(D3D11CreateDeviceAndSwapChain)* D3D11CreateDeviceAndSwapChain =
(decltype(D3D11CreateDeviceAndSwapChain))GetProcAddress(library, "D3D11CreateDeviceAndSwapChain");
if (D3D11CreateDeviceAndSwapChain != nullptr)
{
DXGI_SWAP_CHAIN_DESC SwapChainDesc = {};
SwapChainDesc.BufferCount = 1;
SwapChainDesc.BufferDesc.Width = 1;
SwapChainDesc.BufferDesc.Height = 1;
SwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
SwapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
SwapChainDesc.BufferDesc.RefreshRate.Denominator = 0;
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.OutputWindow = dummy_hWnd;
SwapChainDesc.SampleDesc.Count = 1;
SwapChainDesc.SampleDesc.Quality = 0;
SwapChainDesc.Windowed = TRUE;
D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_NULL, NULL, 0, NULL, NULL, D3D11_SDK_VERSION, &SwapChainDesc, &pSwapChain, &pDevice, NULL, NULL);
}
}
if (pSwapChain != nullptr)
{
PRINT_DEBUG("Hooked IDXGISwapChain::Present to detect DX Version\n");
_dx11_hooked = true;
auto h = DX11_Hook::Inst();
h->loadFunctions(pSwapChain);
Hook_Manager::Inst().AddHook(h);
HookDXGIPresent(pSwapChain);
}
else
{
PRINT_DEBUG("Failed to Hook IDXGISwapChain::Present to detect DX Version\n");
}
if (pDevice) pDevice->Release();
if (pSwapChain) pSwapChain->Release();
}
}
void Renderer_Detector::hook_dx12()
{
if (!_dxgi_hooked && !_renderer_found)
{
create_hwnd();
if (dummy_hWnd == nullptr)
return;
IDXGIFactory4* pDXGIFactory = nullptr;
IDXGISwapChain1* pSwapChain = nullptr;
ID3D12CommandQueue* pCommandQueue = nullptr;
ID3D12Device* pDevice = nullptr;
ID3D12CommandAllocator* pCommandAllocator = nullptr;
ID3D12GraphicsCommandList* pCommandList = nullptr;
HMODULE library = GetModuleHandle(DX12_Hook::DLL_NAME);
if (library != nullptr)
{
decltype(D3D12CreateDevice)* D3D12CreateDevice =
(decltype(D3D12CreateDevice))GetProcAddress(library, "D3D12CreateDevice");
if (D3D12CreateDevice != nullptr)
{
D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&pDevice));
if (pDevice != nullptr)
{
DXGI_SWAP_CHAIN_DESC1 SwapChainDesc = {};
SwapChainDesc.BufferCount = 2;
SwapChainDesc.Width = 1;
SwapChainDesc.Height = 1;
SwapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
SwapChainDesc.Stereo = FALSE;
SwapChainDesc.SampleDesc = { 1, 0 };
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.Scaling = DXGI_SCALING_NONE;
SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
pDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&pCommandQueue));
if (pCommandQueue != nullptr)
{
HMODULE dxgi = GetModuleHandle("dxgi.dll");
if (dxgi != nullptr)
{
decltype(CreateDXGIFactory1)* CreateDXGIFactory1 = (decltype(CreateDXGIFactory1))GetProcAddress(dxgi, "CreateDXGIFactory1");
if (CreateDXGIFactory1 != nullptr)
{
CreateDXGIFactory1(IID_PPV_ARGS(&pDXGIFactory));
if (pDXGIFactory != nullptr)
{
pDXGIFactory->CreateSwapChainForHwnd(pCommandQueue, dummy_hWnd, &SwapChainDesc, NULL, NULL, &pSwapChain);
pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&pCommandAllocator));
if (pCommandAllocator != nullptr)
{
pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, pCommandAllocator, NULL, IID_PPV_ARGS(&pCommandList));
}
}
}
}
}
}//if (pDevice != nullptr)
}//if (D3D12CreateDevice != nullptr)
}//if (library != nullptr)
if (pCommandQueue != nullptr && pSwapChain != nullptr)
{
PRINT_DEBUG("Hooked IDXGISwapChain::Present to detect DX Version\n");
_dx12_hooked = true;
auto h = DX12_Hook::Inst();
h->loadFunctions(pCommandQueue, pSwapChain);
Hook_Manager::Inst().AddHook(h);
HookDXGIPresent(pSwapChain);
}
else
{
PRINT_DEBUG("Failed to Hook IDXGISwapChain::Present to detect DX Version\n");
}
if (pCommandList) pCommandList->Release();
if (pCommandAllocator) pCommandAllocator->Release();
if (pSwapChain) pSwapChain->Release();
if (pDXGIFactory) pDXGIFactory->Release();
if (pCommandQueue) pCommandQueue->Release();
if (pDevice) pDevice->Release();
}
}
void Renderer_Detector::hook_opengl()
{
if (!_ogl_hooked && !_renderer_found)
{
HMODULE library = GetModuleHandle(OpenGL_Hook::DLL_NAME);
decltype(wglMakeCurrent)* wglMakeCurrent = nullptr;
OpenGL_Hook::wglSwapBuffers_t wglSwapBuffers = nullptr;
if (library != nullptr)
{
wglMakeCurrent = (decltype(wglMakeCurrent))GetProcAddress(library, "wglMakeCurrent");
wglSwapBuffers = (decltype(wglSwapBuffers))GetProcAddress(library, "wglSwapBuffers");
}
if (wglMakeCurrent != nullptr && wglSwapBuffers != nullptr)
{
PRINT_DEBUG("Hooked wglMakeCurrent to detect OpenGL\n");
_ogl_hooked = true;
auto h = OpenGL_Hook::Inst();
h->loadFunctions(wglSwapBuffers);
Hook_Manager::Inst().AddHook(h);
HookwglMakeCurrent(wglMakeCurrent);
}
else
{
PRINT_DEBUG("Failed to Hook wglMakeCurrent to detect OpenGL\n");
}
}
}
void Renderer_Detector::create_hook(const char* libname)
{
if (!_stricmp(libname, DX9_Hook::DLL_NAME))
hook_dx9();
else if (!_stricmp(libname, DX10_Hook::DLL_NAME))
hook_dx10();
else if (!_stricmp(libname, DX11_Hook::DLL_NAME))
hook_dx11();
else if (!_stricmp(libname, DX12_Hook::DLL_NAME))
hook_dx12();
else if (!_stricmp(libname, OpenGL_Hook::DLL_NAME))
hook_opengl();
}
void Renderer_Detector::find_renderer_proc(Renderer_Detector* _this)
{
_this->rendererdetect_hook = new Base_Hook();
Hook_Manager& hm = Hook_Manager::Inst();
hm.AddHook(_this->rendererdetect_hook);
std::vector<std::string> const libraries = {
OpenGL_Hook::DLL_NAME,
DX12_Hook::DLL_NAME,
DX11_Hook::DLL_NAME,
DX10_Hook::DLL_NAME,
DX9_Hook::DLL_NAME
};
while (!_this->_renderer_found && !_this->stop_retry())
{
std::vector<std::string>::const_iterator it = libraries.begin();
while (it != libraries.end())
{
{
std::lock_guard<std::mutex> lock(_this->_found_mutex);
if (_this->_renderer_found)
break;
it = std::find_if(it, libraries.end(), [](std::string const& name) {
auto x = GetModuleHandle(name.c_str());
if (x != NULL)
return true;
return false;
});
if (it == libraries.end())
break;
_this->create_hook(it->c_str());
++it;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
_this->destroy_hwnd();
if (_this->game_renderer == nullptr) // Couldn't hook renderer
{
hm.RemoveHook(Windows_Hook::Inst());
}
else
{
hm.AddHook(Windows_Hook::Inst());
}
if (_this->_ogl_hooked)
{
auto h = OpenGL_Hook::Inst();
if (h != _this->game_renderer)
{
_this->_ogl_hooked = false;
hm.RemoveHook(h);
}
}
if (_this->_dx9_hooked)
{
auto h = DX9_Hook::Inst();
if (h != _this->game_renderer)
{
_this->_dx9_hooked = false;
hm.RemoveHook(h);
}
}
if (_this->_dx10_hooked)
{
auto h = DX10_Hook::Inst();
if (h != _this->game_renderer)
{
_this->_dx10_hooked = false;
hm.RemoveHook(h);
}
}
if (_this->_dx11_hooked)
{
auto h = DX11_Hook::Inst();
if (h != _this->game_renderer)
{
_this->_dx11_hooked = false;
hm.RemoveHook(h);
}
}
if (_this->_dx12_hooked)
{
auto h = DX12_Hook::Inst();
if (h != _this->game_renderer)
{
_this->_dx12_hooked = false;
hm.RemoveHook(h);
}
}
delete _this->_hook_thread;
_this->_hook_thread = nullptr;
}
Renderer_Detector::Renderer_Detector():
_hook_thread(nullptr),
_hook_retries(0),
_renderer_found(false),
_dx9_hooked(false),
_dx10_hooked(false),
_dx11_hooked(false),
_dx12_hooked(false),
_dxgi_hooked(false),
_ogl_hooked(false),
rendererdetect_hook(nullptr),
game_renderer(nullptr),
atom(0),
dummy_hWnd(nullptr)
{}
#else// defined(__LINUX__)//!STEAM_WIN32
#include "linux/X11_Hook.h"
#include <dlfcn.h>
extern "C" void *_dl_sym(void *, const char *, void *);
static decltype(glXGetProcAddress)* real_glXGetProcAddress = nullptr;
static decltype(glXGetProcAddressARB)* real_glXGetProcAddressARB = nullptr;
static decltype(dlsym)* real_dlsym = nullptr;
extern "C" int XEventsQueued(Display *display, int mode)
{
auto h = X11_Hook::Inst();
if( h->_XEventsQueued == nullptr )
h->_XEventsQueued = reinterpret_cast<decltype(XEventsQueued)*>(real_dlsym(RTLD_NEXT, "XEventsQueued"));
return X11_Hook::MyXEventsQueued(display, mode);
}
//extern "C" int XPeekEvent(Display *display, XEvent *event)
//{
// auto h = X11_Hook::Inst();
// if( h->_XPeekEvent == nullptr )
// h->_XPeekEvent = reinterpret_cast<decltype(XPeekEvent)*>(real_dlsym(RTLD_NEXT, "XPeekEvent"));
//
// return X11_Hook::MyXPeekEvent(display, mode);
//}
//extern "C" int XNextEvent(Display *display, XEvent *event)
//{
// auto h = X11_Hook::Inst();
// if( h->_XNextEvent == nullptr )
// h->_XNextEvent = reinterpret_cast<decltype(XNextEvent)*>(real_dlsym(RTLD_NEXT, "XNextEvent"));
//
// return X11_Hook::MyXNextEvent(display, mode);
//}
extern "C" int XPending(Display *display)
{
auto h = X11_Hook::Inst();
if( h->_XPending == nullptr )
h->_XPending = reinterpret_cast<decltype(XPending)*>(real_dlsym(RTLD_NEXT, "XPending"));
return X11_Hook::MyXPending(display);
}
extern "C" void glXSwapBuffers(Display *display, GLXDrawable drawable)
{
OpenGLX_Hook::Inst()->start_hook();
OpenGLX_Hook::MyglXSwapBuffers(display, drawable);
}
extern "C" __GLXextFuncPtr glXGetProcAddress(const GLubyte * procName)
{
if( strcmp((const char*)procName, "glXSwapBuffers") == 0 )
{
decltype(glXSwapBuffers)* real_glXSwapBuffers = (decltype(real_glXSwapBuffers))real_glXGetProcAddress(procName);
OpenGLX_Hook::Inst()->loadFunctions(real_glXSwapBuffers);
return (__GLXextFuncPtr)glXSwapBuffers;
}
__GLXextFuncPtr func = (__GLXextFuncPtr)real_glXGetProcAddress(procName);
return func;
}
extern "C" __GLXextFuncPtr glXGetProcAddressARB (const GLubyte* procName)
{
if( strcmp((const char*)procName, "glXSwapBuffers") == 0 )
{
decltype(glXSwapBuffers)* real_glXSwapBuffers = (decltype(real_glXSwapBuffers))real_glXGetProcAddressARB(procName);
OpenGLX_Hook::Inst()->loadFunctions(real_glXSwapBuffers);
return (__GLXextFuncPtr)glXSwapBuffers;
}
__GLXextFuncPtr func = (__GLXextFuncPtr)real_glXGetProcAddressARB(procName);
return func;
}
void Renderer_Detector::find_renderer_proc(Renderer_Detector* _this)
{
/*
_this->rendererdetect_hook = new Base_Hook();
Hook_Manager& hm = Hook_Manager::Inst();
hm.AddHook(_this->rendererdetect_hook);
*/
}
Renderer_Detector::Renderer_Detector():
_hook_thread(nullptr),
_hook_retries(0),
_renderer_found(false),
_oglx_hooked(false),
rendererdetect_hook(nullptr),
game_renderer(nullptr)
{}
extern "C" void* dlsym(void* handle, const char* name)
{
if (real_dlsym == nullptr)
real_dlsym = (decltype(dlsym)*)_dl_sym(RTLD_NEXT, "dlsym", reinterpret_cast<void*>(dlsym));
if ( strcmp(name,"dlsym") == 0 )
return (void*)dlsym;
if( strcmp(name, "glXGetProcAddress") == 0 )
{
if( real_glXGetProcAddress == nullptr )
{
real_glXGetProcAddress = (decltype(glXGetProcAddress)*)real_dlsym(RTLD_NEXT, "glXGetProcAddress");
}
return (void*)glXGetProcAddress;
}
if( strcmp(name, "glXGetProcAddressARB") == 0 )
{
if( real_glXGetProcAddressARB == nullptr )
{
real_glXGetProcAddressARB = (decltype(glXGetProcAddressARB)*)real_dlsym(RTLD_NEXT, "glXGetProcAddressARB");
}
return (void*)glXGetProcAddressARB;
}
if( strcmp(name, "XEventsQueued") == 0 )
{
return (void*)XEventsQueued;
}
//if( strcmp(name, "XNextEvent") == 0 )
//{
// return (void*)XNextEvent;
//}
//if( strcmp(name, "XPeekEvent") == 0 )
//{
// return (void*)XPeekEvent;
//}
if( strcmp(name, "XPending") == 0 )
{
return (void*)XPending;
}
return real_dlsym(handle,name);
}
#endif
void Renderer_Detector::renderer_found(Base_Hook* hook)
{
std::lock_guard<std::mutex> lock(_found_mutex);
Hook_Manager& hm = Hook_Manager::Inst();
_renderer_found = true;
game_renderer = hook;
if (hook == nullptr)
PRINT_DEBUG("We found a renderer but couldn't hook it, aborting overlay hook.\n");
else
PRINT_DEBUG("Hooked renderer in %d/%d tries\n", _hook_retries, max_hook_retries);
hm.RemoveHook(rendererdetect_hook);
}
bool Renderer_Detector::stop_retry()
{
// Retry or not
bool stop = ++_hook_retries >= max_hook_retries;
if (stop)
renderer_found(nullptr);
return stop;
}
void Renderer_Detector::find_renderer()
{
if (_hook_thread == nullptr)
{
_hook_thread = new std::thread(&Renderer_Detector::find_renderer_proc, this);
_hook_thread->detach();
}
}
Renderer_Detector& Renderer_Detector::Inst()
{
static Renderer_Detector inst;
return inst;
}
Base_Hook* Renderer_Detector::get_renderer() const
{
return game_renderer;
}
Renderer_Detector::~Renderer_Detector()
{
if (_hook_thread != nullptr)
{
_hook_retries = max_hook_retries;
}
}

View File

@ -0,0 +1,105 @@
#ifndef __INCLUDED_RENDERER_DETECTOR_H__
#define __INCLUDED_RENDERER_DETECTOR_H__
#include "Base_Hook.h"
#include <thread>
#ifndef NO_OVERLAY
#ifdef __WINDOWS__
struct IDXGISwapChain;
struct IDirect3DDevice9;
struct IDirect3DDevice9Ex;
class Renderer_Detector
{
private:
// Variables
std::thread* _hook_thread;
std::mutex _found_mutex;
unsigned int _hook_retries;
bool _renderer_found; // Is the renderer hooked ?
bool _dx9_hooked;
bool _dx10_hooked;
bool _dx11_hooked;
bool _dx12_hooked;
bool _dxgi_hooked;
bool _ogl_hooked; // wglMakeCurrent is hooked ? (opengl)
Base_Hook* rendererdetect_hook;
Base_Hook* game_renderer;
ATOM atom;
HWND dummy_hWnd;
// Functions
Renderer_Detector();
~Renderer_Detector();
static HRESULT STDMETHODCALLTYPE MyIDXGISwapChain_Present(IDXGISwapChain* _this, UINT SyncInterval, UINT Flags);
static HRESULT STDMETHODCALLTYPE MyPresent(IDirect3DDevice9* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion);
static HRESULT STDMETHODCALLTYPE MyPresentEx(IDirect3DDevice9Ex* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags);
static BOOL WINAPI MywglMakeCurrent(HDC hDC, HGLRC hGLRC);
void HookDXGIPresent(IDXGISwapChain* pSwapChain);
void HookDX9Present(IDirect3DDevice9* pDevice, bool ex);
void HookwglMakeCurrent(decltype(wglMakeCurrent)* wglMakeCurrent);
void hook_dx9();
void hook_dx10();
void hook_dx11();
void hook_dx12();
void hook_opengl();
void create_hwnd();
void destroy_hwnd();
void create_hook(const char* libname);
bool stop_retry();
static void find_renderer_proc(Renderer_Detector* _this);
public:
void find_renderer();
void renderer_found(Base_Hook* hook);
Base_Hook* get_renderer() const;
static Renderer_Detector& Inst();
};
#elif defined __LINUX__
#include "linux/OpenGLX_Hook.h"
class Renderer_Detector
{
// Variables
std::thread* _hook_thread;
std::mutex _found_mutex;
unsigned int _hook_retries;
bool _oglx_hooked;
bool _renderer_found; // Is the renderer hooked ?
Base_Hook* rendererdetect_hook;
Base_Hook* game_renderer;
// Functions
Renderer_Detector();
~Renderer_Detector();
static void MyglXSwapBuffers(Display *dpy, GLXDrawable drawable);
void HookglXSwapBuffers(decltype(glXSwapBuffers)* glXSwapBuffers);
void hook_openglx(const char* libname);
void create_hook(const char* libname);
bool stop_retry();
static void find_renderer_proc(Renderer_Detector* _this);
public:
void find_renderer();
void renderer_found(Base_Hook* hook);
Base_Hook* get_renderer() const;
static Renderer_Detector& Inst();
};
#endif//__WINDOWS__
#endif//NO_OVERLAY
#endif//__INCLUDED_RENDERER_DETECTOR_H__

View File

@ -0,0 +1,175 @@
#include "OpenGLX_Hook.h"
#include "X11_Hook.h"
#include "../Renderer_Detector.h"
#include "../dll/dll.h"
#ifdef __LINUX__
#ifndef NO_OVERLAY
#include <imgui.h>
#include <impls/imgui_impl_opengl3.h>
#include "../steam_overlay.h"
OpenGLX_Hook* OpenGLX_Hook::_inst = nullptr;
bool OpenGLX_Hook::start_hook()
{
bool res = true;
if (!hooked)
{
if (!X11_Hook::Inst()->start_hook())
return false;
GLenum err = glewInit();
if (err == GLEW_OK)
{
PRINT_DEBUG("Hooked OpenGLX\n");
hooked = true;
Renderer_Detector::Inst().renderer_found(this);
/*
UnhookAll();
BeginHook();
HookFuncs(
std::make_pair<void**, void*>(&(void*&)_glXSwapBuffers, (void*)&OpenGLX_Hook::MyglXSwapBuffers)
);
EndHook();
*/
get_steam_client()->steam_overlay->HookReady();
}
else
{
PRINT_DEBUG("Failed to hook OpenGLX\n");
/* Problem: glewInit failed, something is seriously wrong. */
PRINT_DEBUG("Error: %s\n", glewGetErrorString(err));
res = false;
}
}
return true;
}
void OpenGLX_Hook::resetRenderState()
{
if (initialized)
{
ImGui_ImplOpenGL3_Shutdown();
X11_Hook::Inst()->resetRenderState();
ImGui::DestroyContext();
glXDestroyContext(display, context);
display = nullptr;
initialized = false;
}
}
// Try to make this function and overlay's proc as short as possible or it might affect game's fps.
void OpenGLX_Hook::prepareForOverlay(Display* display, GLXDrawable drawable)
{
PRINT_DEBUG("Called SwapBuffer hook");
if( (Window)drawable != X11_Hook::Inst()->get_game_wnd() )
X11_Hook::Inst()->resetRenderState();
if( ! initialized )
{
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = NULL;
ImGui_ImplOpenGL3_Init();
int attributes[] = { //can't be const b/c X11 doesn't like it. Not sure if that's intentional or just stupid.
GLX_RGBA, //apparently nothing comes after this?
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
//Ideally, the size would be 32 (or at least 24), but I have actually seen
// this size (on a modern OS even).
GLX_DEPTH_SIZE, 16,
GLX_DOUBLEBUFFER, True,
None
};
XVisualInfo* visual_info = glXChooseVisual(display, DefaultScreen(display), attributes);
context = glXCreateContext(display, visual_info, nullptr, True);
this->display = display;
get_steam_client()->steam_overlay->CreateFonts();
initialized = true;
}
auto oldContext = glXGetCurrentContext();
glXMakeCurrent(display, drawable, context);
ImGui_ImplOpenGL3_NewFrame();
X11_Hook::Inst()->prepareForOverlay(display, (Window)drawable);
ImGui::NewFrame();
get_steam_client()->steam_overlay->OverlayProc();
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glXMakeCurrent(display, drawable, oldContext);
}
void OpenGLX_Hook::MyglXSwapBuffers(Display* display, GLXDrawable drawable)
{
OpenGLX_Hook::Inst()->prepareForOverlay(display, drawable);
OpenGLX_Hook::Inst()->_glXSwapBuffers(display, drawable);
}
OpenGLX_Hook::OpenGLX_Hook():
initialized(false),
hooked(false),
_glXSwapBuffers(nullptr)
{
//_library = dlopen(DLL_NAME);
}
OpenGLX_Hook::~OpenGLX_Hook()
{
PRINT_DEBUG("OpenGLX Hook removed\n");
if (initialized)
{
ImGui_ImplOpenGL3_Shutdown();
ImGui::DestroyContext();
glXDestroyContext(display, context);
}
//dlclose(_library);
_inst = nullptr;
}
OpenGLX_Hook* OpenGLX_Hook::Inst()
{
if (_inst == nullptr)
_inst = new OpenGLX_Hook;
return _inst;
}
const char* OpenGLX_Hook::get_lib_name() const
{
return DLL_NAME;
}
void OpenGLX_Hook::loadFunctions(decltype(glXSwapBuffers)* pfnglXSwapBuffers)
{
_glXSwapBuffers = pfnglXSwapBuffers;
}
#endif//NO_OVERLAY
#endif//__LINUX__

View File

@ -0,0 +1,49 @@
#ifndef __INCLUDED_OPENGLX_HOOK_H__
#define __INCLUDED_OPENGLX_HOOK_H__
#include "../Base_Hook.h"
#ifdef __LINUX__
#ifndef NO_OVERLAY
#include <GL/glew.h>
#include <GL/glx.h>
class OpenGLX_Hook : public Base_Hook
{
public:
static constexpr const char *DLL_NAME = "libGLX.so";
private:
static OpenGLX_Hook* _inst;
// Variables
bool hooked;
bool initialized;
Display *display;
GLXContext context;
// Functions
OpenGLX_Hook();
void resetRenderState();
void prepareForOverlay(Display* display, GLXDrawable drawable);
// Hook to render functions
decltype(glXSwapBuffers)* _glXSwapBuffers;
public:
static void MyglXSwapBuffers(Display* display, GLXDrawable drawable);
virtual ~OpenGLX_Hook();
bool start_hook();
static OpenGLX_Hook* Inst();
virtual const char* get_lib_name() const;
void loadFunctions(decltype(glXSwapBuffers)* pfnglXSwapBuffers);
};
#endif//NO_OVERLAY
#endif//__LINUX__
#endif//__INCLUDED_OPENGLX_HOOK_H__

View File

@ -0,0 +1,205 @@
#include "X11_Hook.h"
#include "../Renderer_Detector.h"
#include "../dll/dll.h"
#ifdef __LINUX__
#ifndef NO_OVERLAY
#include <imgui.h>
#include <impls/linux/imgui_impl_x11.h>
#include <dlfcn.h>
#include <unistd.h>
#include <fstream>
extern int ImGui_ImplX11_EventHandler(XEvent &event);
X11_Hook* X11_Hook::_inst = nullptr;
bool X11_Hook::start_hook()
{
bool res = true;
if (!hooked)
{
PRINT_DEBUG("Hooked X11\n");
hooked = true;
}
return res;
}
void X11_Hook::resetRenderState()
{
if (initialized)
{
game_wnd = 0;
initialized = false;
ImGui_ImplX11_Shutdown();
}
}
void X11_Hook::prepareForOverlay(Display *display, Window wnd)
{
if (!initialized)
{
ImGui_ImplX11_Init(display, (void*)wnd);
game_wnd = wnd;
initialized = true;
}
ImGui_ImplX11_NewFrame();
}
/////////////////////////////////////////////////////////////////////////////////////
// X11 window hooks
bool IgnoreEvent(XEvent &event)
{
switch(event.type)
{
// Keyboard
case KeyPress: case KeyRelease:
// MouseButton
case ButtonPress: case ButtonRelease:
// Mouse move
case MotionNotify:
return true;
}
return false;
}
int X11_Hook::check_for_overlay(Display *d, int num_events)
{
static Time prev_time = {};
X11_Hook* inst = Inst();
if( inst->initialized )
{
XEvent event;
while(num_events)
{
//inst->_XPeekEvent(d, &event);
XPeekEvent(d, &event);
Steam_Overlay* overlay = get_steam_client()->steam_overlay;
bool show = overlay->ShowOverlay();
// Is the event is a key press
if (event.type == KeyPress)
{
// Tab is pressed and was not pressed before
//if (event.xkey.keycode == inst->_XKeysymToKeycode(d, XK_Tab) && event.xkey.state & ShiftMask)
if (event.xkey.keycode == XKeysymToKeycode(d, XK_Tab) && event.xkey.state & ShiftMask)
{
// if key TAB is held, don't make the overlay flicker :p
if( event.xkey.time != prev_time)
{
overlay->ShowOverlay(!overlay->ShowOverlay());
if (overlay->ShowOverlay())
show = true;
}
}
}
//else if(event.type == KeyRelease && event.xkey.keycode == inst->_XKeysymToKeycode(d, XK_Tab))
else if(event.type == KeyRelease && event.xkey.keycode == XKeysymToKeycode(d, XK_Tab))
{
prev_time = event.xkey.time;
}
if (show)
{
ImGui_ImplX11_EventHandler(event);
if (IgnoreEvent(event))
{
//inst->_XNextEvent(d, &event);
XNextEvent(d, &event);
--num_events;
}
else
break;
}
else
break;
}
}
return num_events;
}
int X11_Hook::MyXEventsQueued(Display *display, int mode)
{
X11_Hook* inst = X11_Hook::Inst();
int res = inst->_XEventsQueued(display, mode);
if( res )
{
res = inst->check_for_overlay(display, res);
}
return res;
}
int X11_Hook::MyXNextEvent(Display* display, XEvent *event)
{
return Inst()->_XNextEvent(display, event);
}
int X11_Hook::MyXPeekEvent(Display* display, XEvent *event)
{
return Inst()->_XPeekEvent(display, event);
}
int X11_Hook::MyXPending(Display* display)
{
int res = Inst()->_XPending(display);
if( res )
{
res = Inst()->check_for_overlay(display, res);
}
return res;
}
/////////////////////////////////////////////////////////////////////////////////////
X11_Hook::X11_Hook() :
initialized(false),
hooked(false),
game_wnd(0),
_XEventsQueued(nullptr),
_XPeekEvent(nullptr),
_XNextEvent(nullptr),
_XPending(nullptr)
{
//_library = dlopen(DLL_NAME, RTLD_NOW);
}
X11_Hook::~X11_Hook()
{
PRINT_DEBUG("X11 Hook removed\n");
resetRenderState();
//dlclose(_library);
_inst = nullptr;
}
X11_Hook* X11_Hook::Inst()
{
if (_inst == nullptr)
_inst = new X11_Hook;
return _inst;
}
const char* X11_Hook::get_lib_name() const
{
return DLL_NAME;
}
#endif//NO_OVERLAY
#endif//#__LINUX__

View File

@ -0,0 +1,63 @@
#ifndef __INCLUDED_X11_HOOK_H__
#define __INCLUDED_X11_HOOK_H__
#include "../Base_Hook.h"
#ifdef __LINUX__
#ifndef NO_OVERLAY
#include <X11/X.h> // XEvent types
#include <X11/Xlib.h> // XEvent structure
extern "C" int XEventsQueued(Display *display, int mode);
extern "C" int XPending(Display *display);
class X11_Hook : public Base_Hook
{
public:
friend int XEventsQueued(Display *display, int mode);
friend int XPending(Display *display);
static constexpr const char* DLL_NAME = "libX11.so";
private:
static X11_Hook* _inst;
// Variables
bool hooked;
bool initialized;
Window game_wnd;
// Functions
X11_Hook();
int check_for_overlay(Display *d, int num_events);
// Hook to X11 window messages
decltype(XEventsQueued)* _XEventsQueued;
decltype(XPeekEvent)* _XPeekEvent;
decltype(XNextEvent)* _XNextEvent;
decltype(XPending)* _XPending;
//decltype(XKeysymToKeycode)* _XKeysymToKeycode;
//decltype(XLookupKeysym)* _XLookupKeysym;
//decltype(XGetGeometry)* _XGetGeometry;
static int MyXEventsQueued(Display * display, int mode);
static int MyXNextEvent(Display* display, XEvent *event);
static int MyXPeekEvent(Display* display, XEvent *event);
static int MyXPending(Display* display);
public:
virtual ~X11_Hook();
void resetRenderState();
void prepareForOverlay(Display *display, Window wnd);
Window get_game_wnd() const{ return game_wnd; }
bool start_hook();
static X11_Hook* Inst();
virtual const char* get_lib_name() const;
};
#endif//NO_OVERLAY
#endif//__LINUX__
#endif//__INCLUDED_X11_HOOK_H__

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,801 @@
#include "steam_overlay.h"
#ifndef NO_OVERLAY
#include <thread>
#include <string>
#include <sstream>
#include <cctype>
#include <imgui.h>
#include "../dll/dll.h"
#include "Renderer_Detector.h"
static constexpr int max_window_id = 10000;
static constexpr int base_notif_window_id = 0 * max_window_id;
static constexpr int base_friend_window_id = 1 * max_window_id;
static constexpr int base_friend_item_id = 2 * max_window_id;
int find_free_id(std::vector<int> & ids, int base)
{
std::sort(ids.begin(), ids.end());
int id = base;
for (auto i : ids)
{
if (id < i)
break;
id = i + 1;
}
return id > (base+max_window_id) ? 0 : id;
}
int find_free_friend_id(std::map<Friend, friend_window_state, Friend_Less> const& friend_windows)
{
std::vector<int> ids;
ids.reserve(friend_windows.size());
std::for_each(friend_windows.begin(), friend_windows.end(), [&ids](std::pair<Friend const, friend_window_state> const& i)
{
ids.emplace_back(i.second.id);
});
return find_free_id(ids, base_friend_window_id);
}
int find_free_notification_id(std::vector<Notification> const& notifications)
{
std::vector<int> ids;
ids.reserve(notifications.size());
std::for_each(notifications.begin(), notifications.end(), [&ids](Notification const& i)
{
ids.emplace_back(i.id);
});
return find_free_id(ids, base_friend_window_id);
}
#ifdef __WINDOWS__
#include "windows/Windows_Hook.h"
#endif
#include "notification.h"
void Steam_Overlay::steam_overlay_run_every_runcb(void* object)
{
Steam_Overlay* _this = reinterpret_cast<Steam_Overlay*>(object);
_this->RunCallbacks();
}
void Steam_Overlay::steam_overlay_callback(void* object, Common_Message* msg)
{
Steam_Overlay* _this = reinterpret_cast<Steam_Overlay*>(object);
_this->Callback(msg);
}
Steam_Overlay::Steam_Overlay(Settings* settings, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Networking* network) :
settings(settings),
callback_results(callback_results),
callbacks(callbacks),
run_every_runcb(run_every_runcb),
network(network),
setup_overlay_called(false),
show_overlay(false),
is_ready(false),
notif_position(ENotificationPosition::k_EPositionBottomLeft),
h_inset(0),
v_inset(0),
overlay_state_changed(false)
{
run_every_runcb->add(&Steam_Overlay::steam_overlay_run_every_runcb, this);
this->network->setCallback(CALLBACK_ID_STEAM_MESSAGES, settings->get_local_steam_id(), &Steam_Overlay::steam_overlay_callback, this);
}
Steam_Overlay::~Steam_Overlay()
{
run_every_runcb->remove(&Steam_Overlay::steam_overlay_run_every_runcb, this);
}
bool Steam_Overlay::Ready() const
{
return is_ready;
}
bool Steam_Overlay::NeedPresent() const
{
return true;
}
void Steam_Overlay::SetNotificationPosition(ENotificationPosition eNotificationPosition)
{
notif_position = eNotificationPosition;
}
void Steam_Overlay::SetNotificationInset(int nHorizontalInset, int nVerticalInset)
{
h_inset = nHorizontalInset;
v_inset = nVerticalInset;
}
void Steam_Overlay::SetupOverlay()
{
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!setup_overlay_called)
{
setup_overlay_called = true;
Renderer_Detector::Inst().find_renderer();
}
}
void Steam_Overlay::HookReady()
{
if (!is_ready)
{
// TODO: Uncomment this and draw our own cursor (cosmetics)
//ImGuiIO &io = ImGui::GetIO();
//io.WantSetMousePos = false;
//io.MouseDrawCursor = false;
//io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
is_ready = true;
}
}
void Steam_Overlay::OpenOverlayInvite(CSteamID lobbyId)
{
ShowOverlay(true);
}
void Steam_Overlay::OpenOverlay(const char* pchDialog)
{
// TODO: Show pages depending on pchDialog
ShowOverlay(true);
}
bool Steam_Overlay::ShowOverlay() const
{
return show_overlay;
}
void Steam_Overlay::ShowOverlay(bool state)
{
if (!Ready() || show_overlay == state)
return;
ImGuiIO &io = ImGui::GetIO();
if(state)
{
io.MouseDrawCursor = true;
}
else
{
io.MouseDrawCursor = false;
}
#ifdef __WINDOWS__
static RECT old_clip;
if (state)
{
HWND game_hwnd = Windows_Hook::Inst()->GetGameHwnd();
RECT cliRect, wndRect, clipRect;
GetClipCursor(&old_clip);
// The window rectangle has borders and menus counted in the size
GetWindowRect(game_hwnd, &wndRect);
// The client rectangle is the window without borders
GetClientRect(game_hwnd, &cliRect);
clipRect = wndRect; // Init clip rectangle
// Get Window width with borders
wndRect.right -= wndRect.left;
// Get Window height with borders & menus
wndRect.bottom -= wndRect.top;
// Compute the border width
int borderWidth = (wndRect.right - cliRect.right) / 2;
// Client top clip is the menu bar width minus bottom border
clipRect.top += wndRect.bottom - cliRect.bottom - borderWidth;
// Client left clip is the left border minus border width
clipRect.left += borderWidth;
// Here goes the same for right and bottom
clipRect.right -= borderWidth;
clipRect.bottom -= borderWidth;
ClipCursor(&clipRect);
}
else
{
ClipCursor(&old_clip);
}
#else
#endif
show_overlay = state;
overlay_state_changed = true;
}
void Steam_Overlay::NotifyUser(friend_window_state& friend_state)
{
if (!(friend_state.window_state & window_state_show) || !show_overlay)
{
friend_state.window_state |= window_state_need_attention;
#ifdef __WINDOWS__
PlaySound((LPCSTR)notif_invite_wav, NULL, SND_ASYNC | SND_MEMORY);
#endif
}
}
void Steam_Overlay::SetLobbyInvite(Friend friendId, uint64 lobbyId)
{
if (!Ready())
return;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto i = friends.find(friendId);
if (i != friends.end())
{
auto& frd = i->second;
frd.lobbyId = lobbyId;
frd.window_state |= window_state_lobby_invite;
// Make sure don't have rich presence invite and a lobby invite (it should not happen but who knows)
frd.window_state &= ~window_state_rich_invite;
AddInviteNotification(*i);
NotifyUser(i->second);
}
}
void Steam_Overlay::SetRichInvite(Friend friendId, const char* connect_str)
{
if (!Ready())
return;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto i = friends.find(friendId);
if (i != friends.end())
{
auto& frd = i->second;
strncpy(frd.connect, connect_str, k_cchMaxRichPresenceValueLength - 1);
frd.window_state |= window_state_rich_invite;
// Make sure don't have rich presence invite and a lobby invite (it should not happen but who knows)
frd.window_state &= ~window_state_lobby_invite;
AddInviteNotification(*i);
NotifyUser(i->second);
}
}
void Steam_Overlay::FriendConnect(Friend _friend)
{
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int id = find_free_friend_id(friends);
if (id != 0)
{
auto& item = friends[_friend];
item.window_title = std::move(_friend.name() + " playing " + std::to_string(_friend.appid()));
item.window_state = window_state_none;
item.id = id;
memset(item.chat_input, 0, max_chat_len);
}
else
PRINT_DEBUG("No more free id to create a friend window\n");
}
void Steam_Overlay::FriendDisconnect(Friend _friend)
{
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto it = friends.find(_friend);
if (it != friends.end())
friends.erase(it);
}
void Steam_Overlay::AddMessageNotification(std::string const& message)
{
int id = find_free_notification_id(notifications);
if (id != 0)
{
Notification notif;
notif.id = id;
notif.type = notification_type_message;
notif.message = message;
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif);
}
else
PRINT_DEBUG("No more free id to create a notification window\n");
}
void Steam_Overlay::AddAchievementNotification(nlohmann::json const& ach)
{
int id = find_free_notification_id(notifications);
if (id != 0)
{
Notification notif;
notif.id = id;
notif.type = notification_type_achievement;
// Load achievement image
notif.message = ach["displayName"].get<std::string>() + "\n" + ach["description"].get<std::string>();
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif);
}
else
PRINT_DEBUG("No more free id to create a notification window\n");
}
void Steam_Overlay::AddInviteNotification(std::pair<const Friend, friend_window_state>& wnd_state)
{
int id = find_free_notification_id(notifications);
if (id != 0)
{
Notification notif;
notif.id = id;
notif.type = notification_type_invite;
notif.frd = &wnd_state;
notif.message = wnd_state.first.name() + " invited you to join a game";
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif);
}
else
PRINT_DEBUG("No more free id to create a notification window\n");
}
bool Steam_Overlay::FriendHasLobby(uint64 friend_id)
{
Steam_Friends* steamFriends = get_steam_client()->steam_friends;
if( std::string(steamFriends->GetFriendRichPresence(friend_id, "connect")).length() > 0 )
return true;
FriendGameInfo_t friend_game_info = {};
steamFriends->GetFriendGamePlayed(friend_id, &friend_game_info);
if (friend_game_info.m_steamIDLobby.IsValid())
return true;
return false;
}
bool Steam_Overlay::IHaveLobby()
{
Steam_Friends* steamFriends = get_steam_client()->steam_friends;
if (std::string(steamFriends->GetFriendRichPresence(settings->get_local_steam_id(), "connect")).length() > 0)
return true;
if (settings->get_lobby().IsValid())
return true;
return false;
}
void Steam_Overlay::BuildContextMenu(Friend const& frd, friend_window_state& state)
{
if (ImGui::BeginPopupContextItem("Friends_ContextMenu", 1))
{
bool close_popup = false;
if (ImGui::Button("Chat"))
{
state.window_state |= window_state_show;
close_popup = true;
}
// If we have the same appid, activate the invite/join buttons
if (settings->get_local_game_id().AppID() == frd.appid())
{
if (IHaveLobby() && ImGui::Button("Invite###PopupInvite"))
{
state.window_state |= window_state_invite;
has_friend_action.push(frd);
close_popup = true;
}
if (FriendHasLobby(frd.id()) && ImGui::Button("Join###PopupJoin"))
{
state.window_state |= window_state_join;
has_friend_action.push(frd);
close_popup = true;
}
}
if( close_popup)
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void Steam_Overlay::BuildFriendWindow(Friend const& frd, friend_window_state& state)
{
if (!(state.window_state & window_state_show))
return;
bool show = true;
bool send_chat_msg = false;
float width = ImGui::CalcTextSize("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").x;
if (state.window_state & window_state_need_attention && ImGui::IsWindowFocused())
{
state.window_state &= ~window_state_need_attention;
}
ImGui::SetNextWindowSizeConstraints(ImVec2{ width, ImGui::GetFontSize()*8 + ImGui::GetItemsLineHeightWithSpacing()*4 },
ImVec2{ std::numeric_limits<float>::max() , std::numeric_limits<float>::max() });
// Window id is after the ###, the window title is the friend name
std::string friend_window_id = std::move("###" + std::to_string(state.id));
if (ImGui::Begin((state.window_title + friend_window_id).c_str(), &show))
{
if (state.window_state & window_state_need_attention && ImGui::IsWindowFocused())
{
state.window_state &= ~window_state_need_attention;
}
// Fill this with the chat box and maybe the invitation
if (state.window_state & (window_state_lobby_invite | window_state_rich_invite))
{
ImGui::LabelText("##label", "%s invited you to join the game.", frd.name().c_str());
ImGui::SameLine();
if (ImGui::Button("Accept"))
{
this->has_friend_action.push(frd);
}
ImGui::SameLine();
if (ImGui::Button("Refuse"))
{
state.window_state &= ~(window_state_lobby_invite | window_state_rich_invite);
}
}
ImGui::ColoredInputTextMultiline("##chat_history", &state.chat_history[0], state.chat_history.length(), { -1.0f, 0 }, ImGuiInputTextFlags_ReadOnly);
// TODO: Fix the layout of the chat line + send button.
// It should be like this: chat input should fill the window size minus send button size (button size is fixed)
// |------------------------------|
// | /--------------------------\ |
// | | | |
// | | chat history | |
// | | | |
// | \--------------------------/ |
// | [____chat line______] [send] |
// |------------------------------|
//
// And it is like this
// |------------------------------|
// | /--------------------------\ |
// | | | |
// | | chat history | |
// | | | |
// | \--------------------------/ |
// | [__chat line__] [send] |
// |------------------------------|
float wnd_width = ImGui::GetWindowContentRegionWidth();
ImGuiStyle &style = ImGui::GetStyle();
wnd_width -= ImGui::CalcTextSize("Send").x + style.FramePadding.x * 2 + style.ItemSpacing.x + 1;
ImGui::PushItemWidth(wnd_width);
if (ImGui::InputText("##chat_line", state.chat_input, max_chat_len, ImGuiInputTextFlags_EnterReturnsTrue))
{
send_chat_msg = true;
}
ImGui::PopItemWidth();
ImGui::SameLine();
if (ImGui::Button("Send"))
{
send_chat_msg = true;
}
if (send_chat_msg)
{
if (!(state.window_state & window_state_send_message))
{
has_friend_action.push(frd);
state.window_state |= window_state_send_message;
}
}
}
// User closed the friend window
if (!show)
state.window_state &= ~window_state_show;
ImGui::End();
}
ImFont *font_default;
ImFont *font_notif;
void Steam_Overlay::BuildNotifications(int width, int height)
{
auto now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
int i = 0;
int font_size = ImGui::GetFontSize();
for (auto it = notifications.begin(); it != notifications.end(); ++it, ++i)
{
auto elapsed_notif = now - it->start_time;
if ( elapsed_notif < Notification::fade_in)
{
float alpha = Notification::max_alpha * (elapsed_notif.count() / static_cast<float>(Notification::fade_in.count()));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, alpha));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(Notification::r, Notification::g, Notification::b, alpha));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, alpha*2));
}
else if ( elapsed_notif > Notification::fade_out_start)
{
float alpha = Notification::max_alpha * ((Notification::show_time - elapsed_notif).count() / static_cast<float>(Notification::fade_out.count()));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, alpha));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(Notification::r, Notification::g, Notification::b, alpha));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, alpha*2));
}
else
{
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, Notification::max_alpha));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(Notification::r, Notification::g, Notification::b, Notification::max_alpha));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, Notification::max_alpha*2));
}
ImGui::SetNextWindowPos(ImVec2((float)width - width * Notification::width, Notification::height * font_size * i ));
ImGui::SetNextWindowSize(ImVec2( width * Notification::width, Notification::height * font_size ));
ImGui::Begin(std::to_string(it->id).c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoDecoration);
switch (it->type)
{
case notification_type_achievement:
ImGui::TextWrapped("%s", it->message.c_str());
break;
case notification_type_invite:
{
ImGui::TextWrapped("%s", it->message.c_str());
if (ImGui::Button("Join"))
{
has_friend_action.push(it->frd->first);
it->start_time = std::chrono::seconds(0);
}
}
break;
case notification_type_message:
ImGui::TextWrapped("%s", it->message.c_str()); break;
}
ImGui::End();
ImGui::PopStyleColor(3);
}
notifications.erase(std::remove_if(notifications.begin(), notifications.end(), [&now](Notification &item) {
return (now - item.start_time) > Notification::show_time;
}), notifications.end());
}
void Steam_Overlay::CreateFonts()
{
ImGuiIO& io = ImGui::GetIO();
ImFontConfig fontcfg;
fontcfg.OversampleH = fontcfg.OversampleV = 1;
fontcfg.PixelSnapH = true;
fontcfg.GlyphRanges = io.Fonts->GetGlyphRangesDefault();
fontcfg.SizePixels = std::round(io.DisplaySize.y / 68);
font_default = io.Fonts->AddFontDefault(&fontcfg);
fontcfg.SizePixels = std::round(io.DisplaySize.y / 60);
font_notif = io.Fonts->AddFontDefault(&fontcfg);
ImGuiStyle& style = ImGui::GetStyle();
style.WindowRounding = 0.0; // Disable round window
}
// Try to make this function as short as possible or it might affect game's fps.
void Steam_Overlay::OverlayProc()
{
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!Ready())
return;
ImGuiIO& io = ImGui::GetIO();
ImGui::PushFont(font_notif);
BuildNotifications(io.DisplaySize.x, io.DisplaySize.y);
ImGui::PopFont();
if (show_overlay)
{
int friend_size = friends.size();
// Set the overlay windows to the size of the game window
ImGui::SetNextWindowPos({ 0,0 });
ImGui::SetNextWindowSize({ static_cast<float>(io.DisplaySize.x),
static_cast<float>(io.DisplaySize.y) });
ImGui::SetNextWindowBgAlpha(0.50);
ImGui::PushFont(font_default);
bool show = true;
if (ImGui::Begin("SteamOverlay", &show, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus))
{
ImGui::LabelText("##label", "Username: %s(%llu) playing %u",
settings->get_local_name(),
settings->get_local_steam_id().ConvertToUint64(),
settings->get_local_game_id().AppID());
ImGui::SameLine();
Base_Hook* hook = Renderer_Detector::Inst().get_renderer();
ImGui::LabelText("##label", "Renderer: %s", (hook == nullptr ? "Unknown" : hook->get_lib_name()));
ImGui::Spacing();
ImGui::LabelText("##label", "Friends");
if (!friends.empty())
{
ImGui::ListBoxHeader("##label", friend_size);
std::for_each(friends.begin(), friends.end(), [this](std::pair<Friend const, friend_window_state> &i)
{
ImGui::PushID(i.second.id-base_friend_window_id+base_friend_item_id);
ImGui::Selectable(i.second.window_title.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick);
BuildContextMenu(i.first, i.second);
if (ImGui::IsItemClicked() && ImGui::IsMouseDoubleClicked(0))
{
i.second.window_state |= window_state_show;
}
ImGui::PopID();
BuildFriendWindow(i.first, i.second);
});
ImGui::ListBoxFooter();
}
}
ImGui::End();
ImGui::PopFont();
if (!show)
ShowOverlay(false);
}// if(show_overlay)
}
void Steam_Overlay::Callback(Common_Message *msg)
{
if (msg->has_steam_messages())
{
Friend frd;
frd.set_id(msg->source_id());
auto friend_info = friends.find(frd);
if (friend_info != friends.end())
{
Steam_Messages const& steam_message = msg->steam_messages();
// Change color to cyan for friend
friend_info->second.chat_history.append("\x1""00FFFFFF", 9).append(steam_message.message()).append("\n", 1);
if (!(friend_info->second.window_state & window_state_show))
{
friend_info->second.window_state |= window_state_need_attention;
}
AddMessageNotification(friend_info->first.name() + " says: " + steam_message.message());
NotifyUser(friend_info->second);
}
}
}
void Steam_Overlay::RunCallbacks()
{
if (overlay_state_changed)
{
GameOverlayActivated_t data = { 0 };
data.m_bActive = show_overlay;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
overlay_state_changed = false;
}
Steam_Friends* steamFriends = get_steam_client()->steam_friends;
Steam_Matchmaking* steamMatchmaking = get_steam_client()->steam_matchmaking;
while (!has_friend_action.empty())
{
auto friend_info = friends.find(has_friend_action.front());
if (friend_info != friends.end())
{
uint64 friend_id = friend_info->first.id();
// The user clicked on "Send"
if (friend_info->second.window_state & window_state_send_message)
{
char* input = friend_info->second.chat_input;
char* end_input = input + strlen(input);
char* printable_char = std::find_if(input, end_input, [](char c) {
return std::isgraph(c);
});
// Check if the message contains something else than blanks
if (printable_char != end_input)
{
// Handle chat send
Common_Message msg;
Steam_Messages* steam_messages = new Steam_Messages;
steam_messages->set_type(Steam_Messages::FRIEND_CHAT);
steam_messages->set_message(friend_info->second.chat_input);
msg.set_allocated_steam_messages(steam_messages);
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_dest_id(friend_id);
network->sendTo(&msg, true);
friend_info->second.chat_history.append("\x1""00FF00FF", 9).append(input).append("\n", 1);
}
*input = 0; // Reset the input field
friend_info->second.window_state &= ~window_state_send_message;
}
// The user clicked on "Invite"
if (friend_info->second.window_state & window_state_invite)
{
std::string connect = steamFriends->GetFriendRichPresence(settings->get_local_steam_id(), "connect");
if (connect.length() > 0)
steamFriends->InviteUserToGame(friend_id, connect.c_str());
else if (settings->get_lobby().IsValid())
steamMatchmaking->InviteUserToLobby(settings->get_lobby(), friend_id);
friend_info->second.window_state &= ~window_state_invite;
}
// The user clicked on "Join"
if (friend_info->second.window_state & window_state_join)
{
std::string connect = steamFriends->GetFriendRichPresence(friend_id, "connect");
if (connect.length() > 0)
{
GameRichPresenceJoinRequested_t data = {};
data.m_steamIDFriend.SetFromUint64(friend_id);
strncpy(data.m_rgchConnect, connect.c_str(), k_cchMaxRichPresenceValueLength - 1);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
else
{
FriendGameInfo_t friend_game_info = {};
steamFriends->GetFriendGamePlayed(friend_id, &friend_game_info);
if (friend_game_info.m_steamIDLobby.IsValid())
{
// TODO: Look if thats really JoinLobby ?
// I saw on steamdev that overlay sends a GameLobbyJoinRequested_t
//GameLobbyJoinRequested_t data;
//data.m_steamIDLobby.SetFromUint64(friend_info->second.lobbyId);
//data.m_steamIDFriend.SetFromUint64(friend_id);
//callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
steamMatchmaking->JoinLobby(friend_game_info.m_steamIDLobby);
}
}
friend_info->second.window_state &= ~window_state_join;
}
// The user got a lobby invite and accepted it
if (friend_info->second.window_state & window_state_lobby_invite)
{
GameLobbyJoinRequested_t data;
data.m_steamIDLobby.SetFromUint64(friend_info->second.lobbyId);
data.m_steamIDFriend.SetFromUint64(friend_id);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
friend_info->second.window_state &= ~window_state_lobby_invite;
}
// The user got a rich presence invite and accepted it
if (friend_info->second.window_state & window_state_rich_invite)
{
GameRichPresenceJoinRequested_t data = {};
data.m_steamIDFriend.SetFromUint64(friend_id);
strncpy(data.m_rgchConnect, friend_info->second.connect, k_cchMaxRichPresenceValueLength - 1);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
friend_info->second.window_state &= ~window_state_rich_invite;
}
}
has_friend_action.pop();
}
}
#endif

View File

@ -0,0 +1,194 @@
#ifndef __INCLUDED_STEAM_OVERLAY_H__
#define __INCLUDED_STEAM_OVERLAY_H__
#include "../dll/base.h"
#include <map>
#include <queue>
static constexpr size_t max_chat_len = 768;
enum window_state
{
window_state_none = 0,
window_state_show = 1<<0,
window_state_invite = 1<<1,
window_state_join = 1<<2,
window_state_lobby_invite = 1<<3,
window_state_rich_invite = 1<<4,
window_state_send_message = 1<<5,
window_state_need_attention = 1<<6,
};
struct friend_window_state
{
int id;
uint8 window_state;
std::string window_title;
union // The invitation (if any)
{
uint64 lobbyId;
char connect[k_cchMaxRichPresenceValueLength];
};
std::string chat_history;
char chat_input[max_chat_len];
};
struct Friend_Less
{
bool operator()(const Friend& lhs, const Friend& rhs) const
{
return lhs.id() < rhs.id();
}
};
enum notification_type
{
notification_type_message = 0,
notification_type_invite,
notification_type_achievement,
};
struct Notification
{
static constexpr float width = 0.25;
static constexpr float height = 5.0;
static constexpr std::chrono::milliseconds fade_in = std::chrono::milliseconds(2000);
static constexpr std::chrono::milliseconds fade_out = std::chrono::milliseconds(2000);
static constexpr std::chrono::milliseconds show_time = std::chrono::milliseconds(6000) + fade_in + fade_out;
static constexpr std::chrono::milliseconds fade_out_start = show_time - fade_out;
static constexpr float r = 0.16;
static constexpr float g = 0.29;
static constexpr float b = 0.48;
static constexpr float max_alpha = 0.5f;
int id;
uint8 type;
std::chrono::seconds start_time;
std::string message;
std::pair<const Friend, friend_window_state>* frd;
};
#ifndef NO_OVERLAY
class Steam_Overlay
{
Settings* settings;
SteamCallResults* callback_results;
SteamCallBacks* callbacks;
RunEveryRunCB* run_every_runcb;
Networking* network;
// friend id, show client window (to chat and accept invite maybe)
std::map<Friend, friend_window_state, Friend_Less> friends;
bool setup_overlay_called;
bool is_ready;
bool show_overlay;
ENotificationPosition notif_position;
int h_inset, v_inset;
// Callback infos
std::queue<Friend> has_friend_action;
std::vector<Notification> notifications;
bool overlay_state_changed;
Steam_Overlay(Steam_Overlay const&) = delete;
Steam_Overlay(Steam_Overlay&&) = delete;
Steam_Overlay& operator=(Steam_Overlay const&) = delete;
Steam_Overlay& operator=(Steam_Overlay&&) = delete;
static void steam_overlay_run_every_runcb(void* object);
static void steam_overlay_callback(void* object, Common_Message* msg);
void Callback(Common_Message* msg);
void RunCallbacks();
bool FriendHasLobby(uint64 friend_id);
bool IHaveLobby();
void NotifyUser(friend_window_state& friend_state);
// Right click on friend
void BuildContextMenu(Friend const& frd, friend_window_state &state);
// Double click on friend
void BuildFriendWindow(Friend const& frd, friend_window_state &state);
// Notifications like achievements, chat and invitations
void BuildNotifications(int width, int height);
public:
Steam_Overlay(Settings* settings, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Networking *network);
~Steam_Overlay();
bool Ready() const;
bool NeedPresent() const;
void SetNotificationPosition(ENotificationPosition eNotificationPosition);
void SetNotificationInset(int nHorizontalInset, int nVerticalInset);
void SetupOverlay();
void HookReady();
void CreateFonts();
void OverlayProc();
void OpenOverlayInvite(CSteamID lobbyId);
void OpenOverlay(const char* pchDialog);
bool ShowOverlay() const;
void ShowOverlay(bool state);
void SetLobbyInvite(Friend friendId, uint64 lobbyId);
void SetRichInvite(Friend friendId, const char* connect_str);
void FriendConnect(Friend _friend);
void FriendDisconnect(Friend _friend);
void AddMessageNotification(std::string const& message);
void AddAchievementNotification(nlohmann::json const& ach);
void AddInviteNotification(std::pair<const Friend, friend_window_state> &wnd_state);
};
#else
class Steam_Overlay
{
public:
Steam_Overlay(Settings* settings, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Networking* network) {}
~Steam_Overlay() {}
bool Ready() const { return false; }
bool NeedPresent() const { return false; }
void SetNotificationPosition(ENotificationPosition eNotificationPosition) {}
void SetNotificationInset(int nHorizontalInset, int nVerticalInset) {}
void SetupOverlay() {}
void HookReady() {}
void CreateFonts() {}
void OverlayProc() {}
void OpenOverlayInvite(CSteamID lobbyId) {}
void OpenOverlay(const char* pchDialog) {}
bool ShowOverlay() const {}
void ShowOverlay(bool state) {}
void SetLobbyInvite(Friend friendId, uint64 lobbyId) {}
void SetRichInvite(Friend friendId, const char* connect_str) {}
void FriendConnect(Friend _friend) {}
void FriendDisconnect(Friend _friend) {}
void AddMessageNotification(std::string const& message) {}
void AddAchievementNotification(nlohmann::json const& ach) {}
void AddInviteNotification(std::pair<const Friend, friend_window_state> &wnd_state) {}
};
#endif
#endif//__INCLUDED_STEAM_OVERLAY_H__

View File

@ -0,0 +1,172 @@
#include "DX10_Hook.h"
#include "Windows_Hook.h"
#include "../Renderer_Detector.h"
#include "../../dll/dll.h"
#ifndef NO_OVERLAY
#include <imgui.h>
#include <impls/windows/imgui_impl_dx10.h>
DX10_Hook* DX10_Hook::_inst = nullptr;
bool DX10_Hook::start_hook()
{
bool res = true;
if (!hooked)
{
if (!Windows_Hook::Inst()->start_hook())
return false;
PRINT_DEBUG("Hooked DirectX 10\n");
hooked = true;
Renderer_Detector::Inst().renderer_found(this);
BeginHook();
HookFuncs(
std::make_pair<void**, void*>(&(PVOID&)DX10_Hook::Present, &DX10_Hook::MyPresent),
std::make_pair<void**, void*>(&(PVOID&)DX10_Hook::ResizeTarget, &DX10_Hook::MyResizeTarget),
std::make_pair<void**, void*>(&(PVOID&)DX10_Hook::ResizeBuffers, &DX10_Hook::MyResizeBuffers)
);
EndHook();
get_steam_client()->steam_overlay->HookReady();
}
return res;
}
void DX10_Hook::resetRenderState()
{
if (initialized)
{
mainRenderTargetView->Release();
ImGui_ImplDX10_Shutdown();
Windows_Hook::Inst()->resetRenderState();
ImGui::DestroyContext();
initialized = false;
}
}
// Try to make this function and overlay's proc as short as possible or it might affect game's fps.
void DX10_Hook::prepareForOverlay(IDXGISwapChain* pSwapChain)
{
DXGI_SWAP_CHAIN_DESC desc;
pSwapChain->GetDesc(&desc);
if (!initialized)
{
if (FAILED(pSwapChain->GetDevice(IID_PPV_ARGS(&pDevice))))
return;
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = NULL;
ID3D10Texture2D* pBackBuffer;
pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
pDevice->CreateRenderTargetView(pBackBuffer, NULL, &mainRenderTargetView);
pBackBuffer->Release();
ImGui_ImplDX10_Init(pDevice);
pDevice->Release();
get_steam_client()->steam_overlay->CreateFonts();
initialized = true;
}
if (ImGui_ImplDX10_NewFrame())
{
Windows_Hook::Inst()->prepareForOverlay(desc.OutputWindow);
ImGui::NewFrame();
get_steam_client()->steam_overlay->OverlayProc();
ImGui::Render();
pDevice->OMSetRenderTargets(1, &mainRenderTargetView, NULL);
ImGui_ImplDX10_RenderDrawData(ImGui::GetDrawData());
}
}
HRESULT STDMETHODCALLTYPE DX10_Hook::MyPresent(IDXGISwapChain *_this, UINT SyncInterval, UINT Flags)
{
DX10_Hook::Inst()->prepareForOverlay(_this);
return (_this->*DX10_Hook::Inst()->Present)(SyncInterval, Flags);
}
HRESULT STDMETHODCALLTYPE DX10_Hook::MyResizeTarget(IDXGISwapChain* _this, const DXGI_MODE_DESC* pNewTargetParameters)
{
DX10_Hook::Inst()->resetRenderState();
return (_this->*DX10_Hook::Inst()->ResizeTarget)(pNewTargetParameters);
}
HRESULT STDMETHODCALLTYPE DX10_Hook::MyResizeBuffers(IDXGISwapChain* _this, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags)
{
DX10_Hook::Inst()->resetRenderState();
return (_this->*DX10_Hook::Inst()->ResizeBuffers)(BufferCount, Width, Height, NewFormat, SwapChainFlags);
}
DX10_Hook::DX10_Hook():
initialized(false),
hooked(false),
pDevice(nullptr),
mainRenderTargetView(nullptr),
Present(nullptr),
ResizeBuffers(nullptr),
ResizeTarget(nullptr)
{
_library = LoadLibrary(DLL_NAME);
}
DX10_Hook::~DX10_Hook()
{
PRINT_DEBUG("DX10 Hook removed\n");
if (initialized)
{
mainRenderTargetView->Release();
ImGui_ImplDX10_InvalidateDeviceObjects();
ImGui::DestroyContext();
initialized = false;
}
FreeLibrary(reinterpret_cast<HMODULE>(_library));
_inst = nullptr;
}
DX10_Hook* DX10_Hook::Inst()
{
if (_inst == nullptr)
_inst = new DX10_Hook;
return _inst;
}
const char* DX10_Hook::get_lib_name() const
{
return DLL_NAME;
}
void DX10_Hook::loadFunctions(IDXGISwapChain *pSwapChain)
{
void** vTable;
vTable = *reinterpret_cast<void***>(pSwapChain);
#define LOAD_FUNC(X) (void*&)X = vTable[(int)IDXGISwapChainVTable::X]
LOAD_FUNC(Present);
LOAD_FUNC(ResizeBuffers);
LOAD_FUNC(ResizeTarget);
#undef LOAD_FUNC
}
#endif//NO_OVERLAY

View File

@ -0,0 +1,51 @@
#ifndef __INCLUDED_DX10_HOOK_H__
#define __INCLUDED_DX10_HOOK_H__
#include "../Base_Hook.h"
#ifndef NO_OVERLAY
#include <d3d10.h>
#include "DirectX_VTables.h"
class DX10_Hook : public Base_Hook
{
public:
static constexpr const char *DLL_NAME = "d3d10.dll";
private:
static DX10_Hook* _inst;
// Variables
bool hooked;
bool initialized;
ID3D10Device* pDevice;
ID3D10RenderTargetView* mainRenderTargetView;
// Functions
DX10_Hook();
void resetRenderState();
void prepareForOverlay(IDXGISwapChain *pSwapChain);
// Hook to render functions
static HRESULT STDMETHODCALLTYPE MyPresent(IDXGISwapChain* _this, UINT SyncInterval, UINT Flags);
static HRESULT STDMETHODCALLTYPE MyResizeTarget(IDXGISwapChain* _this, const DXGI_MODE_DESC* pNewTargetParameters);
static HRESULT STDMETHODCALLTYPE MyResizeBuffers(IDXGISwapChain* _this, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags);
decltype(&IDXGISwapChain::Present) Present;
decltype(&IDXGISwapChain::ResizeBuffers) ResizeBuffers;
decltype(&IDXGISwapChain::ResizeTarget) ResizeTarget;
public:
virtual ~DX10_Hook();
bool start_hook();
static DX10_Hook* Inst();
virtual const char* get_lib_name() const;
void loadFunctions(IDXGISwapChain *pSwapChain);
};
#endif//NO_OVERLAY
#endif//__INCLUDED_DX10_HOOK_H__

View File

@ -0,0 +1,186 @@
#include "DX11_Hook.h"
#include "Windows_Hook.h"
#include "../Renderer_Detector.h"
#include "../../dll/dll.h"
#ifndef NO_OVERLAY
#include <imgui.h>
#include <impls/windows/imgui_impl_dx11.h>
DX11_Hook* DX11_Hook::_inst = nullptr;
HRESULT GetDeviceAndCtxFromSwapchain(IDXGISwapChain* pSwapChain, ID3D11Device** ppDevice, ID3D11DeviceContext** ppContext)
{
HRESULT ret = pSwapChain->GetDevice(IID_PPV_ARGS(ppDevice));
if (SUCCEEDED(ret))
(*ppDevice)->GetImmediateContext(ppContext);
return ret;
}
bool DX11_Hook::start_hook()
{
bool res = true;
if (!hooked)
{
if (!Windows_Hook::Inst()->start_hook())
return false;
PRINT_DEBUG("Hooked DirectX 11\n");
hooked = true;
Renderer_Detector::Inst().renderer_found(this);
BeginHook();
HookFuncs(
std::make_pair<void**, void*>(&(PVOID&)DX11_Hook::Present, &DX11_Hook::MyPresent),
std::make_pair<void**, void*>(&(PVOID&)DX11_Hook::ResizeTarget, &DX11_Hook::MyResizeTarget),
std::make_pair<void**, void*>(&(PVOID&)DX11_Hook::ResizeBuffers, &DX11_Hook::MyResizeBuffers)
);
EndHook();
get_steam_client()->steam_overlay->HookReady();
}
return res;
}
void DX11_Hook::resetRenderState()
{
if (initialized)
{
mainRenderTargetView->Release();
pContext->Release();
ImGui_ImplDX11_Shutdown();
Windows_Hook::Inst()->resetRenderState();
ImGui::DestroyContext();
initialized = false;
}
}
// Try to make this function and overlay's proc as short as possible or it might affect game's fps.
void DX11_Hook::prepareForOverlay(IDXGISwapChain* pSwapChain)
{
DXGI_SWAP_CHAIN_DESC desc;
pSwapChain->GetDesc(&desc);
if (!initialized)
{
ID3D11Device* pDevice = nullptr;
if (FAILED(GetDeviceAndCtxFromSwapchain(pSwapChain, &pDevice, &pContext)))
return;
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = NULL;
ID3D11Texture2D* pBackBuffer;
pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
pDevice->CreateRenderTargetView(pBackBuffer, NULL, &mainRenderTargetView);
pBackBuffer->Release();
ImGui_ImplDX11_Init(pDevice, pContext);
pDevice->Release();
get_steam_client()->steam_overlay->CreateFonts();
initialized = true;
}
if (ImGui_ImplDX11_NewFrame())
{
Windows_Hook::Inst()->prepareForOverlay(desc.OutputWindow);
ImGui::NewFrame();
get_steam_client()->steam_overlay->OverlayProc();
ImGui::Render();
pContext->OMSetRenderTargets(1, &mainRenderTargetView, NULL);
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
}
HRESULT STDMETHODCALLTYPE DX11_Hook::MyPresent(IDXGISwapChain *_this, UINT SyncInterval, UINT Flags)
{
DX11_Hook::Inst()->prepareForOverlay(_this);
return (_this->*DX11_Hook::Inst()->Present)(SyncInterval, Flags);
}
HRESULT STDMETHODCALLTYPE DX11_Hook::MyResizeTarget(IDXGISwapChain* _this, const DXGI_MODE_DESC* pNewTargetParameters)
{
DX11_Hook::Inst()->resetRenderState();
return (_this->*DX11_Hook::Inst()->ResizeTarget)(pNewTargetParameters);
}
HRESULT STDMETHODCALLTYPE DX11_Hook::MyResizeBuffers(IDXGISwapChain* _this, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags)
{
DX11_Hook::Inst()->resetRenderState();
return (_this->*DX11_Hook::Inst()->ResizeBuffers)(BufferCount, Width, Height, NewFormat, SwapChainFlags);
}
DX11_Hook::DX11_Hook():
initialized(false),
hooked(false),
pContext(nullptr),
mainRenderTargetView(nullptr),
Present(nullptr),
ResizeBuffers(nullptr),
ResizeTarget(nullptr)
{
_library = LoadLibrary(DLL_NAME);
}
DX11_Hook::~DX11_Hook()
{
PRINT_DEBUG("DX11 Hook removed\n");
if (initialized)
{
mainRenderTargetView->Release();
pContext->Release();
ImGui_ImplDX11_InvalidateDeviceObjects();
ImGui::DestroyContext();
initialized = false;
}
FreeLibrary(reinterpret_cast<HMODULE>(_library));
_inst = nullptr;
}
DX11_Hook* DX11_Hook::Inst()
{
if (_inst == nullptr)
_inst = new DX11_Hook;
return _inst;
}
const char* DX11_Hook::get_lib_name() const
{
return DLL_NAME;
}
void DX11_Hook::loadFunctions(IDXGISwapChain *pSwapChain)
{
void** vTable;
vTable = *reinterpret_cast<void***>(pSwapChain);
#define LOAD_FUNC(X) (void*&)X = vTable[(int)IDXGISwapChainVTable::X]
LOAD_FUNC(Present);
LOAD_FUNC(ResizeBuffers);
LOAD_FUNC(ResizeTarget);
#undef LOAD_FUNC
}
#endif//NO_OVERLAY

View File

@ -0,0 +1,51 @@
#ifndef __INCLUDED_DX11_HOOK_H__
#define __INCLUDED_DX11_HOOK_H__
#include "../Base_Hook.h"
#ifndef NO_OVERLAY
#include <d3d11.h>
#include "DirectX_VTables.h"
class DX11_Hook : public Base_Hook
{
public:
static constexpr const char *DLL_NAME = "d3d11.dll";
private:
static DX11_Hook* _inst;
// Variables
bool hooked;
bool initialized;
ID3D11DeviceContext* pContext;
ID3D11RenderTargetView* mainRenderTargetView;
// Functions
DX11_Hook();
void resetRenderState();
void prepareForOverlay(IDXGISwapChain* pSwapChain);
// Hook to render functions
static HRESULT STDMETHODCALLTYPE MyPresent(IDXGISwapChain* _this, UINT SyncInterval, UINT Flags);
static HRESULT STDMETHODCALLTYPE MyResizeTarget(IDXGISwapChain* _this, const DXGI_MODE_DESC* pNewTargetParameters);
static HRESULT STDMETHODCALLTYPE MyResizeBuffers(IDXGISwapChain* _this, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags);
decltype(&IDXGISwapChain::Present) Present;
decltype(&IDXGISwapChain::ResizeBuffers) ResizeBuffers;
decltype(&IDXGISwapChain::ResizeTarget) ResizeTarget;
public:
virtual ~DX11_Hook();
bool start_hook();
static DX11_Hook* Inst();
virtual const char* get_lib_name() const;
void loadFunctions(IDXGISwapChain *pSwapChain);
};
#endif//NO_OVERLAY
#endif//__INCLUDED_DX11_HOOK_H__

View File

@ -0,0 +1,319 @@
#include "DX12_Hook.h"
#include "Windows_Hook.h"
#include "../Renderer_Detector.h"
#include "../../dll/dll.h"
#ifndef NO_OVERLAY
#include <imgui.h>
#include <impls/windows/imgui_impl_dx12.h>
#include <dxgi1_4.h>
DX12_Hook* DX12_Hook::_inst = nullptr;
bool DX12_Hook::start_hook()
{
bool res = true;
if (!hooked)
{
if (!Windows_Hook::Inst()->start_hook())
return false;
PRINT_DEBUG("Hooked DirectX 12\n");
hooked = true;
Renderer_Detector::Inst().renderer_found(this);
BeginHook();
HookFuncs(
std::make_pair<void**, void*>(&(PVOID&)DX12_Hook::Present, &DX12_Hook::MyPresent),
std::make_pair<void**, void*>(&(PVOID&)DX12_Hook::ResizeTarget, &DX12_Hook::MyResizeTarget),
std::make_pair<void**, void*>(&(PVOID&)DX12_Hook::ResizeBuffers, &DX12_Hook::MyResizeBuffers),
std::make_pair<void**, void*>(&(PVOID&)DX12_Hook::ExecuteCommandLists, &DX12_Hook::MyExecuteCommandLists)
);
EndHook();
get_steam_client()->steam_overlay->HookReady();
}
return res;
}
void DX12_Hook::resetRenderState()
{
if (initialized)
{
pSrvDescHeap->Release();
for (UINT i = 0; i < bufferCount; ++i)
{
pCmdAlloc[i]->Release();
pBackBuffer[i]->Release();
}
pRtvDescHeap->Release();
delete[]pCmdAlloc;
delete[]pBackBuffer;
ImGui_ImplDX12_Shutdown();
Windows_Hook::Inst()->resetRenderState();
ImGui::DestroyContext();
initialized = false;
}
}
// Try to make this function and overlay's proc as short as possible or it might affect game's fps.
void DX12_Hook::prepareForOverlay(IDXGISwapChain* pSwapChain)
{
if (pCmdQueue == nullptr)
return;
ID3D12CommandQueue* pCmdQueue = this->pCmdQueue;
IDXGISwapChain3* pSwapChain3 = nullptr;
DXGI_SWAP_CHAIN_DESC sc_desc;
pSwapChain->QueryInterface(IID_PPV_ARGS(&pSwapChain3));
if (pSwapChain3 == nullptr)
return;
pSwapChain3->GetDesc(&sc_desc);
if (!initialized)
{
UINT bufferIndex = pSwapChain3->GetCurrentBackBufferIndex();
ID3D12Device* pDevice;
if (pSwapChain3->GetDevice(IID_PPV_ARGS(&pDevice)) != S_OK)
return;
bufferCount = sc_desc.BufferCount;
mainRenderTargets.clear();
{
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
desc.NumDescriptors = 1;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
if (pDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&pSrvDescHeap)) != S_OK)
{
pDevice->Release();
pSwapChain3->Release();
return;
}
}
{
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
desc.NumDescriptors = bufferCount;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
desc.NodeMask = 1;
if (pDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&pRtvDescHeap)) != S_OK)
{
pDevice->Release();
pSwapChain3->Release();
pSrvDescHeap->Release();
return;
}
SIZE_T rtvDescriptorSize = pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = pRtvDescHeap->GetCPUDescriptorHandleForHeapStart();
pCmdAlloc = new ID3D12CommandAllocator * [bufferCount];
for (int i = 0; i < bufferCount; ++i)
{
mainRenderTargets.push_back(rtvHandle);
rtvHandle.ptr += rtvDescriptorSize;
}
}
for (UINT i = 0; i < sc_desc.BufferCount; ++i)
{
if (pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&pCmdAlloc[i])) != S_OK)
{
pDevice->Release();
pSwapChain3->Release();
pSrvDescHeap->Release();
for (UINT j = 0; j < i; ++j)
{
pCmdAlloc[j]->Release();
}
pRtvDescHeap->Release();
delete[]pCmdAlloc;
return;
}
}
if (pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, pCmdAlloc[0], NULL, IID_PPV_ARGS(&pCmdList)) != S_OK ||
pCmdList->Close() != S_OK)
{
pDevice->Release();
pSwapChain3->Release();
pSrvDescHeap->Release();
for (UINT i = 0; i < bufferCount; ++i)
pCmdAlloc[i]->Release();
pRtvDescHeap->Release();
delete[]pCmdAlloc;
return;
}
pBackBuffer = new ID3D12Resource * [bufferCount];
for (UINT i = 0; i < bufferCount; i++)
{
pSwapChain3->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer[i]));
pDevice->CreateRenderTargetView(pBackBuffer[i], NULL, mainRenderTargets[i]);
}
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = NULL;
ImGui_ImplDX12_Init(pDevice, bufferCount, DXGI_FORMAT_R8G8B8A8_UNORM,
pSrvDescHeap->GetCPUDescriptorHandleForHeapStart(),
pSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
get_steam_client()->steam_overlay->CreateFonts();
initialized = true;
pDevice->Release();
}
if (ImGui_ImplDX12_NewFrame())
{
Windows_Hook::Inst()->prepareForOverlay(sc_desc.OutputWindow);
ImGui::NewFrame();
get_steam_client()->steam_overlay->OverlayProc();
UINT bufferIndex = pSwapChain3->GetCurrentBackBufferIndex();
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = pBackBuffer[bufferIndex];
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
pCmdAlloc[bufferIndex]->Reset();
pCmdList->Reset(pCmdAlloc[bufferIndex], NULL);
pCmdList->ResourceBarrier(1, &barrier);
pCmdList->OMSetRenderTargets(1, &mainRenderTargets[bufferIndex], FALSE, NULL);
pCmdList->SetDescriptorHeaps(1, &pSrvDescHeap);
ImGui::Render();
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), pCmdList);
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
pCmdList->ResourceBarrier(1, &barrier);
pCmdList->Close();
pCmdQueue->ExecuteCommandLists(1, (ID3D12CommandList**)&pCmdList);
}
pSwapChain3->Release();
}
HRESULT STDMETHODCALLTYPE DX12_Hook::MyPresent(IDXGISwapChain *_this, UINT SyncInterval, UINT Flags)
{
DX12_Hook::Inst()->prepareForOverlay(_this);
return (_this->*DX12_Hook::Inst()->Present)(SyncInterval, Flags);
}
HRESULT STDMETHODCALLTYPE DX12_Hook::MyResizeTarget(IDXGISwapChain* _this, const DXGI_MODE_DESC* pNewTargetParameters)
{
DX12_Hook::Inst()->resetRenderState();
return (_this->*DX12_Hook::Inst()->ResizeTarget)(pNewTargetParameters);
}
HRESULT STDMETHODCALLTYPE DX12_Hook::MyResizeBuffers(IDXGISwapChain* _this, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags)
{
DX12_Hook::Inst()->resetRenderState();
return (_this->*DX12_Hook::Inst()->ResizeBuffers)(BufferCount, Width, Height, NewFormat, SwapChainFlags);
}
void STDMETHODCALLTYPE DX12_Hook::MyExecuteCommandLists(ID3D12CommandQueue *_this, UINT NumCommandLists, ID3D12CommandList* const* ppCommandLists)
{
DX12_Hook* me = DX12_Hook::Inst();
me->pCmdQueue = _this;
(_this->*DX12_Hook::Inst()->ExecuteCommandLists)(NumCommandLists, ppCommandLists);
}
DX12_Hook::DX12_Hook():
initialized(false),
pCmdQueue(nullptr),
bufferCount(0),
pCmdAlloc(nullptr),
pSrvDescHeap(nullptr),
pCmdList(nullptr),
pRtvDescHeap(nullptr),
hooked(false),
Present(nullptr),
ResizeBuffers(nullptr),
ResizeTarget(nullptr),
ExecuteCommandLists(nullptr)
{
_library = LoadLibrary(DLL_NAME);
PRINT_DEBUG("DX12 support is experimental, don't complain if it doesn't work as expected.\n");
}
DX12_Hook::~DX12_Hook()
{
PRINT_DEBUG("DX12 Hook removed\n");
if (initialized)
{
pSrvDescHeap->Release();
for (UINT i = 0; i < bufferCount; ++i)
{
pCmdAlloc[i]->Release();
pBackBuffer[i]->Release();
}
pRtvDescHeap->Release();
delete[]pCmdAlloc;
delete[]pBackBuffer;
ImGui_ImplDX12_InvalidateDeviceObjects();
ImGui::DestroyContext();
initialized = false;
}
FreeLibrary(reinterpret_cast<HMODULE>(_library));
_inst = nullptr;
}
DX12_Hook* DX12_Hook::Inst()
{
if (_inst == nullptr)
_inst = new DX12_Hook();
return _inst;
}
const char* DX12_Hook::get_lib_name() const
{
return DLL_NAME;
}
void DX12_Hook::loadFunctions(ID3D12CommandQueue* pCommandQueue, IDXGISwapChain *pSwapChain)
{
void** vTable;
vTable = *reinterpret_cast<void***>(pCommandQueue);
#define LOAD_FUNC(X) (void*&)X = vTable[(int)ID3D12CommandQueueVTable::X]
LOAD_FUNC(ExecuteCommandLists);
#undef LOAD_FUNC
vTable = *reinterpret_cast<void***>(pSwapChain);
#define LOAD_FUNC(X) (void*&)X = vTable[(int)IDXGISwapChainVTable::X]
LOAD_FUNC(Present);
LOAD_FUNC(ResizeBuffers);
LOAD_FUNC(ResizeTarget);
#undef LOAD_FUNC
}
#endif//NO_OVERLAY

View File

@ -0,0 +1,60 @@
#ifndef __INCLUDED_DX12_HOOK_H__
#define __INCLUDED_DX12_HOOK_H__
#include "../Base_Hook.h"
#ifndef NO_OVERLAY
#include <d3d12.h>
#include <dxgi1_4.h>
#include "DirectX_VTables.h"
class DX12_Hook : public Base_Hook
{
public:
static constexpr const char *DLL_NAME = "d3d12.dll";
private:
static DX12_Hook* _inst;
// Variables
bool hooked;
bool initialized;
ID3D12CommandQueue* pCmdQueue;
UINT bufferCount;
std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> mainRenderTargets;
ID3D12CommandAllocator** pCmdAlloc;
ID3D12DescriptorHeap* pSrvDescHeap;
ID3D12GraphicsCommandList* pCmdList;
ID3D12DescriptorHeap* pRtvDescHeap;
ID3D12Resource** pBackBuffer;
// Functions
DX12_Hook();
void resetRenderState();
void prepareForOverlay(IDXGISwapChain* pSwapChain);
// Hook to render functions
static HRESULT STDMETHODCALLTYPE MyPresent(IDXGISwapChain* _this, UINT SyncInterval, UINT Flags);
static HRESULT STDMETHODCALLTYPE MyResizeTarget(IDXGISwapChain* _this, const DXGI_MODE_DESC* pNewTargetParameters);
static HRESULT STDMETHODCALLTYPE MyResizeBuffers(IDXGISwapChain* _this, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags);
static void STDMETHODCALLTYPE MyExecuteCommandLists(ID3D12CommandQueue *_this, UINT NumCommandLists, ID3D12CommandList* const* ppCommandLists);
decltype(&IDXGISwapChain::Present) Present;
decltype(&IDXGISwapChain::ResizeBuffers) ResizeBuffers;
decltype(&IDXGISwapChain::ResizeTarget) ResizeTarget;
decltype(&ID3D12CommandQueue::ExecuteCommandLists) ExecuteCommandLists;
public:
virtual ~DX12_Hook();
bool start_hook();
static DX12_Hook* Inst();
virtual const char* get_lib_name() const;
void loadFunctions(ID3D12CommandQueue* pCommandQueue, IDXGISwapChain* pSwapChain);
};
#endif//NO_OVERLAY
#endif//__INCLUDED_DX12_HOOK_H__

View File

@ -0,0 +1,172 @@
#include "DX9_Hook.h"
#include "Windows_Hook.h"
#include "../Renderer_Detector.h"
#include "../../dll/dll.h"
#ifndef NO_OVERLAY
#include <imgui.h>
#include <impls/windows/imgui_impl_dx9.h>
DX9_Hook* DX9_Hook::_inst = nullptr;
bool DX9_Hook::start_hook()
{
if (!hooked)
{
if (!Windows_Hook::Inst()->start_hook())
return false;
PRINT_DEBUG("Hooked DirectX 9\n");
hooked = true;
Renderer_Detector::Inst().renderer_found(this);
BeginHook();
HookFuncs(
std::make_pair<void**, void*>(&(PVOID&)Reset, &DX9_Hook::MyReset),
std::make_pair<void**, void*>(&(PVOID&)Present, &DX9_Hook::MyPresent)
);
if (PresentEx != nullptr)
{
HookFuncs(
std::make_pair<void**, void*>(&(PVOID&)PresentEx, &DX9_Hook::MyPresentEx)
//std::make_pair<void**, void*>(&(PVOID&)EndScene, &DX9_Hook::MyEndScene)
);
}
EndHook();
get_steam_client()->steam_overlay->HookReady();
}
return true;
}
void DX9_Hook::resetRenderState()
{
if (initialized)
{
initialized = false;
ImGui_ImplDX9_Shutdown();
Windows_Hook::Inst()->resetRenderState();
ImGui::DestroyContext();
}
}
// Try to make this function and overlay's proc as short as possible or it might affect game's fps.
void DX9_Hook::prepareForOverlay(IDirect3DDevice9 *pDevice)
{
D3DDEVICE_CREATION_PARAMETERS param;
pDevice->GetCreationParameters(&param);
// Workaround to detect if we changed window.
if (param.hFocusWindow != Windows_Hook::Inst()->GetGameHwnd())
resetRenderState();
if (!initialized)
{
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = NULL;
ImGui_ImplDX9_Init(pDevice);
get_steam_client()->steam_overlay->CreateFonts();
initialized = true;
}
if (ImGui_ImplDX9_NewFrame())
{
Windows_Hook::Inst()->prepareForOverlay(param.hFocusWindow);
ImGui::NewFrame();
get_steam_client()->steam_overlay->OverlayProc();
ImGui::Render();
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
}
}
HRESULT STDMETHODCALLTYPE DX9_Hook::MyReset(IDirect3DDevice9* _this, D3DPRESENT_PARAMETERS* pPresentationParameters)
{
DX9_Hook::Inst()->resetRenderState();
return (_this->*DX9_Hook::Inst()->Reset)(pPresentationParameters);
}
HRESULT STDMETHODCALLTYPE DX9_Hook::MyEndScene(IDirect3DDevice9* _this)
{
if( !DX9_Hook::Inst()->uses_present )
DX9_Hook::Inst()->prepareForOverlay(_this);
return (_this->*DX9_Hook::Inst()->EndScene)();
}
HRESULT STDMETHODCALLTYPE DX9_Hook::MyPresent(IDirect3DDevice9* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion)
{
DX9_Hook::Inst()->uses_present = true;
DX9_Hook::Inst()->prepareForOverlay(_this);
return (_this->*DX9_Hook::Inst()->Present)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
}
HRESULT STDMETHODCALLTYPE DX9_Hook::MyPresentEx(IDirect3DDevice9Ex* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags)
{
DX9_Hook::Inst()->uses_present = true;
DX9_Hook::Inst()->prepareForOverlay(_this);
return (_this->*DX9_Hook::Inst()->PresentEx)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags);
}
DX9_Hook::DX9_Hook():
initialized(false),
hooked(false),
uses_present(false),
EndScene(nullptr),
Present(nullptr),
PresentEx(nullptr),
Reset(nullptr)
{
_library = LoadLibrary(DLL_NAME);
}
DX9_Hook::~DX9_Hook()
{
PRINT_DEBUG("DX9 Hook removed\n");
if (initialized)
{
ImGui_ImplDX9_InvalidateDeviceObjects();
ImGui::DestroyContext();
}
FreeLibrary(reinterpret_cast<HMODULE>(_library));
_inst = nullptr;
}
DX9_Hook* DX9_Hook::Inst()
{
if( _inst == nullptr )
_inst = new DX9_Hook;
return _inst;
}
const char* DX9_Hook::get_lib_name() const
{
return DLL_NAME;
}
void DX9_Hook::loadFunctions(IDirect3DDevice9* pDevice, bool ex)
{
void** vTable = *reinterpret_cast<void***>(pDevice);
#define LOAD_FUNC(X) (void*&)X = vTable[(int)IDirect3DDevice9VTable::X]
LOAD_FUNC(Reset);
LOAD_FUNC(EndScene);
LOAD_FUNC(Present);
if (ex)
LOAD_FUNC(PresentEx);
#undef LOAD_FUNC
}
#endif//NO_OVERLAY

View File

@ -0,0 +1,52 @@
#ifndef __INCLUDED_DX9_HOOK_H__
#define __INCLUDED_DX9_HOOK_H__
#include "../Base_Hook.h"
#ifndef NO_OVERLAY
#include <d3d9.h>
#include "DirectX_VTables.h"
class DX9_Hook : public Base_Hook
{
public:
static constexpr const char *DLL_NAME = "d3d9.dll";
private:
static DX9_Hook* _inst;
// Variables
bool hooked;
bool initialized;
bool uses_present;
// Functions
DX9_Hook();
void resetRenderState();
void prepareForOverlay(IDirect3DDevice9* pDevice);
// Hook to render functions
decltype(&IDirect3DDevice9::Reset) Reset;
decltype(&IDirect3DDevice9::EndScene) EndScene;
decltype(&IDirect3DDevice9::Present) Present;
decltype(&IDirect3DDevice9Ex::PresentEx) PresentEx;
static HRESULT STDMETHODCALLTYPE MyReset(IDirect3DDevice9* _this, D3DPRESENT_PARAMETERS* pPresentationParameters);
static HRESULT STDMETHODCALLTYPE MyEndScene(IDirect3DDevice9 *_this);
static HRESULT STDMETHODCALLTYPE MyPresent(IDirect3DDevice9* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion);
static HRESULT STDMETHODCALLTYPE MyPresentEx(IDirect3DDevice9Ex* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags);
public:
virtual ~DX9_Hook();
bool start_hook();
static DX9_Hook* Inst();
virtual const char* get_lib_name() const;
void loadFunctions(IDirect3DDevice9 *pDevice, bool ex);
};
#endif//NO_OVERLAY
#endif//__INCLUDED_DX9_HOOK_H__

View File

@ -0,0 +1,448 @@
#pragma once
#include <DXGI.h>
enum class IDXGISwapChainVTable
{
// IUnknown
QueryInterface,
AddRef,
Release,
// IDXGIObject
SetPrivateData,
SetPrivateDataInterface,
GetPrivateData,
GetParent,
// IDXGIDeviceSubObject
GetDevice,
// IDXGISwapChain
Present,
GetBuffer,
SetFullscreenState,
GetFullscreenState,
GetDesc,
ResizeBuffers,
ResizeTarget,
GetContainingOutput,
GetFrameStatistics,
GetLastPresentCount,
// IDXGISwapChain1
GetDesc1,
GetFullscreenDesc,
GetHwnd,
GetCoreWindow,
Present1,
IsTemporaryMonoSupported,
GetRestrictToOutput,
SetBackgroundColor,
GetBackgroundColor,
SetRotation,
GetRotation,
};
enum class ID3D12CommandQueueVTable
{
// IUnknown
QueryInterface,
AddRef,
Release,
// ID3D12Object
GetPrivateData,
SetPrivateData,
SetPrivateDataInterface,
SetName,
// ID3D12DeviceChild
GetDevice,
// ID3D12Pageable
// ID3D12CommandQueue
UpdateTileMappings,
CopyTileMappings,
ExecuteCommandLists,
SetMarker,
BeginEvent,
EndEvent,
Signal,
Wait,
GetTimestampFrequency,
GetClockCalibration,
GetDesc,
};
enum class ID3D12GraphicsCommandListVTable
{
// IUnknown
QueryInterface,
AddRef,
Release,
// ID3D12Object
GetPrivateData,
SetPrivateData,
SetPrivateDataInterface,
SetName,
// ID3D12DeviceChild
GetDevice,
// ID3D12CommandList
GetType,
// ID3D12GraphicsCommandList
Close,
Reset,
ClearState,
DrawInstanced,
DrawIndexedInstanced,
Dispatch,
CopyBufferRegion,
CopyTextureRegion,
CopyResource,
CopyTiles,
ResolveSubresource,
IASetPrimitiveTopology,
RSSetViewports,
RSSetScissorRects,
OMSetBlendFactor,
OMSetStencilRef,
SetPipelineState,
ResourceBarrier,
ExecuteBundle,
SetDescriptorHeaps,
SetComputeRootSignature,
SetGraphicsRootSignature,
SetComputeRootDescriptorTable,
SetGraphicsRootDescriptorTable,
SetComputeRoot32BitConstant,
SetGraphicsRoot32BitConstant,
SetComputeRoot32BitConstants,
SetGraphicsRoot32BitConstants,
SetComputeRootConstantBufferView,
SetGraphicsRootConstantBufferView,
SetComputeRootShaderResourceView,
SetGraphicsRootShaderResourceView,
SetComputeRootUnorderedAccessView,
SetGraphicsRootUnorderedAccessView,
IASetIndexBuffer,
IASetVertexBuffers,
SOSetTargets,
OMSetRenderTargets,
ClearDepthStencilView,
ClearRenderTargetView,
ClearUnorderedAccessViewUint,
ClearUnorderedAccessViewFloat,
DiscardResource,
BeginQuery,
EndQuery,
ResolveQueryData,
SetPredication,
SetMarker,
BeginEvent,
EndEvent,
ExecuteIndirect,
};
enum class ID3D11DeviceVTable
{
// IUnknown
QueryInterface,
AddRef,
Release,
// ID3D11Device
CreateBuffer,
CreateTexture1D,
CreateTexture2D,
CreateTexture3D,
CreateShaderResourceView,
CreateUnorderedAccessView,
CreateRenderTargetView,
CreateDepthStencilView,
CreateInputLayout,
CreateVertexShader,
CreateGeometryShader,
CreateGeometryShaderWithStreamOutput,
CreatePixelShader,
CreateHullShader,
CreateDomainShader,
CreateComputeShader,
CreateClassLinkage,
CreateBlendState,
CreateDepthStencilState,
CreateRasterizerState,
CreateSamplerState,
CreateQuery,
CreatePredicate,
CreateCounter,
CreateDeferredContext,
OpenSharedResource,
CheckFormatSupport,
CheckMultisampleQualityLevels,
CheckCounterInfo,
CheckCounter,
CheckFeatureSupport,
GetPrivateData,
SetPrivateData,
SetPrivateDataInterface,
GetFeatureLevel,
GetCreationFlags,
GetDeviceRemovedReason,
GetImmediateContext,
SetExceptionMode,
GetExceptionMode,
};
enum class ID3D10DeviceVTable
{
// IUnknown
QueryInterface,
AddRef,
Release,
// ID3D10Device
VSSetConstantBuffers,
PSSetShaderResources,
PSSetShader,
PSSetSamplers,
VSSetShader,
DrawIndexed,
Draw,
PSSetConstantBuffers,
IASetInputLayout,
IASetVertexBuffers,
IASetIndexBuffer,
DrawIndexedInstanced,
DrawInstanced,
GSSetConstantBuffers,
GSSetShader,
IASetPrimitiveTopology,
VSSetShaderResources,
VSSetSamplers,
SetPredication,
GSSetShaderResources,
GSSetSamplers,
OMSetRenderTargets,
OMSetBlendState,
OMSetDepthStencilState,
SOSetTargets,
DrawAuto,
RSSetState,
RSSetViewports,
RSSetScissorRects,
CopySubresourceRegion,
CopyResource,
UpdateSubresource,
ClearRenderTargetView,
ClearDepthStencilView,
GenerateMips,
ResolveSubresource,
VSGetConstantBuffers,
PSGetShaderResources,
PSGetShader,
PSGetSamplers,
VSGetShader,
PSGetConstantBuffers,
IAGetInputLayout,
IAGetVertexBuffers,
IAGetIndexBuffer,
GSGetConstantBuffers,
GSGetShader,
IAGetPrimitiveTopology,
VSGetShaderResources,
VSGetSamplers,
GetPredication,
GSGetShaderResources,
GSGetSamplers,
OMGetRenderTargets,
OMGetBlendState,
OMGetDepthStencilState,
SOGetTargets,
RSGetState,
RSGetViewports,
RSGetScissorRects,
GetDeviceRemovedReason,
SetExceptionMode,
GetExceptionMode,
GetPrivateData,
SetPrivateData,
SetPrivateDataInterface,
ClearState,
Flush,
CreateBuffer,
CreateTexture1D,
CreateTexture2D,
CreateTexture3D,
CreateShaderResourceView,
CreateRenderTargetView,
CreateDepthStencilView,
CreateInputLayout,
CreateVertexShader,
CreateGeometryShader,
CreateGeometryShaderWithStreamOutput,
CreatePixelShader,
CreateBlendState,
CreateDepthStencilState,
CreateRasterizerState,
CreateSamplerState,
CreateQuery,
CreatePredicate,
CreateCounter,
CheckFormatSupport,
CheckMultisampleQualityLevels,
CheckCounterInfo,
CheckCounter,
GetCreationFlags,
OpenSharedResource,
SetTextFilterSize,
GetTextFilterSize,
};
enum class IDirect3DDevice9VTable
{
// IUnknown
QueryInterface,
AddRef,
Release,
// IDirect3DDevice9
TestCooperativeLevel,
GetAvailableTextureMem,
EvictManagedResources,
GetDirect3D,
GetDeviceCaps,
GetDisplayMode,
GetCreationParameters,
SetCursorProperties,
SetCursorPosition,
ShowCursor,
CreateAdditionalSwapChain,
GetSwapChain,
GetNumberOfSwapChains,
Reset,
Present,
GetBackBuffer,
GetRasterStatus,
SetDialogBoxMode,
SetGammaRamp,
GetGammaRamp,
CreateTexture,
CreateVolumeTexture,
CreateCubeTexture,
CreateVertexBuffer,
CreateIndexBuffer,
CreateRenderTarget,
CreateDepthStencilSurface,
UpdateSurface,
UpdateTexture,
GetRenderTargetData,
GetFrontBufferData,
StretchRect,
ColorFill,
CreateOffscreenPlainSurface,
SetRenderTarget,
GetRenderTarget,
SetDepthStencilSurface,
GetDepthStencilSurface,
BeginScene,
EndScene,
Clear,
SetTransform,
GetTransform,
MultiplyTransform,
SetViewport,
GetViewport,
SetMaterial,
GetMaterial,
SetLight,
GetLight,
LightEnable,
GetLightEnable,
SetClipPlane,
GetClipPlane,
SetRenderState,
GetRenderState,
CreateStateBlock,
BeginStateBlock,
EndStateBlock,
SetClipStatus,
GetClipStatus,
GetTexture,
SetTexture,
GetTextureStageState,
SetTextureStageState,
GetSamplerState,
SetSamplerState,
ValidateDevice,
SetPaletteEntries,
GetPaletteEntries,
SetCurrentTexturePalette,
GetCurrentTexturePalette,
SetScissorRect,
GetScissorRect,
SetSoftwareVertexProcessing,
GetSoftwareVertexProcessing,
SetNPatchMode,
GetNPatchMode,
DrawPrimitive,
DrawIndexedPrimitive,
DrawPrimitiveUP,
DrawIndexedPrimitiveUP,
ProcessVertices,
CreateVertexDeclaration,
SetVertexDeclaration,
GetVertexDeclaration,
SetFVF,
GetFVF,
CreateVertexShader,
SetVertexShader,
GetVertexShader,
SetVertexShaderConstantF,
GetVertexShaderConstantF,
SetVertexShaderConstantI,
GetVertexShaderConstantI,
SetVertexShaderConstantB,
GetVertexShaderConstantB,
SetStreamSource,
GetStreamSource,
SetStreamSourceFreq,
GetStreamSourceFreq,
SetIndices,
GetIndices,
CreatePixelShader,
SetPixelShader,
GetPixelShader,
SetPixelShaderConstantF,
GetPixelShaderConstantF,
SetPixelShaderConstantI,
GetPixelShaderConstantI,
SetPixelShaderConstantB,
GetPixelShaderConstantB,
DrawRectPatch,
DrawTriPatch,
DeletePatch,
CreateQuery,
// IDirect3DDevice9Ex
SetConvolutionMonoKernel,
ComposeRects,
PresentEx,
GetGPUThreadPriority,
SetGPUThreadPriority,
WaitForVBlank,
CheckResourceResidency,
SetMaximumFrameLatency,
GetMaximumFrameLatency,
CheckDeviceState,
CreateRenderTargetEx,
CreateOffscreenPlainSurfaceEx,
CreateDepthStencilSurfaceEx,
ResetEx,
GetDisplayModeEx,
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
#ifndef __IMGUI_SHADER_BLOBS_INCLUDED__
#define __IMGUI_SHADER_BLOBS_INCLUDED__
// Defining this will use d3dcompiler and it will be a dependence of the dll.
//#define USE_D3DCOMPILE
#ifdef USE_D3DCOMPILE
#include <d3dcompiler.h>
//#ifdef _MSC_VER
// #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
//#endif
decltype(D3DCompile)* load_d3dcompile();
void unload_d3dcompile();
#else
#define ImGui_vertexShaderDX10_len 876
extern unsigned char ImGui_vertexShaderDX10[ImGui_vertexShaderDX10_len];
#define ImGui_vertexShaderDX11_len 1104
extern unsigned char ImGui_vertexShaderDX11[ImGui_vertexShaderDX11_len];
#define ImGui_vertexShaderDX11_9_1_len 1104
extern unsigned char ImGui_vertexShaderDX11_9_1[ImGui_vertexShaderDX11_9_1_len];
#define ImGui_vertexShaderDX11_9_2_len ImGui_vertexShaderDX11_9_1_len
#define ImGui_vertexShaderDX11_9_2 ImGui_vertexShaderDX11_9_1
#define ImGui_vertexShaderDX11_9_3_len 1104
extern unsigned char ImGui_vertexShaderDX11_9_3[ImGui_vertexShaderDX11_9_3_len];
#define ImGui_vertexShaderDX11_10_0_len 876
extern unsigned char ImGui_vertexShaderDX11_10_0[ImGui_vertexShaderDX11_10_0_len];
#define ImGui_vertexShaderDX11_10_1_len 880
extern unsigned char ImGui_vertexShaderDX11_10_1[ImGui_vertexShaderDX11_10_1_len];
#define ImGui_vertexShaderDX11_11_0_len 988
extern unsigned char ImGui_vertexShaderDX11_11_0[ImGui_vertexShaderDX11_11_0_len];
#define ImGui_vertexShaderDX11_11_1_len 988
extern unsigned char ImGui_vertexShaderDX11_11_1[ImGui_vertexShaderDX11_11_1_len];
#define ImGui_vertexShaderDX12_len 988
extern unsigned char ImGui_vertexShaderDX12[ImGui_vertexShaderDX12_len];
#define ImGui_pixelShaderDX10_len 660
extern unsigned char ImGui_pixelShaderDX10[ImGui_pixelShaderDX10_len];
#define ImGui_pixelShaderDX11_len 1104
extern unsigned char ImGui_pixelShaderDX11[ImGui_pixelShaderDX11_len];
#define ImGui_pixelShaderDX11_9_1_len 800
extern unsigned char ImGui_pixelShaderDX11_9_1[ImGui_pixelShaderDX11_9_1_len];
#define ImGui_pixelShaderDX11_9_2_len ImGui_pixelShaderDX11_9_1_len
#define ImGui_pixelShaderDX11_9_2 ImGui_pixelShaderDX11_9_1
#define ImGui_pixelShaderDX11_9_3_len 800
extern unsigned char ImGui_pixelShaderDX11_9_3[ImGui_pixelShaderDX11_9_3_len];
#define ImGui_pixelShaderDX11_10_0_len 660
extern unsigned char ImGui_pixelShaderDX11_10_0[ImGui_pixelShaderDX11_10_0_len];
#define ImGui_pixelShaderDX11_10_1_len 664
extern unsigned char ImGui_pixelShaderDX11_10_1[ImGui_pixelShaderDX11_10_1_len];
#define ImGui_pixelShaderDX11_11_0_len 736
extern unsigned char ImGui_pixelShaderDX11_11_0[ImGui_pixelShaderDX11_11_0_len];
#define ImGui_pixelShaderDX11_11_1_len 736
extern unsigned char ImGui_pixelShaderDX11_11_1[ImGui_pixelShaderDX11_11_1_len];
#define ImGui_pixelShaderDX12_len 736
extern unsigned char ImGui_pixelShaderDX12[ImGui_pixelShaderDX12_len];
#endif
#endif//__IMGUI_SHADER_BLOBS_INCLUDED__

View File

@ -0,0 +1,147 @@
#include "OpenGL_Hook.h"
#include "Windows_Hook.h"
#include "../Renderer_Detector.h"
#include "../../dll/dll.h"
#ifndef NO_OVERLAY
#include <imgui.h>
#include <impls/imgui_impl_opengl3.h>
#include <GL/glew.h>
#include "../steam_overlay.h"
OpenGL_Hook* OpenGL_Hook::_inst = nullptr;
bool OpenGL_Hook::start_hook()
{
bool res = true;
if (!hooked)
{
if (!Windows_Hook::Inst()->start_hook())
return false;
GLenum err = glewInit();
if (err == GLEW_OK)
{
PRINT_DEBUG("Hooked OpenGL\n");
hooked = true;
Renderer_Detector::Inst().renderer_found(this);
UnhookAll();
BeginHook();
HookFuncs(
std::make_pair<void**, void*>(&(PVOID&)wglSwapBuffers, &OpenGL_Hook::MywglSwapBuffers)
);
EndHook();
get_steam_client()->steam_overlay->HookReady();
}
else
{
PRINT_DEBUG("Failed to hook OpenGL\n");
/* Problem: glewInit failed, something is seriously wrong. */
PRINT_DEBUG("Error: %s\n", glewGetErrorString(err));
res = false;
}
}
return true;
}
void OpenGL_Hook::resetRenderState()
{
if (initialized)
{
ImGui_ImplOpenGL3_Shutdown();
Windows_Hook::Inst()->resetRenderState();
ImGui::DestroyContext();
initialized = false;
}
}
// Try to make this function and overlay's proc as short as possible or it might affect game's fps.
void OpenGL_Hook::prepareForOverlay(HDC hDC)
{
HWND hWnd = WindowFromDC(hDC);
if (hWnd != Windows_Hook::Inst()->GetGameHwnd())
resetRenderState();
if (!initialized)
{
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = NULL;
ImGui_ImplOpenGL3_Init();
get_steam_client()->steam_overlay->CreateFonts();
initialized = true;
}
if (ImGui_ImplOpenGL3_NewFrame())
{
Windows_Hook::Inst()->prepareForOverlay(hWnd);
ImGui::NewFrame();
get_steam_client()->steam_overlay->OverlayProc();
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
}
BOOL WINAPI OpenGL_Hook::MywglSwapBuffers(HDC hDC)
{
OpenGL_Hook::Inst()->prepareForOverlay(hDC);
return OpenGL_Hook::Inst()->wglSwapBuffers(hDC);
}
OpenGL_Hook::OpenGL_Hook():
initialized(false),
hooked(false),
wglSwapBuffers(nullptr)
{
_library = LoadLibrary(DLL_NAME);
}
OpenGL_Hook::~OpenGL_Hook()
{
PRINT_DEBUG("OpenGL Hook removed\n");
if (initialized)
{
ImGui_ImplOpenGL3_Shutdown();
ImGui::DestroyContext();
}
FreeLibrary(reinterpret_cast<HMODULE>(_library));
_inst = nullptr;
}
OpenGL_Hook* OpenGL_Hook::Inst()
{
if (_inst == nullptr)
_inst = new OpenGL_Hook;
return _inst;
}
const char* OpenGL_Hook::get_lib_name() const
{
return DLL_NAME;
}
void OpenGL_Hook::loadFunctions(wglSwapBuffers_t pfnwglSwapBuffers)
{
wglSwapBuffers = pfnwglSwapBuffers;
}
#endif//NO_OVERLAY

View File

@ -0,0 +1,42 @@
#ifndef __INCLUDED_OPENGL_HOOK_H__
#define __INCLUDED_OPENGL_HOOK_H__
#include "../Base_Hook.h"
#ifndef NO_OVERLAY
class OpenGL_Hook : public Base_Hook
{
public:
static constexpr const char *DLL_NAME = "opengl32.dll";
using wglSwapBuffers_t = BOOL(WINAPI*)(HDC);
private:
static OpenGL_Hook* _inst;
// Variables
bool hooked;
bool initialized;
// Functions
OpenGL_Hook();
void resetRenderState();
void prepareForOverlay(HDC hDC);
// Hook to render functions
static BOOL WINAPI MywglSwapBuffers(HDC hDC);
wglSwapBuffers_t wglSwapBuffers;
public:
virtual ~OpenGL_Hook();
bool start_hook();
static OpenGL_Hook* Inst();
virtual const char* get_lib_name() const;
void loadFunctions(wglSwapBuffers_t pfnwglSwapBuffers);
};
#endif//NO_OVERLAY
#endif//__INCLUDED_OPENGL_HOOK_H__

View File

@ -0,0 +1,209 @@
#include "Windows_Hook.h"
#include "../Renderer_Detector.h"
#include "../../dll/dll.h"
#ifndef NO_OVERLAY
#include <imgui.h>
#include <impls/windows/imgui_impl_win32.h>
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
Windows_Hook* Windows_Hook::_inst = nullptr;
bool Windows_Hook::start_hook()
{
bool res = true;
if (!hooked)
{
GetRawInputBuffer = ::GetRawInputBuffer;
GetRawInputData = ::GetRawInputData;
PRINT_DEBUG("Hooked Windows\n");
BeginHook();
HookFuncs(
std::make_pair<void**, void*>(&(PVOID&)GetRawInputBuffer, &Windows_Hook::MyGetRawInputBuffer),
std::make_pair<void**, void*>(&(PVOID&)GetRawInputData , &Windows_Hook::MyGetRawInputData)
);
EndHook();
hooked = true;
}
return res;
}
void Windows_Hook::resetRenderState()
{
if (initialized)
{
initialized = false;
SetWindowLongPtr(_game_hwnd, GWLP_WNDPROC, (LONG_PTR)_game_wndproc);
_game_hwnd = nullptr;
_game_wndproc = nullptr;
ImGui_ImplWin32_Shutdown();
}
}
void Windows_Hook::prepareForOverlay(HWND hWnd)
{
if (!initialized)
{
ImGui_ImplWin32_Init(hWnd);
_game_hwnd = hWnd;
_game_wndproc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)&Windows_Hook::HookWndProc);
initialized = true;
}
ImGui_ImplWin32_NewFrame();
}
HWND Windows_Hook::GetGameHwnd() const
{
return _game_hwnd;
}
WNDPROC Windows_Hook::GetGameWndProc() const
{
return _game_wndproc;
}
/////////////////////////////////////////////////////////////////////////////////////
// Windows window hooks
bool IgnoreMsg(UINT uMsg)
{
switch (uMsg)
{
// Mouse Events
case WM_MOUSEMOVE:
case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL:
case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
case WM_XBUTTONUP: case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
case WM_MOUSEACTIVATE: case WM_MOUSEHOVER: case WM_MOUSELEAVE:
// Keyboard Events
case WM_KEYDOWN: case WM_KEYUP:
case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_SYSDEADCHAR:
case WM_CHAR: case WM_UNICHAR: case WM_DEADCHAR:
// Raw Input Events
case WM_INPUT:
return true;
}
return false;
}
void RawMouseEvent(RAWINPUT& raw)
{
if (raw.header.dwType == RIM_TYPEMOUSE)
{
if (raw.data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
ImGui_ImplWin32_WndProcHandler(Windows_Hook::Inst()->GetGameHwnd(), WM_LBUTTONDOWN, 0, 0);
else if (raw.data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP)
ImGui_ImplWin32_WndProcHandler(Windows_Hook::Inst()->GetGameHwnd(), WM_LBUTTONUP, 0, 0);
else if (raw.data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN)
ImGui_ImplWin32_WndProcHandler(Windows_Hook::Inst()->GetGameHwnd(), WM_MBUTTONDOWN, 0, 0);
else if (raw.data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_UP)
ImGui_ImplWin32_WndProcHandler(Windows_Hook::Inst()->GetGameHwnd(), WM_MBUTTONUP, 0, 0);
else if (raw.data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN)
ImGui_ImplWin32_WndProcHandler(Windows_Hook::Inst()->GetGameHwnd(), WM_RBUTTONDOWN, 0, 0);
else if (raw.data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP)
ImGui_ImplWin32_WndProcHandler(Windows_Hook::Inst()->GetGameHwnd(), WM_RBUTTONUP, 0, 0);
}
}
LRESULT CALLBACK Windows_Hook::HookWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Steam_Overlay* overlay = get_steam_client()->steam_overlay;
bool show = overlay->ShowOverlay();
// Is the event is a key press
if (uMsg == WM_KEYDOWN)
{
// Tab is pressed and was not pressed before
if (wParam == VK_TAB && !(lParam & (1 << 30)))
{
// If Left Shift is pressed
if (GetAsyncKeyState(VK_LSHIFT) & (1 << 15))
{
show = !overlay->ShowOverlay();
overlay->ShowOverlay(show);
}
}
}
ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam);
if (show)
{
if (IgnoreMsg(uMsg))
return 0;
}
// Call the overlay window procedure
return CallWindowProc(Windows_Hook::Inst()->_game_wndproc, hWnd, uMsg, wParam, lParam);
}
UINT WINAPI Windows_Hook::MyGetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader)
{
if (pData == nullptr || !get_steam_client()->steam_overlay->ShowOverlay())
return Windows_Hook::Inst()->GetRawInputBuffer(pData, pcbSize, cbSizeHeader);
int num = Windows_Hook::Inst()->GetRawInputBuffer(pData, pcbSize, cbSizeHeader);
for (int i = 0; i < num; ++i)
RawMouseEvent(pData[i]);
return 0;
}
UINT WINAPI Windows_Hook::MyGetRawInputData(HRAWINPUT hRawInput, UINT uiCommand, LPVOID pData, PUINT pcbSize, UINT cbSizeHeader)
{
if (pData == nullptr || !get_steam_client()->steam_overlay->ShowOverlay())
return Windows_Hook::Inst()->GetRawInputData(hRawInput, uiCommand, pData, pcbSize, cbSizeHeader);
Windows_Hook::Inst()->GetRawInputData(hRawInput, uiCommand, pData, pcbSize, cbSizeHeader);
RawMouseEvent(*reinterpret_cast<RAWINPUT*>(pData));
*pcbSize = 0;
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////
Windows_Hook::Windows_Hook() :
initialized(false),
hooked(false),
_game_hwnd(nullptr),
_game_wndproc(nullptr),
GetRawInputBuffer(nullptr),
GetRawInputData(nullptr)
{
//_library = LoadLibrary(DLL_NAME);
}
Windows_Hook::~Windows_Hook()
{
PRINT_DEBUG("Windows Hook removed\n");
resetRenderState();
//FreeLibrary(reinterpret_cast<HMODULE>(_library));
_inst = nullptr;
}
Windows_Hook* Windows_Hook::Inst()
{
if (_inst == nullptr)
_inst = new Windows_Hook;
return _inst;
}
const char* Windows_Hook::get_lib_name() const
{
return DLL_NAME;
}
#endif//NO_OVERLAY

View File

@ -0,0 +1,50 @@
#ifndef __INCLUDED_WINDOWS_HOOK_H__
#define __INCLUDED_WINDOWS_HOOK_H__
#include "../Base_Hook.h"
#ifdef __WINDOWS__
#ifndef NO_OVERLAY
class Windows_Hook : public Base_Hook
{
public:
static constexpr const char* DLL_NAME = "user32.dll";
private:
static Windows_Hook* _inst;
// Variables
bool hooked;
bool initialized;
HWND _game_hwnd;
WNDPROC _game_wndproc;
// Functions
Windows_Hook();
// Hook to Windows window messages
decltype(GetRawInputBuffer)* GetRawInputBuffer;
decltype(GetRawInputData)* GetRawInputData;
static LRESULT CALLBACK HookWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static UINT WINAPI MyGetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader);
static UINT WINAPI MyGetRawInputData(HRAWINPUT hRawInput, UINT uiCommand, LPVOID pData, PUINT pcbSize, UINT cbSizeHeader);
public:
virtual ~Windows_Hook();
void resetRenderState();
void prepareForOverlay(HWND);
HWND GetGameHwnd() const;
WNDPROC GetGameWndProc() const;
bool start_hook();
static Windows_Hook* Inst();
virtual const char* get_lib_name() const;
};
#endif//NO_OVERLAY
#endif//__WINDOWS__
#endif//__INCLUDED_WINDOWS_HOOK_H__

0
scripts/find_interfaces.sh Normal file → Executable file
View File