Awesome
GLFW C++ wrapper - glfwpp
<p align="center"> <img src="https://i.stack.imgur.com/cmt94.gif"> </p>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 namespace
s, methods instead of free functions, scoped enum
s 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.
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.
- 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 usesstd::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 timestd::function
has much greater flexibility than raw function pointers.- Hints passed through structures (
glfw::InitHints
andglfw::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.
Here is a quick comparison of GLFW and GLFWPP. The following code creates a OpenGL 4.6 context and clears the screen.
<table> <tr> <th> GLFW </th> <th> GLFWPP </th> </tr> <tr> <td> <pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'JetBrains Mono',monospace;font-size:9.8pt;"><span style="color:#bbb529;">#include </span><span style="color:#0cc21a;"><GLFW/glfw3.h> </span><span style="color:#0cc21a;"> </span><span style="color:#ed3792;">int </span><span style="color:#ffc66d;font-weight:bold;">main</span><span style="color:#6989bb;">() </span><span style="color:#6897bb;">{ </span><span style="color:#6897bb;"> </span><span style="color:#ed3792;">if </span><span style="color:#6989bb;">(</span><span style="color:#af3681;">!</span><span style="color:#e0ff76;">glfwInit</span><span style="color:#6989bb;">()) </span><span style="color:#6989bb;"> </span><span style="color:#ed3792;">return </span><span style="color:#af3681;">-</span><span style="color:#0cc21a;">1</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#af3681;"> </span><span style="color:#e0ff76;">glfwWindowHint</span><span style="color:#6989bb;">(</span><span style="color:#58a517;">GLFW_CONTEXT_VERSION_MAJOR</span><span style="color:#af3681;">, </span><span style="color:#0cc21a;">4</span><span style="color:#6989bb;">)</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#e0ff76;">glfwWindowHint</span><span style="color:#6989bb;">(</span><span style="color:#58a517;">GLFW_CONTEXT_VERSION_MINOR</span><span style="color:#af3681;">, </span><span style="color:#0cc21a;">6</span><span style="color:#6989bb;">)</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#e0ff76;">glfwWindowHint</span><span style="color:#6989bb;">(</span><span style="color:#58a517;">GLFW_OPENGL_PROFILE</span><span style="color:#af3681;">, </span><span style="color:#58a517;">GLFW_OPENGL_CORE_PROFILE</span><span style="color:#6989bb;">)</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#836bc6;">GLFWwindow</span><span style="color:#af3681;">* </span><span style="color:#6dc68c;font-style:italic;">window </span><span style="color:#af3681;">= </span><span style="color:#e0ff76;">glfwCreateWindow</span><span style="color:#6989bb;">(</span><span style="color:#0cc21a;">640</span><span style="color:#af3681;">, </span><span style="color:#0cc21a;">480</span><span style="color:#af3681;">, </span><span style="color:#0cc21a;">"Hello World"</span><span style="color:#af3681;">, </span><span style="color:#ed3792;">nullptr</span><span style="color:#af3681;">, </span><span style="color:#ed3792;">nullptr</span><span style="color:#6989bb;">)</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#ed3792;">if </span><span style="color:#6989bb;">(</span><span style="color:#af3681;">!</span><span style="color:#6dc68c;font-style:italic;">window</span><span style="color:#6989bb;">) </span><span style="color:#6897bb;">{ </span><span style="color:#6897bb;"> </span><span style="color:#e0ff76;">glfwTerminate</span><span style="color:#6989bb;">()</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#ed3792;">return </span><span style="color:#af3681;">-</span><span style="color:#0cc21a;">1</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#6897bb;">} </span><span style="color:#6897bb;"> </span><span style="color:#6897bb;"> </span><span style="color:#e0ff76;">glfwMakeContextCurrent</span><span style="color:#6989bb;">(</span><span style="color:#6dc68c;font-style:italic;">window</span><span style="color:#6989bb;">)</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#af3681;"> </span><span style="color:#ed3792;">while </span><span style="color:#6989bb;">(</span><span style="color:#af3681;">!</span><span style="color:#e0ff76;">glfwWindowShouldClose</span><span style="color:#6989bb;">(</span><span style="color:#6dc68c;font-style:italic;">window</span><span style="color:#6989bb;">)) </span><span style="color:#6989bb;"> </span><span style="color:#6897bb;">{ </span><span style="color:#6897bb;"> </span><span style="color:#e0ff76;">glClear</span><span style="color:#6989bb;">(</span><span style="color:#58a517;">GL_COLOR_BUFFER_BIT</span><span style="color:#6989bb;">)</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#af3681;"> </span><span style="color:#e0ff76;">glfwSwapBuffers</span><span style="color:#6989bb;">(</span><span style="color:#6dc68c;font-style:italic;">window</span><span style="color:#6989bb;">)</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#e0ff76;">glfwPollEvents</span><span style="color:#6989bb;">()</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#6897bb;">} </span><span style="color:#6897bb;"> </span><span style="color:#6897bb;"> </span><span style="color:#e0ff76;">glfwTerminate</span><span style="color:#6989bb;">()</span><span style="color:#af3681;">; </span><span style="color:#6897bb;">}</span></pre> </td> <td> <pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'JetBrains Mono',monospace;font-size:9.8pt;"><span style="color:#bbb529;">#include </span><span style="color:#0cc21a;"><glfwpp/glfwpp.h> </span><span style="color:#0cc21a;"> </span><span style="color:#ed3792;">int </span><span style="color:#ffc66d;font-weight:bold;">main</span><span style="color:#6989bb;">() </span><span style="color:#6897bb;">{ </span><span style="color:#6897bb;"> </span><span style="color:#ed3792;">auto </span><span style="color:#6dc68c;font-style:italic;">GLFW </span><span style="color:#af3681;">= </span><span style="color:#51bcff;">glfw</span><span style="color:#af3681;">::</span><span style="color:#e0ff76;">init</span><span style="color:#6989bb;">()</span><span style="color:#af3681;">;<br> </span><span style="color:#af3681;"> </span><span style="color:#af3681;"> </span><span style="color:#51bcff;">glfw</span><span style="color:#af3681;">::</span><span style="color:#e0ff76;">WindowHints</span><span style="color:#6897bb;">{ </span><span style="color:#61c669;font-style:italic;">.contextVersionMajor </span><span style="color:#af3681;">= </span><span style="color:#0cc21a;">4</span><span style="color:#af3681;">, </span><span style="color:#af3681;"> </span><span style="color:#61c669;font-style:italic;">.contextVersionMinor </span><span style="color:#af3681;">= </span><span style="color:#0cc21a;">6</span><span style="color:#af3681;">, </span><span style="color:#af3681;"> </span><span style="color:#61c669;font-style:italic;">.openglProfile </span><span style="color:#af3681;">= </span><span style="color:#51bcff;">glfw</span><span style="color:#af3681;">::</span><span style="color:#5a60c6;">OpenGlProfile</span><span style="color:#af3681;">::</span><span style="color:#0aa516;font-style:italic;">Core </span><span style="color:#6897bb;">}</span><span style="color:#af3681;">.</span><span style="color:#e0ff76;">apply</span><span style="color:#6989bb;">()</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#51bcff;">glfw</span><span style="color:#af3681;">::</span><span style="color:#5a60c6;">Window </span><span style="color:#6dc68c;font-style:italic;">window</span><span style="color:#6897bb;">{</span><span style="color:#0cc21a;">640</span><span style="color:#af3681;">, </span><span style="color:#0cc21a;">480</span><span style="color:#af3681;">, </span><span style="color:#0cc21a;">"Hello World"</span><span style="color:#6897bb;">}</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#808080;">// If window creation fails, an exception is thrown </span><span style="color:#808080;"> </span><span style="color:#808080;"> </span><span style="color:#808080;"> </span><span style="color:#808080;"> </span><span style="color:#808080;"> </span><span style="color:#51bcff;">glfw</span><span style="color:#af3681;">::</span><span style="color:#e0ff76;">makeContextCurrent</span><span style="color:#6989bb;">(</span><span style="color:#6dc68c;font-style:italic;">window</span><span style="color:#6989bb;">)</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#af3681;"> </span><span style="color:#ed3792;">while </span><span style="color:#6989bb;">(</span><span style="color:#af3681;">!</span><span style="color:#6dc68c;font-style:italic;">window</span><span style="color:#af3681;">.</span><span style="color:#e0ff76;">shouldClose</span><span style="color:#6989bb;">()) </span><span style="color:#6989bb;"> </span><span style="color:#6897bb;">{ </span><span style="color:#6897bb;"> </span><span style="color:#e0ff76;">glClear</span><span style="color:#6989bb;">(</span><span style="color:#58a517;">GL_COLOR_BUFFER_BIT</span><span style="color:#6989bb;">)</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#af3681;"> </span><span style="color:#6dc68c;font-style:italic;">window</span><span style="color:#af3681;">.</span><span style="color:#e0ff76;">swapBuffers</span><span style="color:#6989bb;">()</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#51bcff;">glfw</span><span style="color:#af3681;">::</span><span style="color:#e0ff76;">pollEvents</span><span style="color:#6989bb;">()</span><span style="color:#af3681;">; </span><span style="color:#af3681;"> </span><span style="color:#6897bb;">} </span><span style="color:#6897bb;"> </span><span style="color:#6897bb;"> </span><span style="color:#808080;">// GlfwLibrary destructor calls glfwTerminate automatically </span><span style="color:#6897bb;">}</span></pre> </td> </tr> </table> </details> <details><summary>:open_file_folder: <b>File structure</b></summary><br />The functionality is split between files, as follows:
-
error.h
- things related to error handling (exception types etc.). All GLFW errors are detected by the library and thrown as exceptions. The exception type matches the respective GLFW error code. -
glfwpp.h
- main header with, includes all other headers. Contains:- The
init
function. Initialization hints are passed withglfw::InitHints
. The RAII wrapperglfw::GlfwLibrary
takes care of callingglfwTerminate()
. - Time input.
- Clipboard input and output.
- Vulkan specific functionality. Compatible with both
vulkan.h
and Vulkan-Hpp.
- The
-
event.h
-glfw::Event
class used for specifying all user callbacks as well as event management functions. -
joystick.h
-glfw::Joystick
class and functionality related to joystick input -
monitor.h
-glfw::Monitor
and other functionality related to monitor management. -
window.h
-glfw::Window
class,glfw::Cursor
class,glfw::KeyCode
class and other functionality related to managing windows, window contexts and window input (clipboard and time IO inglfwpp.h
). Window hints are specified usingglfw::WindowHints
. -
native.h
- functions for native access wrapping aroundglfw3native.h
. -
version.h
- function for querying the GLFW runtime and compile time version and version string.
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 callglfwTerminate
yourself and depend on it being called by the destructor ofglfw::GlfwLibrary
. You may callglfwInit
though, but it won't have any effect. Also you should not useglfwSetErrorCallback
,glfwSetMonitorCallback
norglfwSetJoystickCallback
and instead use the appropriateglfw::XXXXevent
s to register your handlers. - If GLFW is initialized with
glfwInit
, you can initialize it again withglfw::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 appropriateglfw::XXXXevent
. - Where applicable,
glfw::
objects provide conversion operation to and from the underlyingGLFWxxxx*
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 notglfwDestroy
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 notglfwDestroy
them yourself.