XWindows SetCursorCenter & SetCursorFocused
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m50s

This commit is contained in:
2025-06-15 16:38:04 -04:00
parent 199642b1a9
commit 42deab60f4
6 changed files with 94 additions and 37 deletions

View File

@@ -61,6 +61,8 @@ protected:
bool focused = true;
bool vsync = false;
bool cursor_visible = true;
bool cursor_focused = false;
bool toggling_cursor_focus = false;
bool closing = false;
bool key_repeat = false;
@@ -194,7 +196,6 @@ public:
void Close();
/// Closes the window immediately, potentially without allowing finalization to occur.
void ForceClose();
void ForceCloseAndTerminateProgram();
/** Display a small window with some text and an "OK" button.
* I tried un-defining the macro, Calling it in a lambda,
@@ -203,7 +204,7 @@ public:
* It just wouldn't let me name it MessageBox - Redacted. */
/// @note Execution of the parent window is stopped while the message box is up.
void UniqueFunctionNameForMessageBoxBecauseMicrosoftUsesMacros(const std::string& title, const std::string& message);
void DialogOK(const std::string& title, const std::string& message);
/// Sets whether or not to make the window fullscreen.
/// @note This is implemented per-OS, and as such, it simply requests the OS to do what we want. No guarantee about follow-through can be given.
@@ -278,13 +279,17 @@ public:
void SetCursorCustomIcon() const;
void SetCursorLocked();
/// @returns Where the cursor was just before we teleported it to the center.
/// @note This is useful for 3D games.
std::pair<int, int> SetCursorCenter();
void SetCursorCenter();
/// Tells the operating system to not allow the cursor to go off our window.
/// @note This is useful for 3D games.
// TODO recreate this behavior on Windows. On X-Windows, The cursor is focused on the next mouse movement because of an X11 timing issue.
void SetCursorFocused(bool state);
void RestoreCursorFromLastCenter(); // Feels nicer for users
/// Hides the cursor when it's inside of our window. Useful for 3D game camera.
/// Hides the cursor when it's inside of our window.
/// @note This is useful for 3D games.
void SetCursorVisible(bool cursor_enable);
bool GetCursorVisible();

View File

@@ -69,10 +69,10 @@ int main() {
window->SetFullscreen(false);
window->SetVsyncEnabled(true);
window->SetResizable(true);
window->SetCursorVisible(false);
window->SetKeyRepeatEnabled(true);
window->UniqueFunctionNameForMessageBoxBecauseMicrosoftUsesMacros("MessageBox", "Generic message from a ReWindow MessageBox.");
//window->SetCursorVisible(true);
window->SetKeyRepeatEnabled(false);
//window->DialogOK("MessageBox", "Generic message from a ReWindow MessageBox.");
window->SetCursorFocused(true);
Logger::Debug(std::format("Window '{}' flags: IN_FOCUS={} FULLSCREEN={} RESIZEABLE={} VSYNC={} KEY_REPEAT={} QUIT={}",
window->GetTitle(), window->IsFocused(), window->IsFullscreen(),
@@ -83,7 +83,8 @@ int main() {
window->OnMouseButtonDownEvent += [&] (MouseButtonDownEvent e) { jlog::Debug(e.Button.Mnemonic + std::to_string(e.Button.ButtonIndex)); };
window->OnMouseWheelEvent += [&, window] (MouseWheelEvent e) { std::cout << window->GetMouseWheelPersistent() << std::endl; };
while (!window->IsClosing())
while (!window->IsClosing()) {
window->ManagedRefresh();
}
delete window;
}

View File

@@ -77,8 +77,7 @@ void OpenGLWindow::SwapBuffers() {
}
void OpenGLWindow::SetVsyncEnabled(bool state) {
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) OpenGL::glXGetProcAddressARB((const GLubyte *) "glXSwapIntervalEXT");
auto glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) OpenGL::glXGetProcAddressARB((const GLubyte *) "glXSwapIntervalEXT");
glXSwapIntervalEXT(platform->display, platform->window, state);
}
@@ -228,6 +227,7 @@ OpenGLWindow::OpenGLWindow(const std::string& title, int width, int height, uint
bool OpenGLWindow::SoftwareRendered() {
std::string renderer((const char*) OpenGL::glGetString(GL_RENDERER));
if (renderer.find("llvmpipe"))
return true;
if (renderer.find("softpipe"))

View File

@@ -53,6 +53,22 @@ platform = new Platform();
extant = this;
}
void RWindow::SetCursorFocused(bool state) {
toggling_cursor_focus = state;
if (!state && cursor_focused) {
XUngrabPointer(platform->display, CurrentTime);
cursor_focused = state;
return;
}
}
std::pair<int, int> RWindow::SetCursorCenter() {
auto current_pos = GetAccurateMouseCoordinates();
XWarpPointer(platform->display, None, platform->window, 0, 0, 0, 0, width / 2, height / 2);
return current_pos;
}
RWindow::RWindow(const std::string& wTitle, int wWidth, int wHeight, bool wFullscreen, bool wResizable, bool wVsync)
: title(wTitle), width(wWidth), height(wHeight), fullscreen_mode(wFullscreen), resizable(wResizable), vsync(wVsync)
{ platform = new Platform(); extant = this; }
@@ -65,7 +81,7 @@ void RWindow::Raise() {
XRaiseWindow(platform->display, platform->window);
}
void RWindow::UniqueFunctionNameForMessageBoxBecauseMicrosoftUsesMacros(const std::string& window_title, const std::string& message) {
void RWindow::DialogOK(const std::string& window_title, const std::string& message) {
int padding = 10;
XFontStruct* font = XLoadQueryFont(platform->display, "6x13");
@@ -82,7 +98,7 @@ void RWindow::UniqueFunctionNameForMessageBoxBecauseMicrosoftUsesMacros(const st
WhitePixel(platform->display, platform->defaultScreen));
XStoreName(platform->display, window, window_title.c_str());
XSelectInput(platform->display, window, ExposureMask | ButtonPressMask);
XSelectInput(platform->display, window, ExposureMask | ButtonPressMask | StructureNotifyMask);
// No resizing.
XSizeHints hints;
@@ -91,19 +107,13 @@ void RWindow::UniqueFunctionNameForMessageBoxBecauseMicrosoftUsesMacros(const st
hints.min_height = hints.max_height = dimensions.second;
XSetWMNormalHints(platform->display, window, &hints);
Atom wm_delete_window = XInternAtom(platform->display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(platform->display, window, &wm_delete_window, 1);
XMapWindow(platform->display, window);
GC gc = XCreateGC(platform->display, window, 0, nullptr);
XSetForeground(platform->display, gc, BlackPixel(platform->display, platform->defaultScreen));
/* TODO positioning the window to be in the center of the parent window doesn't always work.
if (platform->window) {
XWindowAttributes parent_window_attributes;
XGetWindowAttributes(platform->display, platform->window, &parent_window_attributes);
XMoveWindow(platform->display, window, (parent_window_attributes.width - dimensions.first) / 2, (parent_window_attributes.height - dimensions.second) / 2);
}
*/
XEvent event;
while (true) {
XNextEvent(platform->display, &event);
@@ -114,10 +124,19 @@ void RWindow::UniqueFunctionNameForMessageBoxBecauseMicrosoftUsesMacros(const st
XDrawRectangle(platform->display, window, gc, button_pos.first, button_pos.second, button_size.first, button_size.second);
}
else if (event.type == ButtonPress)
if (event.xbutton.x >= button_pos.first && event.xbutton.x <= button_pos.first + button_size.first
&& event.xbutton.y >= button_pos.second && event.xbutton.y <= button_pos.second + button_size.second)
else if (event.type == ClientMessage) {
if (event.xclient.message_type == XInternAtom(platform->display, "WM_PROTOCOLS", False) &&
static_cast<Atom>(event.xclient.data.l[0]) == wm_delete_window)
break;
}
else if (event.type == ButtonPress) {
if (event.xbutton.x >= button_pos.first && event.xbutton.x <= button_pos.first + button_size.first
&& event.xbutton.y >= button_pos.second && event.xbutton.y <= button_pos.second + button_size.second)
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
XFreeFont(platform->display, font);
@@ -133,6 +152,7 @@ void RWindow::Lower()
void RWindow::DestroyOSWindowHandle() {
// Turn key repeat back on.
XAutoRepeatOn(platform->display);
XFlush(platform->display);
Logger::Debug(std::format("Destroying sub-windows for window '{}'", this->title));
XDestroySubwindows(platform->display, platform->window);
@@ -143,8 +163,6 @@ void RWindow::DestroyOSWindowHandle() {
XCloseDisplay(platform->display);
}
//void RWindow::
void RWindow::SetCursorVisible(bool cursor_enable) {
cursor_visible = cursor_enable;
if (platform->invisible_cursor == 0) {
@@ -193,6 +211,13 @@ void RWindow::PollEvents() {
XAutoRepeatOff(platform->display);
else
XAutoRepeatOn(platform->display);
XFlush(platform->display);
if (cursor_focused) {
cursor_focused = false;
SetCursorFocused(true);
}
if (platform->wm_hints) {
platform->wm_hints->flags &= ~XUrgencyHint;
@@ -209,9 +234,22 @@ void RWindow::PollEvents() {
focused = true;
}
if (platform->xev.type == UnmapNotify) {
if (cursor_focused) {
SetCursorFocused(false);
cursor_focused = true;
}
}
if (platform->xev.type == FocusOut) {
Logger::Debug(std::format("Event '{}'", "FocusOut"));
XAutoRepeatOn(platform->display);
XFlush(platform->display);
if (cursor_focused) {
SetCursorFocused(false);
cursor_focused = true;
}
if (!cursor_visible)
XUndefineCursor(platform->display, platform->window);
@@ -268,9 +306,27 @@ void RWindow::PollEvents() {
Logger::Debug(std::format("Event '{}'", "Expose"));
}
// NOTE: This event is functionally useless, as it only informs of the very beginning and end of a mouse movement.
if (platform->xev.type == MotionNotify) {
Logger::Debug(std::format("Event '{}'", "MotionNotify"));
if (toggling_cursor_focus) {
XWindowAttributes attrs;
XGetWindowAttributes(platform->display, platform->window, &attrs);
if (attrs.map_state == IsViewable) {
int result = XGrabPointer
(
platform->display, platform->window, True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
GrabModeAsync, GrabModeAsync, platform->window, None, CurrentTime
);
if (result == GrabSuccess) {
toggling_cursor_focus = false;
cursor_focused = true;
}
}
}
}
if (platform->xev.type == ConfigureNotify) {

View File

@@ -256,11 +256,6 @@ bool RWindow::IsFocused() const {
DestroyOSWindowHandle();
}
void RWindow::ForceCloseAndTerminateProgram() {
ForceClose();
exit(0);
}
bool MouseState::IsDown(const MouseButton &btn) const {
if (btn == MouseButtons::Left) return Buttons.LMB;
if (btn == MouseButtons::Right) return Buttons.RMB;

View File

@@ -270,7 +270,7 @@ vsync(wVsync) {
platform = new Platform();
}
void RWindow::UniqueFunctionNameForMessageBoxBecauseMicrosoftUsesMacros(const std::string& title, const std::string& message_box_text) {
void RWindow::DialogOK(const std::string& title, const std::string& message_box_text) {
MessageBox(nullptr, message_box_text.c_str(), title.c_str(), MB_OK);
}