From ecd753422b50850c13c286034255499981434504 Mon Sep 17 00:00:00 2001 From: Nemirtingas Date: Tue, 3 Sep 2019 10:32:32 +0200 Subject: [PATCH] Updated Linux X11 & OLGX hooks. Use LD_PRELOAD=$PWD/libsteam_api.so app_name to load overlay. --- overlay_experimental/Renderer_Detector.cpp | 180 +++++++++++++++----- overlay_experimental/linux/OpenGLX_Hook.cpp | 20 +-- overlay_experimental/linux/OpenGLX_Hook.h | 6 +- overlay_experimental/linux/X11_Hook.cpp | 136 +++++++-------- overlay_experimental/linux/X11_Hook.h | 29 +++- 5 files changed, 232 insertions(+), 139 deletions(-) diff --git a/overlay_experimental/Renderer_Detector.cpp b/overlay_experimental/Renderer_Detector.cpp index 85e4548..18b8446 100644 --- a/overlay_experimental/Renderer_Detector.cpp +++ b/overlay_experimental/Renderer_Detector.cpp @@ -509,7 +509,13 @@ void Renderer_Detector::find_renderer_proc(Renderer_Detector* _this) Hook_Manager& hm = Hook_Manager::Inst(); hm.AddHook(_this->rendererdetect_hook); - std::vector const libraries = { "opengl32.dll", "d3d12.dll", "d3d11.dll", "d3d10.dll", "d3d9.dll" }; + std::vector 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()) { @@ -615,8 +621,12 @@ Renderer_Detector::Renderer_Detector(): #include #include -static decltype(glXSwapBuffers)* _glXSwapBuffers = nullptr; +extern "C" void *_dl_sym(void *, const char *, void *); +static decltype(glXGetProcAddress)* real_glXGetProcAddress = nullptr; +static decltype(glXGetProcAddressARB)* real_glXGetProcAddressARB = nullptr; + +/* void Renderer_Detector::MyglXSwapBuffers(Display *dpy, GLXDrawable drawable) { Renderer_Detector& inst = Renderer_Detector::Inst(); @@ -653,7 +663,7 @@ void Renderer_Detector::hook_openglx(const char* libname) } if (glXSwapBuffers != nullptr) { - PRINT_DEBUG("Hooked glXMakeCurrent to detect OpenGLX\n"); + PRINT_DEBUG("Hooked glXSwapBuffers to detect OpenGLX\n"); _oglx_hooked = true; auto h = OpenGLX_Hook::Inst(); @@ -663,7 +673,7 @@ void Renderer_Detector::hook_openglx(const char* libname) } else { - PRINT_DEBUG("Failed to Hook glXMakeCurrent to detect OpenGLX\n"); + PRINT_DEBUG("Failed to Hook glXSwapBuffers to detect OpenGLX\n"); } } } @@ -673,54 +683,15 @@ void Renderer_Detector::create_hook(const char* libname) if (strcasestr(libname, OpenGLX_Hook::DLL_NAME) != nullptr) hook_openglx(libname); } +*/ 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 const libraries = { "libGLX.so" }; - - while (!_this->_renderer_found && !_this->stop_retry()) - { - std::ifstream flibrary("/proc/self/maps"); - std::string line; - while( std::getline(flibrary, line) ) - { - std::for_each(libraries.begin(), libraries.end(), [&line, &_this]( std::string const& library ) - { - if( std::search(line.begin(), line.end(), library.begin(), library.end()) != line.end() ) - { - _this->create_hook(line.substr(line.find('/')).c_str()); - } - }); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - - if (_this->game_renderer == nullptr) // Couldn't hook renderer - { - hm.RemoveHook(X11_Hook::Inst()); - } - else - { - hm.AddHook(X11_Hook::Inst()); - } - if (_this->_oglx_hooked) - { - auto h = OpenGLX_Hook::Inst(); - if (h != _this->game_renderer) - { - _this->_oglx_hooked = false; - hm.RemoveHook(h); - } - } - - delete _this->_hook_thread; - _this->_hook_thread = nullptr; + */ } Renderer_Detector::Renderer_Detector(): @@ -732,6 +703,123 @@ Renderer_Detector::Renderer_Detector(): game_renderer(nullptr) {} +static decltype(dlsym)* real_dlsym = nullptr; + +// hook library static loading +extern "C" int XEventsQueued(Display *display, int mode) +{ + auto h = X11_Hook::Inst(); + if( h->get_XEventsQueued() == nullptr ) + h->loadXEventsQueued((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->get_XPeekEvent() == nullptr ) +// h->loadXPeekEvent((decltype(XPeekEvent)*)real_dlsym(RTLD_NEXT, "XPeekEvent")); +// +// return X11_Hook::MyXPeekEvent(display, event); +//} + +//extern "C" int XNextEvent(Display *display, XEvent *event) +//{ +// auto h = X11_Hook::Inst(); +// if( h->get_XNextEvent() == nullptr ) +// h->loadXNextEvent((decltype(XNextEvent)*)real_dlsym(RTLD_NEXT, "XNextEvent")); +// +// return X11_Hook::MyXNextEvent(display, event); +//} + +extern "C" int XPending(Display *display) +{ + auto h = X11_Hook::Inst(); + if( h->get_XPending() == nullptr ) + h->loadXPending((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; +} + +extern "C" void* dlsym(void* handle, const char* name) +{ + if (real_dlsym == nullptr) + real_dlsym = (decltype(dlsym)*)_dl_sym(RTLD_NEXT, "dlsym", reinterpret_cast(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) diff --git a/overlay_experimental/linux/OpenGLX_Hook.cpp b/overlay_experimental/linux/OpenGLX_Hook.cpp index bc6f114..86b884a 100644 --- a/overlay_experimental/linux/OpenGLX_Hook.cpp +++ b/overlay_experimental/linux/OpenGLX_Hook.cpp @@ -30,12 +30,14 @@ bool OpenGLX_Hook::start_hook() 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(); } @@ -55,7 +57,7 @@ void OpenGLX_Hook::resetRenderState() if (initialized) { ImGui_ImplOpenGL3_Shutdown(); - //X11_Hook::Inst()->resetRenderState(); + X11_Hook::Inst()->resetRenderState(); ImGui::DestroyContext(); initialized = false; @@ -63,10 +65,13 @@ void OpenGLX_Hook::resetRenderState() } // 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) +void OpenGLX_Hook::prepareForOverlay(Display* display, GLXDrawable drawable) { PRINT_DEBUG("Called SwapBuffer hook"); + if( (Window)drawable != X11_Hook::Inst()->get_game_wnd() ) + resetRenderState(); + if( ! initialized ) { ImGui::CreateContext(); @@ -81,17 +86,12 @@ void OpenGLX_Hook::prepareForOverlay(Display* display) { 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); + X11_Hook::Inst()->prepareForOverlay(display, (Window)drawable); ImGui::NewFrame(); - get_steam_client()->steam_overlay->OverlayProc(width, height); + get_steam_client()->steam_overlay->OverlayProc(io.DisplaySize.x, io.DisplaySize.y); ImGui::EndFrame(); @@ -103,7 +103,7 @@ void OpenGLX_Hook::prepareForOverlay(Display* display) void OpenGLX_Hook::MyglXSwapBuffers(Display* display, GLXDrawable drawable) { - OpenGLX_Hook::Inst()->prepareForOverlay(display); + OpenGLX_Hook::Inst()->prepareForOverlay(display, drawable); OpenGLX_Hook::Inst()->_glXSwapBuffers(display, drawable); } diff --git a/overlay_experimental/linux/OpenGLX_Hook.h b/overlay_experimental/linux/OpenGLX_Hook.h index 0a204c4..b440961 100644 --- a/overlay_experimental/linux/OpenGLX_Hook.h +++ b/overlay_experimental/linux/OpenGLX_Hook.h @@ -24,14 +24,16 @@ private: OpenGLX_Hook(); void resetRenderState(); - void prepareForOverlay(Display* display); + void prepareForOverlay(Display* display, GLXDrawable drawable); // Hook to render functions - static void MyglXSwapBuffers(Display* display, GLXDrawable drawable); + decltype(glXSwapBuffers)* _glXSwapBuffers; public: + static void MyglXSwapBuffers(Display* display, GLXDrawable drawable); + virtual ~OpenGLX_Hook(); bool start_hook(); diff --git a/overlay_experimental/linux/X11_Hook.cpp b/overlay_experimental/linux/X11_Hook.cpp index dbb9a83..65d755d 100644 --- a/overlay_experimental/linux/X11_Hook.cpp +++ b/overlay_experimental/linux/X11_Hook.cpp @@ -21,44 +21,8 @@ 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); - } - } - } + PRINT_DEBUG("Hooked X11\n"); + hooked = true; } return res; } @@ -67,44 +31,24 @@ void X11_Hook::resetRenderState() { if (initialized) { + game_wnd = 0; initialized = false; ImGui_ImplX11_Shutdown(); } } -void X11_Hook::prepareForOverlay(Display *display) +void X11_Hook::prepareForOverlay(Display *display, Window wnd) { 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); + game_wnd = wnd; initialized = true; } - ImGui_ImplX11_NewFrame(); + ImGui_ImplX11_NewFrame((void*)wnd); } ///////////////////////////////////////////////////////////////////////////////////// @@ -126,28 +70,28 @@ bool IgnoreEvent(XEvent &event) 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 ) + if( res ) { - /// 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); + static Time prev_time = {}; + + XEvent event; + //inst->_XPeekEvent(display, &event); + 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 (event.xkey.keycode == inst->_XKeysymToKeycode(display, XK_Tab) && event.xkey.state & ShiftMask) + if (event.xkey.keycode == XKeysymToKeycode(display, XK_Tab) && event.xkey.state & ShiftMask) { - // if key TAB is help, don't make the overlay flicker :p + // if key TAB is held, don't make the overlay flicker :p if( event.xkey.time != prev_time) { overlay->ShowOverlay(!overlay->ShowOverlay()); @@ -157,7 +101,8 @@ int X11_Hook::MyXEventsQueued(Display *display, int mode) } } } - else if(event.type == KeyRelease && event.xkey.keycode == inst->_XKeysymToKeycode(display, XK_Tab)) + //else if(event.type == KeyRelease && event.xkey.keycode == inst->_XKeysymToKeycode(display, XK_Tab)) + else if(event.type == KeyRelease && event.xkey.keycode == XKeysymToKeycode(display, XK_Tab)) { prev_time = event.xkey.time; } @@ -168,22 +113,61 @@ int X11_Hook::MyXEventsQueued(Display *display, int mode) if (IgnoreEvent(event)) { - inst->_XNextEvent(display, &event); - res = 0; + //inst->_XNextEvent(display, &event); + XNextEvent(display, &event); + return 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; } +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 && get_steam_client()->steam_overlay->ShowOverlay() ) + { + XEvent event; + //Inst()->_XPeekEvent(display, &event); + XPeekEvent(display, &event); + if( IgnoreEvent(event) ) + { + ImGui_ImplX11_EventHandler(event); + + //Inst()->_XNextEvent(display, &event); + XNextEvent(display, &event); + res = 0; + } + } + + return res; +} + ///////////////////////////////////////////////////////////////////////////////////// X11_Hook::X11_Hook() : initialized(false), - hooked(false) + hooked(false), + game_wnd(0), + _XEventsQueued(nullptr), + _XPeekEvent(nullptr), + _XNextEvent(nullptr), + _XPending(nullptr) { //_library = dlopen(DLL_NAME, RTLD_NOW); } diff --git a/overlay_experimental/linux/X11_Hook.h b/overlay_experimental/linux/X11_Hook.h index e4692de..13161aa 100644 --- a/overlay_experimental/linux/X11_Hook.h +++ b/overlay_experimental/linux/X11_Hook.h @@ -20,29 +20,48 @@ private: // Variables bool hooked; bool initialized; + Window game_wnd; // 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; + decltype(XPending)* _XPending; + //decltype(XKeysymToKeycode)* _XKeysymToKeycode; + //decltype(XLookupKeysym)* _XLookupKeysym; + //decltype(XGetGeometry)* _XGetGeometry; public: + 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); + virtual ~X11_Hook(); void resetRenderState(); - void prepareForOverlay(Display *display); + 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; + + inline decltype(XEventsQueued)* get_XEventsQueued() const { return _XEventsQueued; } + inline decltype(XPeekEvent)* get_XPeekEvent() const { return _XPeekEvent; } + inline decltype(XNextEvent)* get_XNextEvent() const { return _XNextEvent; } + inline decltype(XPending)* get_XPending() const { return _XPending; } + + inline void loadXEventsQueued(decltype(XEventsQueued)* pfnXEventsQueued) { _XEventsQueued = pfnXEventsQueued; } + inline void loadXPeekEvent(decltype(XPeekEvent)* pfnXPeekEvent) { _XPeekEvent = pfnXPeekEvent; } + inline void loadXNextEvent(decltype(XNextEvent)* pfnXNextEvent) { _XNextEvent = pfnXNextEvent; } + inline void loadXPending(decltype(XPending)* pfnXPending) { _XPending = pfnXPending; } }; #endif//NO_OVERLAY