stone/backends/pixel/pixel.go
2022-11-02 18:51:33 -04:00

184 lines
4.3 KiB
Go

package pixel
import "time"
import "golang.org/x/image/font"
import "github.com/faiface/pixel"
import "github.com/faiface/pixel/pixelgl"
import "golang.org/x/image/font/basicfont"
import "git.tebibyte.media/sashakoshka/stone"
type Backend struct {
window *pixelgl.Window
boundsDirty bool
previousBounds pixel.Vec
fontFace font.Face
application *stone.Application
config *stone.Config
metrics struct {
cellWidth int
cellHeight int
}
}
func (backend *Backend) Run (callback func (application *stone.Application)) {
if backend.fontFace == nil {
backend.fontFace = basicfont.Face7x13
}
faceMetrics := backend.fontFace.Metrics()
backend.metrics.cellHeight = faceMetrics.Height.Round()
// FIXME?: this might not be the best way to get the cell width
faceAdvance, ok := backend.fontFace.GlyphAdvance('M')
if ok {
backend.metrics.cellWidth = faceAdvance.Round()
} else {
backend.metrics.cellWidth = backend.metrics.cellHeight / 2
}
pixelgl.Run (func () {
var err error
backend.window, err = pixelgl.NewWindow (pixelgl.WindowConfig {
Resizable: true,
Undecorated: true,
VSync: true,
NoIconify: true,
Title: backend.application.Title(),
Bounds: backend.calculateWindowSize(),
})
backend.Poll()
if err != nil { panic(err.Error()) }
callback(backend.application)
})
}
func (backend *Backend) Await (timeout time.Duration) (keepRunning bool) {
if backend.window == nil {
panic("call to Backend.Await before window exists")
}
backend.draw()
backend.window.UpdateInputWait(timeout)
backend.processEvents()
keepRunning = !backend.window.Closed()
return
}
func (backend *Backend) Poll () (keepRunning bool) {
if backend.window == nil {
panic("call to Backend.Poll before window exists")
}
backend.draw()
backend.window.UpdateInput()
backend.processEvents()
keepRunning = !backend.window.Closed()
return
}
func (backend *Backend) SetTitle (title string) {
if backend.window != nil {
backend.window.SetTitle(title)
}
}
func (backend *Backend) JustPressed (button stone.Button) (pressed bool) {
pressed = backend.window.JustPressed(pixelgl.Button(button))
return
}
func (backend *Backend) JustReleased (button stone.Button) (released bool) {
released = backend.window.JustReleased(pixelgl.Button(button))
return
}
func (backend *Backend) Pressed (button stone.Button) (pressed bool) {
pressed = backend.window.Pressed(pixelgl.Button(button))
return
}
func (backend *Backend) Repeated (button stone.Button) (repeated bool) {
repeated = backend.window.Repeated(pixelgl.Button(button))
return
}
func (backend *Backend) Typed () (text string) {
text = backend.window.Typed()
return
}
func (backend *Backend) Resized () (resized bool) {
resized = backend.boundsDirty
return
}
func (backend *Backend) draw () {
// didDrawing := false
if backend.boundsDirty {
backend.window.Clear (
backend.config.Color(stone.ColorApplication))
backend.boundsDirty = false
// didDrawing = true
} else {
// TODO: clear out dirty cells before drawing them (we don't
// want to clear them out if we have already just cleared
// everything out)
}
// TODO: draw dirty cells.
width, height := backend.application.Size()
for x := 0; x < width; x ++ {
for y := 0; y < height; y ++ {
clean := backend.application.Clean(x, y)
// cell := application.content[index]
if clean { continue }
// draw cell
cell := backend.application.Cell(x, y)
content := cell.Rune()
if content < 32 { continue }
// didDrawing = true // TODO: set didDrawing up there ^
backend.application.MarkClean(x, y)
}
}
backend.window.SwapBuffers()
}
func (backend *Backend) processEvents () {
newBounds := backend.window.Bounds().Max
backend.boundsDirty = backend.previousBounds != newBounds
backend.previousBounds = newBounds
if backend.boundsDirty {
// TODO: set size of buffer
}
}
func (backend *Backend) calculateWindowSize () (bounds pixel.Rect) {
width, height := backend.application.Size()
bounds = pixel.R (
0, 0,
float64(width * backend.metrics.cellWidth),
float64(height * backend.metrics.cellHeight))
return
}
func factory (application *stone.Application) (output stone.Backend, err error) {
backend := &Backend {
application: application,
config: application.Config(),
}
output = backend
return
}
func init () {
stone.RegisterBackend(factory)
}