322 lines
8.6 KiB
C++
322 lines
8.6 KiB
C++
/*
|
|
* 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
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "X11_Hook.h"
|
|
|
|
#include <imgui.h>
|
|
#include <backends/imgui_impl_x11.h>
|
|
#include <System/Library.h>
|
|
|
|
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<bool(bool)>& _key_combination_callback, std::set<ingame_overlay::ToggleKey> 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<decltype(::XEventsQueued)>("XEventsQueued");
|
|
XPending = libX11.GetSymbol<decltype(::XPending)>("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**, void*>(&(void*&)XEventsQueued, (void*)&X11_Hook::MyXEventsQueued),
|
|
std::make_pair<void**, void*>(&(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;
|
|
}
|
|
|