Skip to content

Commit

Permalink
Added example frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
djhworld committed Sep 16, 2018
1 parent e97a477 commit 3a80cd7
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 122 deletions.
File renamed without changes.
34 changes: 34 additions & 0 deletions _examples/README.md
@@ -0,0 +1,34 @@
This is an example of how to write a frontend for the emulator.

It uses [tcell](https://github.com/gdamore/tcell) to render the screen to your terminal and handle keyboard input. It requires a terminal with 256 colour support (haven't tested it on others).

You will probably want to adjust your terminals font settings and vertical character spacing as the screen will probably scroll off the end of your terminal window.


![screenshot](https://github.com/djhworld/gomeboycolor/raw/master/_examples/terminal.png)


## How to run

```
go run . <path-to-rom-file>
```

Note: Pressing `Esc` will quit the application

## Overview of files

### terminal\_io.go

Sets up the IO related stuff to handle screen drawing and keyboard updates.

In this particular case we are rendering to the terminal.

### noop\_store.go

Sets up a no-op battery save store. You can change this to write to a filesystem or other storage medium, but for this example it just does nothing.

### main.go

Glues everything together and runs the application

2 changes: 0 additions & 2 deletions examples/go.mod → _examples/go.mod
@@ -1,3 +1 @@
module github.com/djhworld/gomeboycolor/dummy-gomeboycolor

require github.com/djhworld/gomeboycolor v0.0.8
Empty file added _examples/go.sum
Empty file.
2 changes: 1 addition & 1 deletion examples/main.go → _examples/main.go
Expand Up @@ -59,7 +59,7 @@ func createEmulator(romFile string) (*gbc.GomeboyColor, error) {
saveStore := NewNoopStore()

// 4. Create IO handler
ioHandler := NewNoopIO(conf.FrameRateLock, conf.Headless, conf.DisplayFPS)
ioHandler := NewTerminalIO(conf.FrameRateLock, conf.Headless, conf.DisplayFPS)

// 5. Initialise emulator
return gbc.Init(
Expand Down
2 changes: 1 addition & 1 deletion examples/noop_store.go → _examples/noop_store.go
Expand Up @@ -5,8 +5,8 @@ import (
"log"
)

// NoopStore does nothing with battery saves
type NoopStore struct {
baseDir string
}

func NewNoopStore() *NoopStore {
Expand Down
Binary file added _examples/terminal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
123 changes: 123 additions & 0 deletions _examples/terminal_io.go
@@ -0,0 +1,123 @@
package main

import (
"log"
"time"

"github.com/djhworld/gomeboycolor/inputoutput"
"github.com/djhworld/gomeboycolor/types"
"github.com/gdamore/tcell"
)

// Setup the gameboy controls -> key code mappings
var DummyControlScheme inputoutput.ControlScheme = inputoutput.ControlScheme{
UP: int(tcell.KeyUp),
DOWN: int(tcell.KeyDown),
LEFT: int(tcell.KeyLeft),
RIGHT: int(tcell.KeyRight),
A: 122,
B: 120,
START: 97,
SELECT: 115,
}

// TerminalIO is simple IO handler that outputs screen data to a terminal
type TerminalIO struct {
*inputoutput.CoreIO
terminalDisplay *terminalDisplay
}

func NewTerminalIO(frameRateLock int64, headless bool, displayFps bool) *TerminalIO {
log.Println("Creating TERMINAL based IO Handler")

frameRateReporter := func(v float32) {
if displayFps {
//log.Printf("Average frame rate\t%.2f\tfps", v)
}
}

terminalDisplay := new(terminalDisplay)

return &TerminalIO{
inputoutput.NewCoreIO(frameRateLock, headless, frameRateReporter, terminalDisplay),
terminalDisplay,
}
}

func (i *TerminalIO) Init(title string, screenSize int, onCloseHandler func()) error {
i.OnCloseHandler = onCloseHandler

// 1. Initialise key handler with control scheme
i.KeyHandler.Init(DummyControlScheme)

// 2. Initialise tcell screen
screen, err := tcell.NewScreen()
if err != nil {
return err
}

if err = screen.Init(); err != nil {
return err
}

screen.Clear()

// 3. Initialise display
err = i.terminalDisplay.init(screen)
if err != nil {
return err
}

// 4. Setup key handler
go func() {
loop:
for {
ev := screen.PollEvent()
switch ev := ev.(type) {
case *tcell.EventKey:
switch ev.Key() {
case tcell.KeyEscape:
i.StopChannel <- 1
break loop
case tcell.KeyRune:
i.KeyHandler.KeyDown(int(ev.Rune()))
time.Sleep(50 * time.Millisecond)
i.KeyHandler.KeyUp(int(ev.Rune()))
default:
i.KeyHandler.KeyDown(int(ev.Key()))
time.Sleep(50 * time.Millisecond)
i.KeyHandler.KeyUp(int(ev.Key()))
}
}
}
}()

return err
}

type terminalDisplay struct {
screen tcell.Screen
}

func (n *terminalDisplay) init(screen tcell.Screen) error {
n.screen = screen
return nil
}

func (n *terminalDisplay) Stop() {
n.screen.Fini()
}

// DrawFrame receives the screen data that you can use to draw to your display
func (n *terminalDisplay) DrawFrame(screenData *types.Screen) {
st := tcell.StyleDefault
for y := 0; y < inputoutput.SCREEN_HEIGHT; y++ {
for x := 0; x < inputoutput.SCREEN_WIDTH; x++ {
var pixel types.RGB = screenData[y][x]
color := tcell.NewRGBColor(int32(pixel.Red), int32(pixel.Green), int32(pixel.Blue))
st = st.Background(color)
n.screen.SetCell(x, y, st, ' ')
}
}
n.screen.Show()
}
12 changes: 0 additions & 12 deletions examples/go.sum

This file was deleted.

106 changes: 0 additions & 106 deletions examples/noop_io.go

This file was deleted.

0 comments on commit 3a80cd7

Please sign in to comment.