Skip to content

bnema/purego-cef

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

90 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

purego-cef

Go bindings for the Chromium Embedded Framework C API, using purego instead of cgo.

Linux only. CGO_ENABLED=0.

Note

purego-cef is usable today, but it is still pre-1.0. Expect API changes while the handwritten cef layer settles.

Status

purego-cef exposes the CEF C API through generated bindings and adds a smaller handwritten cef layer for the common paths.

What to expect today:

  • generated low-level bindings across the CEF C API
  • a smaller ergonomic cef layer for init, clients, and common handlers
  • shipped mocks for testing
  • ongoing API cleanup before 1.0

Architecture

The binding layer is generated from CEF C headers (~114 files). The generator parses all headers and produces:

  • Inbound port interfaces for every CEF handler and type
  • Outbound port interfaces grouping CEF free functions and object methods by domain
  • A composite CAPI interface embedding all outbound ports
  • Typed constructors for handlers and wrappers for CEF objects with automatic refcount management
  • C struct layouts and purego symbol registration
  • Doc comments extracted from CEF C headers

The public cef package then adds a small handwritten layer for the places where the raw C API is too awkward to expose directly.

A hand-written core domain handles UTF-16 string conversion, reference counting, CEF lifecycle orchestration, and the Bridge adapter implementing the CAPI interface.

Mocks for all interfaces are generated by Mockery v3 and shipped in cef/mocks/.

API shape

For most code, prefer the ergonomic handwritten layer when it exists:

  • cef.Settings instead of cef.RawSettings
  • cef.NewClient(...) with cef.Client
  • cef.NewLifeSpanHandler(...) with cef.LifeSpanHandler
  • cef.NewAudioHandler(...) with the decoded cef.AudioHandler interface
  • cef.ExecuteSubprocess() when you want subprocess status and errors without implicit os.Exit or stderr writes; cef.MaybeExitSubprocess() remains the short helper for main packages
  • cef.ExecuteSubprocessWithApp(app) when helper subprocess startup also needs a custom cef.App
  • cef.LifeSpanHandler popup callbacks receive a callback-scoped *cef.RawClientWriteSlot when CEF exposes a writable popup client out-param; check for nil before calling Set or Clear

Treat Raw* types as advanced escape hatches.

Usage

For main packages, cef.MaybeExitSubprocess() is the short path. If you need explicit subprocess status and error handling, use cef.ExecuteSubprocess() or cef.ExecuteSubprocessWithApp(app).

package main

import (
	"runtime"

	"github.com/bnema/purego-cef/cef"
)

func main() {
	runtime.LockOSThread()
	cef.MaybeExitSubprocess()

	if err := cef.Init(cef.DefaultSettings()); err != nil {
		panic(err)
	}
	defer cef.Shutdown()

	client := cef.NewClient(myClient{})
	_ = client

	// Your render loop here
	for {
		cef.DoMessageLoopWork()
	}
}

// Implement the user-facing client interface — only the methods you need.
type myClient struct{}

func (c myClient) GetLifeSpanHandler() cef.LifeSpanHandler { return nil }
func (c myClient) GetRenderHandler() cef.RenderHandler     { return myRenderer{} }
// ... other handler getters return nil

Requirements

  • Go 1.26+
  • CEF 147 runtime (the minimum accepted Chrome/CEF major; earlier versions are rejected at load time)

Runtime discovery

purego-cef locates libcef.so using this order:

  1. CEF_DIR environment variable
  2. Explicit Settings.CEFDir argument passed to cef.Init()
  3. /usr/lib/cef (the Arch Linux cef package install path), if libcef.so exists there
  4. ~/.local/share/cef

Arch Linux

sudo pacman -S cef

This installs /usr/lib/cef/libcef.so and the required Resources — no env var needed.

Runtime version override / diagnostics

  • CEF_VERSION=146 sets the minimum accepted Chrome/CEF major to 146.
  • CEF_SKIP_VERSION_CHECK=1 bypasses the version guard entirely (diagnostics only; use with care).

Building

CGO_ENABLED=0 go build ./...
CGO_ENABLED=0 go test ./...

Integration tests need the CEF runtime (adjust the directory as needed):

go test -tags=cef_integration ./integration

If your runtime is in a non-standard location, set CEF_DIR:

CEF_DIR=/custom/path go test -tags=cef_integration ./integration

Regenerating bindings

go generate ./...

Or manually:

go run ./cmd/cefgen \
  --headers-dir ~/.local/share/cef/include \
  --capi-dir internal/capi \
  --port-in-dir internal/ports/in \
  --port-out-dir internal/ports/out \
  --public-dir cef
mockery

Project layout

cef/                 public API (generated) + mocks
  bridge.go          hand-written: handler constructors, string helpers
  init.go            hand-written: Init(), Shutdown(), orchestration
internal/
  ports/in/          inbound port interfaces (generated)
  ports/out/         outbound port interfaces (generated) + composite CAPI (hand-written) + mocks
  core/              domain logic: engine, string marshaling, refcount (hand-written)
  capi/              purego bindings + bridge (generated + hand-written)
  loader/            dlopen libcef.so + version validation (hand-written)
cmd/cefgen/          binding generator (parser, model, emitter, templates)
integration/         integration tests (require CEF runtime)

License

MIT

About

Go bindings for the Chromium Embedded Framework C API, using purego instead of cgo.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages