Skip to content

janekb04/glfwpp

Repository files navigation

GLFW C++ wrapper - glfwpp logo

CMake clang-format dependabot Total alerts Language grade: C/C++ Codacy Badge CodeFactor FOSSA Status

GLFWPP or (GLFW C++ Wrapper) is a thin, modern C++17 layer on top of GLFW. It supports GLFW versions from 3.2 up to the current 3.3.6. From the official GLFW website:

GLFW is an Open Source, multi-platform library for OpenGL, OpenGL ES and Vulkan development on the desktop. It provides a simple API for creating windows, contexts and surfaces, receiving input and events. GLFW is written in C and supports Windows, macOS, X11 and Wayland. GLFW is licensed under the zlib/libpng license.

I like C++ and OOP, so when I find a C library, I immediately look for a wrapper which offers RAII objects instead of free create and destroy functions, identifiers wrapped in namespaces, methods instead of free functions, scoped enums instead of macros and exceptions instead of error codes. In case of GLFW I didn't really find such a library, so I made one myself.


⏱️ Quick Start

To use, just clone the repo recursively:

git clone https://github.com/janekb04/glfwpp --recurse-submodules

Remember to install the necessary GLFW dependencies, if you're on Linux. Make sure to disable building the examples by setting the option GLFWPP_BUILD_EXAMPLES to OFF using set(GLFWPP_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) in your CMakeLists.txt, if you don't want them built, as they are built by default. If you don't disable them, you will also have to install the Vulkan SDK.

You can then link against the target GLFWPP using CMake:

add_executable(myExecutable mySource1.cpp mySource2.cpp mySource3.cpp)

set(GLFWPP_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # disable building GLFWPP examples
add_subdirectory("path/to/glfwpp")
target_link_libraries(myExecutable PRIVATE GLFWPP)

Now, you just have to include glfwpp.h and you're ready to go:

#include <glfwpp/glfwpp.h>
int main()
{
    auto GLFW = glfw::init();
    glfw::Window window{ 640, 480, "Hello GLFWPP"};
    while (!window.shouldClose())
        glfw::pollEvents();
}

You can also consult cmake.yml to see the complete installation and building process of GLFWPP, its dependencies and the examples on Ubuntu, macOS and Windows. Examples may be found in the /examples directory. Alternatively, just copy-paste the headers and include glfwpp.h (not recommended).

Note: To use functionality from glfw3native.h, native.h has to be included separately.

📌 Main Features
  • Error handling using exceptions (defined in error.h).
  • Strongly typed scoped enums for all GLFW constants that catch all GLFW_INVALID_ENUM errors at compile time.
  • Everything wrapped in namespace glfw to avoid name clashing
  • RAII wrappers for windows (glfw::Window), cursors (glfw::Cursor), key codes (glfw::KeyCode), monitors (glfw::Monitor), joysticks (glfw::Joystick) and the entire library (glfw::GlfwLibrary) for automatic resource management.
  • glfw::Event class to allow to specify any invocable (function, method, lambda, functor, etc.) as a callback. Note: it uses std::function which is infamous for its poor performance. However, events occur relatively rarely (probably generally no more than a dozen a frame) and as such I wouldn't expect this to be a performance issue. At the same time std::function has much greater flexibility than raw function pointers.
  • Hints passed through structures (glfw::InitHints and glfw::WindowHints) instead of through functions with an enum constant.
  • Mostly very thin wrapping matching nearly exactly the original GLFW naming which makes it both easier to port and allows to use the official GLFW documentation.
  • Performance overhead should be low, due to the thin nature of the wrapper. Note: The glfw::Event as mentioned above could have a little performance overhead, but it shouldn't be an issue. Another factor is the use of exceptions for error handling. However, most exception implementations have performance penalties only in the exceptional path, which, by definition, happens rarely.
  • Now also compatible with Vulkan-Hpp.
  • Now also compatible with Emscripten.
🎓 Example

Here is a quick comparison of GLFW and GLFWPP. The following code creates a OpenGL 4.6 context and clears the screen.

GLFW GLFWPP
#include <GLFW/glfw3.h>

int main()
{
    if (!glfwInit())
        return -1;

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    GLFWwindow* window = glfwCreateWindow(640, 480, "Hello World", nullptr, nullptr);
    if (!window) {
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    while (!glfwWindowShouldClose(window))
    {
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
}
#include <glfwpp/glfwpp.h>

int main()
{
    auto GLFW = glfw::init();
glfw::WindowHints{ .contextVersionMajor = 4, .contextVersionMinor = 6, .openglProfile = glfw::OpenGlProfile::Core }.apply(); glfw::Window window{640, 480, "Hello World"}; // If window creation fails, an exception is thrown glfw::makeContextCurrent(window); while (!window.shouldClose()) { glClear(GL_COLOR_BUFFER_BIT); window.swapBuffers(); glfw::pollEvents(); } // GlfwLibrary destructor calls glfwTerminate automatically }
📂 File structure

The functionality is split between files, as follows:

🔗 Interoperability

GLFWPP code and GLFW can be mixed with no issues as long as you mind these rules:

  • If GLFW is initialized with glfw::GlfwLibrary, you must not call glfwTerminate yourself and depend on it being called by the destructor of glfw::GlfwLibrary. You may call glfwInit though, but it won't have any effect. Also you should not use glfwSetErrorCallback, glfwSetMonitorCallback nor glfwSetJoystickCallback and instead use the appropriate glfw::XXXXevents to register your handlers.
  • If GLFW is initialized with glfwInit, you can initialize it again with glfw::GlfwLibrary. All the created GLFW objects will remain in a valid and all state will be preserved except that the handlers error callback, monitor callback and joystick callback handlers will be intercepted by GLFWPP and to register your own handlers you will have to use the appropriate glfw::XXXXevent.
  • Where applicable, glfw:: objects provide conversion operation to and from the underlying GLFWxxxx* handles. However it must be noted that the conversion to the underlying handles retains the ownership of those handles. As such, for example, you must not glfwDestroy them. At the same time the constructors from handles take the ownership of the given handle and as such in this case you also must not glfwDestroy them yourself.
⚖️ License

FOSSA Status