Merge branch 'overlay_h' of https://gitlab.com/Nemirtingas/goldberg_emulator
# Conflicts: # build_steamos.sh # dll/dll.cppmerge-requests/28/head
commit
ffdaf72597
|
@ -15,3 +15,4 @@ rtlgenrandom*
|
||||||
steamclient.exp
|
steamclient.exp
|
||||||
steamclient.lib
|
steamclient.lib
|
||||||
out/*
|
out/*
|
||||||
|
glew
|
||||||
|
|
|
@ -59,7 +59,8 @@ build_windows:
|
||||||
image: fedora:29
|
image: fedora:29
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- dnf -y install wine wget p7zip sed dos2unix
|
- dnf -y install wine wget p7zip sed dos2unix unzip
|
||||||
|
- ./download_glew.sh
|
||||||
- unix2dos *.txt
|
- unix2dos *.txt
|
||||||
- unix2dos files_example/*.txt files_example/*/*.txt
|
- unix2dos files_example/*.txt files_example/*/*.txt
|
||||||
- sed -i 's/..\\vcpkg\\packages\\/.\\/g' build_set_protobuf_directories.bat
|
- sed -i 's/..\\vcpkg\\packages\\/.\\/g' build_set_protobuf_directories.bat
|
||||||
|
@ -71,6 +72,11 @@ build_windows:
|
||||||
- 7za x sdk_standalone.7z -osdk_standalone
|
- 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 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 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 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
|
- DLL_FILES="$(ls steamclient_loader/*.cpp | tr "\n" " ")"; sed "s|steamclient_loader/\*.cpp|$DLL_FILES|g" -i *.bat
|
||||||
- export WINEDEBUG=-all
|
- export WINEDEBUG=-all
|
||||||
|
@ -98,7 +104,7 @@ build_cmake_linux:
|
||||||
script:
|
script:
|
||||||
- mkdir cmake-builds && cd cmake-builds
|
- mkdir cmake-builds && cd cmake-builds
|
||||||
- mkdir x64-release && cd x64-release
|
- 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 ..
|
- cd ..
|
||||||
# - mkdir x64-experimental-release && cd x64-experimental-release
|
# - mkdir x64-experimental-release && cd x64-experimental-release
|
||||||
# - cmake ../../ -G "Ninja" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DEMU_EXPERIMENTAL_BUILD:BOOL=ON && ninja
|
# - cmake ../../ -G "Ninja" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DEMU_EXPERIMENTAL_BUILD:BOOL=ON && ninja
|
||||||
|
@ -114,13 +120,14 @@ build_cmake_windows:
|
||||||
image: fedora:29
|
image: fedora:29
|
||||||
|
|
||||||
before_script:
|
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'
|
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/48db8f434a193aae872279dc4f5dde6a/sdk_standalone.7z'
|
||||||
- 7za x sdk_standalone.7z -osdk_standalone
|
- 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'
|
- 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
|
- 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'
|
- 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
|
- 7za x protobuf_x64-windows-static.7z -oprotobuf_x64-windows-static
|
||||||
|
- ./download_glew.sh
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- export WINEDEBUG=-all
|
- export WINEDEBUG=-all
|
||||||
|
@ -128,13 +135,13 @@ build_cmake_windows:
|
||||||
- mkdir cmake-builds && cd cmake-builds
|
- mkdir cmake-builds && cd cmake-builds
|
||||||
- mkdir x64-release && cd x64-release
|
- mkdir x64-release && cd x64-release
|
||||||
- echo call .\\..\\..\\sdk_standalone\\set_vars64.bat >> cmake-build.bat
|
- 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
|
- echo nmake.exe >> cmake-build.bat
|
||||||
- wine cmd /c cmake-build.bat
|
- wine cmd /c cmake-build.bat
|
||||||
- cd ..
|
- cd ..
|
||||||
- mkdir x64-experimental-release && cd x64-experimental-release
|
- mkdir x64-experimental-release && cd x64-experimental-release
|
||||||
- echo call .\\..\\..\\sdk_standalone\\set_vars64.bat >> cmake-build.bat
|
- 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
|
- echo nmake.exe >> cmake-build.bat
|
||||||
- wine cmd /c cmake-build.bat
|
- wine cmd /c cmake-build.bat
|
||||||
- cd ..
|
- cd ..
|
||||||
|
|
|
@ -50,12 +50,14 @@ if(WIN32)
|
||||||
set(LIB_STEAMNETWORKINGSOCKETS steamnetworkingsockets64)
|
set(LIB_STEAMNETWORKINGSOCKETS steamnetworkingsockets64)
|
||||||
set(BIN_LOBBY_CONNECT lobby_connect64)
|
set(BIN_LOBBY_CONNECT lobby_connect64)
|
||||||
set(BIN_GENERATE_INTERFACES_FILE generate_interfaces_file64)
|
set(BIN_GENERATE_INTERFACES_FILE generate_interfaces_file64)
|
||||||
|
link_directories(glew/lib/Release/x64)
|
||||||
else()
|
else()
|
||||||
set(LIB_STEAM_API steam_api)
|
set(LIB_STEAM_API steam_api)
|
||||||
set(LIB_STEAMCLIENT steamclient)
|
set(LIB_STEAMCLIENT steamclient)
|
||||||
set(LIB_STEAMNETWORKINGSOCKETS steamnetworkingsockets)
|
set(LIB_STEAMNETWORKINGSOCKETS steamnetworkingsockets)
|
||||||
set(BIN_LOBBY_CONNECT lobby_connect)
|
set(BIN_LOBBY_CONNECT lobby_connect)
|
||||||
set(BIN_GENERATE_INTERFACES_FILE generate_interfaces_file)
|
set(BIN_GENERATE_INTERFACES_FILE generate_interfaces_file)
|
||||||
|
link_directories(glew/lib/Release/Win32)
|
||||||
endif()
|
endif()
|
||||||
elseif(UNIX AND NOT APPLE)
|
elseif(UNIX AND NOT APPLE)
|
||||||
set(LIB_STEAM_API steam_api)
|
set(LIB_STEAM_API steam_api)
|
||||||
|
@ -77,6 +79,24 @@ file(GLOB DETOURS_SRC_SHARED
|
||||||
detours/*.cpp
|
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
|
# Setup for the steam_api(64).dll / libsteam_api.so
|
||||||
###################################################
|
###################################################
|
||||||
|
@ -85,6 +105,7 @@ file(GLOB DETOURS_SRC_SHARED
|
||||||
add_library(${LIB_STEAM_API}
|
add_library(${LIB_STEAM_API}
|
||||||
SHARED
|
SHARED
|
||||||
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:${DETOURS_SRC_SHARED}>
|
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:${DETOURS_SRC_SHARED}>
|
||||||
|
$<$<AND:$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<NOT:$<BOOL:${NO_OVERLAY}>>>:${OVERLAY_EXPERIMENTAL_SRC_SHARED}>
|
||||||
${DLL_SRC_SHARED}
|
${DLL_SRC_SHARED}
|
||||||
${PROTO_SRCS}
|
${PROTO_SRCS}
|
||||||
${PROTO_HDRS}
|
${PROTO_HDRS}
|
||||||
|
@ -95,6 +116,8 @@ target_include_directories(${LIB_STEAM_API}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${PROTOBUF_INCLUDE_DIRS}
|
${PROTOBUF_INCLUDE_DIRS}
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ImGui
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/glew/include
|
||||||
)
|
)
|
||||||
|
|
||||||
# Link the required libraries
|
# Link the required libraries
|
||||||
|
@ -103,6 +126,9 @@ target_link_libraries(${LIB_STEAM_API}
|
||||||
protobuf::libprotobuf
|
protobuf::libprotobuf
|
||||||
$<$<BOOL:${WIN32}>:ws2_32>
|
$<$<BOOL:${WIN32}>:ws2_32>
|
||||||
$<$<BOOL:${WIN32}>:iphlpapi>
|
$<$<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
|
# Add target compile definitions
|
||||||
|
@ -113,6 +139,8 @@ target_compile_definitions(${LIB_STEAM_API}
|
||||||
$<$<CONFIG:RelWithDebInfo>:EMU_RELEASE_BUILD>
|
$<$<CONFIG:RelWithDebInfo>:EMU_RELEASE_BUILD>
|
||||||
$<$<CONFIG:MinSizeRel>:EMU_RELEASE_BUILD>
|
$<$<CONFIG:MinSizeRel>:EMU_RELEASE_BUILD>
|
||||||
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:EMU_EXPERIMENTAL_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
|
# Install the target
|
||||||
|
@ -237,6 +265,7 @@ target_link_libraries(${BIN_LOBBY_CONNECT}
|
||||||
target_compile_definitions(${BIN_LOBBY_CONNECT}
|
target_compile_definitions(${BIN_LOBBY_CONNECT}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
NO_DISK_WRITES
|
NO_DISK_WRITES
|
||||||
|
NO_OVERLAY
|
||||||
LOBBY_CONNECT
|
LOBBY_CONNECT
|
||||||
$<$<CONFIG:>:EMU_RELEASE_BUILD>
|
$<$<CONFIG:>:EMU_RELEASE_BUILD>
|
||||||
$<$<CONFIG:Release>:EMU_RELEASE_BUILD>
|
$<$<CONFIG:Release>:EMU_RELEASE_BUILD>
|
||||||
|
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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, ¤t_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();
|
||||||
|
}
|
|
@ -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();
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
*/
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
|
@ -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 *)¤t_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;
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
*/
|
|
@ -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.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -7,10 +7,10 @@ mkdir -p linux/tools
|
||||||
cp scripts/find_interfaces.sh linux/tools/
|
cp scripts/find_interfaces.sh linux/tools/
|
||||||
cp scripts/steamclient_loader.sh linux/tools/
|
cp scripts/steamclient_loader.sh linux/tools/
|
||||||
../protobuf/prefix_x86/bin/protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
|
../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 -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 -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 -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 -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 -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
|
../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++ -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++ -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 -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++ -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++ -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
|
||||||
|
|
|
@ -4,13 +4,13 @@ call build_set_protobuf_directories.bat
|
||||||
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||||
call build_env_x86.bat
|
call build_env_x86.bat
|
||||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
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 steamclient.cpp /EHsc /MP12 /link /OUT:steamclient.dll
|
||||||
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets.dll
|
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets.dll
|
||||||
|
|
||||||
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||||
call build_env_x64.bat
|
call build_env_x64.bat
|
||||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
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 steamclient.cpp /EHsc /MP12 /link /OUT:steamclient64.dll
|
||||||
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets64.dll
|
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets64.dll
|
||||||
|
|
|
@ -6,7 +6,7 @@ call build_set_protobuf_directories.bat
|
||||||
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||||
call build_env_x86.bat
|
call build_env_x86.bat
|
||||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
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\*.lib
|
||||||
del /Q /S release\lobby_connect\*.exp
|
del /Q /S release\lobby_connect\*.exp
|
||||||
copy Readme_lobby_connect.txt release\lobby_connect\Readme.txt
|
copy Readme_lobby_connect.txt release\lobby_connect\Readme.txt
|
||||||
|
|
|
@ -10,12 +10,12 @@ call build_set_protobuf_directories.bat
|
||||||
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||||
call build_env_x86.bat
|
call build_env_x86.bat
|
||||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
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
|
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
|
||||||
call build_set_protobuf_directories.bat
|
|
||||||
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||||
call build_env_x64.bat
|
call build_env_x64.bat
|
||||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
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
|
copy Readme_release.txt release\Readme.txt
|
||||||
xcopy /s files_example\* release\
|
xcopy /s files_example\* release\
|
||||||
call build_win_release_experimental.bat
|
call build_win_release_experimental.bat
|
||||||
|
|
|
@ -6,11 +6,11 @@ call build_set_protobuf_directories.bat
|
||||||
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||||
call build_env_x86.bat
|
call build_env_x86.bat
|
||||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
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
|
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
|
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||||
call build_env_x64.bat
|
call build_env_x64.bat
|
||||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
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
|
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
|
copy Readme_experimental.txt release\experimental\Readme.txt
|
||||||
|
|
24
dll/base.h
24
dll/base.h
|
@ -18,7 +18,29 @@
|
||||||
#ifndef BASE_INCLUDE
|
#ifndef BASE_INCLUDE
|
||||||
#define 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
|
#define STEAM_WIN32
|
||||||
#pragma warning( disable : 4716)
|
#pragma warning( disable : 4716)
|
||||||
#ifndef NOMINMAX
|
#ifndef NOMINMAX
|
||||||
|
|
|
@ -223,6 +223,12 @@ STEAMAPI_API bool S_CALLTYPE SteamAPI_Init()
|
||||||
load_old_interface_versions();
|
load_old_interface_versions();
|
||||||
user_steam_pipe = get_steam_client()->CreateSteamPipe();
|
user_steam_pipe = get_steam_client()->CreateSteamPipe();
|
||||||
get_steam_client()->ConnectToGlobalUser(user_steam_pipe);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
message Common_Message {
|
||||||
uint64 source_id = 1;
|
uint64 source_id = 1;
|
||||||
uint64 dest_id = 2;
|
uint64 dest_id = 2;
|
||||||
|
@ -200,6 +211,7 @@ message Common_Message {
|
||||||
Friend_Messages friend_messages = 11;
|
Friend_Messages friend_messages = 11;
|
||||||
Network_Old network_old = 12;
|
Network_Old network_old = 12;
|
||||||
Networking_Sockets networking_sockets = 13;
|
Networking_Sockets networking_sockets = 13;
|
||||||
|
Steam_Messages steam_messages = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 source_ip = 128;
|
uint32 source_ip = 128;
|
||||||
|
|
|
@ -574,6 +574,11 @@ void Networking::do_callbacks_message(Common_Message *msg)
|
||||||
PRINT_DEBUG("has_networking_sockets\n");
|
PRINT_DEBUG("has_networking_sockets\n");
|
||||||
run_callbacks(CALLBACK_ID_NETWORKING_SOCKETS, msg);
|
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)
|
bool Networking::handle_tcp(Common_Message *msg, struct TCP_Socket &socket)
|
||||||
|
|
|
@ -60,6 +60,7 @@ enum Callback_Ids {
|
||||||
CALLBACK_ID_AUTH_TICKET,
|
CALLBACK_ID_AUTH_TICKET,
|
||||||
CALLBACK_ID_FRIEND_MESSAGES,
|
CALLBACK_ID_FRIEND_MESSAGES,
|
||||||
CALLBACK_ID_NETWORKING_SOCKETS,
|
CALLBACK_ID_NETWORKING_SOCKETS,
|
||||||
|
CALLBACK_ID_STEAM_MESSAGES,
|
||||||
|
|
||||||
CALLBACK_IDS_MAX
|
CALLBACK_IDS_MAX
|
||||||
};
|
};
|
||||||
|
|
|
@ -46,9 +46,13 @@ static void background_thread(Steam_Client *client)
|
||||||
|
|
||||||
Steam_Client::Steam_Client()
|
Steam_Client::Steam_Client()
|
||||||
{
|
{
|
||||||
|
|
||||||
uint32 appid = create_localstorage_settings(&settings_client, &settings_server, &local_storage);
|
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);
|
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();
|
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());
|
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_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_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_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 = 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_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);
|
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");
|
PRINT_DEBUG("client init gameserver\n");
|
||||||
steam_gameserver = new Steam_GameServer(settings_server, network, callbacks_server);
|
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_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_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);
|
steam_gameserver_http = new Steam_HTTP(settings_server, network, callback_results_server, callbacks_server);
|
||||||
|
|
|
@ -53,6 +53,8 @@
|
||||||
#include "steam_gameserverstats.h"
|
#include "steam_gameserverstats.h"
|
||||||
#include "steam_masterserver_updater.h"
|
#include "steam_masterserver_updater.h"
|
||||||
|
|
||||||
|
#include "../overlay_experimental/steam_overlay.h"
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
enum Steam_Pipe {
|
enum Steam_Pipe {
|
||||||
|
@ -127,6 +129,9 @@ public:
|
||||||
Steam_Game_Coordinator *steam_gameserver_game_coordinator;
|
Steam_Game_Coordinator *steam_gameserver_game_coordinator;
|
||||||
Steam_Masterserver_Updater *steam_masterserver_updater;
|
Steam_Masterserver_Updater *steam_masterserver_updater;
|
||||||
|
|
||||||
|
Steam_Overlay* steam_overlay;
|
||||||
|
|
||||||
|
bool enable_overlay = true;
|
||||||
bool user_logged_in = false;
|
bool user_logged_in = false;
|
||||||
bool server_init = false;
|
bool server_init = false;
|
||||||
std::thread background_keepalive;
|
std::thread background_keepalive;
|
||||||
|
|
|
@ -15,7 +15,11 @@
|
||||||
License along with the Goldberg Emulator; if not, see
|
License along with the Goldberg Emulator; if not, see
|
||||||
<http://www.gnu.org/licenses/>. */
|
<http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#ifndef __INCLUDED_STEAM_FRIENDS_H__
|
||||||
|
#define __INCLUDED_STEAM_FRIENDS_H__
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
#include "../overlay_experimental/steam_overlay.h"
|
||||||
|
|
||||||
#define SEND_FRIEND_RATE 4.0
|
#define SEND_FRIEND_RATE 4.0
|
||||||
|
|
||||||
|
@ -46,6 +50,7 @@ public ISteamFriends
|
||||||
class SteamCallBacks *callbacks;
|
class SteamCallBacks *callbacks;
|
||||||
class SteamCallResults *callback_results;
|
class SteamCallResults *callback_results;
|
||||||
class RunEveryRunCB *run_every_runcb;
|
class RunEveryRunCB *run_every_runcb;
|
||||||
|
class Steam_Overlay* overlay;
|
||||||
|
|
||||||
Friend us;
|
Friend us;
|
||||||
bool modified;
|
bool modified;
|
||||||
|
@ -128,13 +133,14 @@ static void steam_friends_run_every_runcb(void *object)
|
||||||
steam_friends->RunCallbacks();
|
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, 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_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);
|
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 )
|
void ActivateGameOverlay( const char *pchDialog )
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("Steam_Friends::ActivateGameOverlay %s\n", pchDialog);
|
PRINT_DEBUG("Steam_Friends::ActivateGameOverlay %s\n", pchDialog);
|
||||||
|
overlay->OpenOverlay(pchDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -574,7 +581,7 @@ void SetPlayedWith( CSteamID steamIDUserPlayedWith )
|
||||||
void ActivateGameOverlayInviteDialog( CSteamID steamIDLobby )
|
void ActivateGameOverlayInviteDialog( CSteamID steamIDLobby )
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayInviteDialog\n");
|
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
|
// 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; });
|
auto f = std::find_if(friends.begin(), friends.end(), [&id](Friend const& item) { return item.id() == id; });
|
||||||
if (friends.end() != f) {
|
if (friends.end() != f) {
|
||||||
persona_change((uint64)f->id(), k_EPersonaChangeStatus);
|
persona_change((uint64)f->id(), k_EPersonaChangeStatus);
|
||||||
|
overlay->FriendDisconnect(*f);
|
||||||
friends.erase(f);
|
friends.erase(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1039,6 +1047,7 @@ void Callback(Common_Message *msg)
|
||||||
if (!f) {
|
if (!f) {
|
||||||
if (msg->friend_().id() != settings->get_local_steam_id().ConvertToUint64()) {
|
if (msg->friend_().id() != settings->get_local_steam_id().ConvertToUint64()) {
|
||||||
friends.push_back(msg->friend_());
|
friends.push_back(msg->friend_());
|
||||||
|
overlay->FriendConnect(msg->friend_());
|
||||||
persona_change((uint64)msg->friend_().id(), k_EPersonaChangeName);
|
persona_change((uint64)msg->friend_().id(), k_EPersonaChangeName);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1059,16 +1068,31 @@ void Callback(Common_Message *msg)
|
||||||
if (msg->has_friend_messages()) {
|
if (msg->has_friend_messages()) {
|
||||||
if (msg->friend_messages().type() == Friend_Messages::LOBBY_INVITE) {
|
if (msg->friend_messages().type() == Friend_Messages::LOBBY_INVITE) {
|
||||||
PRINT_DEBUG("Steam_Friends Got Lobby Invite\n");
|
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
|
//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;
|
GameLobbyJoinRequested_t data;
|
||||||
data.m_steamIDLobby = CSteamID((uint64)msg->friend_messages().lobby_id());
|
data.m_steamIDLobby = CSteamID((uint64)msg->friend_messages().lobby_id());
|
||||||
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
|
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
|
||||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (msg->friend_messages().type() == Friend_Messages::GAME_INVITE) {
|
if (msg->friend_messages().type() == Friend_Messages::GAME_INVITE) {
|
||||||
PRINT_DEBUG("Steam_Friends Got Game Invite\n");
|
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
|
//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();
|
std::string const& connect_str = msg->friend_messages().connect_str();
|
||||||
GameRichPresenceJoinRequested_t data = {};
|
GameRichPresenceJoinRequested_t data = {};
|
||||||
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
|
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
|
||||||
|
@ -1076,6 +1100,9 @@ void Callback(Common_Message *msg)
|
||||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif//__INCLUDED_STEAM_FRIENDS_H__
|
||||||
|
|
|
@ -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()) {
|
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());
|
Lobby *lobby = get_lobby((uint64)msg->lobby().room_id());
|
||||||
if (!lobby) {
|
if (!lobby) {
|
||||||
unsigned int old_size = lobbies.size();
|
size_t old_size = lobbies.size();
|
||||||
lobbies.resize(old_size + 1);
|
lobbies.resize(old_size + 1);
|
||||||
lobbies[old_size].set_room_id(msg->lobby().room_id());
|
lobbies[old_size].set_room_id(msg->lobby().room_id());
|
||||||
lobby = &(lobbies[old_size]);
|
lobby = &(lobbies[old_size]);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "local_storage.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();
|
static std::chrono::time_point<std::chrono::steady_clock> app_initialized_time = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
@ -34,13 +35,14 @@ public ISteamUtils
|
||||||
private:
|
private:
|
||||||
Settings *settings;
|
Settings *settings;
|
||||||
class SteamCallResults *callback_results;
|
class SteamCallResults *callback_results;
|
||||||
|
Steam_Overlay* overlay;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Steam_Utils(Settings *settings, class SteamCallResults *callback_results)
|
Steam_Utils(Settings *settings, class SteamCallResults *callback_results, Steam_Overlay *overlay):
|
||||||
{
|
settings(settings),
|
||||||
this->settings = settings;
|
callback_results(callback_results),
|
||||||
this->callback_results = callback_results;
|
overlay(overlay)
|
||||||
}
|
{}
|
||||||
|
|
||||||
// return the number of seconds since the user
|
// return the number of seconds since the user
|
||||||
uint32 GetSecondsSinceAppActive()
|
uint32 GetSecondsSinceAppActive()
|
||||||
|
@ -144,6 +146,7 @@ uint32 GetAppID()
|
||||||
void SetOverlayNotificationPosition( ENotificationPosition eNotificationPosition )
|
void SetOverlayNotificationPosition( ENotificationPosition eNotificationPosition )
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("SetOverlayNotificationPosition\n");
|
PRINT_DEBUG("SetOverlayNotificationPosition\n");
|
||||||
|
overlay->SetNotificationPosition(eNotificationPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -218,8 +221,7 @@ void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction )
|
||||||
bool IsOverlayEnabled()
|
bool IsOverlayEnabled()
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("IsOverlayEnabled\n");
|
PRINT_DEBUG("IsOverlayEnabled\n");
|
||||||
//TODO
|
return overlay->Ready();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,7 +237,7 @@ bool IsOverlayEnabled()
|
||||||
bool BOverlayNeedsPresent()
|
bool BOverlayNeedsPresent()
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("BOverlayNeedsPresent\n");
|
PRINT_DEBUG("BOverlayNeedsPresent\n");
|
||||||
return false;
|
return overlay->NeedPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -305,6 +307,7 @@ bool IsSteamRunningInVR()
|
||||||
void SetOverlayNotificationInset( int nHorizontalInset, int nVerticalInset )
|
void SetOverlayNotificationInset( int nHorizontalInset, int nVerticalInset )
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("SetOverlayNotificationInset\n");
|
PRINT_DEBUG("SetOverlayNotificationInset\n");
|
||||||
|
overlay->SetNotificationInset(nHorizontalInset, nVerticalInset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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__
|
|
@ -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
|
|
@ -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__
|
|
@ -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, ¶ms, 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, ¶ms, 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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__
|
|
@ -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__
|
|
@ -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__
|
|
@ -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__
|
|
@ -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
|
@ -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
|
|
@ -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__
|
|
@ -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
|
|
@ -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__
|
|
@ -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
|
|
@ -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__
|
|
@ -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
|
|
@ -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__
|
|
@ -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(¶m);
|
||||||
|
|
||||||
|
// 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
|
|
@ -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__
|
|
@ -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
|
@ -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__
|
|
@ -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
|
|
@ -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__
|
|
@ -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
|
|
@ -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__
|
Loading…
Reference in New Issue