/* * 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; uint32_t ToggleKeyToNativeKey(ingame_overlay::ToggleKey k) { struct { ingame_overlay::ToggleKey lib_key; uint32_t native_key; } mapping[] = { { ingame_overlay::ToggleKey::ALT , XK_Alt_L }, { ingame_overlay::ToggleKey::CTRL , XK_Control_L }, { ingame_overlay::ToggleKey::SHIFT, XK_Shift_L }, { ingame_overlay::ToggleKey::TAB , XK_Tab }, { ingame_overlay::ToggleKey::F1 , XK_F1 }, { ingame_overlay::ToggleKey::F2 , XK_F2 }, { ingame_overlay::ToggleKey::F3 , XK_F3 }, { ingame_overlay::ToggleKey::F4 , XK_F4 }, { ingame_overlay::ToggleKey::F5 , XK_F5 }, { ingame_overlay::ToggleKey::F6 , XK_F6 }, { ingame_overlay::ToggleKey::F7 , XK_F7 }, { ingame_overlay::ToggleKey::F8 , XK_F8 }, { ingame_overlay::ToggleKey::F9 , XK_F9 }, { ingame_overlay::ToggleKey::F10 , XK_F10 }, { ingame_overlay::ToggleKey::F11 , XK_F11 }, { ingame_overlay::ToggleKey::F12 , XK_F12 }, }; for (auto const& item : mapping) { if (item.lib_key == k) return item.native_key; } return 0; } bool GetKeyState(Display* d, KeySym keySym, char szKey[32]) { int iKeyCodeToFind = XKeysymToKeycode(d, keySym); return szKey[iKeyCodeToFind / 8] & (1 << (iKeyCodeToFind % 8)); } bool X11_Hook::StartHook(std::function& _key_combination_callback, std::set const& toggle_keys) { if (!_Hooked) { if (!_key_combination_callback) { SPDLOG_ERROR("Failed to hook X11: No key combination callback."); return false; } if (toggle_keys.empty()) { SPDLOG_ERROR("Failed to hook X11: No key combination."); return false; } 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); for (auto& key : toggle_keys) { uint32_t k = ToggleKeyToNativeKey(key); if (k != 0) { _NativeKeyCombination.insert(k); } } _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(); char szKey[32]; if( _Initialized ) { XEvent event; while(num_events) { bool skip_input = _KeyCombinationCallback(false); XPeekEvent(d, &event); ImGui_ImplX11_EventHandler(event); // Is the event is a key press if (event.type == KeyPress || event.type == KeyRelease) { XQueryKeymap(d, szKey); int key_count = 0; for (auto const& key : inst->_NativeKeyCombination) { if (GetKeyState(d, key, szKey)) ++key_count; } if (key_count == inst->_NativeKeyCombination.size()) {// All shortcut keys are pressed if (!inst->_KeyCombinationPushed) { if (inst->_KeyCombinationCallback(true)) { skip_input = true; // Save the last known cursor pos when opening the overlay // so we can spoof the GetCursorPos return value. //inst->GetCursorPos(&inst->_SavedCursorPos); } inst->_KeyCombinationPushed = true; } } else { inst->_KeyCombinationPushed = false; } } 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), _KeyCombinationPushed(false), 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; }