nefergui

package module
v0.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: GPL-3.0 Imports: 1 Imported by: 0

README

NeferGUI

A pure-Go GUI renderer for Linux: build your view from the current model, style it with CSS, and draw it with Vulkan into DMA-BUF buffers. Controls return events during the view call. No cgo required.

NeferGUI imports no Wayland library. Your application owns the Wayland client (connection, surface, linux-dmabuf feedback, buffer import, seat events) and drives a Renderer; github.com/bnema/neferclient is a client toolkit built for this. The two do not import each other: the application copies plain fields between them.

[!WARNING] Early development. No release yet. Expect bugs and breaking changes. Platform accessibility is not connected.

A small example

The view is plain Go. The model type is yours.

type Model struct{ Count int }

view := func(f *nefergui.Frame, m *Model) {
	root := f.Root().Column()
	root.Text("Hello, Wayland!")
	if root.Button("Count", nefergui.Key("count")).Activated() {
		m.Count++
	}
	root.Text(fmt.Sprintf("Clicked %d times", m.Count))
}

The client loop, on one owner goroutine:

r, err := nefergui.NewRenderer(nefergui.RendererConfig{MainDevice: dev, Formats: formats})
r.Resize(320, 200, 1)          // on configure and scale changes
r.Input(&nefergui.Input{...})  // for each pointer, key and focus event

var out nefergui.Output
if ok, err := r.Render(&out, &model, view); ok && err == nil {
	// import out.Planes as a wl_buffer when out.NewBuffer, attach with the
	// acquire/release points, commit
}
r.Released(buffer) // when out.ReleaseFD becomes readable

RendererConfig.Styles loads your own CSS; default styles work without it. See ExampleRenderer and runtime for the full contract.

Demo

examples/demo is a document workspace (toolbar, document list, text editor, properties pane, light and dark themes) in a Wayland window, using neferclient as the client. It lives in its own module, so NeferGUI does not depend on neferclient, and it always builds against the NeferGUI code in this checkout:

cd examples && go run ./demo

Requirements

Go 1.27, libvulkan.so.1 and usable fonts. The GPU must export DMA-BUF and support DRM syncobj timelines (kernel 6.6 or later for eventfd waits). The compositor, reached through your Wayland client, must offer linux-dmabuf and linux-drm-syncobj; fractional scaling also needs viewporter. No X11 or software-rendering fallback.

go get github.com/bnema/nefergui

Documentation

License

GNU GPL v3. Third-party code and test fonts retain their own licenses and attribution.

Documentation

Overview

Package nefergui is a pure-Go immediate-mode GUI renderer styled with a bounded CSS dialect (see docs/css.md). It draws with Vulkan into DMA-BUF buffers and imports no Wayland library: the application owns the Wayland client (for example with github.com/bnema/neferclient) and feeds events to a Renderer.

A view function builds each frame from a Frame; Frame and its Node handles are valid only during that call. Key identifies a child among its siblings across frames; without a key, position and type determine identity. ID is CSS metadata, not frame identity.

The frame tree, controls, editors and input routing are implemented in internal/ui; this package is the public facade and re-exports those types.

Builds support CGO_ENABLED=0. At runtime NeferGUI requires libvulkan.so.1 and a GPU with DMA-BUF export and DRM syncobj timelines. See docs/runtime.md and docs/troubleshooting.md.

Index

Examples

Constants

View Source
const (
	InputPointerMotion  = ui.InputPointerMotion
	InputPointerPress   = ui.InputPointerPress
	InputPointerRelease = ui.InputPointerRelease
	InputPointerAxis    = ui.InputPointerAxis
	InputPointerLeave   = ui.InputPointerLeave
	InputKey            = ui.InputKey
	InputFocusIn        = ui.InputFocusIn
	InputFocusOut       = ui.InputFocusOut
	InputReset          = ui.InputReset

	ModShift = ui.ModShift
	ModCtrl  = ui.ModCtrl
	ModAlt   = ui.ModAlt
	ModSuper = ui.ModSuper

	ModCapsLock = ui.ModCapsLock
	ModNumLock  = ui.ModNumLock
)
View Source
const (
	CursorDefault    = ui.CursorDefault
	CursorPointer    = ui.CursorPointer
	CursorText       = ui.CursorText
	CursorNotAllowed = ui.CursorNotAllowed
)

Variables

This section is empty.

Functions

func Class

func Class(class string) commonOption

Class adds a CSS class.

func Disabled

func Disabled(v bool) disabledOption

Disabled blocks focus and interaction on an interactive control.

func ID

func ID(id string) commonOption

ID sets the CSS ID; it does not affect frame identity.

func Inline

func Inline(src string) commonOption

Inline applies inline CSS declarations.

func Key

func Key(key string) commonOption

Key identifies an element among its siblings across frames.

Types

type ButtonEvent

type ButtonEvent = ui.ButtonEvent

ButtonEvent is an immutable snapshot queried with Activated. Alias methods are listed by go doc github.com/bnema/nefergui/internal/ui.ButtonEvent.

type ButtonOption

type ButtonOption = ui.ButtonOption

Option families are sealed: ContainerOption, ButtonOption, EditOption, ValueOption and HeadingOption accept the common CSS options (Key, ID, Class, Inline) plus the control-specific options listed on each function.

type ChangeEvent

type ChangeEvent = ui.ChangeEvent

ChangeEvent is an immutable snapshot queried with Changed. Alias methods are listed by go doc github.com/bnema/nefergui/internal/ui.ChangeEvent.

type Clipboard added in v0.5.0

type Clipboard = ui.Clipboard

Clipboard is an optional asynchronous clipboard; IME an optional input method.

type ContainerOption

type ContainerOption = ui.ContainerOption

Option families are sealed: ContainerOption, ButtonOption, EditOption, ValueOption and HeadingOption accept the common CSS options (Key, ID, Class, Inline) plus the control-specific options listed on each function.

type Cursor added in v0.5.0

type Cursor = ui.Cursor

Cursor is the pointer shape the surface wants.

type EditEvent

type EditEvent = ui.EditEvent

EditEvent is an immutable snapshot queried with Changed and Submitted. Alias methods are listed by go doc github.com/bnema/nefergui/internal/ui.EditEvent.

type EditOption

type EditOption = ui.EditOption

Option families are sealed: ContainerOption, ButtonOption, EditOption, ValueOption and HeadingOption accept the common CSS options (Key, ID, Class, Inline) plus the control-specific options listed on each function.

func Password

func Password(v bool) EditOption

Password masks displayed editor text and blocks copy and cut.

func Placeholder

func Placeholder(v string) EditOption

Placeholder sets the text shown in an empty editor.

type Format added in v0.5.0

type Format = ui.Format

Format is a DRM format and modifier the compositor accepts.

type Frame

type Frame = ui.Frame

Frame is valid only during one call to the view function. Root builds the application element; Diagnostics reports identity problems in debug builds. Alias methods are listed by go doc github.com/bnema/nefergui/internal/ui.Frame.

type HeadingOption

type HeadingOption = ui.HeadingOption

Option families are sealed: ContainerOption, ButtonOption, EditOption, ValueOption and HeadingOption accept the common CSS options (Key, ID, Class, Inline) plus the control-specific options listed on each function.

func Level

func Level(level int) HeadingOption

Level sets the accessibility heading level (1 through 6).

type IME added in v0.5.0

type IME = ui.IME

Clipboard is an optional asynchronous clipboard; IME an optional input method.

type Input added in v0.5.0

type Input = ui.Input

Input is one platform input event; Text is valid only during Renderer.Input.

type InputKind added in v0.2.0

type InputKind = ui.InputKind

InputKind classifies an Input; Modifiers is a bit set of held modifiers and latched Caps Lock and Num Lock state.

type Modifiers added in v0.2.0

type Modifiers = ui.Modifiers

InputKind classifies an Input; Modifiers is a bit set of held modifiers and latched Caps Lock and Num Lock state.

type Node

type Node = ui.Node

Node is an ephemeral handle into the current frame. Do not retain it between frames. Container methods (Box, Row, Column, Stack, Scroll, Header, Nav, Main, Section, Aside, Footer, Element) return child nodes; control methods (Button, Checkbox, Radio, Slider, Input, Textarea, Text, Heading, Image, Icon, Separator, Spacer) declare controls. Interactive controls return event snapshots. Alias methods are listed by go doc github.com/bnema/nefergui/internal/ui.Node.

type Output added in v0.5.0

type Output = ui.Output

Output describes one frame to present, in physical pixels. All file descriptors stay owned by the Renderer: duplicate them before handing them to a transport that consumes descriptors. Slices are valid until the next Render.

type Plane added in v0.5.0

type Plane = ui.Plane

Plane, Timeline and Retired are parts of Output. A Timeline FD is valid only when Output.NewTimelines is set. The client must stop watching and destroy each Retired buffer before the next Render.

type Rect added in v0.2.0

type Rect = ui.Rect

Rect is an integer logical-pixel rectangle in surface coordinates, used by Frame.SetInputRects and Output.InputRects.

type Renderer added in v0.5.0

type Renderer = ui.Renderer

Renderer renders views into DMA-BUF buffers for a Wayland client the application owns. It imports no Wayland library, starts no goroutine and is used from one owner goroutine. Typical loop: forward Wayland events with Input and Resize, call Render when a redraw is needed, import and present the Output buffer, and call Released when its ReleaseFD becomes readable.

Pending() reports a built frame waiting for the GPU rather than the compositor; arm a short timer (about 2 ms) and call Render again.

Render is generic over the model type, so it is a generic method:

ok, err := r.Render(&out, &model, view)

Measure sizes a view before a surface exists (owner goroutine, not for every frame); it changes no Renderer state:

width, height, err := r.Measure(&model, view, maxWidth)
Example

ExampleRenderer shows the shape of a client loop. The Wayland side (display connection, linux-dmabuf feedback, buffer import, event dispatch) belongs to the application, for instance through github.com/bnema/neferclient; it is elided here. The example needs a GPU and a compositor, so it is compiled but not run by go test.

package main

import (
	"fmt"
	"log"

	"github.com/bnema/nefergui"
)

func main() {
	type model struct{ count int }
	view := func(f *nefergui.Frame, m *model) {
		root := f.Root().Column()
		root.Text("Hello, Wayland!")
		if root.Button("Count", nefergui.Key("count")).Activated() {
			m.count++
		}
		root.Text(fmt.Sprintf("Clicked %d times", m.count))
	}

	// mainDevice and formats come from the compositor's linux-dmabuf feedback.
	var (
		mainDevice uint64
		formats    []nefergui.Format
	)
	r, err := nefergui.NewRenderer(nefergui.RendererConfig{MainDevice: mainDevice, Formats: formats})
	if err != nil {
		log.Fatal(err)
	}
	defer r.Close()

	// Forward the surface size, then wire events as they arrive.
	r.Resize(320, 200, 1)
	r.Input(&nefergui.Input{Kind: nefergui.InputPointerMotion, X: 10, Y: 10})

	m := &model{}
	var out nefergui.Output
	// One iteration of the client loop; real clients repeat it forever.
	drawn, err := r.Render(&out, m, view)
	if err != nil {
		log.Fatal(err)
	}
	if drawn {
		// Import out.Planes as a wl_buffer when out.NewBuffer is set, attach
		// it with the Acquire/Release points, commit, and call r.Released
		// when out.ReleaseFD becomes readable.
		_ = out.Buffer
	}
	// Then sleep until a Wayland event, <-r.Wake(), or a short timer when
	// r.Pending() reports a frame waiting for the GPU.
}

func NewRenderer added in v0.5.0

func NewRenderer(cfg RendererConfig) (*Renderer, error)

NewRenderer opens the GPU named by cfg.MainDevice and loads fonts and styles. It allocates no images.

type RendererConfig added in v0.5.0

type RendererConfig = ui.RendererConfig

RendererConfig configures NewRenderer. MainDevice and Formats come from the compositor's linux-dmabuf feedback.

type Retired added in v0.5.0

type Retired = ui.Retired

Plane, Timeline and Retired are parts of Output. A Timeline FD is valid only when Output.NewTimelines is set. The client must stop watching and destroy each Retired buffer before the next Render.

type Timeline added in v0.5.0

type Timeline = ui.Timeline

Plane, Timeline and Retired are parts of Output. A Timeline FD is valid only when Output.NewTimelines is set. The client must stop watching and destroy each Retired buffer before the next Render.

type ValueOption

type ValueOption = ui.ValueOption

Option families are sealed: ContainerOption, ButtonOption, EditOption, ValueOption and HeadingOption accept the common CSS options (Key, ID, Class, Inline) plus the control-specific options listed on each function.

Directories

Path Synopsis
internal
bidi
Package bidi contains functionality for bidirectional text support.
Package bidi contains functionality for bidirectional text support.
css
edit
Package edit owns headless, grapheme-safe text state.
Package edit owns headless, grapheme-safe text state.
image
Package image provides bounded standard-library raster image ingestion.
Package image provides bounded standard-library raster image ingestion.
layout
Package layout computes logical-pixel boxes and an ordered, serializable paint list.
Package layout computes logical-pixel boxes and an ordered, serializable paint list.
presentation/buffers
Package buffers models buffer ownership between the renderer and the compositor without GPU dependencies.
Package buffers models buffer ownership between the renderer and the compositor without GPU dependencies.
presentation/session
Package session is the Wayland-free presentation core: Vulkan rendering into DMA-BUF buffers, DRM syncobj timelines and buffer ownership.
Package session is the Wayland-free presentation core: Vulkan rendering into DMA-BUF buffers, DRM syncobj timelines and buffer ownership.
presentation/shaders
Package shaders owns the committed SPIR-V compiled from its sibling GLSL sources.
Package shaders owns the committed SPIR-V compiled from its sibling GLSL sources.
presentation/syncobj
Package syncobj bridges Vulkan SYNC_FD semaphores and Wayland DRM syncobj timelines on the compositor-selected render node.
Package syncobj bridges Vulkan SYNC_FD semaphores and Wayland DRM syncobj timelines on the compositor-selected render node.
presentation/vkdevice
Package vkdevice selects a Vulkan graphics device by compositor-advertised DRM dev_t.
Package vkdevice selects a Vulkan graphics device by compositor-advertised DRM dev_t.
render
Package render prepares ordered physical-pixel primitives for Vulkan submission.
Package render prepares ordered physical-pixel primitives for Vulkan submission.
text
Package text implements CPU-side font selection, shaping, measurement and grayscale glyph preparation.
Package text implements CPU-side font selection, shaping, measurement and grayscale glyph preparation.
ui
Package ui owns the immediate-mode frame tree, controls, editors, input routing and the Renderer behind the public nefergui facade.
Package ui owns the immediate-mode frame tree, controls, editors, input routing and the Renderer behind the public nefergui facade.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL