From 86f94bc3372dbe427dac5874394bac60f66f86e1 Mon Sep 17 00:00:00 2001 From: Nemirtingas Date: Sat, 31 Aug 2019 20:49:46 +0200 Subject: [PATCH] Linux hooks --- overlay_experimental/Base_Hook.cpp | 73 ++++++--- overlay_experimental/OpenGLX_Hook.cpp | 150 ++++++++++++++++++ overlay_experimental/OpenGLX_Hook.h | 45 ++++++ overlay_experimental/X11_Hook.cpp | 214 ++++++++++++++++++++++++++ overlay_experimental/X11_Hook.h | 50 ++++++ 5 files changed, 514 insertions(+), 18 deletions(-) create mode 100644 overlay_experimental/OpenGLX_Hook.cpp create mode 100644 overlay_experimental/OpenGLX_Hook.h create mode 100644 overlay_experimental/X11_Hook.cpp create mode 100644 overlay_experimental/X11_Hook.h diff --git a/overlay_experimental/Base_Hook.cpp b/overlay_experimental/Base_Hook.cpp index 34ff4f6..418f50b 100644 --- a/overlay_experimental/Base_Hook.cpp +++ b/overlay_experimental/Base_Hook.cpp @@ -1,20 +1,13 @@ #include "Base_Hook.h" #include "Hook_Manager.h" -#ifndef NO_OVERLAY - #include + +#ifndef NO_OVERLAY +#ifdef STEAM_WIN32 + #include "../detours/detours.h" -Base_Hook::Base_Hook(): - _library(nullptr) -{} - -Base_Hook::~Base_Hook() -{ - UnhookAll(); -} - void Base_Hook::BeginHook() { DetourTransactionBegin(); @@ -26,6 +19,12 @@ void Base_Hook::EndHook() DetourTransactionCommit(); } +void Base_Hook::HookFunc(std::pair hook) +{ + if( DetourAttach(hook.first, hook.second) == 0 ) + _hooked_funcs.emplace_back(hook); +} + void Base_Hook::UnhookAll() { if (_hooked_funcs.size()) @@ -39,15 +38,53 @@ void Base_Hook::UnhookAll() } } +#else +#include "Linux_Detour.h" + +void Base_Hook::BeginHook() +{ + Linux_Detour::transaction_begin(); + Linux_Detour::update_thread(pthread_self()); +} + +void Base_Hook::EndHook() +{ + Linux_Detour::transaction_commit(); +} + +void Base_Hook::HookFunc(std::pair hook) +{ + if( Linux_Detour::hook_func(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& hook) { + Linux_Detour::unhook_func(hook.first, hook.second); + }); + EndHook(); + _hooked_funcs.clear(); + } +} + +#endif + +Base_Hook::Base_Hook(): + _library(nullptr) +{} + +Base_Hook::~Base_Hook() +{ + UnhookAll(); +} + const char* Base_Hook::get_lib_name() const { return ""; } -void Base_Hook::HookFunc(std::pair hook) -{ - if( DetourAttach(hook.first, hook.second) == 0 ) - _hooked_funcs.emplace_back(hook); -} - -#endif//NO_OVERLAY \ No newline at end of file +#endif//NO_OVERLAY diff --git a/overlay_experimental/OpenGLX_Hook.cpp b/overlay_experimental/OpenGLX_Hook.cpp new file mode 100644 index 0000000..a4fa3da --- /dev/null +++ b/overlay_experimental/OpenGLX_Hook.cpp @@ -0,0 +1,150 @@ +#include "OpenGLX_Hook.h" +#include "X11_Hook.h" +#include "Renderer_Detector.h" +#include "../dll/dll.h" + +#ifndef NO_OVERLAY + +#include +#include + +#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*&)_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(); + + 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) +{ + PRINT_DEBUG("Called SwapBuffer hook"); + + if( ! initialized ) + { + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + io.IniFilename = NULL; + + ImGui_ImplOpenGL3_Init(); + initialized = true; + } + + if( initialized ) + { + ImGuiIO& io = ImGui::GetIO(); + + GLint m_viewport[4]; + glGetIntegerv( GL_VIEWPORT, m_viewport ); + int width = m_viewport[2]; + int height = m_viewport[3]; + + ImGui_ImplOpenGL3_NewFrame(); + X11_Hook::Inst()->prepareForOverlay(display); + + ImGui::NewFrame(); + + get_steam_client()->steam_overlay->OverlayProc(width, height); + + ImGui::EndFrame(); + + ImGui::Render(); + + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + } +} + +void OpenGLX_Hook::MyglXSwapBuffers(Display* display, GLXDrawable drawable) +{ + OpenGLX_Hook::Inst()->prepareForOverlay(display); + 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(); + } + + //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 diff --git a/overlay_experimental/OpenGLX_Hook.h b/overlay_experimental/OpenGLX_Hook.h new file mode 100644 index 0000000..271d50f --- /dev/null +++ b/overlay_experimental/OpenGLX_Hook.h @@ -0,0 +1,45 @@ +#ifndef __INCLUDED_OPENGLX_HOOK_H__ +#define __INCLUDED_OPENGLX_HOOK_H__ + +#include "Base_Hook.h" + +#ifdef __LINUX__ +#ifndef NO_OVERLAY +#include +#include + +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; + + // Functions + OpenGLX_Hook(); + + void resetRenderState(); + void prepareForOverlay(Display* display); + + // Hook to render functions + static void MyglXSwapBuffers(Display* display, GLXDrawable drawable); + + decltype(glXSwapBuffers)* _glXSwapBuffers; + +public: + 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__ diff --git a/overlay_experimental/X11_Hook.cpp b/overlay_experimental/X11_Hook.cpp new file mode 100644 index 0000000..9ad5de8 --- /dev/null +++ b/overlay_experimental/X11_Hook.cpp @@ -0,0 +1,214 @@ +#include "X11_Hook.h" +#include "Renderer_Detector.h" +#include "../dll/dll.h" + +#ifndef NO_OVERLAY + +#include +#include + +#include +#include +#include + +extern int ImGui_ImplX11_EventHandler(XEvent &event); + +X11_Hook* X11_Hook::_inst = nullptr; + +bool X11_Hook::start_hook() +{ + bool res = true; + if (!hooked) + { + res = false; + std::ifstream flibrary("/proc/self/maps"); + std::string line; + while( std::getline(flibrary, line) ) + { + if( strcasestr(line.c_str(), DLL_NAME) != nullptr ) + { + _library = dlopen(line.substr(line.find('/')).c_str(), RTLD_NOW); + + if( _library != nullptr ) + { + // Hook only this + _XEventsQueued = (decltype(_XEventsQueued))dlsym(_library, "XEventsQueued"); + // Thoses are needed to ignore some events when overlay is on + _XPeekEvent = (decltype(_XPeekEvent))dlsym(_library, "XPeekEvent"); + _XNextEvent = (decltype(_XNextEvent))dlsym(_library, "XNextEvent"); + _XKeysymToKeycode = (decltype(_XKeysymToKeycode))dlsym(_library, "XKeysymToKeycode"); + _XLookupKeysym = (decltype(_XLookupKeysym))dlsym(_library, "XLookupKeysym"); + + if( _XEventsQueued != nullptr && _XPeekEvent != nullptr + && _XNextEvent != nullptr && _XKeysymToKeycode != nullptr + && _XLookupKeysym != nullptr ) + { + PRINT_DEBUG("Hooked X11\n"); + hooked = true; + + BeginHook(); + HookFuncs( + std::make_pair(&(void*&)_XEventsQueued, (void*)&X11_Hook::MyXEventsQueued) + ); + + EndHook(); + break; + } + dlclose(_library); + } + } + } + } + return res; +} + +void X11_Hook::resetRenderState() +{ + if (initialized) + { + initialized = false; + ImGui_ImplX11_Shutdown(); + } +} + +void X11_Hook::prepareForOverlay(Display *display) +{ + if (!initialized) + { + ImGui_ImplX11_Init(display); + + ImGuiIO &io = ImGui::GetIO(); + io.KeyMap[ImGuiKey_Tab] = _XKeysymToKeycode(display, XK_Tab); + io.KeyMap[ImGuiKey_LeftArrow] = _XKeysymToKeycode(display, XK_Left); + io.KeyMap[ImGuiKey_RightArrow] = _XKeysymToKeycode(display, XK_Right); + io.KeyMap[ImGuiKey_UpArrow] = _XKeysymToKeycode(display, XK_Up); + io.KeyMap[ImGuiKey_DownArrow] = _XKeysymToKeycode(display, XK_Down); + io.KeyMap[ImGuiKey_PageUp] = _XKeysymToKeycode(display, XK_Prior); + io.KeyMap[ImGuiKey_PageDown] = _XKeysymToKeycode(display, XK_Next); + io.KeyMap[ImGuiKey_Home] = _XKeysymToKeycode(display, XK_Home); + io.KeyMap[ImGuiKey_End] = _XKeysymToKeycode(display, XK_End); + io.KeyMap[ImGuiKey_Insert] = _XKeysymToKeycode(display, XK_Insert); + io.KeyMap[ImGuiKey_Delete] = _XKeysymToKeycode(display, XK_Delete); + io.KeyMap[ImGuiKey_Backspace] = _XKeysymToKeycode(display, XK_BackSpace); + io.KeyMap[ImGuiKey_Space] = _XKeysymToKeycode(display, XK_space); + io.KeyMap[ImGuiKey_Enter] = _XKeysymToKeycode(display, XK_Return); + io.KeyMap[ImGuiKey_Escape] = _XKeysymToKeycode(display, XK_Escape); + io.KeyMap[ImGuiKey_A] = _XKeysymToKeycode(display, XK_A); + io.KeyMap[ImGuiKey_C] = _XKeysymToKeycode(display, XK_C); + io.KeyMap[ImGuiKey_V] = _XKeysymToKeycode(display, XK_V); + io.KeyMap[ImGuiKey_X] = _XKeysymToKeycode(display, XK_X); + io.KeyMap[ImGuiKey_Y] = _XKeysymToKeycode(display, XK_Y); + io.KeyMap[ImGuiKey_Z] = _XKeysymToKeycode(display, XK_Z); + + 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: case ClientMessage: + return true; + } + return false; +} + +int X11_Hook::MyXEventsQueued(Display *display, int mode) +{ + static Time prev_time = {}; + X11_Hook* inst = X11_Hook::Inst(); + XEvent event = {}; + + int res = inst->_XEventsQueued(display, mode); + + if( res > 0 ) + { + /// The XPeekEvent function returns the first event from the event queue, + /// but it does not remove the event from the queue. If the queue is + /// empty, XPeekEvent flushes the output buffer and blocks until an event + /// is received. + inst->_XPeekEvent(display, &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(display, XK_Tab) && event.xkey.state & ShiftMask) + { + // if key TAB is help, 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(display, XK_Tab)) + { + prev_time = event.xkey.time; + } + + if (show) + { + ImGui_ImplX11_EventHandler(event); + + if (IgnoreEvent(event)) + { + inst->_XNextEvent(display, &event); + res = 0; + } + } + } + // XEventsQueued returns the num of events available. + // Usually, games tend to read all events queued before calling again XEventsQueued + // making us unavailable to intercept undesired events + return res; +} + +///////////////////////////////////////////////////////////////////////////////////// + +X11_Hook::X11_Hook() : + initialized(false), + hooked(false) +{ + //_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 diff --git a/overlay_experimental/X11_Hook.h b/overlay_experimental/X11_Hook.h new file mode 100644 index 0000000..5c6228d --- /dev/null +++ b/overlay_experimental/X11_Hook.h @@ -0,0 +1,50 @@ +#ifndef __INCLUDED_X11_HOOK_H__ +#define __INCLUDED_X11_HOOK_H__ + +#include "Base_Hook.h" + +#ifdef __LINUX__ +#ifndef NO_OVERLAY + +#include // XEvent types +#include // XEvent structure + +class X11_Hook : public Base_Hook +{ +public: + static constexpr const char* DLL_NAME = "libX11.so"; + +private: + static X11_Hook* _inst; + + // Variables + bool hooked; + bool initialized; + + // Functions + X11_Hook(); + + // Hook to X11 window messages + static int MyXEventsQueued(Display * display, int mode); + + decltype(XEventsQueued)* _XEventsQueued; + + decltype(XPeekEvent)* _XPeekEvent; + decltype(XNextEvent)* _XNextEvent; + decltype(XKeysymToKeycode)* _XKeysymToKeycode; + decltype(XLookupKeysym)* _XLookupKeysym; + +public: + virtual ~X11_Hook(); + + void resetRenderState(); + void prepareForOverlay(Display *display); + + bool start_hook(); + static X11_Hook* Inst(); + virtual const char* get_lib_name() const; +}; + +#endif//NO_OVERLAY +#endif//__LINUX__ +#endif//__INCLUDED_X11_HOOK_H__