22nd of August, 2022 · In Technical · Tagged with GUI, C, Nuklear

Writing a GUI app with Nuklear

Writing desktop applicaitons has become much less fashionable as a multitude of companies have grown their fortunes on a purely web-app / Electron stack. There are many reasons for this shift in the industry which I won’t detail here, but one result is obvious: desktop applications have become much slower than the hardware is capable of due to reliance on web rendering engines.

One conundrum worth puzzling over is why is it that very graphically impressive video games can be produced, but Slack (which is arguably the best GUI chat client) relies on sluggish Electron and is full of weird bugs and sub 60FPS rendering.

Part of the problem is that the two big proprietary operating systems have written graphics apis (DirectX and Metal) which require special tweaking to use directly. So most companies have found it easier to rely on Google and the chrome team to work out the rendering bit and “focus on building the buisness logic” in an Electron application. Consumers have gotten so used to these Electron applications that they often don’t remember what fast native applications are like, and how pleasant it is to have immediate feedback from the computer.

Some solutions to the electron problem: Immediate mode GUIs

One of the reasons that the cross platform desktop application frameworks like QT and GTK have lagged behind electron in their overall complexity when compared to HTML / CSS / Javascript. However, here we would like to introduce to those who are uninitiated Immediate Mode GUI frameworks. These lightweight frameworks are not nearly as oppinionated in rendering as QT / GTK and run wicked fast. The two most compelling libraries for immediate mode guis are Dear ImGui ad Nuklear. Here we will look at Nuklear which is more minimal.

Nuklear has a very straightforward api (with many language bindings), for example to render a simple button you just call:

if(nk_button_label(ctx, "Play / Pause")) {
  puts("Hi");
}

Here we see a button being rendered. If the button is not being pressed the function returns false, and if it is the function nk_button_label returns truthy causing the program to write “Hi” to stdout.

This may seem weird if you come from the world of web apps and the DOM. In a web application you don’t have to care about the main rendering loop of the application – that is taken care of by the browser itself. In a web app you manipulate the dom / styles and then the browser works to render the current DOM / styles on each frame. Here (Nuklear) is calling the OpenGL rendering functions directly on each frame to render the button to the screen.

Therefore it is not only in the sense of using a more efficient language for rendering, but indeed the rendering iself is closer to the bare metal of a machine.

Building a simple application

On my GNU/linux machine I wanted a very simple GUI logout manager. All I need it to do is call various system actions like openrc-shutdown etc.

I saw that Distrotube had written one in Haskell and GTK but I thought I could make one in about an hour that was more minimal using Nuklear. It couldn’t have been easier.

lm Simple logout manager

All I do is keep a struct of various actions. Loop rendering buttons in the Nuklear UI, and use the Nuklear X11 rendering backend for extra minimalness.

/* Initilize actions with their enum / display / command */
Action actions[5] = {
  {"Logout", LOGOUT, "bspc quit"},
  {"Cancel", CLOSE, ""},
  {"Shutdown", SHUTDOWN, "shutdown -h now"},
  {"Reboot", REBOOT, "reboot"},
  {"Lock", LOCK, "slock"},
};

/* Loop through actions and set the chosen one */
for (i = 0; i < 5; ++i) {
  if(nk_button_label(ctx, actions[i].label)) {
    c = actions[i].choice;
    copy_string(cmd, actions[i].command);
    goto cleanup;
  }
}


/* In the cleanup call a system command if one was selected */
switch(c) {
  case LOGOUT:
  case REBOOT:
  case SHUTDOWN:
  case LOCK:
    system(cmd);
    break;
  case CLOSE:
    return 0;
  case NONE:
    return 1;
 }

There are a few extensions that I probably need to add like keyboard navigation, filtering, and configuration via config file, but it works perfectly well for my simple needs at the moment. The full source for the logout manager can be found here.

If you only have ever built web-apps I recommend giving Nuklear a try (if you don’t know any of the languages with bindings you probably should just try it in C for the easiest time).