Mesen2/Linux/LinuxMouseManager.cpp
2024-09-19 21:19:43 +09:00

113 lines
3.2 KiB
C++

#include <string>
#include <chrono>
#include <thread>
#include <stdint.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#include "LinuxMouseManager.h"
#include "Core/Shared/MessageManager.h"
LinuxMouseManager::LinuxMouseManager(void* windowHandle)
{
_mainWindow = (Window) windowHandle;
_display = XOpenDisplay(nullptr);
_defaultScreen = XDefaultScreen(_display);
_rootWindow = XRootWindow(_display, _defaultScreen);
_defaultCursor = XCreateFontCursor(_display, XC_left_ptr);
_crossCursor = XCreateFontCursor(_display, XC_crosshair);
XColor color = {};
uint8_t nullCursorData = 0;
Pixmap pixmap = XCreateBitmapFromData(_display, _rootWindow, (const char*) &nullCursorData, 1, 1);
_hiddenCursor = XCreatePixmapCursor(_display, pixmap, pixmap, &color, &color, 0, 0);
}
LinuxMouseManager::~LinuxMouseManager() {}
SystemMouseState LinuxMouseManager::GetSystemMouseState(void* rendererHandle)
{
SystemMouseState state = {};
Window root = 0;
Window c = 0;
int rootX, rootY, childX, childY;
uint32_t mask;
XGrabServer(_display);
XQueryPointer(_display, _rootWindow, &root, &c, &rootX, &rootY, &childX, &childY, &mask);
if(root != _rootWindow) c = root;
while(c != 0) {
XQueryPointer(_display, c, &root, &c, &rootX, &rootY, &childX, &childY, &mask);
}
XUngrabServer(_display);
XFlush(_display);
state.XPosition = rootX;
state.YPosition = rootY;
state.LeftButton = (mask & (1 << 8)) != 0;
state.RightButton = (mask & (1 << 10)) != 0;
state.MiddleButton = (mask & (1 << 9)) != 0;
//TODO back/forward are not supported by XQueryPointer?
//state.Button4 = (mask & (1 << 11)) != 0;
//state.Button5 = (mask & (1 << 12)) != 0;
return state;
}
bool LinuxMouseManager::CaptureMouse(int32_t x, int32_t y, int32_t width, int32_t height, void* rendererHandle)
{
if(rendererHandle == nullptr) {
//Due to the mouse position constantly being set to the center of the window and the cursor being hidden
//actually capturing the cursor when using the software renderer is not strictly needed
return true;
}
for(int i = 0; i < 10; i++) {
int result = XGrabPointer(_display, (Window) rendererHandle, true, NoEventMask, GrabModeAsync, GrabModeAsync, (Window) rendererHandle, _hiddenCursor, 0);
XFlush(_display);
if(result == 1 && i < 9) {
//XGrabPointer can fail with AlreadyGrabbed - this can be normal, retry a few times
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(100));
} else {
if(result != 0) {
std::string message = "XGrabPointer failed: ";
MessageManager::Log(message + std::to_string(result));
return false;
}
break;
}
}
return true;
}
void LinuxMouseManager::ReleaseMouse()
{
XUngrabPointer(_display, 0);
XFlush(_display);
}
void LinuxMouseManager::SetSystemMousePosition(int32_t x, int32_t y)
{
XWarpPointer(_display, 0, _rootWindow, 0, 0, 0, 0, x, y);
XFlush(_display);
}
void LinuxMouseManager::SetCursorImage(CursorImage cursor)
{
switch(cursor) {
case CursorImage::Hidden: XDefineCursor(_display, _mainWindow, _hiddenCursor); break;
case CursorImage::Arrow: XDefineCursor(_display, _mainWindow, _defaultCursor); break;
case CursorImage::Cross: XDefineCursor(_display, _mainWindow, _crossCursor); break;
}
XFlush(_display);
}
double LinuxMouseManager::GetPixelScale()
{
return 1.0;
}