mrizaln / glfw-cpp

C++ GLFW wrapper with multi-window and multithreading in mind

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

glfw-cpp

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:

TODO

  • Add event queue mechanism in addition to callback on 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
  • Eliminate glfw_cpp::WindowManager move limitation

Dependencies

  • 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.

Usage

Setting up

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 main)

# 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

Option

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.

Example

Using this library is as simple as

single.cpp

#include <glad/glad.h>
#include <glfw_cpp/glfw_cpp.hpp>

#include <iostream>
#include <cmath>

namespace glfw = glfw_cpp;

int main()
{
    // init() calls glfwInit() internally and Instance::Handle will call glfwTerminate() on dtor.
    // Note that the graphics API can't be changed later, this is a design choice.
    glfw::Instance::Handle instance = glfw::init(glfw::Api::OpenGL{
        .m_major   = 3,
        .m_minor   = 3,
        .m_profile = glfw::Api::OpenGL::Profile::CORE,
        .m_loader  = [](glfw::Api::GlContext /* handle */,
                       glfw::Api::GlGetProc proc) { gladLoadGLLoader((GLADloadproc)proc); },
    });

    // WindowManager is responsible for managing windows (think of window group)
    glfw::WindowManager wm = instance->createWindowManager();

    // graphics API hints are omitted from the WindowHint, only other relevant hints are included.
    glfw::WindowHint hint = {};    // use default hint

    glfw::Window window = wm.createWindow(hint, "Learn glfw-cpp", 800, 600);

    window.run([&, elapsed = 0.0F](const glfw::EventQueue& events) mutable {
        // events
        for (const glfw::Event& event : events) {
            if (auto* e = event.getIf<glfw::Event::KeyPressed>()) {
                if (e->m_key == glfw::KeyCode::Q) {
                    window.requestClose();
                }
            } else if (auto* e = event.getIf<glfw::Event::FramebufferResized>()) {
                glViewport(0, 0, e->m_width, e->m_height);
            }
        }

        // continuous key input (for movement for example)
        {
            using K          = glfw::KeyCode;
            const auto& keys = window.properties().m_keyState;

            if (keys.allPressed({ K::H, K::J, K::L, K::K })) {
                std::cout << "HJKL key pressed all at once";
            }

            if (keys.isPressed(K::LEFT_SHIFT) && keys.anyPressed({ K::W, K::A, K::S, K::D })) {
                std::cout << "WASD key pressed with shift key being held";
            } else if (keys.anyPressed({ K::W, K::A, K::S, K::D })) {
                std::cout << "WASD key pressed";
            }
        }

        elapsed += static_cast<float>(window.deltaTime());

        // 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.pollEvents();
    });
}

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::Handle outlive glfw_cpp::WindowManager and glfw_cpp::WindowManager outlive glfw_cpp::Windows 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 directory (I also use a different OpenGL loader library there).

Limitation

One limitation is that glfw_cpp::WindowManager should not be moved after it created glfw_cpp::Windows since each window created from it has a pointer to the glfw_cpp::WindowManager. If you are doing that it may leads to undefined behavior (dereferencing a pointer to a destroyed glfw_cpp::WindowManager for example). If the glfw_cpp::WindowManager hasn't created any glfw_cpp::Windows (or they are already destroyed) it is okay to move it.

About

C++ GLFW wrapper with multi-window and multithreading in mind


Languages

Language:C++ 94.9%Language:C 3.2%Language:CMake 1.6%Language:Python 0.2%Language:GLSL 0.1%