glfw abstraction

This commit is contained in:
2026-09-05 07:55:09 -06:00
parent 3af24825ec
commit 574fbc8f8e
2 changed files with 354 additions and 0 deletions
+180
View File
@@ -0,0 +1,180 @@
#include "Window.hpp"
#include <stdexcept>
namespace display {
Window::Window(int width, int height, std::string_view title, GLFWmonitor* monitor, GLFWwindow* share) {
// Create the native GLFW window (string_view needs null-terminated C-string conversion)
handle = glfwCreateWindow(width, height, title.data(), monitor, share);
if (!handle) return;
// Attach 'this' pointer to the GLFW user pointer field.
// This allows static C callbacks to retrieve the C++ class instance later.
glfwSetWindowUserPointer(handle, this);
}
Window::~Window() noexcept {
if (handle) {
// Clear user pointer prior to destruction to avoid dangling references during teardown
glfwSetWindowUserPointer(handle, nullptr);
glfwDestroyWindow(handle);
handle = nullptr;
}
}
Window::Window(Window&& other) noexcept : handle(std::exchange(other.handle, nullptr)), callbacks(std::move(other.callbacks)) {
// Update the native handle's user pointer to point to the newly moved instance
if (handle) glfwSetWindowUserPointer(handle, this);
}
Window& Window::operator=(Window&& other) noexcept {
if (this != &other) {
// Free current window resource before taking over the new one
if (handle) glfwDestroyWindow(handle);
// Steal handle and move state
handle = std::exchange(other.handle, nullptr);
callbacks = std::move(other.callbacks);
// Update user pointer to new host object
if (handle) glfwSetWindowUserPointer(handle, this);
}
return *this;
}
bool Window::isValid() const noexcept {
return handle != nullptr;
}
bool Window::shouldClose() const noexcept {
return handle ? glfwWindowShouldClose(handle) : true;
}
void Window::setShouldClose(bool value) noexcept {
if (!handle) return;
glfwSetWindowShouldClose(handle, value ? GLFW_TRUE : GLFW_FALSE);
}
void Window::swapBuffers() noexcept {
if (!handle) return;
glfwSwapBuffers(handle);
}
void Window::makeContextCurrent() noexcept {
if (!handle) return;
glfwMakeContextCurrent(handle);
}
std::pair<int, int> Window::getSize() const noexcept {
int w = 0, h = 0;
if (handle) glfwGetWindowSize(handle, &w, &h);
return { w, h };
}
std::pair<int, int> Window::getFramebufferSize() const noexcept {
int w = 0, h = 0;
if (handle) glfwGetFramebufferSize(handle, &w, &h);
return { w, h };
}
void Window::setTitle(std::string_view title) {
if (!handle) return;
glfwSetWindowTitle(handle, title.data());
}
void Window::setVisible(bool val) noexcept {
if(val) glfwShowWindow(handle);
else glfwHideWindow(handle);
}
GLFWwindow* Window::getNativeHandle() const noexcept {
return handle;
}
Window::operator GLFWwindow* () const noexcept {
return handle;
}
// --- CALLBACKS --- //
void Window::setResizeCallback(ResizeFn fn) {
if (!handle) return;
callbacks.onResize = std::move(fn);
glfwSetFramebufferSizeCallback(handle, callbacks.onResize ? Window::glfwFramebufferSizeBridge : nullptr);
}
void Window::setKeyCallback(KeyFn fn) {
if (!handle) return;
callbacks.onKey = std::move(fn);
glfwSetKeyCallback(handle, callbacks.onKey ? Window::glfwKeyBridge : nullptr);
}
void Window::setCloseCallback(CloseFn fn) {
if (!handle) return;
callbacks.onClose = std::move(fn);
glfwSetWindowCloseCallback(handle, callbacks.onClose ? Window::glfwWindowCloseBridge : nullptr);
}
void Window::setCursorPosCallback(CursorPosFn fn) {
if (!handle) return;
callbacks.onCursorPos = std::move(fn);
glfwSetCursorPosCallback(handle, callbacks.onCursorPos ? Window::glfwCursorPosBridge : nullptr);
}
void Window::setMouseButtonCallback(MouseButtonFn fn) {
if (!handle) return;
callbacks.onMouseButton = std::move(fn);
glfwSetMouseButtonCallback(handle, callbacks.onMouseButton ? Window::glfwMouseButtonBridge : nullptr);
}
void Window::setScrollCallback(ScrollFn fn) {
if (!handle) return;
callbacks.onScroll = std::move(fn);
glfwSetScrollCallback(handle, callbacks.onScroll ? Window::glfwScrollBridge : nullptr);
}
void Window::setCursorEnterCallback(CursorEnterFn fn) {
if (!handle) return;
callbacks.onCursorEnter = std::move(fn);
glfwSetCursorEnterCallback(handle, callbacks.onCursorEnter ? Window::glfwCursorEnterBridge : nullptr);
}
void Window::glfwFramebufferSizeBridge(GLFWwindow* handle, int width, int height) {
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(handle));
if (self && self->callbacks.onResize) self->callbacks.onResize(width, height);
}
void Window::glfwWindowCloseBridge(GLFWwindow* handle) {
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(handle));
if (self && self->callbacks.onClose) self->callbacks.onClose();
}
void Window::glfwKeyBridge(GLFWwindow* handle, int key, int scancode, int action, int mods) {
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(handle));
if (self && self->callbacks.onKey) self->callbacks.onKey(key, scancode, action, mods);
}
void Window::glfwCursorPosBridge(GLFWwindow* handle, double xpos, double ypos) {
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(handle));
if (self && self->callbacks.onCursorPos) self->callbacks.onCursorPos(xpos, ypos);
}
void Window::glfwMouseButtonBridge(GLFWwindow* handle, int button, int action, int mods) {
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(handle));
if (self && self->callbacks.onMouseButton) self->callbacks.onMouseButton(button, action, mods);
}
void Window::glfwScrollBridge(GLFWwindow* handle, double xoffset, double yoffset) {
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(handle));
if (self && self->callbacks.onScroll) self->callbacks.onScroll(xoffset, yoffset);
}
void Window::glfwCursorEnterBridge(GLFWwindow* handle, int entered) {
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(handle));
if (self && self->callbacks.onCursorEnter) self->callbacks.onCursorEnter(entered == GLFW_TRUE);
}
}
+174
View File
@@ -0,0 +1,174 @@
#pragma once
#include <GLFW/glfw3.h>
#include <functional>
#include <string>
#include <string_view>
#include <utility>
namespace display {
/**
* Represents a window backed by GLFW.
* This abstraction considers this to be a physical
* window on the user's desktop.
*/
class Window {
public:
using ResizeFn = std::function<void(int width, int height)>;
using CloseFn = std::function<void()>;
using KeyFn = std::function<void(int key, int scancode, int action, int mods)>;
using CursorPosFn = std::function<void(double xpos, double ypos)>;
using MouseButtonFn = std::function<void(int button, int action, int mods)>;
using ScrollFn = std::function<void(double xoffset, double yoffset)>;
using CursorEnterFn = std::function<void(bool entered)>;
struct Callbacks {
ResizeFn onResize{ nullptr };
CloseFn onClose{ nullptr };
KeyFn onKey{ nullptr };
CursorPosFn onCursorPos{ nullptr };
MouseButtonFn onMouseButton{ nullptr };
ScrollFn onScroll{ nullptr };
CursorEnterFn onCursorEnter{ nullptr };
};
private:
GLFWwindow* handle{ nullptr };
Callbacks callbacks{ nullptr };
public:
/**
* @brief Constructs an uninitialized, null Window wrapper.
*/
Window() noexcept = default;
/**
* @brief Constructs and initializes a physical GLFW window.
*
* @param width Initial width in screen coordinates.
* @param height Initial height in screen coordinates.
* @param title Title displayed in the window header bar.
* @param monitor Monitor to use for full screen, or nullptr for windowed mode.
* @param share Window whose context to share resources with, or nullptr.
*
* @throws std::runtime_error If window creation fails.
*/
Window(int width, int height, std::string_view title, GLFWmonitor* monitor = nullptr, GLFWwindow* share = nullptr);
/**
* @brief Destructor. Destroys the underlying GLFW window handle if valid.
*/
~Window() noexcept;
// --- Prevent Copying (RAII Single-Ownership) ---
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
// --- Enable Move Semantics ---
/**
* @brief Move constructor. Takes ownership of another Window's handle.
*/
Window(Window&& other) noexcept;
public:
/**
* @brief Move assignment operator. Safely cleans up current handle and steals other's handle.
*/
Window& operator=(Window&& other) noexcept;
public:
// --- Core Operations ---
/**
* @brief Checks if the underlying GLFW handle is non-null.
*/
bool isValid() const noexcept;
/**
* @brief Returns true if the user or system requested this window to close.
*/
bool shouldClose() const noexcept;
/**
* @brief Sets the close flag for the window.
*/
void setShouldClose(bool value) noexcept;
/**
* @brief Swaps the front and back color buffers.
*/
void swapBuffers() noexcept;
void setVisible(bool val) noexcept;
/**
* @brief Makes this window's OpenGL context current on the calling thread.
*/
void makeContextCurrent() noexcept;
// --- Window State Query & Mutators ---
std::pair<int, int> getSize() const noexcept;
std::pair<int, int> getFramebufferSize() const noexcept;
void setTitle(std::string_view title);
public: // --- Callbacks ---
/**
* @brief Attaches a modern C++ callback for window resize events.
*/
void setResizeCallback(ResizeFn callback);
void setCloseCallback(CloseFn callback);
void setKeyCallback(KeyFn callback);
void setCursorPosCallback(CursorPosFn fn);
void setMouseButtonCallback(MouseButtonFn fn);
void setScrollCallback(ScrollFn fn);
void setCursorEnterCallback(CursorEnterFn fn);
public: // --- Native Accessors ---
/**
* @brief Returns the raw, underlying GLFWwindow pointer for low-level or library integration.
*/
GLFWwindow* getNativeHandle() const noexcept;
/**
* @brief Implicit conversion operator to GLFWwindow* for ergonomic API interoperability.
*/
operator GLFWwindow* () const noexcept;
private:
static void glfwFramebufferSizeBridge(GLFWwindow* handle, int width, int height);
static void glfwWindowCloseBridge(GLFWwindow* handle);
static void glfwKeyBridge(GLFWwindow* handle, int key, int scancode, int action, int mods);
static void glfwCursorPosBridge(GLFWwindow* handle, double xpos, double ypos);
static void glfwMouseButtonBridge(GLFWwindow* handle, int button, int action, int mods);
static void glfwScrollBridge(GLFWwindow* handle, double xoffset, double yoffset);
static void glfwCursorEnterBridge(GLFWwindow* handle, int entered);
};
}