mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
holding down mouse button without any mouse movement does not produce an SDL event. this meant that holding down the STEP SCANLINE button slowed down dramatically after the short keepAwake period
186 lines
5.8 KiB
Go
186 lines
5.8 KiB
Go
// This file is part of Gopher2600.
|
|
//
|
|
// Gopher2600 is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Gopher2600 is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package sdlimgui
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/jetsetilly/gopher2600/debugger/govern"
|
|
"github.com/veandco/go-sdl2/sdl"
|
|
)
|
|
|
|
// time periods in milliseconds that each mode sleeps for at the end of each
|
|
// service() call. this changes depending primarily on whether we're in debug
|
|
// or play mode.
|
|
const (
|
|
// very small sleep period if emulator is in play mode
|
|
playSleepPeriod = 1
|
|
|
|
// slightly longer sleep period if emulator is in play mode and is paused
|
|
playPausedSleepPeriod = 40
|
|
|
|
// a small sleep if emulator is in the debugger. this strikes a nice
|
|
// balance between CPU usage and responsiveness
|
|
debugSleepPeriod = 40
|
|
|
|
// if emulator is actually running however then there should be hardly any
|
|
// sleep period at all
|
|
debugSleepPeriodRunning = 1
|
|
|
|
// debugIdleSleepPeriod should not be too long because the sleep is not preempted.
|
|
idleSleepPeriod = 500
|
|
|
|
// the period of inactivity required before the main sleep period drops to
|
|
// the idlsSleepPeriod value.
|
|
wakefullnessPeriod = 3000 // 3 seconds
|
|
|
|
// the amount of time between thumbnail generation in the timeline. short
|
|
// enough to feel responsive but long enough to give the mail emulation (if
|
|
// running) time to update
|
|
timelineThumbnailerPeriod = 50
|
|
|
|
// the amount of time between interface renders caused by window resizing
|
|
resizePeriod = 5
|
|
)
|
|
|
|
type polling struct {
|
|
img *SdlImgui
|
|
|
|
// queue of pumped events for the frame
|
|
pumpedEvents []sdl.Event
|
|
|
|
// functions that need to be performed in the main thread are queued for
|
|
// serving by the service() function
|
|
service chan func()
|
|
serviceErr chan error
|
|
|
|
// ReqFeature() and GetFeature() hands off requests to the featureReq
|
|
// channel for servicing. think of these as pecial instances of the
|
|
// service chan
|
|
featureSet chan featureRequest
|
|
featureSetErr chan error
|
|
|
|
// the following are not used in playmode
|
|
|
|
// alerted is used to preempt the tickers when we want to communicate between
|
|
// iterations of the service loop. for example, closing sdlimgui windows
|
|
// might feel laggy without it (see commentary in service loop for
|
|
// explanation).
|
|
alerted bool
|
|
|
|
// the keepAwake flag is set to true for a short time (defined by
|
|
// wakefullnessPeriod) after the last event was received. this improves
|
|
// responsiveness for certain GUI operations.
|
|
keepAwake bool
|
|
lastEvent time.Time
|
|
|
|
lastThmbTime time.Time
|
|
lastResizeTime time.Time
|
|
}
|
|
|
|
func newPolling(img *SdlImgui) *polling {
|
|
pol := &polling{
|
|
img: img,
|
|
pumpedEvents: make([]sdl.Event, 64),
|
|
service: make(chan func(), 1),
|
|
serviceErr: make(chan error, 1),
|
|
featureSet: make(chan featureRequest, 1),
|
|
featureSetErr: make(chan error, 1),
|
|
}
|
|
|
|
return pol
|
|
}
|
|
|
|
// wait for an SDL event or for a timeout. the timeout duration depends on the
|
|
// state of the emulation and receent user input.
|
|
func (pol *polling) wait() sdl.Event {
|
|
select {
|
|
case f := <-pol.service:
|
|
f()
|
|
case r := <-pol.featureSet:
|
|
pol.img.serviceSetFeature(r)
|
|
default:
|
|
}
|
|
|
|
// the amount of timeout depends on mode and state
|
|
var timeout int
|
|
|
|
if pol.img.isPlaymode() {
|
|
if pol.img.dbg.State() != govern.Paused || pol.img.prefs.activePause.Get().(bool) || pol.img.wm.playmodeWindows[winSelectROMID].playmodeIsOpen() {
|
|
timeout = playSleepPeriod
|
|
} else {
|
|
timeout = playPausedSleepPeriod
|
|
}
|
|
} else {
|
|
// if mouse is being held (eg. on the "step scanline" button) then it
|
|
// will not be detected as an event we therefore must explicitely test
|
|
// if it's being held
|
|
//
|
|
// (16/06/24 note that this may mean that the alerted flag is no longer
|
|
// required but we'll keep it in because it's a useful idea)
|
|
_, _, mouseState := sdl.GetMouseState()
|
|
mouseHeld := mouseState&sdl.ButtonLMask() != 0
|
|
|
|
if mouseHeld {
|
|
timeout = debugSleepPeriodRunning
|
|
pol.keepAwake = true
|
|
} else if pol.img.dbg.State() == govern.Running || pol.img.wm.debuggerWindows[winSelectROMID].debuggerIsOpen() {
|
|
timeout = debugSleepPeriodRunning
|
|
} else if pol.alerted || pol.keepAwake {
|
|
// this branch used to depend on a debugger flag "hasChanged". this
|
|
// no longer seems necessary, maybe because the govern.Running state
|
|
// is so well defined
|
|
timeout = debugSleepPeriod
|
|
} else {
|
|
timeout = idleSleepPeriod
|
|
}
|
|
}
|
|
|
|
ev := sdl.WaitEventTimeout(timeout)
|
|
|
|
if ev != nil {
|
|
// an event has been received so set keepAwake flag and note time of event
|
|
pol.keepAwake = true
|
|
pol.lastEvent = time.Now()
|
|
} else if pol.keepAwake {
|
|
// keepAwake flag set for wakefullnessPeriod milliseconds
|
|
pol.keepAwake = time.Since(pol.lastEvent).Milliseconds() < wakefullnessPeriod
|
|
}
|
|
|
|
// always reset alerted flag
|
|
pol.alerted = false
|
|
|
|
return ev
|
|
}
|
|
|
|
// returns true if sufficient time has passed since the last thumbnail generation
|
|
func (pol *polling) throttleTimelineThumbnailer() bool {
|
|
if time.Since(pol.lastThmbTime).Milliseconds() > timelineThumbnailerPeriod {
|
|
pol.lastThmbTime = time.Now()
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// returns true if sufficient time has passed since the last window resize event
|
|
func (pol *polling) throttleResize() bool {
|
|
if time.Since(pol.lastResizeTime).Milliseconds() > resizePeriod {
|
|
pol.lastResizeTime = time.Now()
|
|
pol.lastEvent = pol.lastResizeTime
|
|
return true
|
|
}
|
|
return false
|
|
}
|