mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-05-10 09:24:07 -04:00
rewinding by mouse wheel works in debugger
rewinds/FF by 10 frames or by a single frame if shift key is held
This commit is contained in:
parent
7e1f1f17b6
commit
8f2eb8753f
|
@ -214,20 +214,17 @@ type Debugger struct {
|
|||
// moving or for how long the keyboard (or gamepad bumpers) have been
|
||||
// depressed.
|
||||
//
|
||||
// when rewinding by mousewheel, events are likely to be sent during the
|
||||
// rewind catchup loop so we accumulate the mousewheel delta and rewind
|
||||
// when we return to the normal loop
|
||||
//
|
||||
// keyboard/bumper rewind is slightly different. for every machine cycle
|
||||
// (in the normal playloop - not the catchup loop) that the keyboard is
|
||||
// held down we increase (or decrease when going backwards) the
|
||||
// accumulation value. we use this to determine how quickly the rewind
|
||||
// should progress. the accumulation value is zeroed when the key/bumpers
|
||||
// are released
|
||||
//
|
||||
// * playmode only
|
||||
// for keyboard rewinding, the amount to rewind by increases for as long as
|
||||
// the key-combo (gamepad bumper) is being held. the amount is reset when
|
||||
// the key-combo/bumper is released
|
||||
rewindKeyboardAccumulation int
|
||||
|
||||
// when rewinding by mouse wheel we just use the delta information from the
|
||||
// input device. however, we might miss mousewheel events during the
|
||||
// debugger catchup loop. the rewindMouseWheelAccumulation value allows us
|
||||
// to accumulate the delta value until we have the opportunity to issue
|
||||
// another rewind instruction
|
||||
rewindMouseWheelAccumulation int
|
||||
rewindKeyboardAccumulation int
|
||||
|
||||
// audio tracker stores audio state over time
|
||||
Tracker *tracker.Tracker
|
||||
|
|
|
@ -25,16 +25,20 @@ import (
|
|||
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
||||
)
|
||||
|
||||
// RewindByAmount moves forwards or backwards by specified frames. Negative
|
||||
// numbers indicate backwards
|
||||
// RewindByAmount moves forwards or backwards by specified frames. Positive
|
||||
// numbers to "rewind" forwards and negative numbers to rewind backwards.
|
||||
func (dbg *Debugger) RewindByAmount(amount int) {
|
||||
if dbg.state.Load().(govern.State) == govern.Rewinding {
|
||||
return
|
||||
}
|
||||
|
||||
switch dbg.Mode() {
|
||||
case govern.ModePlay:
|
||||
coords := dbg.vcs.TV.GetCoords()
|
||||
fn := dbg.vcs.TV.GetCoords().Frame
|
||||
tl := dbg.Rewind.GetTimeline()
|
||||
|
||||
if amount < 0 {
|
||||
if coords.Frame-1 < tl.AvailableStart {
|
||||
if fn-1 < tl.AvailableStart {
|
||||
dbg.setState(govern.Paused, govern.PausedAtStart)
|
||||
return
|
||||
}
|
||||
|
@ -42,30 +46,61 @@ func (dbg *Debugger) RewindByAmount(amount int) {
|
|||
}
|
||||
|
||||
if amount > 0 {
|
||||
if coords.Frame+1 > tl.AvailableEnd {
|
||||
if fn+1 > tl.AvailableEnd {
|
||||
dbg.setState(govern.Paused, govern.PausedAtEnd)
|
||||
return
|
||||
}
|
||||
dbg.setState(govern.Rewinding, govern.RewindingForwards)
|
||||
}
|
||||
|
||||
dbg.Rewind.GotoFrame(coords.Frame + amount)
|
||||
dbg.Rewind.GotoFrame(fn + amount)
|
||||
dbg.setState(govern.Paused, govern.Normal)
|
||||
|
||||
return
|
||||
}
|
||||
case govern.ModeDebugger:
|
||||
fn := dbg.vcs.TV.GetCoords().Frame
|
||||
fn += amount
|
||||
|
||||
panic(fmt.Sprintf("Rewind: unsupported mode (%v)", dbg.Mode()))
|
||||
// the function to push to the debugger/emulation routine
|
||||
doRewind := func() error {
|
||||
// upate catchup context before starting rewind process
|
||||
dbg.catchupContext = catchupRewindToFrame
|
||||
|
||||
err := dbg.Rewind.GotoFrame(fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// how we push the doRewind() function depends on what kind of inputloop we
|
||||
// are currently in
|
||||
dbg.PushFunctionImmediate(func() {
|
||||
// set state to govern.Rewinding as soon as possible (but
|
||||
// remembering that we must do it in the debugger goroutine)
|
||||
//
|
||||
// not that we're not worried about detecing PausedAtEnd and
|
||||
// PausedAtStart conditions like we do in the ModePlay case
|
||||
if amount > 0 {
|
||||
dbg.setState(govern.Rewinding, govern.RewindingForwards)
|
||||
} else {
|
||||
dbg.setState(govern.Rewinding, govern.RewindingBackwards)
|
||||
}
|
||||
dbg.unwindLoop(doRewind)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// RewindToFrame measure from the current frame.
|
||||
//
|
||||
// This function should be run only from debugger mode.
|
||||
func (dbg *Debugger) RewindToFrame(fn int, last bool) bool {
|
||||
if dbg.State() == govern.Rewinding {
|
||||
return false
|
||||
}
|
||||
|
||||
switch dbg.Mode() {
|
||||
case govern.ModeDebugger:
|
||||
if dbg.State() == govern.Rewinding {
|
||||
return false
|
||||
}
|
||||
|
||||
// the function to push to the debugger/emulation routine
|
||||
doRewind := func() error {
|
||||
// upate catchup context before starting rewind process
|
||||
|
@ -106,13 +141,15 @@ func (dbg *Debugger) RewindToFrame(fn int, last bool) bool {
|
|||
}
|
||||
|
||||
// GotoCoords rewinds the emulation to the specified coordinates.
|
||||
//
|
||||
// This function should be run only from debugger mode.
|
||||
func (dbg *Debugger) GotoCoords(toCoords coords.TelevisionCoords) bool {
|
||||
if dbg.State() == govern.Rewinding {
|
||||
return false
|
||||
}
|
||||
|
||||
switch dbg.Mode() {
|
||||
case govern.ModeDebugger:
|
||||
if dbg.State() == govern.Rewinding {
|
||||
return false
|
||||
}
|
||||
|
||||
// the function to push to the debugger/emulation routine
|
||||
doRewind := func() error {
|
||||
// upate catchup context before starting rewind process
|
||||
|
@ -148,13 +185,15 @@ func (dbg *Debugger) GotoCoords(toCoords coords.TelevisionCoords) bool {
|
|||
}
|
||||
|
||||
// RerunLastNFrames measured from the current frame.
|
||||
//
|
||||
// This function should be run only from debugger mode.
|
||||
func (dbg *Debugger) RerunLastNFrames(frames int) bool {
|
||||
if dbg.State() == govern.Rewinding {
|
||||
return false
|
||||
}
|
||||
|
||||
switch dbg.Mode() {
|
||||
case govern.ModeDebugger:
|
||||
if dbg.State() == govern.Rewinding {
|
||||
return false
|
||||
}
|
||||
|
||||
// the disadvantage of RerunLastNFrames() is that it will always land on a
|
||||
// CPU instruction boundary (this is because we must unwind the existing
|
||||
// input loop before calling the rewind function)
|
||||
|
@ -189,7 +228,7 @@ func (dbg *Debugger) RerunLastNFrames(frames int) bool {
|
|||
|
||||
// set state to govern.Rewinding as soon as possible (but
|
||||
// remembering that we must do it in the debugger goroutine)
|
||||
dbg.setState(govern.Rewinding, govern.Normal)
|
||||
dbg.setState(govern.Rewinding, govern.RewindingForwards)
|
||||
dbg.unwindLoop(doRewind)
|
||||
})
|
||||
|
||||
|
|
|
@ -46,12 +46,24 @@ func (dbg *Debugger) userInputHandler(ev userinput.Event) error {
|
|||
|
||||
// special handling of some user input (not passed to the VCS as controller input)
|
||||
switch dbg.Mode() {
|
||||
case govern.ModeDebugger:
|
||||
switch ev := ev.(type) {
|
||||
case userinput.EventMouseWheel:
|
||||
var amount int
|
||||
switch ev.Mod {
|
||||
case userinput.KeyModShift:
|
||||
amount = int(ev.Delta)
|
||||
default:
|
||||
amount = int(ev.Delta) * 5
|
||||
}
|
||||
dbg.RewindByAmount(dbg.rewindMouseWheelAccumulation + amount)
|
||||
return nil
|
||||
}
|
||||
|
||||
case govern.ModePlay:
|
||||
switch ev := ev.(type) {
|
||||
case userinput.EventMouseWheel:
|
||||
amount := int(ev.Delta) + dbg.rewindMouseWheelAccumulation
|
||||
dbg.rewindMouseWheelAccumulation = 0
|
||||
dbg.RewindByAmount(amount)
|
||||
dbg.RewindByAmount(int(ev.Delta))
|
||||
return nil
|
||||
|
||||
case userinput.EventKeyboard:
|
||||
|
|
|
@ -18,7 +18,6 @@ package sdlimgui
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/debugger/govern"
|
||||
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
|
||||
"github.com/jetsetilly/gopher2600/logger"
|
||||
"github.com/jetsetilly/gopher2600/userinput"
|
||||
|
@ -209,24 +208,38 @@ func (img *SdlImgui) Service() {
|
|||
img.polling.alerted = true
|
||||
|
||||
case *sdl.MouseWheelEvent:
|
||||
var deltaX, deltaY float32
|
||||
if ev.X > 0 {
|
||||
deltaX++
|
||||
} else if ev.X < 0 {
|
||||
deltaX--
|
||||
}
|
||||
if ev.Y > 0 {
|
||||
deltaY++
|
||||
} else if ev.Y < 0 {
|
||||
deltaY--
|
||||
}
|
||||
imgui.CurrentIO().AddMouseWheelDelta(-deltaX/4, deltaY/4)
|
||||
// only respond to mouse wheel events if the window has
|
||||
// input focus. this is because without input focus
|
||||
// getKeyMod() will always return userinput.KeyModNone. it
|
||||
// is confusing if the mousewheel is working but no keyboard
|
||||
// modifiers are affecting it
|
||||
if img.plt.window.GetFlags()&sdl.WINDOW_INPUT_FOCUS == sdl.WINDOW_INPUT_FOCUS {
|
||||
var deltaX, deltaY float32
|
||||
if ev.X > 0 {
|
||||
deltaX++
|
||||
} else if ev.X < 0 {
|
||||
deltaX--
|
||||
}
|
||||
if ev.Y > 0 {
|
||||
deltaY++
|
||||
} else if ev.Y < 0 {
|
||||
deltaY--
|
||||
}
|
||||
|
||||
if img.mode.Load().(govern.Mode) != govern.ModePlay || !img.wm.playmodeWindows[winSelectROMID].playmodeIsOpen() {
|
||||
select {
|
||||
case input <- userinput.EventMouseWheel{Delta: deltaY}:
|
||||
default:
|
||||
logger.Log("sdlimgui", "dropped mouse wheel event")
|
||||
// forward mouse wheel event to emulation only for playmode
|
||||
// or if the mouse is immediately over the TV image in the
|
||||
// debugger TV screen window
|
||||
if img.isPlaymode() || img.wm.dbgScr.mouseHover {
|
||||
select {
|
||||
case input <- userinput.EventMouseWheel{
|
||||
Delta: deltaY,
|
||||
Mod: getKeyMod(),
|
||||
}:
|
||||
default:
|
||||
logger.Log("sdlimgui", "dropped mouse wheel event")
|
||||
}
|
||||
} else {
|
||||
imgui.CurrentIO().AddMouseWheelDelta(-deltaX/4, deltaY/4)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -254,19 +254,6 @@ func (img *SdlImgui) serviceKeyboard(ev *sdl.KeyboardEvent) {
|
|||
|
||||
// forward keypresses to userinput.Event channel
|
||||
if img.isCaptured() || (img.isPlaymode() && !imgui.IsAnyItemActive()) {
|
||||
mod := userinput.KeyModNone
|
||||
|
||||
if sdl.GetModState()&sdl.KMOD_LALT == sdl.KMOD_LALT ||
|
||||
sdl.GetModState()&sdl.KMOD_RALT == sdl.KMOD_RALT {
|
||||
mod = userinput.KeyModAlt
|
||||
} else if sdl.GetModState()&sdl.KMOD_LSHIFT == sdl.KMOD_LSHIFT ||
|
||||
sdl.GetModState()&sdl.KMOD_RSHIFT == sdl.KMOD_RSHIFT {
|
||||
mod = userinput.KeyModShift
|
||||
} else if sdl.GetModState()&sdl.KMOD_LCTRL == sdl.KMOD_LCTRL ||
|
||||
sdl.GetModState()&sdl.KMOD_RCTRL == sdl.KMOD_RCTRL {
|
||||
mod = userinput.KeyModCtrl
|
||||
}
|
||||
|
||||
switch ev.Type {
|
||||
case sdl.KEYDOWN:
|
||||
fallthrough
|
||||
|
@ -275,7 +262,7 @@ func (img *SdlImgui) serviceKeyboard(ev *sdl.KeyboardEvent) {
|
|||
case img.dbg.UserInput() <- userinput.EventKeyboard{
|
||||
Key: sdl.GetScancodeName(ev.Keysym.Scancode),
|
||||
Down: ev.Type == sdl.KEYDOWN,
|
||||
Mod: mod,
|
||||
Mod: getKeyMod(),
|
||||
}:
|
||||
default:
|
||||
logger.Log("sdlimgui", "dropped keyboard event")
|
||||
|
@ -293,3 +280,17 @@ func (img *SdlImgui) serviceKeyboard(ev *sdl.KeyboardEvent) {
|
|||
img.updateKeyModifier()
|
||||
}
|
||||
}
|
||||
|
||||
func getKeyMod() userinput.KeyMod {
|
||||
if sdl.GetModState()&sdl.KMOD_LALT == sdl.KMOD_LALT ||
|
||||
sdl.GetModState()&sdl.KMOD_RALT == sdl.KMOD_RALT {
|
||||
return userinput.KeyModAlt
|
||||
} else if sdl.GetModState()&sdl.KMOD_LSHIFT == sdl.KMOD_LSHIFT ||
|
||||
sdl.GetModState()&sdl.KMOD_RSHIFT == sdl.KMOD_RSHIFT {
|
||||
return userinput.KeyModShift
|
||||
} else if sdl.GetModState()&sdl.KMOD_LCTRL == sdl.KMOD_LCTRL ||
|
||||
sdl.GetModState()&sdl.KMOD_RCTRL == sdl.KMOD_RCTRL {
|
||||
return userinput.KeyModCtrl
|
||||
}
|
||||
return userinput.KeyModNone
|
||||
}
|
||||
|
|
|
@ -90,6 +90,9 @@ type winDbgScr struct {
|
|||
// magnification fields
|
||||
magnifyTooltip dbgScrMagnifyTooltip
|
||||
magnifyWindow dbgScrMagnifyWindow
|
||||
|
||||
// whether mouse is hovering over screen image
|
||||
mouseHover bool
|
||||
}
|
||||
|
||||
func newWinDbgScr(img *SdlImgui) (window, error) {
|
||||
|
@ -201,11 +204,15 @@ func (win *winDbgScr) draw() {
|
|||
imgui.PushStyleVarVec2(imgui.StyleVarFramePadding, imgui.Vec2{0.0, 0.0})
|
||||
|
||||
imgui.PushStyleColor(imgui.StyleColorDragDropTarget, win.img.cols.Transparent)
|
||||
|
||||
if !win.crtPreview && win.elements {
|
||||
imgui.ImageButton(imgui.TextureID(win.elementsTexture.getID()), imgui.Vec2{win.scaledWidth, win.scaledHeight})
|
||||
} else {
|
||||
imgui.ImageButton(imgui.TextureID(win.displayTexture.getID()), imgui.Vec2{win.scaledWidth, win.scaledHeight})
|
||||
}
|
||||
|
||||
win.mouseHover = imgui.IsItemHovered()
|
||||
|
||||
win.paintDragAndDrop()
|
||||
imgui.PopStyleColor()
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ type EventMouseButton struct {
|
|||
// EventMouseWheel data is generated by the mouse wheel (jog wheel).
|
||||
type EventMouseWheel struct {
|
||||
Delta float32
|
||||
Mod KeyMod
|
||||
}
|
||||
|
||||
// DPadDirection indentifies the direction the dpad is being pressed.
|
||||
|
|
Loading…
Reference in a new issue