goldberg_emulator/overlay_experimental/linux/X11_Hook.cpp

244 lines
6.1 KiB
C++
Raw Normal View History

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