refactor(launch system): Make shared.Run cancellable
Not sure if it works yet
This commit is contained in:
parent
781f6a10ec
commit
11747b4158
2 changed files with 101 additions and 15 deletions
67
launcher/viewMain.go
Normal file
67
launcher/viewMain.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package launcher
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"gioui.org/app"
|
||||
"gioui.org/io/event"
|
||||
"git.mstar.dev/mstar/timer/shared"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type LauncherState struct {
|
||||
possibleTargets map[string]func() shared.WindowState
|
||||
possibleTargetsLock sync.RWMutex
|
||||
activeWindows map[string]bool
|
||||
activeWindowsLock sync.RWMutex
|
||||
initialSetupDone bool
|
||||
initialSetupLock sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *LauncherState) Run(globalState *shared.GlobalState, event event.Event) shared.NewState {
|
||||
if !s.initialSetupDone {
|
||||
s.initialSetup(globalState)
|
||||
}
|
||||
switch event := event.(type) {
|
||||
case app.FrameEvent:
|
||||
return s.handleFrameEvent(globalState, &event)
|
||||
case app.DestroyEvent:
|
||||
return s.handleQuitEvent()
|
||||
case app.WaylandViewEvent, app.X11ViewEvent:
|
||||
return shared.EmptyEvent()
|
||||
default:
|
||||
log.Debug().Any("event", event).Type("type", event).Msg("Unknown event")
|
||||
return shared.NewState{}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LauncherState) Exit(globalState *shared.GlobalState, wg *sync.WaitGroup) {
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func (s *LauncherState) AddTarget(name string, builder func() shared.WindowState) {
|
||||
s.possibleTargets[name] = builder
|
||||
}
|
||||
|
||||
func (s *LauncherState) initialSetup(globalState *shared.GlobalState) {
|
||||
s.activeWindowsLock.Lock()
|
||||
defer s.activeWindowsLock.Unlock()
|
||||
s.possibleTargetsLock.Lock()
|
||||
defer s.possibleTargetsLock.Unlock()
|
||||
s.initialSetupLock.Lock()
|
||||
defer s.initialSetupLock.Unlock()
|
||||
s.initialSetupDone = true
|
||||
s.possibleTargets = map[string]func() shared.WindowState{}
|
||||
s.activeWindows = map[string]bool{}
|
||||
}
|
||||
|
||||
func (s *LauncherState) handleQuitEvent() shared.NewState {
|
||||
return shared.NewState{ExitCode: shared.ErrExitOk}
|
||||
}
|
||||
|
||||
func (s *LauncherState) handleFrameEvent(
|
||||
globalState *shared.GlobalState,
|
||||
frameEvent *app.FrameEvent,
|
||||
) shared.NewState {
|
||||
return shared.NewState{}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
package shared
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"gioui.org/app"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/widget/material"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
@ -14,7 +16,7 @@ type GlobalState struct {
|
|||
state WindowState
|
||||
}
|
||||
|
||||
func Run(window *app.Window, initialState WindowState) error {
|
||||
func Run(ctx context.Context, window *app.Window, initialState WindowState) error {
|
||||
// Set up global data first
|
||||
state := GlobalState{}
|
||||
theme := material.NewTheme()
|
||||
|
@ -26,21 +28,38 @@ func Run(window *app.Window, initialState WindowState) error {
|
|||
// Then start showing the window and polling for events
|
||||
// Though technically, I don't think window.Event will cause the window to display yet
|
||||
// since what should it display? It doesn't have anything to display. So first display happens at event.Frame
|
||||
for {
|
||||
e := window.Event()
|
||||
// Let the current view handle the event, then update accordingly
|
||||
newState := state.state.Run(&state, e)
|
||||
if newState.NextState != nil {
|
||||
state.state = newState.NextState
|
||||
eventChan := make(chan event.Event)
|
||||
go func() {
|
||||
for {
|
||||
eventChan <- window.Event()
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if newState.ExitCode != nil {
|
||||
log.Info().Msg("Received shutdown request")
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go state.state.Exit(&state, &wg)
|
||||
wg.Wait()
|
||||
log.Info().Msg("Shutdown of window complete")
|
||||
return nil
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Info().Msg("Context cancelled, preparing shutdown")
|
||||
// Force one update for polling goroutine to exit
|
||||
state.Window.Invalidate()
|
||||
// Then keep awaiting more events. Since quit is just another event
|
||||
// and the event channel is unbuffered, at least one more event has to be read for the poller to exit
|
||||
case e := <-eventChan:
|
||||
// Let the current view handle the event, then update accordingly
|
||||
newState := state.state.Run(&state, e)
|
||||
if newState.NextState != nil {
|
||||
state.state = newState.NextState
|
||||
}
|
||||
if newState.ExitCode != nil {
|
||||
log.Info().Msg("Received shutdown request from window")
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go state.state.Exit(&state, &wg)
|
||||
wg.Wait()
|
||||
log.Info().Msg("Shutdown of window complete")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue