Database is more usable now. All pages have been updated to use the database, and share an application configuration. Page Stack has been removed, no more pages hanging around in memory. Replaced with a 'Router' which uses an Enum to determine which page should be displayed, destroys the old page and initializes the new page all in one go. No more push, pop, rollback, etc. No more holding instances of pages. Logger has been updated with proper timestamps and sorting now works as expected."
189 lines
4.4 KiB
Go
189 lines
4.4 KiB
Go
package page
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/go-gl/gl/v3.3-core/gl"
|
|
"github.com/veandco/go-sdl2/sdl"
|
|
"rhemawavetech.com/rhemawave/internal/colors"
|
|
"rhemawavetech.com/rhemawave/internal/fontManager"
|
|
"rhemawavetech.com/rhemawave/internal/shaderManager"
|
|
)
|
|
|
|
type updateCheckState int
|
|
|
|
const (
|
|
updateCheckStateChecking updateCheckState = iota
|
|
updateCheckStateDone
|
|
)
|
|
|
|
type UpdateCheckPage struct {
|
|
Base
|
|
textShader *shaderManager.Shader
|
|
|
|
state updateCheckState
|
|
|
|
// "Checking for Updates." / ".." / "..."
|
|
checkingTextures [3]StringItem
|
|
|
|
// "No updates available" and "Press F3 to return"
|
|
doneMessage StringItem
|
|
doneHint StringItem
|
|
|
|
// Error message if service failed to start
|
|
errorMessage StringItem
|
|
hasError bool
|
|
|
|
pollTimer float32
|
|
dotIndex int
|
|
dotTimer float32
|
|
}
|
|
|
|
func (p *UpdateCheckPage) Init(ctx *PageCtx) error {
|
|
var err error
|
|
if err := p.Base.Init(ctx); err != nil {
|
|
return fmt.Errorf("failed base page initialization: %w", err)
|
|
}
|
|
|
|
fm := fontManager.Get()
|
|
sm := shaderManager.Get()
|
|
|
|
font, ok := fm.GetFont("Roboto-Regular")
|
|
if !ok {
|
|
return fmt.Errorf("failed to get font")
|
|
}
|
|
|
|
p.textShader, ok = sm.Get("text")
|
|
if !ok {
|
|
return fmt.Errorf("failed to get text shader")
|
|
}
|
|
|
|
// Get scale
|
|
baseFontSize := ctx.CfgPtr.InitialFontSize
|
|
scale := float32(48) / float32(baseFontSize)
|
|
|
|
// Create checking textures with animated dots
|
|
dots := []string{"Checking for Updates.", "Checking for Updates..", "Checking for Updates..."}
|
|
for i, text := range dots {
|
|
item, err := NewStringItem(text, font, colors.White)
|
|
if err != nil {
|
|
return fmt.Errorf("creating checking texture %d: %w", i, err)
|
|
}
|
|
item.Scale(scale)
|
|
p.checkingTextures[i] = item
|
|
}
|
|
|
|
// Create done textures
|
|
hintScale := float32(32) / float32(baseFontSize)
|
|
|
|
p.doneMessage, err = NewStringItem("No updates available", font, colors.White)
|
|
if err != nil {
|
|
return fmt.Errorf("creating done message: %w", err)
|
|
}
|
|
p.doneMessage.Scale(scale)
|
|
|
|
p.doneHint, err = NewStringItem("Press F3 to return", font, colors.White)
|
|
if err != nil {
|
|
return fmt.Errorf("creating done hint: %w", err)
|
|
}
|
|
p.doneHint.Scale(hintScale)
|
|
|
|
// Start the update service
|
|
p.state = updateCheckStateChecking
|
|
if err := exec.Command("systemctl", "start", "rhemawave-update.service").Run(); err != nil {
|
|
p.errorMessage, _ = NewStringItem("Update service unavailable", font, colors.White)
|
|
p.errorMessage.Scale(scale)
|
|
p.hasError = true
|
|
p.state = updateCheckStateDone
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *UpdateCheckPage) Update(dt float32) error {
|
|
if p.state != updateCheckStateChecking {
|
|
return nil
|
|
}
|
|
|
|
// Animate dots
|
|
p.dotTimer += dt
|
|
if p.dotTimer >= 0.5 {
|
|
p.dotTimer -= 0.5
|
|
p.dotIndex = (p.dotIndex + 1) % 3
|
|
}
|
|
|
|
// Poll service status every 2 seconds
|
|
p.pollTimer += dt
|
|
if p.pollTimer >= 2.0 {
|
|
p.pollTimer -= 2.0
|
|
|
|
output, _ := exec.Command("systemctl", "is-active", "rhemawave-update.service").Output()
|
|
status := strings.TrimSpace(string(output))
|
|
if status != "active" && status != "activating" {
|
|
p.state = updateCheckStateDone
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *UpdateCheckPage) HandleEvent(event *sdl.Event) error {
|
|
if p.state != updateCheckStateDone {
|
|
return nil
|
|
}
|
|
|
|
switch e := (*event).(type) {
|
|
case *sdl.KeyboardEvent:
|
|
if e.Type != sdl.KEYDOWN {
|
|
return nil
|
|
}
|
|
switch e.Keysym.Sym {
|
|
case sdl.K_F3, sdl.K_BACKSPACE:
|
|
p.Context.NavigateTo(SETTINGS)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *UpdateCheckPage) Render() error {
|
|
switch p.state {
|
|
case updateCheckStateChecking:
|
|
item := p.checkingTextures[p.dotIndex]
|
|
item.X = p.ScreenCenterX - item.W/2
|
|
item.Y = p.ScreenCenterY - item.H/2
|
|
p.RenderTexture(p.textShader, item.ID, item.Position(), item.RawDimensions(), item.Color)
|
|
|
|
case updateCheckStateDone:
|
|
var msg *StringItem
|
|
if p.hasError {
|
|
msg = &p.errorMessage
|
|
} else {
|
|
msg = &p.doneMessage
|
|
}
|
|
|
|
msg.X = p.ScreenCenterX - msg.W/2
|
|
msg.Y = p.ScreenCenterY + msg.H/2
|
|
p.RenderTexture(p.textShader, msg.ID, msg.Position(), msg.RawDimensions(), msg.Color)
|
|
|
|
p.doneHint.X = p.ScreenCenterX - p.doneHint.W/2
|
|
p.doneHint.Y = msg.Y - p.doneHint.H - 20
|
|
p.RenderTexture(p.textShader, p.doneHint.ID, p.doneHint.Position(), p.doneHint.RawDimensions(), p.doneHint.Color)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *UpdateCheckPage) Destroy() error {
|
|
for i := range p.checkingTextures {
|
|
gl.DeleteTextures(1, &p.checkingTextures[i].ID)
|
|
}
|
|
gl.DeleteTextures(1, &p.doneMessage.ID)
|
|
gl.DeleteTextures(1, &p.doneHint.ID)
|
|
if p.hasError {
|
|
gl.DeleteTextures(1, &p.errorMessage.ID)
|
|
}
|
|
return nil
|
|
}
|