tomo/backend.go
2024-06-07 18:57:16 -04:00

110 lines
3.4 KiB
Go

package tomo
import "sort"
import "image"
import "errors"
import "git.tebibyte.media/tomo/tomo/canvas"
// Backend is any Tomo implementation. Backends handle window creation, layout,
// rendering, and events so that there can be as many platform-specific
// optimizations as possible.
type Backend interface {
// These methods create new objects. The backend must reject any object
// that was not made by it.
NewBox () Box
NewTextBox () TextBox
NewCanvasBox () CanvasBox
NewSurfaceBox () (SurfaceBox, error)
NewContainerBox () ContainerBox
// NewWindow creates a normal Window and returns it.
NewWindow (image.Rectangle) (Window, error)
// NewPlainWindow creates an undecorated window that does not appear in
// window lists and returns it. This is intended for making things like
// panels, docks, etc.
NewPlainWindow (image.Rectangle) (Window, error)
// NewTexture creates a new texture from an image. The backend must
// reject any texture that was not made by it.
NewTexture (image.Image) canvas.TextureCloser
// NewCanvas creates a new canvas with the specified bounds. The backend
// must reject any canvas that was not made by it.
NewCanvas (image.Rectangle) canvas.CanvasCloser
// SetStyle sets the style that will be used on objects. The backend is
// in charge of applying the style to objects. When this method is
// called, it must propagate a StyleChange event to all boxes it is
// keeping track of.
SetStyle (Style)
// SetIcons sets the icon set that icons will be pulled from. When this
// method is called, it must propagate an IconChange event to all boxes
// it is keeping track of.
SetIcons (Icons)
// Run runs the event loop until Stop() is called, or the backend
// experiences a fatal error.
Run () error
// Stop must unblock run.
Stop ()
// Do performs a callback function in the event loop thread as soon as
// possible. This method must be safe to call concurrently.
Do (func ())
}
// Factory is a function that attempts to instatiate a backend. If the
// instantiation process fails at any point, it must clean up all resources and
// return nil and an error explaining what happened.
type Factory func () (Backend, error)
var registered []factoryEntry
type factoryEntry struct {
Factory
priority int
}
// Register registers a backend factory with the given priority number.
func Register (priority int, factory Factory) {
registered = append(registered, factoryEntry {
priority: priority,
Factory: factory,
})
}
// initialize instantiates a backend. The first backend (sorted by priority)
// that does not throw an error when initialized is used. If no backend could be
// instantiated, this function returns an error. This function should be called
// only once.
func initialize () (Backend, error) {
backend, err := instantiate()
if err != nil { return nil, err }
return backend, err
}
func instantiate () (Backend, error) {
if len(registered) < 0 {
return nil, errors.New("no available backends")
}
// sort backends by priority
sort.Slice(registered, func (left, right int) bool {
return registered[left].priority < registered[right].priority
})
// attempt to instantiate
errorLog := ""
for _, factory := range registered {
backend, err := factory.Factory()
if err == nil {
return backend, nil
} else {
errorLog += " " + err.Error() + "\n"
}
}
return nil, errors.New("all backends failed:\n" + errorLog)
}