Initial release: RadiantWave v1.0
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
package page
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"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/db"
|
||||
"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(app ApplicationInterface) error {
|
||||
if err := p.Base.Init(app); 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
|
||||
baseFontSizeStr, err := db.GetConfigValue("init_font_size")
|
||||
if err != nil {
|
||||
return fmt.Errorf("retrieving init_font_size from db: %w", err)
|
||||
}
|
||||
baseFontSize, err := strconv.Atoi(baseFontSizeStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing init_font_size: %w", err)
|
||||
}
|
||||
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.App.UnwindToPage(&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
|
||||
}
|
||||
Reference in New Issue
Block a user