A C++ wrapper for GLFW with RAII support and with multi-window and multithreading in mind.
This wrapper is personalized and written to suit my needs. This is not a wrapper that is a one-to-one "port" of glfw C-API to C++ API. This repository will be updated iteratively as needed. I mainly use this repository to facilitate my graphics programming project that requires painless window management. You can see my projects here:
- C++20
- GLFW 3.3
You can use your system package manager or c++ package manager like Conan (see example) or even manually clone the GLFW repository and call add_subdirectory
to add the GLFW dependency.
You can clone this repository (or add as submodule) inside your project. I recommend using FetchContent though as it is easier to do.
# If you are using FetchContent
include(FetchContent)
FetchContent_Declare(
glfw-cpp
GIT_REPOSITORY https://github.com/mrizaln/glfw-cpp
GIT_TAG v0.10.0)
# set this variable to ON to enable Vulkan support (requires Vulkan loader and headers)
option(GLFW_CPP_VULKAN_SUPPORT "vulkan support" ON)
FetchContent_MakeAvailable(glfw-cpp)
# # If you clone/submodule the repository
# add_subdirectory(path/to/the/cloned/repository)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE glfw-cpp) # you don't need to link to glfw here, glfw-cpp already link to it
This library supports Vulkan, just set GLFW_CPP_VULKAN_SUPPORT
before adding this repository to the project to enable it. Note that it requires Vulkan loader and the headers to compile.
Using this library is as simple as
#include <glad/glad.h>
#include <glfw_cpp/glfw_cpp.hpp>
#include <cmath>
#include <iostream>
int main()
{
// `glfw_cpp::init()` calls `glfwInit()` internally and returns an `glfw_cpp::Instance::Unique` that will
// call `glfwTerminate()` on dtor. Note that the graphics API can't be changed later, this is a design
// choice.
auto instance = glfw_cpp::init(glfw_cpp::Api::OpenGL{
.m_major = 3,
.m_minor = 3,
.m_profile = glfw_cpp::Api::OpenGL::Profile::Core,
.m_loader = [](glfw_cpp::Api::GlContext /* handle */,
glfw_cpp::Api::GlGetProc proc) { gladLoadGLLoader((GLADloadproc)proc); },
});
// `WindowManager` is responsible for managing windows (think of window group). The only way to construct
// it is through this `glfw_cpp::Instance::create_window_manager()` function which returns a
// `std::shared_ptr<WindowManager>`. Each window created with this instance claims ownership of it (hence
// the shared_ptr).
auto wm = instance->create_window_manager();
// graphics API hints are omitted from the `WindowHint`, only other relevant hints are included.
auto hint = glfw_cpp::WindowHint{}; // use default hint
auto window = wm->create_window(hint, "Learn glfw-cpp", 800, 600);
window.run([&, elapsed = 0.0F](const glfw_cpp::EventQueue& events) mutable {
// handling events
{
using E = glfw_cpp::Event;
using K = glfw_cpp::KeyCode;
// clang-format off
events.visit(E::Overloaded{
[&](const E::KeyPressed& e) { if (e.m_key == K::Q) window.request_close(); },
[&](const E::FramebufferResized& e) { glViewport(0, 0, e.m_width, e.m_height); },
[&](const auto& e) { std::cout << "event happened " << (void*)&e << '\n'; }, // catch-all case
});
// clang-format on
}
// `glfw_cpp::Window` keep a copy of (almost) every properties of the window (like pressed keys) in
// itself. You can query it for continuous key input (for movement) for example.
{
using K = glfw_cpp::KeyCode;
const auto& keys = window.properties().m_key_state;
if (keys.all_pressed({ K::H, K::J, K::L, K::K })) {
std::cout << "HJKL key pressed all at once\n";
}
if (keys.is_pressed(K::LeftShift) && keys.any_pressed({ K::W, K::A, K::S, K::D })) {
std::cout << "WASD key pressed with shift key being held\n";
} else if (keys.any_pressed({ K::W, K::A, K::S, K::D })) {
std::cout << "WASD key pressed\n";
}
}
elapsed += static_cast<float>(window.delta_time());
// funny color cycle
const float r = (std::sin(23.0F / 8.0F * elapsed) + 1.0F) * 0.1F + 0.4F;
const float g = (std::cos(13.0F / 8.0F * elapsed) + 1.0F) * 0.2F + 0.3F;
const float b = (std::sin(41.0F / 8.0F * elapsed) + 1.5F) * 0.2F;
glClearColor(r, g, b, 1.0F);
glClear(GL_COLOR_BUFFER_BIT);
wm->poll_events();
});
}
No manual cleanup necessary, the classes defined already using RAII pattern.
One thing to keep in mind is that you need to make sure that glfw_cpp::Instance::Unique
outlive glfw_cpp::WindowManager
and glfw_cpp::Window
s in order for the program to be well defined and not crashing.
The above example is a single-threaded, one window example. For a multi-window and multithreaded example, you can see here or here (I also use a different OpenGL loader library there).
The project is documented using Doxygen. There is a Doxygen configuration in docs that can be used to generate a HTML documentation page.
From the root of the project, just run this command (require doxygen
binary to be installed). The output of the HTML page is in docs/doxygen/html
cd docs
doxygen docs/Doxygen
- Add event queue mechanism
in addition to callbackon input handling per window (see) -
Handle GLFW internal error- GLFW internal errors are mainly occurs from invalid enumeration passed into a function, invalid value for an enum passed into a function, and failure in maintaining invariants (like GLFW is initialized or not).
- This library is a C++ library so errors from passing invalid enumeration and/or invalid value for an enum is avoided by using the stricter type-system of C++.
- This library also is trying it's best in maintaining the invariants so hopefully the errors that surface from these are also avoided.
- That leaves out platform-related errors which I can report easily as exceptions.
- Add the ability to handle window events in separate thread from Window and WindowManager
While the handling itself still in the thread
WindowManager
is, with the introduction ofIEventInterceptor
, user can intercept events polled byWindowManager
before getting pushed intoEventQueue
insideWindowManager
(you can control whether to continue forwarding the event to the underlyingWindow
or not). The interceptedEvent
then can be sent to other thread for example. - Eliminate
glfw_cpp::WindowManager
move limitationBy making
WindowManager
be exclusivelyshared_ptr
, this issue is completely eliminated. - Add documentation.
- Add LICENSE