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 NewContainerBox () ContainerBox // NewWindow creates a normal MainWindow and returns it. NewWindow (image.Rectangle) (MainWindow, 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) (MainWindow, 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 // 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) }