glfw abstraction
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user