diff --git a/emulator/emulator.go b/emulator/emulator.go new file mode 100644 index 0000000..4c837ac --- /dev/null +++ b/emulator/emulator.go @@ -0,0 +1,128 @@ +package emulator + +import ( + "image" + "image/color" + "log" + + "sync" + + "golang.org/x/exp/shiny/driver" + "golang.org/x/exp/shiny/imageutil" + "golang.org/x/exp/shiny/screen" + "golang.org/x/mobile/event/paint" + "golang.org/x/mobile/event/size" +) + +var ( + black = color.RGBA{0x00, 0x00, 0x00, 0xff} + red = color.RGBA{0x7f, 0x00, 0x00, 0x7f} +) + +var margin = 10 + +type Emulator struct { + PixelPitch int + Gutter int + Width int + Height int + + leds []color.Color + w screen.Window + isReady bool + + wg sync.WaitGroup +} + +func (e *Emulator) Init() { + e.leds = make([]color.Color, 2048) + + e.wg.Add(1) + go e.init() + e.wg.Wait() +} + +func (e *Emulator) init() { + driver.Main(func(s screen.Screen) { + var err error + e.w, err = s.NewWindow(&screen.NewWindowOptions{ + Title: "Basic Shiny Example", + }) + + if err != nil { + panic(err) + } + + defer e.w.Release() + + var sz size.Event + for { + evn := e.w.NextEvent() + switch evn := evn.(type) { + case paint.Event: + for _, r := range imageutil.Border(sz.Bounds(), margin) { + e.w.Fill(r, red, screen.Src) + } + + e.w.Fill(sz.Bounds().Inset(margin), black, screen.Src) + e.w.Publish() + if e.isReady { + continue + } + + e.Apply(make([]color.Color, 2048)) + e.wg.Done() + e.isReady = true + case size.Event: + sz = evn + + case error: + log.Print(e) + } + } + }) +} + +func (e *Emulator) Geometry() (width, height int) { + return e.Width, e.Height +} + +func (e *Emulator) Apply(leds []color.Color) error { + defer func() { e.leds = make([]color.Color, 2048) }() + + for col := 0; col < e.Width; col++ { + for row := 0; row < e.Height; row++ { + x := col * (e.PixelPitch + e.Gutter) + y := row * (e.PixelPitch + e.Gutter) + + x += margin * 2 + y += margin * 2 + + c := e.At(col + (row * e.Width)) + e.w.Fill(image.Rect(x, y, x+e.PixelPitch, y+e.PixelPitch), c, screen.Over) + } + } + + e.w.Publish() + return nil +} + +func (e *Emulator) Render() error { + return e.Apply(e.leds) +} + +func (e *Emulator) At(position int) color.Color { + if e.leds[position] == nil { + return color.Black + } + + return e.leds[position] +} + +func (e *Emulator) Set(position int, c color.Color) { + e.leds[position] = color.RGBAModel.Convert(c) +} + +func (e *Emulator) Close() error { + return nil +}