added ports.TimedInputEvent to accompany ports.InputEvent

the Time field was in ports.InputEvent but it didn't make sense to
specify a time value in all contexts
This commit is contained in:
JetSetIlly 2021-12-05 21:11:23 +00:00
parent 76d6d163b0
commit 2942d35320
13 changed files with 84 additions and 72 deletions

View file

@ -22,14 +22,12 @@ import (
"github.com/jetsetilly/gopher2600/hardware/riot/ports"
"github.com/jetsetilly/gopher2600/hardware/television"
"github.com/jetsetilly/gopher2600/hardware/television/coords"
)
// TV defines the television functions required by a bot.
type TV interface {
AddPixelRenderer(television.PixelRenderer)
AddAudioMixer(television.AudioMixer)
GetCoords() coords.TelevisionCoords
}
// VCS defines the VCS functions required by a bot.

View file

@ -362,9 +362,9 @@ func (bot *videoChessBot) moveCursorOnceStep(portid plugging.PortID, direction p
waiting := true
for waiting {
bot.vcs.QueueEvent(ports.InputEvent{Time: bot.tv.GetCoords(), Port: portid, Ev: direction, D: ports.DataStickTrue})
bot.vcs.QueueEvent(ports.InputEvent{Port: portid, Ev: direction, D: ports.DataStickTrue})
bot.waitForFrames(downDuration)
bot.vcs.QueueEvent(ports.InputEvent{Time: bot.tv.GetCoords(), Port: portid, Ev: direction, D: ports.DataStickFalse})
bot.vcs.QueueEvent(ports.InputEvent{Port: portid, Ev: direction, D: ports.DataStickFalse})
select {
case <-bot.obs.audioFeedback:
waiting = false
@ -460,9 +460,9 @@ func (bot *videoChessBot) moveCursor(moveCol int, moveRow int, shortcut bool) {
waiting := true
for waiting {
bot.vcs.QueueEvent(ports.InputEvent{Time: bot.tv.GetCoords(), Port: plugging.PortLeftPlayer, Ev: ports.Fire, D: true})
bot.vcs.QueueEvent(ports.InputEvent{Port: plugging.PortLeftPlayer, Ev: ports.Fire, D: true})
bot.waitForFrames(downDuration)
bot.vcs.QueueEvent(ports.InputEvent{Time: bot.tv.GetCoords(), Port: plugging.PortLeftPlayer, Ev: ports.Fire, D: false})
bot.vcs.QueueEvent(ports.InputEvent{Port: plugging.PortLeftPlayer, Ev: ports.Fire, D: false})
select {
case <-bot.obs.audioFeedback:
waiting = false

View file

@ -97,7 +97,7 @@ func NewComparison(driverVCS *hardware.VCS) (*Comparison, error) {
driverVCS.TV.AddPixelRenderer(&cmp.driver)
// synchronise RIOT ports
sync := make(chan ports.InputEvent, 32)
sync := make(chan ports.TimedInputEvent, 32)
err = cmp.VCS.RIOT.Ports.SynchroniseWithDriver(sync, tv)
if err != nil {
return nil, curated.Errorf("comparison: %v", err)

View file

@ -1351,55 +1351,55 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
arg, _ := tokens.Get()
switch strings.ToUpper(arg) {
case "P0":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelTogglePlayer0Pro, D: nil}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelTogglePlayer0Pro, D: nil}
_, err = dbg.vcs.HandleInputEvent(inp)
case "P1":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelTogglePlayer1Pro, D: nil}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelTogglePlayer1Pro, D: nil}
_, err = dbg.vcs.HandleInputEvent(inp)
case "COL":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelToggleColor, D: nil}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelToggleColor, D: nil}
_, err = dbg.vcs.HandleInputEvent(inp)
}
case "SET":
arg, _ := tokens.Get()
switch strings.ToUpper(arg) {
case "P0PRO":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelSetPlayer0Pro, D: true}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetPlayer0Pro, D: true}
_, err = dbg.vcs.HandleInputEvent(inp)
case "P1PRO":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelSetPlayer1Pro, D: true}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetPlayer1Pro, D: true}
_, err = dbg.vcs.HandleInputEvent(inp)
case "P0AM":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelSetPlayer0Pro, D: false}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetPlayer0Pro, D: false}
_, err = dbg.vcs.HandleInputEvent(inp)
case "P1AM":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelSetPlayer1Pro, D: false}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetPlayer1Pro, D: false}
_, err = dbg.vcs.HandleInputEvent(inp)
case "COL":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelSetColor, D: true}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetColor, D: true}
_, err = dbg.vcs.HandleInputEvent(inp)
case "BW":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelSetColor, D: false}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetColor, D: false}
_, err = dbg.vcs.HandleInputEvent(inp)
}
case "HOLD":
arg, _ := tokens.Get()
switch strings.ToUpper(arg) {
case "SELECT":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelSelect, D: true}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSelect, D: true}
_, err = dbg.vcs.HandleInputEvent(inp)
case "RESET":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelReset, D: true}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelReset, D: true}
_, err = dbg.vcs.HandleInputEvent(inp)
}
case "RELEASE":
arg, _ := tokens.Get()
switch strings.ToUpper(arg) {
case "SELECT":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelSelect, D: false}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSelect, D: false}
_, err = dbg.vcs.HandleInputEvent(inp)
case "RESET":
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortPanel, Ev: ports.PanelReset, D: false}
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelReset, D: false}
_, err = dbg.vcs.HandleInputEvent(inp)
}
}
@ -1456,10 +1456,10 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
n, _ := strconv.Atoi(stick)
switch n {
case 0:
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortLeftPlayer, Ev: event, D: value}
inp := ports.InputEvent{Port: plugging.PortLeftPlayer, Ev: event, D: value}
_, err = dbg.vcs.HandleInputEvent(inp)
case 1:
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortRightPlayer, Ev: event, D: value}
inp := ports.InputEvent{Port: plugging.PortRightPlayer, Ev: event, D: value}
_, err = dbg.vcs.HandleInputEvent(inp)
}
@ -1477,18 +1477,18 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
switch n {
case 0:
if strings.ToUpper(key) == "NONE" {
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortLeftPlayer, Ev: ports.KeypadUp, D: nil}
inp := ports.InputEvent{Port: plugging.PortLeftPlayer, Ev: ports.KeypadUp, D: nil}
_, err = dbg.vcs.HandleInputEvent(inp)
} else {
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortLeftPlayer, Ev: ports.KeypadDown, D: rune(key[0])}
inp := ports.InputEvent{Port: plugging.PortLeftPlayer, Ev: ports.KeypadDown, D: rune(key[0])}
_, err = dbg.vcs.HandleInputEvent(inp)
}
case 1:
if strings.ToUpper(key) == "NONE" {
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortRightPlayer, Ev: ports.KeypadUp, D: nil}
inp := ports.InputEvent{Port: plugging.PortRightPlayer, Ev: ports.KeypadUp, D: nil}
_, err = dbg.vcs.HandleInputEvent(inp)
} else {
inp := ports.InputEvent{Time: dbg.vcs.TV.GetCoords(), Port: plugging.PortLeftPlayer, Ev: ports.KeypadDown, D: rune(key[0])}
inp := ports.InputEvent{Port: plugging.PortLeftPlayer, Ev: ports.KeypadDown, D: rune(key[0])}
_, err = dbg.vcs.HandleInputEvent(inp)
}
}

View file

@ -303,7 +303,7 @@ func NewDebugger(create CreateUserInterface, spec string, useSavekey bool, fpsCa
}
// create userinput/controllers handler
dbg.controllers = userinput.NewControllers(tv)
dbg.controllers = userinput.NewControllers()
dbg.controllers.AddInputHandler(dbg.vcs)
// replace player 1 port with savekey

View file

@ -104,15 +104,20 @@ const (
DataStickSet EventDataStick = "set"
)
// InputEvent defines the data required for single input event. Time can be
// uninitialised if necessary.
// InputEvent defines the data required for single input event.
type InputEvent struct {
Time coords.TelevisionCoords
Port plugging.PortID
Ev Event
D EventData
}
// TimedInputEvent embeds the InputEvent type and adds a Time field (time
// measured by TelevisionCoords).
type TimedInputEvent struct {
Time coords.TelevisionCoords
InputEvent
}
// Playback implementations feed controller Events to the device on request
// with the CheckInput() function.
//
@ -121,7 +126,7 @@ type InputEvent struct {
type EventPlayback interface {
// note the type restrictions on EventData in the type definition's
// commentary
GetPlayback() (InputEvent, error)
GetPlayback() (TimedInputEvent, error)
}
// EventRecorder implementations mirror an incoming event.
@ -130,5 +135,5 @@ type EventPlayback interface {
// peripheral at once. The ID parameter of the EventRecord() function will help
// to differentiate between multiple devices.
type EventRecorder interface {
RecordEvent(InputEvent) error
RecordEvent(TimedInputEvent) error
}

View file

@ -64,7 +64,7 @@ func (p *Ports) handleDrivenEvents() error {
for !done {
c := p.tv.GetCoords()
if coords.Equal(c, inp.Time) {
_, err := p.HandleInputEvent(inp)
_, err := p.HandleInputEvent(inp.InputEvent)
if err != nil {
return err
}
@ -125,7 +125,7 @@ func (p *Ports) handlePlaybackEvents() error {
morePlayback = inp.Port != plugging.PortUnplugged && inp.Ev != NoEvent
if morePlayback {
_, err := p.HandleInputEvent(inp)
_, err := p.HandleInputEvent(inp.InputEvent)
if err != nil {
return err
}
@ -156,7 +156,7 @@ func (p *Ports) HandleInputEvent(inp InputEvent) (bool, error) {
// forward to passenger if one is defined
if handled && p.toPassenger != nil {
select {
case p.toPassenger <- inp:
case p.toPassenger <- TimedInputEvent{Time: p.tv.GetCoords(), InputEvent: inp}:
default:
return handled, curated.Errorf("ports: passenger event queue is full: input dropped")
}
@ -169,7 +169,7 @@ func (p *Ports) HandleInputEvent(inp InputEvent) (bool, error) {
// record event with the EventRecorder
for _, r := range p.recorder {
return handled, r.RecordEvent(inp)
return handled, r.RecordEvent(TimedInputEvent{Time: p.tv.GetCoords(), InputEvent: inp})
}
return handled, nil

View file

@ -90,10 +90,10 @@ type Ports struct {
// the following fields all relate to driven input, for either the driver
// or for the passenger (the driven)
fromDriver chan InputEvent
toPassenger chan InputEvent
fromDriver chan TimedInputEvent
toPassenger chan TimedInputEvent
checkForDriven bool
drivenInputData InputEvent
drivenInputData TimedInputEvent
// the time of driven events are measured by television coordinates
//
@ -269,7 +269,7 @@ func (p *Ports) Step() {
// SynchroniseWithDriver implies that the emulation will receive driven events
// from another emulation.
func (p *Ports) SynchroniseWithDriver(driver chan InputEvent, tv TV) error {
func (p *Ports) SynchroniseWithDriver(driver chan TimedInputEvent, tv TV) error {
if p.toPassenger != nil {
return curated.Errorf("ports: cannot sync with driver: emulation already defined as a driver of input")
}
@ -283,7 +283,7 @@ func (p *Ports) SynchroniseWithDriver(driver chan InputEvent, tv TV) error {
// SynchroniseWithPassenger connects the emulation to a second emulation (the
// passenger) to which user input events will be "driven".
func (p *Ports) SynchroniseWithPassenger(passenger chan InputEvent, tv TV) error {
func (p *Ports) SynchroniseWithPassenger(passenger chan TimedInputEvent, tv TV) error {
if p.fromDriver != nil {
return curated.Errorf("ports: cannot sync with passenger: emulation already defined as being driven")
}

View file

@ -69,4 +69,9 @@
//
// A good additionl policy would be to only roll if several, consecutive
// unsynced frames are indicated.
//
// Concurrency
//
// None of the functions in the Television type are safe to be called from
// goroutines other than the one the type was created in.
package television

View file

@ -760,6 +760,9 @@ func (tv *Television) GetLastSignal() signal.SignalAttributes {
}
// GetCoords returns an instance of coords.TelevisionCoords.
//
// Like all Television functions this function is not safe to call from
// goroutines other than the one that created the Television.
func (tv *Television) GetCoords() coords.TelevisionCoords {
return tv.state.GetCoords()
}

View file

@ -32,7 +32,7 @@ import (
)
type playbackEntry struct {
event ports.InputEvent
event ports.TimedInputEvent
hash string
// the line in the recording file the playback event appears
@ -217,36 +217,43 @@ const (
// GetPlayback returns an event and source PortID for an event occurring at the
// current TV frame/scanline/clock.
func (plb *Playback) GetPlayback() (ports.InputEvent, error) {
// we've reached the end of the list of events for this id
if plb.seqCt >= len(plb.sequence) {
return ports.InputEvent{
Port: plugging.PortUnplugged,
Ev: ports.NoEvent,
}, nil
}
func (plb *Playback) GetPlayback() (ports.TimedInputEvent, error) {
// get current state of the television
c := plb.vcs.TV.GetCoords()
// we've reached the end of the list of events for this id
if plb.seqCt >= len(plb.sequence) {
return ports.TimedInputEvent{
Time: c,
InputEvent: ports.InputEvent{
Port: plugging.PortUnplugged,
Ev: ports.NoEvent,
},
}, nil
}
// compare current state with the recording
entry := plb.sequence[plb.seqCt]
if coords.Equal(entry.event.Time, c) {
plb.seqCt++
if entry.hash != plb.digest.Hash() {
return ports.InputEvent{
return ports.TimedInputEvent{
Time: c,
Port: plugging.PortUnplugged,
Ev: ports.NoEvent,
InputEvent: ports.InputEvent{
Port: plugging.PortUnplugged,
Ev: ports.NoEvent,
},
}, curated.Errorf(PlaybackHashError, entry.line, c.Frame)
}
return entry.event, nil
}
// next event does not match
return ports.InputEvent{
return ports.TimedInputEvent{
Time: c,
Port: plugging.PortUnplugged,
Ev: ports.NoEvent,
InputEvent: ports.InputEvent{
Port: plugging.PortUnplugged,
Ev: ports.NoEvent,
},
}, nil
}

View file

@ -101,10 +101,12 @@ func NewRecorder(transcript string, vcs *hardware.VCS) (*Recorder, error) {
// End flushes all remaining events to the output file and closes it.
func (rec *Recorder) End() error {
off := ports.InputEvent{
off := ports.TimedInputEvent{
Time: rec.vcs.TV.GetCoords(),
Port: plugging.PortPanel,
Ev: ports.PanelPowerOff,
InputEvent: ports.InputEvent{
Port: plugging.PortPanel,
Ev: ports.PanelPowerOff,
},
}
// write the power off event to the transcript
@ -122,7 +124,7 @@ func (rec *Recorder) End() error {
}
// RecordEvent implements the ports.EventRecorder interface.
func (rec *Recorder) RecordEvent(inp ports.InputEvent) error {
func (rec *Recorder) RecordEvent(inp ports.TimedInputEvent) error {
var err error
// write header if it's not been written already

View file

@ -18,26 +18,18 @@ package userinput
import (
"github.com/jetsetilly/gopher2600/hardware/riot/ports"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
"github.com/jetsetilly/gopher2600/hardware/television/coords"
)
// TV defines the television functions required by the Controllers type.
type TV interface {
GetCoords() coords.TelevisionCoords
}
// Controllers keeps track of hardware userinput options.
type Controllers struct {
tv TV
inputHandlers []HandleInput
trigger GamepadTrigger
paddle float32
}
// Controllers is the preferred method of initialisation for the Controllers type.
func NewControllers(tv TV) *Controllers {
func NewControllers() *Controllers {
return &Controllers{
tv: tv,
inputHandlers: make([]HandleInput, 0),
}
}
@ -50,7 +42,7 @@ func NewControllers(tv TV) *Controllers {
func (c *Controllers) handleEvents(id plugging.PortID, ev ports.Event, d ports.EventData) (bool, error) {
var handled bool
for _, h := range c.inputHandlers {
v, err := h.HandleInputEvent(ports.InputEvent{Time: c.tv.GetCoords(), Port: id, Ev: ev, D: d})
v, err := h.HandleInputEvent(ports.InputEvent{Port: id, Ev: ev, D: d})
if err != nil {
return handled, err
}
@ -142,7 +134,7 @@ func (c *Controllers) differentiateKeyboard(key string, down bool) (bool, error)
}
// all differentiated keyboard events go to the right player port
v, err := h.HandleInputEvent(ports.InputEvent{Time: c.tv.GetCoords(), Port: plugging.PortRightPlayer, Ev: ev, D: d})
v, err := h.HandleInputEvent(ports.InputEvent{Port: plugging.PortRightPlayer, Ev: ev, D: d})
if err != nil {
return handled, err
}