Skip to content

Commit c9b8ba0

Browse files
committed
Rework add-on manager to track add-on modules and add version information to the add-on list overlay
1 parent 2005711 commit c9b8ba0

File tree

10 files changed

+297
-139
lines changed

10 files changed

+297
-139
lines changed

deps/Windows.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
88
</ClCompile>
99
<Link>
10-
<AdditionalDependencies>Shlwapi.lib;WinInet.lib;WS2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
10+
<AdditionalDependencies>ShLwApi.lib;WinInet.lib;WS2_32.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
1111
</Link>
1212
</ItemDefinitionGroup>
1313
</Project>

include/README.md

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ A ReShade add-on is simply a DLL that uses the header-only ReShade API to regist
88
Here is a very basic code example of an add-on that registers a callback that gets executed every time a new frame is presented to the screen:
99

1010
```cpp
11-
#define RESHADE_ADDON_IMPL // Define this before including the ReShade header in exactly one source file
1211
#include <reshade.hpp>
1312

1413
static void on_present(reshade::api::command_queue *queue, reshade::api::swapchain *swapchain)
@@ -21,19 +20,19 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
2120
switch (fdwReason)
2221
{
2322
case DLL_PROCESS_ATTACH:
24-
// Call 'reshade::register_addon' before you call any other function of the ReShade API
25-
// This will look for the ReShade instance in the current process and initialize the API when found
23+
// Call 'reshade::register_addon' before you call any other function of the ReShade API.
24+
// This will look for the ReShade instance in the current process and initialize the API when found.
2625
if (!reshade::register_addon(hinstDLL))
2726
return FALSE;
28-
// This registers a callback for the 'present' event, which occurs every time a new frame is presented to the screen
29-
// The function signature has to match the type defined by 'reshade::addon_event_traits<reshade::addon_event::present>::decl'
30-
// For more details check the inline documentation for each event in 'reshade_events.hpp'
27+
// This registers a callback for the 'present' event, which occurs every time a new frame is presented to the screen.
28+
// The function signature has to match the type defined by 'reshade::addon_event_traits<reshade::addon_event::present>::decl'.
29+
// For more details check the inline documentation for each event in 'reshade_events.hpp'.
3130
reshade::register_event<reshade::addon_event::present>(on_present);
3231
break;
3332
case DLL_PROCESS_DETACH:
34-
// Before the add-on is unloaded, be sure to unregister any event callbacks that where previously registered
33+
// This unregisters the event callback that was previously registered during process attachment.
3534
reshade::unregister_event<reshade::addon_event::present>(on_present);
36-
// And finally unregister the add-on from ReShade
35+
// And finally unregister the add-on from ReShade (this will automatically clean up any events registered by this add-on as well).
3736
reshade::unregister_addon(hinstDLL);
3837
break;
3938
}
@@ -46,12 +45,16 @@ For more complex examples, see also the built-in add-ons in [source/addon](../so
4645
## Overlays
4746
4847
It is also supported to add an overlay, which can e.g. be used to display debug information or interact with the user in-application.
49-
Overlays are created with the use of [Dear ImGui](https://github.com/ocornut/imgui/). Including the [`reshade.hpp`](reshade.hpp) header after `imgui.h` will automatically overwrite all Dear ImGui functions to use the instance created and managed by ReShade. This means all you have to do is include these two headers and use Dear ImGui as usual (without actually having to build its source code files, only the header files are needed):
48+
Overlays are created with the use of [Dear ImGui](https://github.com/ocornut/imgui/). Including the [`reshade.hpp`](reshade.hpp) header after `imgui.h` will automatically overwrite all Dear ImGui functions to use the instance created and managed by ReShade. This means all you have to do is include these two headers, define the function table variable in one of your source code file and use Dear ImGui as usual (without actually having to build its source code files, only the header files are needed):
5049
5150
```cpp
5251
#include <imgui.h>
5352
#include <reshade.hpp>
5453
54+
// Define this variable in exactly one of your source code files.
55+
// The function table is automatically populated in the call to 'reshade::register_addon' and overwrites all Dear ImGui functions.
56+
imgui_function_table g_imgui_function_table = {};
57+
5558
bool g_popup_window_visible = false;
5659
5760
static void draw_debug_overlay(reshade::api::effect_runtime *runtime, void *imgui_context)
@@ -69,20 +72,31 @@ static void draw_debug_overlay(reshade::api::effect_runtime *runtime, void *imgu
6972
}
7073
}
7174
75+
static void draw_settings_overlay(reshade::api::effect_runtime *runtime, void *imgui_context)
76+
{
77+
ImGui::Checkbox("Popup window is visible", &g_popup_window_visible);
78+
}
79+
7280
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
7381
{
7482
switch (fdwReason)
7583
{
7684
case DLL_PROCESS_ATTACH:
77-
if (!reshade::init_addon())
85+
// This will also populate the Dear ImGui function table.
86+
if (!reshade::register_addon(hinstDLL))
7887
return FALSE;
7988
// This registers a new overlay with the specified name with ReShade.
8089
// It will be displayed as an additional window when the ReShade overlay is opened.
8190
// Its contents are defined by Dear ImGui commands issued in the specified callback function.
8291
reshade::register_overlay("Test", draw_debug_overlay);
92+
// It is also possible to register a special settings overlay by passing "nullptr" instead of a title.
93+
// This is shown beneath the add-on information in the add-on list of the ReShade overlay and can be used to present settings to users.
94+
reshade::register_overlay(nullptr, draw_settings_overlay);
8395
break;
8496
case DLL_PROCESS_DETACH:
8597
reshade::unregister_overlay("Test");
98+
// And finally unregister the add-on from ReShade (this will automatically clean up any overlays registered by this add-on as well).
99+
reshade::unregister_addon(hinstDLL);
86100
break;
87101
}
88102
return TRUE;

include/reshade.hpp

Lines changed: 64 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,58 +10,80 @@
1010
#include <Windows.h>
1111
#include <Psapi.h>
1212

13-
extern HMODULE g_module_handle;
14-
1513
namespace reshade
1614
{
1715
/// <summary>
18-
/// Writes a message to the ReShade log.
16+
/// Gets the handle to the ReShade module in the current process.
17+
/// </summary>
18+
inline HMODULE &get_reshade_module_handle()
19+
{
20+
static HMODULE handle = nullptr;
21+
if (handle == nullptr)
22+
{
23+
// Use the kernel32 variant of module enumeration functions so it can be safely called from 'DllMain'
24+
HMODULE modules[1024]; DWORD num = 0;
25+
if (K32EnumProcessModules(GetCurrentProcess(), modules, sizeof(modules), &num))
26+
{
27+
if (num > sizeof(modules))
28+
num = sizeof(modules);
29+
30+
for (DWORD i = 0; i < num / sizeof(HMODULE); ++i)
31+
{
32+
if (GetProcAddress(modules[i], "ReShadeRegisterAddon") &&
33+
GetProcAddress(modules[i], "ReShadeUnregisterAddon"))
34+
{
35+
handle = modules[i];
36+
break;
37+
}
38+
}
39+
}
40+
}
41+
return handle;
42+
}
43+
/// <summary>
44+
/// Gets the handle to the current add-on module.
45+
/// </summary>
46+
inline HMODULE &get_current_module_handle()
47+
{
48+
static HMODULE handle = nullptr;
49+
return handle;
50+
}
51+
52+
/// <summary>
53+
/// Writes a message to ReShade's log.
1954
/// </summary>
2055
/// <param name="level">Severity level (1 = error, 2 = warning, 3 = info, 4 = debug).</param>
2156
/// <param name="message">A null-terminated message string.</param>
2257
inline void log_message(int level, const char *message)
2358
{
24-
static const auto func = reinterpret_cast<void(*)(int, const char *)>(
25-
GetProcAddress(g_module_handle, "ReShadeLogMessage"));
26-
func(level, message);
59+
static const auto func = reinterpret_cast<void(*)(HMODULE, int, const char *)>(
60+
GetProcAddress(get_reshade_module_handle(), "ReShadeLogMessage"));
61+
func(get_current_module_handle(), level, message);
2762
}
2863

2964
/// <summary>
30-
/// Registers this module as an add-on with ReShade. Call this in 'DllMain' during process attach, before any of the other API functions!
65+
/// Registers this module as an add-on with ReShade.
66+
/// Call this in 'DllMain' during process attach, before any of the other API functions!
3167
/// </summary>
68+
/// <param name="module">Handle of the current add-on module.</param>
3269
inline bool register_addon(HMODULE module)
3370
{
34-
// Use the kernel32 variant of module enumeration functions so it can be safely called from 'DllMain'
35-
HMODULE modules[1024]; DWORD num = 0;
36-
if (K32EnumProcessModules(GetCurrentProcess(), modules, sizeof(modules), &num))
37-
{
38-
if (num > sizeof(modules))
39-
num = sizeof(modules);
71+
get_current_module_handle() = module;
4072

41-
for (DWORD i = 0; i < num / sizeof(HMODULE); ++i)
42-
{
43-
if (GetProcAddress(modules[i], "ReShadeRegisterAddon") &&
44-
GetProcAddress(modules[i], "ReShadeUnregisterAddon"))
45-
{
46-
g_module_handle = modules[i];
47-
break;
48-
}
49-
}
50-
}
51-
52-
if (g_module_handle == nullptr)
73+
const HMODULE reshade_module = get_reshade_module_handle();
74+
if (reshade_module == nullptr)
5375
return false;
5476

5577
// Check that the ReShade module supports the used API
5678
const auto func = reinterpret_cast<bool(*)(HMODULE, uint32_t)>(
57-
GetProcAddress(g_module_handle, "ReShadeRegisterAddon"));
79+
GetProcAddress(reshade_module, "ReShadeRegisterAddon"));
5880
if (!func(module, RESHADE_API_VERSION))
5981
return false;
6082

6183
#ifdef IMGUI_VERSION
6284
// Check that the ReShade module was built with imgui support
6385
const auto imgui_func = reinterpret_cast<const imgui_function_table *(*)(unsigned int)>(
64-
GetProcAddress(g_module_handle, "ReShadeGetImGuiFunctionTable"));
86+
GetProcAddress(reshade_module, "ReShadeGetImGuiFunctionTable"));
6587
if (imgui_func == nullptr)
6688
return false;
6789

@@ -77,65 +99,62 @@ namespace reshade
7799
/// <summary>
78100
/// Unregisters this module. Call this in 'DllMain' during process detach, after any of the other API functions.
79101
/// </summary>
102+
/// <param name="module">Handle of the current add-on module.</param>
80103
inline void unregister_addon(HMODULE module)
81104
{
82-
if (g_module_handle == nullptr)
105+
const HMODULE reshade_module = get_reshade_module_handle();
106+
if (reshade_module == nullptr)
83107
return;
84108

85109
const auto func = reinterpret_cast<bool(*)(HMODULE)>(
86-
GetProcAddress(g_module_handle, "ReShadeUnregisterAddon"));
110+
GetProcAddress(reshade_module, "ReShadeUnregisterAddon"));
87111
func(module);
88112
}
89113

90114
/// <summary>
91115
/// Registers a callback for the specified event (via template) with ReShade.
92116
/// <para>The callback function is then called whenever the application performs a task associated with this event (see also the <see cref="addon_event"/> enumeration).</para>
93117
/// </summary>
118+
/// <param name="callback">Pointer to the callback function.</param>
94119
template <reshade::addon_event ev>
95120
inline void register_event(typename reshade::addon_event_traits<ev>::decl callback)
96121
{
97122
static const auto func = reinterpret_cast<void(*)(reshade::addon_event, void *)>(
98-
GetProcAddress(g_module_handle, "ReShadeRegisterEvent"));
123+
GetProcAddress(get_reshade_module_handle(), "ReShadeRegisterEvent"));
99124
func(ev, static_cast<void *>(callback));
100125
}
101126
/// <summary>
102127
/// Unregisters a callback for the specified event (via template) that was previously registered via <see cref="register_event"/>.
103128
/// </summary>
129+
/// <param name="callback">Pointer to the callback function.</param>
104130
template <reshade::addon_event ev>
105131
inline void unregister_event(typename reshade::addon_event_traits<ev>::decl callback)
106132
{
107133
static const auto func = reinterpret_cast<void(*)(reshade::addon_event, void *)>(
108-
GetProcAddress(g_module_handle, "ReShadeUnregisterEvent"));
134+
GetProcAddress(get_reshade_module_handle(), "ReShadeUnregisterEvent"));
109135
func(ev, static_cast<void *>(callback));
110136
}
111137

112138
/// <summary>
113139
/// Registers an overlay with ReShade.
114140
/// <para>The callback function is then called whenever the ReShade overlay is visible and allows adding imgui widgets for user interaction.</para>
115141
/// </summary>
142+
/// <param name="title">A null-terminated title string, or <c>nullptr</c> to register a settings overlay for this add-on.</param>
143+
/// <param name="callback">Pointer to the callback function.</param>
116144
inline void register_overlay(const char *title, void(*callback)(reshade::api::effect_runtime *runtime, void *imgui_context))
117145
{
118-
static const auto func = reinterpret_cast<decltype(&register_overlay)>(
119-
GetProcAddress(g_module_handle, "ReShadeRegisterOverlay"));
146+
static const auto func = reinterpret_cast<void(*)(const char *, void(*)(reshade::api::effect_runtime *, void *))>(
147+
GetProcAddress(get_reshade_module_handle(), "ReShadeRegisterOverlay"));
120148
func(title, callback);
121149
}
122150
/// <summary>
123151
/// Unregisters an overlay that was previously registered via <see cref="register_overlay"/>.
124152
/// </summary>
153+
/// <param name="title">A null-terminated title string.</param>
125154
inline void unregister_overlay(const char *title)
126155
{
127-
static const auto func = reinterpret_cast<decltype(&unregister_overlay)>(
128-
GetProcAddress(g_module_handle, "ReShadeUnregisterOverlay"));
156+
static const auto func = reinterpret_cast<void(*)(const char *)>(
157+
GetProcAddress(get_reshade_module_handle(), "ReShadeUnregisterOverlay"));
129158
func(title);
130159
}
131160
}
132-
133-
// Define 'RESHADE_ADDON_IMPL' before including this header in exactly one source file to create the implementation
134-
#ifdef RESHADE_ADDON_IMPL
135-
136-
HMODULE g_module_handle = nullptr;
137-
#ifdef IMGUI_VERSION
138-
imgui_function_table g_imgui_function_table = {};
139-
#endif
140-
141-
#endif

source/addon.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,24 @@
55

66
#if RESHADE_ADDON
77

8-
#include <imgui.h>
98
#include "addon.hpp"
109
#include "dll_log.hpp"
10+
#include <imgui.h>
11+
#include <Windows.h>
1112

12-
extern "C" __declspec(dllexport) void ReShadeLogMessage(int level, const char *message)
13+
extern reshade::addon::info *find_addon(HMODULE module);
14+
15+
extern "C" __declspec(dllexport) void ReShadeLogMessage(HMODULE module, int level, const char *message)
1316
{
14-
reshade::log::message(static_cast<reshade::log::level>(level)) << "Add-on | " << message;
17+
std::string prefix;
18+
if (module != nullptr)
19+
{
20+
reshade::addon::info *const info = find_addon(module);
21+
if (info != nullptr)
22+
prefix = "[" + info->name + "] ";
23+
}
24+
25+
reshade::log::message(static_cast<reshade::log::level>(level)) << prefix << message;
1526
}
1627

1728
#if RESHADE_GUI

source/addon.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,20 @@ namespace reshade::addon
8686
{
8787
struct info
8888
{
89-
void *handle;
89+
void *handle = nullptr;
90+
9091
std::string name;
9192
std::string description;
93+
std::string file;
94+
std::string author;
95+
std::string version;
96+
97+
std::vector<std::pair<uint32_t, void *>> event_callbacks;
98+
#if RESHADE_GUI
99+
float settings_height = 0.0f;
100+
void(*settings_overlay_callback)(api::effect_runtime *, void *) = nullptr;
101+
std::vector<std::string> overlay_titles;
102+
#endif
92103
};
93104

94105
/// <summary>

0 commit comments

Comments
 (0)