/* * Copyright (C) 2019-2020 Nemirtingas * This file is part of the ingame overlay project * * The ingame overlay project 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 ingame overlay project 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 ingame overlay project; if not, see * . */ #include "X11_Hook.h" #include #include #include extern int ImGui_ImplX11_EventHandler(XEvent &event); constexpr decltype(X11_Hook::DLL_NAME) X11_Hook::DLL_NAME; X11_Hook* X11_Hook::_inst = nullptr; bool X11_Hook::StartHook(std::function& _key_combination_callback) { if (!_Hooked) { void* hX11 = System::Library::GetLibraryHandle(DLL_NAME); if (hX11 == nullptr) { SPDLOG_WARN("Failed to hook X11: Cannot find {}", DLL_NAME); return false; } System::Library::Library libX11; LibraryName = System::Library::GetLibraryPath(hX11); if (!libX11.OpenLibrary(LibraryName, false)) { SPDLOG_WARN("Failed to hook X11: Cannot load {}", LibraryName); return false; } XEventsQueued = libX11.GetSymbol("XEventsQueued"); XPending = libX11.GetSymbol("XPending"); if (XPending == nullptr || XEventsQueued == nullptr) { SPDLOG_WARN("Failed to hook X11: Cannot load functions.({}, {})", DLL_NAME, (void*)XEventsQueued, (void*)XPending); return false; } SPDLOG_INFO("Hooked X11"); _KeyCombinationCallback = std::move(_key_combination_callback); _Hooked = true; UnhookAll(); BeginHook(); HookFuncs( std::make_pair(&(void*&)XEventsQueued, (void*)&X11_Hook::MyXEventsQueued), std::make_pair(&(void*&)XPending, (void*)&X11_Hook::MyXPending) ); EndHook(); } return true; } void X11_Hook::ResetRenderState() { if (_Initialized) { _GameWnd = 0; _Initialized = false; ImGui_ImplX11_Shutdown(); } } void X11_Hook::SetInitialWindowSize(Display* display, Window wnd) { unsigned int width, height; Window unused_window; int unused_int; unsigned int unused_unsigned_int; XGetGeometry(display, wnd, &unused_window, &unused_int, &unused_int, &width, &height, &unused_unsigned_int, &unused_unsigned_int); ImGui::GetIO().DisplaySize = ImVec2((float)width, (float)height); } bool X11_Hook::PrepareForOverlay(Display *display, Window wnd) { if(!_Hooked) return false; if (_GameWnd != wnd) ResetRenderState(); if (!_Initialized) { ImGui_ImplX11_Init(display, (void*)wnd); _GameWnd = wnd; _Initialized = true; } ImGui_ImplX11_NewFrame(); return true; } ///////////////////////////////////////////////////////////////////////////////////// // X11 window hooks bool IgnoreEvent(XEvent &event) { switch(event.type) { // Keyboard case KeyPress: case KeyRelease: // MouseButton case ButtonPress: case ButtonRelease: // Mouse move case MotionNotify: // Copy to clipboard request case SelectionRequest: return true; } return false; } int X11_Hook::_CheckForOverlay(Display *d, int num_events) { static Time prev_time = {}; X11_Hook* inst = Inst(); if( inst->_Initialized ) { XEvent event; while(num_events) { bool skip_input = inst->_KeyCombinationCallback(false); XPeekEvent(d, &event); ImGui_ImplX11_EventHandler(event); // Is the event is a key press if (event.type == KeyPress) { // Tab is pressed and was not pressed before 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) { skip_input = true; inst->_KeyCombinationCallback(true); } } } else if(event.type == KeyRelease && event.xkey.keycode == XKeysymToKeycode(d, XK_Tab)) { prev_time = event.xkey.time; } if (!skip_input || !IgnoreEvent(event)) { if(num_events) num_events = 1; break; } XNextEvent(d, &event); --num_events; } } 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->_CheckForOverlay(display, res); } return res; } int X11_Hook::MyXPending(Display* display) { int res = Inst()->XPending(display); if( res ) { res = Inst()->_CheckForOverlay(display, res); } return res; } ///////////////////////////////////////////////////////////////////////////////////// X11_Hook::X11_Hook() : _Initialized(false), _Hooked(false), _GameWnd(0), XEventsQueued(nullptr), XPending(nullptr) { } X11_Hook::~X11_Hook() { SPDLOG_INFO("X11 Hook removed"); ResetRenderState(); _inst = nullptr; } X11_Hook* X11_Hook::Inst() { if (_inst == nullptr) _inst = new X11_Hook; return _inst; } std::string X11_Hook::GetLibraryName() const { return LibraryName; }