2022-08-05 06:06:42 +00:00
|
|
|
/*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
2019-09-01 18:53:16 +00:00
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
#include "X11_Hook.h"
|
2019-09-01 18:53:16 +00:00
|
|
|
|
|
|
|
#include <imgui.h>
|
2022-08-05 06:06:42 +00:00
|
|
|
#include <backends/imgui_impl_x11.h>
|
|
|
|
#include <System/Library.h>
|
2019-09-01 18:53:16 +00:00
|
|
|
|
|
|
|
extern int ImGui_ImplX11_EventHandler(XEvent &event);
|
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
constexpr decltype(X11_Hook::DLL_NAME) X11_Hook::DLL_NAME;
|
|
|
|
|
2019-09-01 18:53:16 +00:00
|
|
|
X11_Hook* X11_Hook::_inst = nullptr;
|
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
bool X11_Hook::StartHook(std::function<bool(bool)>& _key_combination_callback)
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
if (!_Hooked)
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
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);
|
|
|
|
_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();
|
2019-09-01 18:53:16 +00:00
|
|
|
}
|
2022-08-05 06:06:42 +00:00
|
|
|
return true;
|
2019-09-01 18:53:16 +00:00
|
|
|
}
|
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
void X11_Hook::ResetRenderState()
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
if (_Initialized)
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
_GameWnd = 0;
|
|
|
|
_Initialized = false;
|
2019-09-01 18:53:16 +00:00
|
|
|
ImGui_ImplX11_Shutdown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-10 07:22:23 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
bool X11_Hook::PrepareForOverlay(Display *display, Window wnd)
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
if(!_Hooked)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (_GameWnd != wnd)
|
|
|
|
ResetRenderState();
|
|
|
|
|
|
|
|
if (!_Initialized)
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2019-09-03 15:24:34 +00:00
|
|
|
ImGui_ImplX11_Init(display, (void*)wnd);
|
2022-08-05 06:06:42 +00:00
|
|
|
_GameWnd = wnd;
|
2019-09-01 18:53:16 +00:00
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
_Initialized = true;
|
2019-09-01 18:53:16 +00:00
|
|
|
}
|
|
|
|
|
2019-09-03 15:24:34 +00:00
|
|
|
ImGui_ImplX11_NewFrame();
|
2022-08-05 06:06:42 +00:00
|
|
|
|
|
|
|
return true;
|
2019-09-01 18:53:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// X11 window hooks
|
|
|
|
bool IgnoreEvent(XEvent &event)
|
|
|
|
{
|
|
|
|
switch(event.type)
|
|
|
|
{
|
|
|
|
// Keyboard
|
|
|
|
case KeyPress: case KeyRelease:
|
|
|
|
// MouseButton
|
|
|
|
case ButtonPress: case ButtonRelease:
|
|
|
|
// Mouse move
|
|
|
|
case MotionNotify:
|
2022-08-05 06:06:42 +00:00
|
|
|
// Copy to clipboard request
|
|
|
|
case SelectionRequest:
|
2019-09-01 18:53:16 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
int X11_Hook::_CheckForOverlay(Display *d, int num_events)
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2019-09-03 15:24:34 +00:00
|
|
|
static Time prev_time = {};
|
|
|
|
X11_Hook* inst = Inst();
|
2019-09-01 18:53:16 +00:00
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
if( inst->_Initialized )
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2019-09-03 08:32:32 +00:00
|
|
|
XEvent event;
|
2019-09-03 15:24:34 +00:00
|
|
|
while(num_events)
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
bool skip_input = inst->_KeyCombinationCallback(false);
|
|
|
|
|
2019-09-03 15:24:34 +00:00
|
|
|
XPeekEvent(d, &event);
|
2022-08-05 06:06:42 +00:00
|
|
|
ImGui_ImplX11_EventHandler(event);
|
2019-09-03 15:24:34 +00:00
|
|
|
|
|
|
|
// Is the event is a key press
|
|
|
|
if (event.type == KeyPress)
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2019-09-03 15:24:34 +00:00
|
|
|
// Tab is pressed and was not pressed before
|
|
|
|
if (event.xkey.keycode == XKeysymToKeycode(d, XK_Tab) && event.xkey.state & ShiftMask)
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2019-09-03 15:24:34 +00:00
|
|
|
// if key TAB is held, don't make the overlay flicker :p
|
2022-08-05 06:06:42 +00:00
|
|
|
if (event.xkey.time != prev_time)
|
2019-09-03 15:24:34 +00:00
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
skip_input = true;
|
|
|
|
inst->_KeyCombinationCallback(true);
|
2019-09-03 15:24:34 +00:00
|
|
|
}
|
2019-09-01 18:53:16 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-03 15:24:34 +00:00
|
|
|
else if(event.type == KeyRelease && event.xkey.keycode == XKeysymToKeycode(d, XK_Tab))
|
|
|
|
{
|
|
|
|
prev_time = event.xkey.time;
|
|
|
|
}
|
2019-09-01 18:53:16 +00:00
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
if (!skip_input || !IgnoreEvent(event))
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
if(num_events)
|
|
|
|
num_events = 1;
|
2019-09-03 15:24:34 +00:00
|
|
|
break;
|
2022-08-05 06:06:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
XNextEvent(d, &event);
|
|
|
|
--num_events;
|
2019-09-01 18:53:16 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-03 15:24:34 +00:00
|
|
|
return num_events;
|
|
|
|
}
|
|
|
|
|
|
|
|
int X11_Hook::MyXEventsQueued(Display *display, int mode)
|
|
|
|
{
|
|
|
|
X11_Hook* inst = X11_Hook::Inst();
|
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
int res = inst->XEventsQueued(display, mode);
|
2019-09-03 15:24:34 +00:00
|
|
|
|
|
|
|
if( res )
|
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
res = inst->_CheckForOverlay(display, res);
|
2019-09-03 15:24:34 +00:00
|
|
|
}
|
2019-09-03 08:32:32 +00:00
|
|
|
|
2019-09-01 18:53:16 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2019-09-03 08:32:32 +00:00
|
|
|
int X11_Hook::MyXPending(Display* display)
|
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
int res = Inst()->XPending(display);
|
2019-09-03 08:32:32 +00:00
|
|
|
|
2019-09-03 15:24:34 +00:00
|
|
|
if( res )
|
2019-09-03 08:32:32 +00:00
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
res = Inst()->_CheckForOverlay(display, res);
|
2019-09-03 08:32:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2019-09-01 18:53:16 +00:00
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
X11_Hook::X11_Hook() :
|
2022-08-05 06:06:42 +00:00
|
|
|
_Initialized(false),
|
|
|
|
_Hooked(false),
|
|
|
|
_GameWnd(0),
|
|
|
|
XEventsQueued(nullptr),
|
|
|
|
XPending(nullptr)
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
X11_Hook::~X11_Hook()
|
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
SPDLOG_INFO("X11 Hook removed");
|
2019-09-01 18:53:16 +00:00
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
ResetRenderState();
|
2019-09-01 18:53:16 +00:00
|
|
|
|
|
|
|
_inst = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
X11_Hook* X11_Hook::Inst()
|
|
|
|
{
|
|
|
|
if (_inst == nullptr)
|
|
|
|
_inst = new X11_Hook;
|
|
|
|
|
|
|
|
return _inst;
|
|
|
|
}
|
|
|
|
|
2022-08-05 06:06:42 +00:00
|
|
|
std::string X11_Hook::GetLibraryName() const
|
2019-09-01 18:53:16 +00:00
|
|
|
{
|
2022-08-05 06:06:42 +00:00
|
|
|
return LibraryName;
|
2019-09-01 18:53:16 +00:00
|
|
|
}
|