Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / DirectX

Introduction to RGFW, the Lightweight Single-Header Windowing Framework: Philosophy and Tutorial

5.00/5 (4 votes)
9 Aug 2024CPOL3 min read 5.2K  
RGFW introduction and tutorial. RGFW is a single-header windowing framework library & lightweight GLFW alternative.
RGFW is a lightweight, single-header windowing framework library. It provides a light abstraction layer over OS-dependent API code, similar to GLFW. RGFW supports X11, Windows (XP+), MacOS and HTML5. RGFW also has an experimental wayland backend. This article includes information about RGFW and a tutorial for how you can get RGFW up and running for your software.

Introduction

(The GitHub repository can be found here)

While working on my backend for RSGL, (a different project) I struggled to implement certain backends and features. This led me to consider using GLFW as a backend.

When I researched GLFW, I realized it wouldn't be a good choice for a backend because it includes an unreasonable amount of bloat. GLFW's binaries are ~1mb and its source code is ~10mbs. I knew based on the code I had already written, I could make something better.

That's when I came up with the idea for RGFW, a GLFW alternative library, a lightweight single-header file, with nearly all the same functionality as GLFW and more. The result: RGFW's binaries are only around ~100kbs, not that you need to use binaries and their source code is around ~300kbs. This is significantly more lightweight than GLFW.

Currently, RGFW's backend supports X11, Windows (XP+), MacOS, HTML5, and experimental support for Wayland. I also have plans for more backends.  RGFW can also load graphics contexts for OpenGL, DirectX, Vulkan, Metal, and buffer (software) rendering.

Single-header design philosophy

A critique of RGFW is that it shouldn't be a single-header file. This critique ignores that the major difference between normal libraries and single-header libraries is the design philosophy.

For example, GLFW is designed using functional programming. Functional programming is a paradigm in which the programmer thinks of his code as different subroutines interacting. This creates compartmentalized code and causes GLFW's code to become bloated. To concretize this point, when you run the function, "glfwSetCursorPos" it runs a function loaded into the global GLFW structure based on the platform "_glfw.platform.setCursorPos" which needs to be initialized during runtime.

RGFW on the other hand defines the function during compile time based on the target OS. Just like any proper single-header file RGFW's code is designed to be lightweight.  To be lightweight, your code cannot be abstraction-oriented. The code must be oriented around getting the job done and the underlying processes happening to do the job. This is similar to the idea of data-oriented programming, although RGFW is not yet properly data-oriented, I try to apply certain data-oriented principles to RGFW. 

NOTE: Not all single-header files are designed to be lightweight. Such libraries should rethink their design or reconsider its status as a single-header file.

Using RGFW

RGFW is a single-header file, so you must request the implementation code. You can also compile the RGFW implementation and link it separately.

C
#define RGFW_IMPLEMENTATION
#include "RGFW.h"

After this, you can create your first RGFW window and use RGFW_window_close to close and free the window data.

C
RGFW_window* window = RGFW_createWindow("name", RGFW_RECT(0, 0, 200, 200), RGFW_CENTER); // create window at the center of the screen 

// ...  to the end of the main file 

RGFW_window_close(window); // close the window

Next, you want to create a loop that handles events and renders. Luckily RGFW supports multiple ways of managing events. For example, here are three ways you can handle keyPress events.

C
void keyPress(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed) { 
    if (RGFW_isHeld(win, win->event.keyCode))
        return;
    printf("callback: key @ keycode %i %s with lockstate: %i\n", keycode,
        pressed ? "pressed" : "released", lockState);
} 

// ...

RGFW_setKeyCallback(keyPress);

while (RGFW_window_shouldClose(window) == RGFW_FALSE) {
    while (RGFW_window_checkEvent(window)) {
        if (window->event.type == RGFW_quit) break;
        if (window->event.type == RGFW_keyPressed && !RGFW_isHeld(window, window->event.keyCode))
            printf("loop: key @ keycode %i pressed with lockstate: %i\n", window->event.keyCode,
                      window->event.lockState);
    }

    // if key was pressed then released
    if (RGFW_wasPressed(window, RGFW_Up))
    printf("The up key was pressed\n");

After this, if you're using OpenGL, DirectX, or buffer rendering, you can use RGFW_window_swapBuffer to render to the screen.

Here's a basic example with OpenGL:

...

glClearColor(0xca, 0x00, 0xba, 0xff);
glClear(GL_COLOR_BUFFER_BIT);

glBegin(GL_TRIANGLES);
    glColor3f(1, 0, 0); glVertex2f(-0.6, -0.75);
    glColor3f(0, 1, 0); glVertex2f(0.6, -0.75);
    glColor3f(0, 0, 1); glVertex2f(0, 0.75);
glEnd();

RGFW_window_swapBuffers(window);
// ...
} // end of game loop

Here's the full code:

C
#define RGFW_IMPLEMENTATION 
#include "RGFW.h"

void keyPress(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed) { 
    if (RGFW_isHeld(win, win->event.keyCode))
        return;
    printf("callback: key @ keycode %i %s with lockstate: %i\n", keycode, 
                                      pressed ? "pressed" : "released", lockState); 
} 

int main () {
    RGFW_window* window = RGFW_createWindow("name", RGFW_RECT(0, 0, 200, 200), RGFW_CENTER); // create window at the center of the screen 
    
    RGFW_setKeyCallback(keyPress); 
    while (RGFW_window_shouldClose(window) == RGFW_FALSE) { 
        while (RGFW_window_checkEvent(window)) { 
            if (window->event.type == RGFW_quit) break; 
            if (window->event.type == RGFW_keyPressed && !RGFW_isHeld(window, window->event.keyCode)) 
                printf("loop: key @ keycode %i pressed with lockstate: %i\n", window->event.keyCode, 
                                                      window->event.lockState); 
        } 
        
        // if key was pressed then released 
        if (RGFW_wasPressed(window, RGFW_Up))
            printf("The up key was pressed\n"); 
        
        glClearColor(0xca, 0x00, 0xba, 0xff);
        glClear(GL_COLOR_BUFFER_BIT); 
        
        glBegin(GL_TRIANGLES);     
            glColor3f(1, 0, 0);
            glVertex2f(-0.6, -0.75);
            glColor3f(0, 1, 0); 
            glVertex2f(0.6, -0.75); 
            glColor3f(0, 0, 1); 
            glVertex2f(0, 0.75); 
        glEnd(); 
        RGFW_window_swapBuffers(window); 
    } 
    RGFW_window_close(window); 
}

To compile this example run:

Bash
linux:
    gcc main.c -lX11 -lXrandr -lGL -lm

windows: (mingw)
    gcc main.c -lgdi32 -lopengl32 -lwinmm -lm

macos:
    gcc main.c -lm -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo

HTML5:
     emcc test.c -s WASM=1 -s ASYNCIFY -s USE_WEBGL2 -s LEGACY_GL_EMULATION -D LEGACY_GL_EMULATION -sGL_UNSAFE_OPTS=0  -s EXPORTED_RUNTIME_METHODS="['stringToNewUTF8']"

Image 1

Closing Notes

RGFW is a single-header lightweight windowing framework. It's designed as an alternative to GLFW but is lightweight and a reality-oriented design philosophy while GLFW uses design philosophy oriented around an abstraction. RGFW is also very flexible offering many ways of doing the same thing, for example, there are multiple ways the user can handle events.

More information can be found on the RGFW repository such as documentation, code examples (you can even test the examples on the RGFW GitHub website),  a wiki page (that compares GLFW directly to RGFW, including supported features, platforms, etc), and more. If you are interested in RGFW, please check out the RGFW GitHub repository, star it, and share it with people you think would be interested. This will help RGFW's community grow and for RGFW to improve.

If you have issues or suggestions for RGFW, please file them in the repository so they can be looked into ASAP. RGFW is actively being maintained to fix bugs, add support for other APIs, and add any missing features.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)