diff --git a/GlosSITarget/GlosSITarget.vcxproj b/GlosSITarget/GlosSITarget.vcxproj
index 0da4432..560eab5 100644
--- a/GlosSITarget/GlosSITarget.vcxproj
+++ b/GlosSITarget/GlosSITarget.vcxproj
@@ -80,14 +80,14 @@
true
- ..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;$(ExternalIncludePath)
+ ..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;..\deps\subhook;$(ExternalIncludePath)
..\deps\SFML\out\build\x64-Debug\lib;$(LibraryPath)
false
true
false
- ..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\subhook;$(ExternalIncludePath)
+ ..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;..\deps\subhook;$(ExternalIncludePath)
..\deps\SFML\out\build\x64-Release\lib;$(LibraryPath)
@@ -122,7 +122,7 @@
Level3
true
- _DEBUG;_CONSOLE;SPDLOG_WCHAR_TO_UTF8_SUPPORT;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions)
+ _DEBUG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SUBHOOK_STATIC;%(PreprocessorDefinitions)
true
stdcpp20
@@ -138,7 +138,7 @@
true
true
true
- NDEBUG;_CONSOLE;SPDLOG_WCHAR_TO_UTF8_SUPPORT;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions)
+ NDEBUG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SUBHOOK_STATIC;%(PreprocessorDefinitions)
true
stdcpp20
@@ -150,12 +150,14 @@
+
+
diff --git a/GlosSITarget/GlosSITarget.vcxproj.filters b/GlosSITarget/GlosSITarget.vcxproj.filters
index 53f0b4b..299bf55 100644
--- a/GlosSITarget/GlosSITarget.vcxproj.filters
+++ b/GlosSITarget/GlosSITarget.vcxproj.filters
@@ -27,6 +27,9 @@
Source Files
+
+ Source Files
+
@@ -41,6 +44,9 @@
Header Files
+
+ Header Files
+
diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp
index 6bcc1da..ab3215f 100644
--- a/GlosSITarget/SteamTarget.cpp
+++ b/GlosSITarget/SteamTarget.cpp
@@ -24,16 +24,33 @@ limitations under the License.
#include
#include
+#include
+
+#ifdef _WIN32
+subhook::Hook getFgWinHook;
+static HWND target_hwnd = nullptr;
+
+HWND keepForegroundWindow()
+{
+ return target_hwnd;
+}
+
+#endif
+
SteamTarget::SteamTarget(int argc, char *argv[])
- : window_([this] { run_ = false; }),
+ : window_([this] { run_ = false; }, getScreenshotHotkey()),
detector_([this](bool overlay_open) { onOverlayChanged(overlay_open); }), target_window_handle_(window_.getSystemHandle())
{
+#ifdef _WIN32
+ target_hwnd = target_window_handle_;
+#endif
}
int SteamTarget::run()
{
run_ = true;
window_.setFpsLimit(90);
+ keepControllerConfig(true);
while (run_) {
detector_.update();
window_.update();
@@ -57,11 +74,17 @@ void SteamTarget::focusWindow(WindowHandle hndl)
{
#ifdef _WIN32
- //MH_DisableHook(&GetForegroundWindow); // TODO: when GetForegroundWindow hooked, unhook!
- // store last focused window for later restore
+ if (hndl == target_window_handle_) {
+ spdlog::info("Bring own window to foreground");
+ }
+ else {
+ spdlog::info("Bring window \"{:#x}\" to foreground", reinterpret_cast(hndl));
+ }
+
+ keepControllerConfig(false); // unhook GetForegroundWindow
last_foreground_window_ = GetForegroundWindow();
const DWORD fg_thread = GetWindowThreadProcessId(last_foreground_window_, nullptr);
- //MH_EnableHook(&GetForegroundWindow); // TODO: when GetForegroundWindow hooked, re-hook!
+ keepControllerConfig(true); // re-hook GetForegroundWindow
// lot's of ways actually bringing our window to foreground...
const DWORD current_thread = GetCurrentThreadId();
@@ -91,7 +114,9 @@ std::wstring SteamTarget::getSteamPath()
// TODO: check if keys/value exist
// steam should always be open and have written reg values...
winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam"};
- return key.GetStringValue(L"SteamPath");
+ const auto res = key.GetStringValue(L"SteamPath");
+ spdlog::info(L"Detected Steam Path: {}", res);
+ return res;
#else
return L""; // TODO
#endif
@@ -103,7 +128,9 @@ std::wstring SteamTarget::getSteamUserId()
// TODO: check if keys/value exist
// steam should always be open and have written reg values...
winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess"};
- return std::to_wstring(key.GetDwordValue(L"ActiveUser"));
+ const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser"));
+ spdlog::info(L"Detected Steam UserId: {}", res);
+ return res;
#else
return L""; // TODO
#endif
@@ -111,10 +138,7 @@ std::wstring SteamTarget::getSteamUserId()
std::vector SteamTarget::getOverlayHotkey()
{
- const auto steam_path = getSteamPath();
- const auto user_id = getSteamUserId();
-
- const auto config_path = steam_path + std::wstring(user_data_path_) + user_id + std::wstring(config_file_name_);
+ const auto config_path = steam_path_ + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_);
std::ifstream config_file(config_path);
// TODO: check if file exists
auto root = tyti::vdf::read(config_file);
@@ -125,7 +149,42 @@ std::vector SteamTarget::getOverlayHotkey()
// has anyone more than 4 keys to open overlay?!
std::smatch m;
if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) {
- return {"Shift", "KEY_TAB"};
+ spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab");
+ return {"Shift", "KEY_TAB"}; // default
+ }
+
+ std::vector res;
+ for (auto i = 1; i < m.size(); i++) {
+ const auto s = std::string(m[i]);
+ if (!s.empty()) {
+ res.push_back(s);
+ }
+ }
+ if (res.empty()) {
+ spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab");
+ return {"Shift", "KEY_TAB"}; // default
+ }
+ spdlog::info("Detected Overlay hotkey(s): {}", std::accumulate(
+ res.begin() + 1, res.end(), res[0],
+ [](auto acc, const auto curr) { return acc += "+" + curr; }));
+ return res;
+}
+
+std::vector SteamTarget::getScreenshotHotkey()
+{
+ const auto config_path = steam_path_ + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_);
+ std::ifstream config_file(config_path);
+ // TODO: check if file exists
+ auto root = tyti::vdf::read(config_file);
+
+ auto children = root.childs["system"];
+ auto hotkeys = children->attribs.at("InGameOverlayScreenshotHotKey");
+
+ // has anyone more than 4 keys to screenshot?!
+ std::smatch m;
+ if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) {
+ spdlog::warn("Couldn't detect overlay hotkey, using default: F12");
+ return {"KEY_F12"}; //default
}
std::vector res;
@@ -135,39 +194,63 @@ std::vector SteamTarget::getOverlayHotkey()
res.push_back(s);
}
}
- spdlog::info("Detected Overlay hotkeys: {}", std::accumulate(
- res.begin() + 1, res.end(), res[0],
- [](auto acc, const auto curr) { return acc += "+" + curr; }));
+ if (res.empty()) {
+ spdlog::warn("Couldn't detect overlay hotkey, using default: F12");
+ return {"KEY_F12"}; //default
+ }
+ spdlog::info("Detected screenshot hotkey(s): {}", std::accumulate(
+ res.begin() + 1, res.end(), res[0],
+ [](auto acc, const auto curr) { return acc += "+" + curr; }));
return res;
}
+void SteamTarget::keepControllerConfig(bool keep)
+{
+#ifdef _WIN32
+ if (keep && !getFgWinHook.IsInstalled()) {
+ spdlog::debug("Hooking GetForegroudnWindow (in own process)");
+ getFgWinHook.Install(&GetForegroundWindow, &keepForegroundWindow, subhook::HookFlags::HookFlag64BitOffset);
+ if (!getFgWinHook.IsInstalled()) {
+ spdlog::error("Couldn't install GetForegroundWindow hook!");
+ }
+ }
+ else if (!keep && getFgWinHook.IsInstalled()) {
+ spdlog::debug("Un-Hooking GetForegroudnWindow (in own process)");
+ getFgWinHook.Remove();
+ if (getFgWinHook.IsInstalled()) {
+ spdlog::error("Couldn't un-install GetForegroundWindow hook!");
+ }
+ }
+#endif
+}
+
void SteamTarget::overlayHotkeyWorkaround()
{
static bool pressed = false;
- if (std::all_of(overlay_hotkey_.begin(), overlay_hotkey_.end(),
- [](const auto &key) {
- return sf::Keyboard::isKeyPressed(keymap::sfkey[key]);
- })) {
+ if (std::ranges::all_of(overlay_hotkey_,
+ [](const auto &key) {
+ return sf::Keyboard::isKeyPressed(keymap::sfkey[key]);
+ })) {
+ spdlog::debug("Detected overlay hotkey(s)");
pressed = true;
- std::for_each(
- overlay_hotkey_.begin(), overlay_hotkey_.end(), [this](const auto &key) {
+ std::ranges::for_each(overlay_hotkey_, [this](const auto &key) {
#ifdef _WIN32
- PostMessage(target_window_handle_, WM_KEYDOWN, keymap::winkey[key], 0);
+ PostMessage(target_window_handle_, WM_KEYDOWN, keymap::winkey[key], 0);
#else
#endif
- });
+ });
spdlog::debug("Sending Overlay KeyDown events...");
- } else if (pressed) {
+ }
+ else if (pressed) {
pressed = false;
- std::for_each(
- overlay_hotkey_.begin(), overlay_hotkey_.end(), [this](const auto &key) {
+ std::ranges::for_each(overlay_hotkey_, [this](const auto &key) {
#ifdef _WIN32
- PostMessage(target_window_handle_, WM_KEYUP, keymap::winkey[key], 0);
+ PostMessage(target_window_handle_, WM_KEYUP, keymap::winkey[key], 0);
#else
#endif
- });
+ });
spdlog::debug("Sending Overlay KeyUp events...");
}
}
diff --git a/GlosSITarget/SteamTarget.h b/GlosSITarget/SteamTarget.h
index d46ef0f..1124ceb 100644
--- a/GlosSITarget/SteamTarget.h
+++ b/GlosSITarget/SteamTarget.h
@@ -16,8 +16,8 @@ limitations under the License.
#pragma once
#include "OverlayDetector.h"
-#include "TargetWindow.h"
+#include "TargetWindow.h"
class SteamTarget {
public:
@@ -29,8 +29,16 @@ class SteamTarget {
void focusWindow(WindowHandle hndl);
std::wstring getSteamPath();
std::wstring getSteamUserId();
+
+ std::wstring steam_path_ = getSteamPath();
+ std::wstring steam_user_id_ = getSteamUserId();
+
std::vector getOverlayHotkey();
+ std::vector getScreenshotHotkey();
+ // Keep controllerConfig even is window is switched.
+ // On Windoze hooking "GetForeGroundWindow" is enough;
+ void keepControllerConfig(bool keep);
/*
* Run once per frame
* detects steam configured overlay hotkey, and simulates key presses to window
@@ -49,5 +57,6 @@ class SteamTarget {
static constexpr std::wstring_view user_data_path_ = L"/userdata/";
static constexpr std::wstring_view config_file_name_ = L"/config/localconfig.vdf";
- static constexpr std::string_view hotkey_name_ = "InGameOverlayShortcutKey ";
+ static constexpr std::string_view overlay_hotkey_name_ = "InGameOverlayShortcutKey ";
+ static constexpr std::string_view screenshot_hotkey_name_ = "InGameOverlayScreenshotHotKey ";
};
diff --git a/GlosSITarget/TargetWindow.cpp b/GlosSITarget/TargetWindow.cpp
index ec12c51..448ef3b 100644
--- a/GlosSITarget/TargetWindow.cpp
+++ b/GlosSITarget/TargetWindow.cpp
@@ -15,20 +15,24 @@ limitations under the License.
*/
#include "TargetWindow.h"
+#include "steam_sf_keymap.h"
+
#include
#include
#include
+#include
#ifdef _WIN32
+#include
#include
#include
#endif
static const bool DEV_MODE = false;
-TargetWindow::TargetWindow(std::function on_close)
- : on_close_(std::move(on_close))
+TargetWindow::TargetWindow(std::function on_close, std::vector screenshot_hotkey)
+ : on_close_(std::move(on_close)), screenshot_keys_(std::move(screenshot_hotkey))
{
if (DEV_MODE) {
window_.create(sf::VideoMode{1920, 1080}, "GlosSITarget", sf::Style::Default);
@@ -82,6 +86,7 @@ void TargetWindow::update()
on_close_();
}
}
+
if (DEV_MODE) {
window_.clear(sf::Color(0, 0, 0, 128));
}
@@ -89,6 +94,7 @@ void TargetWindow::update()
window_.clear(sf::Color::Transparent);
}
+ screenShotWorkaround();
window_.display();
}
@@ -98,6 +104,79 @@ void TargetWindow::close()
on_close_();
}
+void TargetWindow::screenShotWorkaround()
+{
+#ifdef _WIN32
+ if (std::ranges::all_of(screenshot_keys_,
+ [](const auto &key) {
+ return sf::Keyboard::isKeyPressed(keymap::sfkey[key]);
+ })) {
+ spdlog::debug("Detected screenshot hotkey(s); Taking screenshot");
+
+ // stolen from: https://en.sfml-dev.org/forums/index.php?topic=14323.15
+ // no time to do this shit.
+ HDC hScreenDC = GetDC(nullptr);
+ HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
+ int width = GetDeviceCaps(hScreenDC, HORZRES);
+ int height = GetDeviceCaps(hScreenDC, VERTRES);
+ HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
+ auto hOldBitmap = static_cast(SelectObject(hMemoryDC, hBitmap));
+ BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
+ hBitmap = static_cast(SelectObject(hMemoryDC, hOldBitmap));
+ BITMAP bm;
+ GetObject(hBitmap, sizeof(bm), &bm);
+ BITMAPINFO bmpInfo;
+ bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmpInfo.bmiHeader.biWidth = bm.bmWidth;
+ bmpInfo.bmiHeader.biHeight = -bm.bmHeight;
+ bmpInfo.bmiHeader.biPlanes = 1;
+ bmpInfo.bmiHeader.biBitCount = 32;
+ bmpInfo.bmiHeader.biCompression = BI_RGB;
+ bmpInfo.bmiHeader.biSizeImage = 0;
+ bmpInfo.bmiHeader.biClrImportant = 0;
+ std::vector pixel;
+ pixel.resize(bm.bmWidth * bm.bmHeight);
+ sf::Image captureImage;
+ captureImage.create(bm.bmWidth, bm.bmHeight, sf::Color::Black);
+ GetDIBits(hMemoryDC, hBitmap, 0, bm.bmHeight, pixel.data(), &bmpInfo, DIB_RGB_COLORS);
+ unsigned int j = 0;
+ for (unsigned int y = 0; y < bm.bmHeight; ++y) {
+ for (unsigned int x = 0; x < bm.bmWidth; ++x) {
+ const COLORREF this_color = pixel[j++];
+ captureImage.setPixel(x, y, sf::Color(GetBValue(this_color), GetGValue(this_color), GetRValue(this_color)));
+ }
+ }
+ ReleaseDC(NULL, hScreenDC);
+ DeleteObject(hBitmap);
+ DeleteDC(hMemoryDC);
+ DeleteDC(hScreenDC);
+
+ sf::Sprite sprite;
+ sf::Texture texture;
+ texture.loadFromImage(captureImage);
+ sprite.setTexture(texture);
+
+ spdlog::debug("Sending screenshot key events and rendering screen...");
+ std::ranges::for_each(screenshot_keys_, [this](const auto &key) {
+ PostMessage(window_.getSystemHandle(), WM_KEYDOWN, keymap::winkey[key], 0);
+ });
+ std::ranges::for_each(screenshot_keys_, [this](const auto &key) {
+ PostMessage(window_.getSystemHandle(), WM_KEYUP, keymap::winkey[key], 0);
+ });
+ //actually run event loop, so steam gets notified about keys.
+ sf::Event event{};
+ while (window_.pollEvent(event)) {
+ }
+ // steam takes screenshot on next frame, so render our screenshot and dipslay...
+ window_.clear(sf::Color::Black);
+ window_.draw(sprite);
+ window_.display();
+ // finally, draw another transparent frame.
+ window_.clear(sf::Color::Transparent);
+ }
+#endif
+}
+
WindowHandle TargetWindow::getSystemHandle() const
{
return window_.getSystemHandle();
diff --git a/GlosSITarget/TargetWindow.h b/GlosSITarget/TargetWindow.h
index 620badb..0057b07 100644
--- a/GlosSITarget/TargetWindow.h
+++ b/GlosSITarget/TargetWindow.h
@@ -29,17 +29,32 @@ using WindowHandle = int; // ???
class TargetWindow {
public:
- explicit TargetWindow(std::function on_close = []() {});
+ explicit TargetWindow(
+ std::function on_close = []() {}, std::vector screenshot_hotkey = {"KEY_F12"});
void setFpsLimit(unsigned int fps_limit);
void setClickThrough(bool click_through);
void update();
void close();
+ /*
+ * Run once per frame
+ * - detects steam configured screenshot hotkey
+ * - takes actual screenshot
+ * - renders it to window
+ * - simulates screenshot keys
+ * - Wait a few millis...
+ * (- steam takes screenshot)
+ * - return to normal
+ *
+ */
+ void screenShotWorkaround();
+
WindowHandle getSystemHandle() const;
private:
const std::function on_close_;
sf::RenderWindow window_;
+ std::vector screenshot_keys_;
};
diff --git a/GlosSITarget/main.cpp b/GlosSITarget/main.cpp
index 4f03975..99bd64b 100644
--- a/GlosSITarget/main.cpp
+++ b/GlosSITarget/main.cpp
@@ -24,19 +24,21 @@ limitations under the License.
#include
#include
-//int CALLBACK WinMain(
-// _In_ HINSTANCE hInstance,
-// _In_ HINSTANCE hPrevInstance,
-// _In_ LPSTR lpCmdLine,
-// _In_ int nCmdShow
-//)
-//{
-// SteamTarget target(__argc, __argv);
-// target.init();
-// return SteamTarget::exec();
-//}
-
+#define CONSOLE
+#ifdef _WIN32
+#ifdef CONSOLE
+int main(int argc, char *argv[])
+#else
+int CALLBACK WinMain(
+ _In_ HINSTANCE hInstance,
+ _In_ HINSTANCE hPrevInstance,
+ _In_ LPSTR lpCmdLine,
+ _In_ int nCmdShow
+)
+#endif
+#else
int main(int argc, char *argv[])
+#endif
{
const auto console_sink = std::make_shared();
console_sink->set_level(spdlog::level::trace);
@@ -51,8 +53,11 @@ int main(int argc, char *argv[])
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::info);
spdlog::set_default_logger(logger);
-
+#ifdef _WIN32
+ SteamTarget target(__argc, __argv);
+#else
SteamTarget target(argc, argv);
+#endif
const auto exit = target.run();
spdlog::shutdown();
return exit;
diff --git a/GlosSITarget/steam_sf_keymap.h b/GlosSITarget/steam_sf_keymap.h
index 8f6728d..4984037 100644
--- a/GlosSITarget/steam_sf_keymap.h
+++ b/GlosSITarget/steam_sf_keymap.h
@@ -5,11 +5,13 @@
#define QQ(x) #x
#define QUOTE(x) QQ(x)
-#define KEYCONVSF(KEY) \
- { QUOTE(KEY), sf::Keyboard::Key::KEY }
+#define KEYCONVSF(KEY) \
+ { \
+ QUOTE(KEY), sf::Keyboard::Key::KEY \
+ }
namespace keymap {
-std::unordered_map sfkey = {
+static std::unordered_map sfkey = {
{"Shift", sf::Keyboard::Key::LShift},
{"Alt", sf::Keyboard::Key::LAlt},
{"Ctrl", sf::Keyboard::Key::LControl},
@@ -56,20 +58,31 @@ std::unordered_map sfkey = {
KEYCONVSF(W),
KEYCONVSF(X),
KEYCONVSF(Y),
- KEYCONVSF(Z)
-};
+ KEYCONVSF(Z),
+ {"KEY_F1", sf::Keyboard::Key::F1},
+ {"KEY_F2", sf::Keyboard::Key::F2},
+ {"KEY_F3", sf::Keyboard::Key::F3},
+ {"KEY_F4", sf::Keyboard::Key::F4},
+ {"KEY_F5", sf::Keyboard::Key::F5},
+ {"KEY_F6", sf::Keyboard::Key::F6},
+ {"KEY_F7", sf::Keyboard::Key::F7},
+ {"KEY_F8", sf::Keyboard::Key::F8},
+ {"KEY_F9", sf::Keyboard::Key::F9},
+ {"KEY_F10", sf::Keyboard::Key::F10},
+ {"KEY_F11", sf::Keyboard::Key::F11},
+ {"KEY_F12", sf::Keyboard::Key::F12}};
#ifdef _WIN32
#define NOMINMAX
#include
//yep.. there are smarter ways to tho this...
-std::unordered_map winkey = {
+static std::unordered_map winkey = {
{"Shift", VK_SHIFT},
{"Alt", VK_MENU},
{"Ctrl", VK_CONTROL},
{"Del", VK_DELETE},
- {"Ins",VK_INSERT},
- {"Home",VK_HOME},
+ {"Ins", VK_INSERT},
+ {"Home", VK_HOME},
{"Space", VK_SPACE},
{"Backspace", VK_BACK},
{"Enter", VK_RETURN},
@@ -110,8 +123,18 @@ std::unordered_map winkey = {
{"KEY_W", 0x57},
{"KEY_X", 0x58},
{"KEY_Y", 0x59},
- {"KEY_Z", 0x5A}
-
-};
+ {"KEY_Z", 0x5A},
+ {"KEY_F1", VK_F1},
+ {"KEY_F2", VK_F2},
+ {"KEY_F3", VK_F3},
+ {"KEY_F4", VK_F4},
+ {"KEY_F5", VK_F5},
+ {"KEY_F6", VK_F6},
+ {"KEY_F7", VK_F7},
+ {"KEY_F8", VK_F8},
+ {"KEY_F9", VK_F9},
+ {"KEY_F10", VK_F10},
+ {"KEY_F11", VK_F11},
+ {"KEY_F12", VK_F12}};
#endif
-}
+} // namespace keymap