goldberg_emulator/overlay_experimental/linux/X11_Hook.cpp

232 lines
5.7 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;
bool X11_Hook::StartHook(std::function<bool(bool)>& _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<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();
}
return true;
}
void X11_Hook::ResetRenderState()
{
if (_Initialized)
{
_GameWnd = 0;
_Initialized = false;
ImGui_ImplX11_Shutdown();
}
}
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;
}