Skip to content
Berry-OSPublic

Latest commit

Β 

History

43 Commits

Folders and files

Repository files navigation

πŸŒ™ Luna UI

Header-only, multiplatform HTML/CSS UI for native OpenGL applications.

Sponsor

Luna UI parses HTML and CSS, lays out a lightweight DOM, and renders it directly with OpenGL. The same core is used by the built-in Windows, Linux, macOS, iOS, Android, and Web hosts.

luna-ui.h is the main public include. Define LUNA_UI_IMPLEMENTATION in exactly one translation unit and the appropriate native host is selected automatically.

✨ Screenshots

This frame is rendered by the real examples/example.c application and the current Luna UI renderer.

Luna UI header-only example rendered by the native OpenGL renderer

The screenshot was captured from the real GLFW/OpenGL application with Luna UI's built-in F12 framebuffer capture path at 960 Γ— 640. It replaces the older broken and stale example-state images.

Standalone demo gallery

These screenshots were captured by running the migrated demo applications in examples/ with the real GLFW/OpenGL host:

Aurora dashboard CSS/OpenGL showcase
Aurora dashboard demo CSS and OpenGL showcase
Modal, backdrop and focus trap Select menu and native controls
Apply Changes confirmation modal rendered by Luna UI Theme select menu and form controls rendered by Luna UI

The interaction shots above are not mockups: the demo was launched under the GLFW/OpenGL host, the controls were clicked through Luna UI's input system, and the resulting framebuffer was captured with the built-in F12 screenshot path.

Minimal card sample Profile studio and text fields
Minimal Luna UI sample Profile studio sample

The samples now bind F12 directly to Luna UI's PNG capture path. For an automated, reproducible capture, set LUNA_SCREENSHOT to the output filename; the sample renders three settling frames, writes the PNG, and exits. The images above use the samples' real 800 Γ— 500 and 860 Γ— 620 framebuffers respectively.

Build and run the gallery demos locally with:

make -C examples
./examples/luna-ui --size 1024x700
./examples/opengl_gui --size 1024x700
./examples/sample_01
./examples/sample_02
./examples/tooltip
# or: LUNA_SCREENSHOT=sample.png ./examples/sample_01

CSS tooltips

Luna UI supports dynamic pseudo-classes on ancestor selectors, so a tooltip can use the same nested HTML/CSS pattern as the web. Add keyboard focus selectors alongside hover so the description is not pointer-only:

<button class="tooltip">
  Save
  <span class="tooltip-text">Save the current document</span>
</button>
.tooltip { position: relative; }
.tooltip-text {
  position: absolute;
  opacity: 0;
  visibility: hidden;
}
.tooltip:hover .tooltip-text,
.tooltip:focus .tooltip-text,
.tooltip:focus-visible .tooltip-text {
  opacity: 1;
  visibility: visible;
}

Build and run the complete example with make -C examples tooltip followed by ./examples/tooltip. It includes above/below placement and an arrow made with ::before.

πŸš€ Quick start

#define LUNA_UI_IMPLEMENTATION
#include "luna-ui.h"

static const char* html =
    "<body><main class=\"card\">"
    "<h1>Hello, Luna UI</h1>"
    "<p>HTML/CSS rendered by native OpenGL.</p>"
    "</main></body>";

static const char* css =
    "body{display:flex;align-items:center;justify-content:center;"
    "background:#0f172a;color:white;}"
    ".card{padding:32px;border-radius:20px;background:#1e293b;}";

int main(void) {
    LunaAppConfig app = {0};
    app.title = "My Luna app";
    app.width = 960;
    app.height = 640;
    app.resizable = 1;
    app.vsync = 1;
    app.html = html;
    app.css = css;
    return luna_app_run(&app);
}

HTML/CSS can be supplied either as strings (html, css) or paths (html_path, css_path). examples/example.c also demonstrates registering a native C callback for an onclick handler and changing DOM text at runtime.

🧩 Current architecture

The repository currently consists of a shared UI/rendering core plus header-only platform hosts:

  • luna-ui.h β€” DOM, CSS, layout, input, OpenGL renderer, public UI API, and LunaPlatform host ABI v3.
  • luna_windows.h β€” Win32 + WGL/OpenGL host.
  • luna_glfw.h β€” GLFW + OpenGL Linux host (the default). Luna UI does not call X11, Wayland, GLX, or EGL directly from this backend.
  • luna_kms.h β€” bare-console Linux host: DRM/KMS + GBM + EGL + libinput, no display server. Selected with -DLUNA_UI_BACKEND_KMS.
  • luna_macos.h β€” Cocoa + OpenGL host.
  • luna_ios.h β€” UIKit + OpenGL ES host.
  • luna_android.h β€” NativeActivity + EGL/OpenGL ES host.
  • luna_web.h β€” Emscripten + WebGL 2 host.
  • luna-window.h β€” optional reusable window chrome, themes, dialogs, toast/notification UI, and desktop file-dialog/file-manager support.
  • cssparser.h, stb_truetype.h, stb_image.h, stb_image_write.h β€” bundled single-header dependencies.

There is no separate luna_platform.h. Platform selection is performed from luna-ui.h in the implementation translation unit.

πŸͺŸ Window and platform services

The built-in hosts provide the native window/context lifecycle and connect platform services to the shared LunaPlatform ABI. The current v3 ABI includes hooks for:

  • OpenGL procedure lookup and monotonic time
  • resource and font loading
  • clipboard access
  • IME/text-input activation
  • cursor changes and display scale
  • close, minimize, maximize, title changes, move and resize requests
  • redraw requests
  • PNG saving
  • system notifications

LunaPlatform includes both struct_size and api_version (LUNA_UI_API_VERSION is currently 0x00030000) so custom hosts can validate the host structure they provide.

Common helpers include:

void  luna_clipboard_set(const char* utf8);
char* luna_clipboard_get(void);
void  luna_clipboard_free(char* utf8);
float luna_platform_scale(void);

πŸ‘† Touch and tablet input

LunaTouchEvent carries a stable contact ID, phase, logical coordinates, pressure, contact radius, tool type (finger, stylus, or eraser), and stylus tilt. Native hosts forward it with luna_touch(); applications that need raw gestures can install LunaAppConfig.on_touch and consume selected events.

The built-in recognizer supports taps, control dragging, one-finger scrolling, and multiple active contacts. iOS, Android, Web, and Windows (WM_POINTER) provide native touch data. GLFW has no touch API, so the Linux GLFW host receives the compatibility mouse events supplied by the window system. A Linux native host can define LUNA_UI_NO_PLATFORM and feed libinput/Wayland contacts directly to luna_touch() without adding those dependencies to Luna UI.

🎨 luna-window.h

Applications that want consistent Luna-styled desktop chrome can additionally use luna-window.h:

#define LUNA_UI_IMPLEMENTATION
#include "luna-ui.h"

#define LUNA_WINDOW_IMPLEMENTATION
#include "luna-window.h"

It provides standard titlebar/resize markup and CSS, light/dark/custom themes, alerts, confirmation and prompt dialogs, toast notifications, and notification fallback UI.

For the reusable desktop file dialog/file manager implementation, define LUNA_WINDOW_FILE_DIALOG_IMPLEMENTATION in the same translation unit as both implementation macros:

#define LUNA_UI_IMPLEMENTATION
#include "luna-ui.h"
#define LUNA_WINDOW_IMPLEMENTATION
#define LUNA_WINDOW_FILE_DIALOG_IMPLEMENTATION
#include "luna-window.h"

The file-dialog API supports file manager, open file(s), select folder, and save file modes through LunaFileDialogConfig and luna_file_dialog_run().

πŸ–₯️ Desktop builds

Linux

The default Linux host uses GLFW for windowing/input and OpenGL for rendering:

cc -O2 -std=c11 examples/example.c -o luna-example \
  -lGL -lm -lglfw

No direct -lX11 dependency is required by Luna UI itself. The GLFW package used by your system may of course depend on X11 or Wayland internally.

For a machine with no display server β€” a kiosk or a shell that starts right after boot β€” build the same source against the KMS host instead. It takes the DRM master's primary plane, page-flips scanout buffers straight out of EGL/GBM, draws the pointer on the hardware cursor plane, and reads the seat through libinput:

cc -O2 -std=c11 -DLUNA_UI_BACKEND_KMS examples/example.c -o luna-example \
  $(pkg-config --cflags libdrm xkbcommon) \
  -lEGL -lgbm -ldrm -linput -ludev -lxkbcommon -lm

It needs read/write access to /dev/dri/cardN (the video group, or root) and a seat it can claim through seatd/logind. LUNA_DRM_DEVICE picks a specific node and LUNA_SEAT a specific seat.

Embedding a host in your own main loop

luna_app_run() owns the loop, which is what an application wants. A shell usually cannot give that up: it has to wait on its own descriptors, decide its own repaint cadence, and pick a backend at runtime. Such an embedder defines LUNA_UI_NO_PLATFORM, includes the host headers itself with LUNA_UI_HOST_PRELUDE / LUNA_UI_HOST_BODY, and drives LunaHostOps:

const LunaHostOps* host = use_kms ? luna_host_kms() : luna_host_glfw();
if (!host->start(&host_cfg)) return 1;   /* installs LunaPlatform too */
luna_init(&(LunaInitConfig){ w, h, luna_platform_get_proc(), 0 });
while (!host->should_close()) {
    host->wait_events(timeout_ms, my_fds, my_fd_count);
    luna_render(w, h);
    host->swap_buffers();
}

wait_events() sleeps on host input and the caller's descriptors at once, returning which of them became readable. The built-in luna_app_run() loops are written on exactly these ops, so both paths share one implementation of each backend. apps/luna-shell is the worked example.

Windows / MSVC

cl /std:c11 /O2 examples\example.c

The Windows host uses Win32/WGL. With MinGW, link the normal Win32/OpenGL libraries (for example opengl32, gdi32, user32, imm32, and shell32).

macOS

Compile the implementation translation unit as Objective-C or Objective-C++:

clang -O2 -x objective-c examples/example.c -o luna-example \
  -framework Cocoa -framework OpenGL -framework CoreText

Declaration-only users of luna-ui.h can remain ordinary C/C++ translation units.

🌐 Web

emcc examples/example.c -o examples/luna-example.js \
  -sUSE_WEBGL2=1 -sFULL_ES3=1 -sALLOW_MEMORY_GROWTH=1 \
  -sEXPORTED_FUNCTIONS=_main,_malloc,_free,_luna_web_commit_utf8 \
  -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8,lengthBytesUTF8

Serve the examples directory over HTTP and open examples/index.html.

πŸ“± iOS

Add examples/example_ios.m and the Luna headers to an iOS application target. Link UIKit, QuartzCore, OpenGLES, and CoreText. The iOS host supplies the application/view lifecycle, EAGLContext, touch input, and text input bridge.

πŸ€– Android

The Android host uses android.app.NativeActivity, EGL, and OpenGL ES 3. The implementation exports ANativeActivity_onCreate.

NativeActivity has no main(), so the host calls luna_app_config() when the application defines it. A portable app defines that one function, calls it from its own main() on the other platforms, and can compile the implementation straight from the header β€” no per-app implementation file:

cc -x c -DLUNA_UI_IMPLEMENTATION -c luna-ui.h -o luna-ui.o   # -x objective-c on Apple

Alternatively the config function can be named before including the implementation:

#define LUNA_ANDROID_APP_CONFIG luna_android_make_config
#define LUNA_UI_IMPLEMENTATION
#include "luna-ui.h"

LunaAppConfig luna_android_make_config(void) {
    LunaAppConfig app = {0};
    app.html_path = "ui/main.html";
    app.css_path = "ui/main.css";
    app.vsync = 1;
    return app;
}

Link android, EGL, GLESv3, dl, and log. Pack HTML, CSS, images, and bundled fonts in the APK assets as appropriate.

πŸ”§ Custom host / embedding

The renderer can be embedded in another window system without any built-in host:

#define LUNA_UI_NO_PLATFORM
#define LUNA_UI_IMPLEMENTATION
#include "luna-ui.h"

Create a compatible OpenGL context, populate LunaPlatform, call luna_set_platform(), initialize Luna with luna_init(), forward input events, and drive luna_update() / luna_render() from your own loop.

This is also the intended extension point for alternative window systems and future rendering hosts without changing the DOM/CSS/layout API.

πŸ“¦ Implementation rules

  • Define LUNA_UI_IMPLEMENTATION in exactly one translation unit.
  • Keep the Luna headers and bundled single-header dependencies in the include tree.
  • On macOS/iOS, compile the implementation translation unit as Objective-C/Objective-C++.
  • Use LUNA_UI_NO_PLATFORM only when supplying your own host.
  • Define LUNA_WINDOW_IMPLEMENTATION only if using the optional common window layer.

πŸ“ Notes

The current renderer is shared across the native OpenGL/OpenGL ES/WebGL hosts. macOS OpenGL and iOS OpenGL ES are legacy Apple APIs, but they keep the host layer compact while the higher-level DOM, CSS, layout, input, and application APIs remain portable.

πŸ“„ License

This project is licensed under the Mozilla Public License 2.0 (MPL-2.0).

Copyright (c) Yuichiro Nakada, Berry OS / Luna Desktop contributors.

See LICENSE for the full license text. Bundled third-party single-header libraries retain their original licenses.

Releases

Packages

Contributors

Languages