refactor: remove cached AppConfig pointer and read configuration directly from DB

Replace in-memory atomic AppConfig caching with direct DB queries via db.GetConfig
across application and page layers. This simplifies state management and ensures
all components always operate on the latest persisted configuration.

Key changes:
- Remove Application.Config atomic cache and refreshCache mechanism
- Eliminate PageCtx.CfgPtr and pass DB handle instead
- Replace all cfg reads with db.GetConfig(app.DB)
- Update mixer, audio, UI pages, and initialization paths accordingly
- Explicitly set AppConfig table name to avoid GORM pluralization issues
- Reduce DB connection pooling (SetMaxIdleConns(1))
- Standardize application font to SpaceMonoNerdFont-Regular
- Lower default audio volume (1.0 → 0.2)
- Add Unicode checkmark glyph to font map

Behavioral impact:
- Configuration is no longer cached in memory
- Every config access now hits the DB (more consistent, slightly more overhead)
This commit is contained in:
NeilClack
2026-06-26 22:51:32 -05:00
parent 6fd5ec919c
commit 2735af5fbb
15 changed files with 239 additions and 138 deletions
-28
View File
@@ -1,28 +0,0 @@
# Changelog
## [Unreleased]
### Added
- Added proclamation sync when entering "proclamations" settings page
### Changed
- Changed audio devices page to be strictly an audio on/off toggle
- Changed settings menu items to be left-aligned with eachother
- Changed settings entry organization
### Deprecated
### Removed
- Removed list of audio devices from audio device page
### Fixed
- Fixed audio device not being initialized on first entry to F2 screen
### Security
---
+33 -24
View File
@@ -4,7 +4,6 @@ import (
"errors" "errors"
"os" "os"
"path/filepath" "path/filepath"
"sync/atomic"
"time" "time"
"github.com/go-gl/gl/v3.3-core/gl" "github.com/go-gl/gl/v3.3-core/gl"
@@ -51,7 +50,6 @@ type Application struct {
pendingAction func() pendingAction func()
running bool running bool
DB *gorm.DB DB *gorm.DB
Config atomic.Pointer[db.AppConfig]
PageRouter *page.Router PageRouter *page.Router
state State state State
} }
@@ -112,7 +110,7 @@ func Run() {
// Populate the initial Config cache OR create defaults // Populate the initial Config cache OR create defaults
firstRun := false firstRun := false
result = DB.First(&initialConfig) result = DB.Unscoped().First(&initialConfig)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
logger.Info("First run detected: Creating default configuration...") logger.Info("First run detected: Creating default configuration...")
@@ -148,8 +146,6 @@ func Run() {
app.state = FIRST_RUN app.state = FIRST_RUN
} }
app.refreshCache()
w, h := app.initializeSDLAndOpenGL() w, h := app.initializeSDLAndOpenGL()
defer app.cleanup() defer app.cleanup()
@@ -159,12 +155,10 @@ func Run() {
downloadProgressChan := make(chan download.DownloadStatus, 1000) downloadProgressChan := make(chan download.DownloadStatus, 1000)
app.PageRouter = page.NewRouter(page.PageCtx{ app.PageRouter = page.NewRouter(page.PageCtx{
CfgPtr: app.Config.Load(),
DbPtr: app.DB, DbPtr: app.DB,
W: w, W: w,
H: h, H: h,
MessageChannel: downloadProgressChan, MessageChannel: downloadProgressChan,
RefreshConfig: app.refreshCache,
}) })
// Sync the proclamations from the API to the Database // Sync the proclamations from the API to the Database
@@ -188,7 +182,12 @@ func Run() {
"PinealGland.wav", "PinealGland.wav",
} }
go download.DownloadAudioFiles(app.PageRouter.Context.MessageChannel, audioFileNames, filepath.Join(app.PageRouter.Context.CfgPtr.AssetsDir, "audio")) config, err := db.GetConfig(app.DB)
if err != nil {
panic(err)
}
go download.DownloadAudioFiles(app.PageRouter.Context.MessageChannel, audioFileNames, filepath.Join(config.AssetsDir, "audio"))
app.runEventLoop() app.runEventLoop()
} }
@@ -221,7 +220,12 @@ func (app *Application) checkState() {
} }
// Then check for an email address // Then check for an email address
if app.Config.Load().EmailAddress == "" { config, err := db.GetConfig(app.DB)
if err != nil {
panic(err)
}
if config.EmailAddress == "" {
if app.state != NEEDS_EMAIL_ADDRESS { if app.state != NEEDS_EMAIL_ADDRESS {
app.PageRouter.NavigateTo(page.EMAIL_ADDRESS) app.PageRouter.NavigateTo(page.EMAIL_ADDRESS)
app.state = NEEDS_EMAIL_ADDRESS app.state = NEEDS_EMAIL_ADDRESS
@@ -230,7 +234,11 @@ func (app *Application) checkState() {
} }
// Then check for a license key // Then check for a license key
if app.Config.Load().LicenseKey == "" { config, err = db.GetConfig(app.DB)
if err != nil {
panic(err)
}
if config.LicenseKey == "" {
if app.state != NEEDS_LICENSE_KEY { if app.state != NEEDS_LICENSE_KEY {
app.PageRouter.NavigateTo(page.LICENSE_KEY) app.PageRouter.NavigateTo(page.LICENSE_KEY)
app.state = NEEDS_LICENSE_KEY app.state = NEEDS_LICENSE_KEY
@@ -242,14 +250,6 @@ func (app *Application) checkState() {
app.PageRouter.NavigateTo(page.HOME) app.PageRouter.NavigateTo(page.HOME)
} }
func (app *Application) refreshCache() {
var freshConfig db.AppConfig
if result := app.DB.First(&freshConfig); result.Error != nil {
logger.Error("failed to fetch configuration from the database: %v", result.Error)
}
app.Config.Store(&freshConfig)
}
func (app *Application) initializeSDLAndOpenGL() (W, H int32) { func (app *Application) initializeSDLAndOpenGL() (W, H int32) {
logger.Info("Initializing SDL") logger.Info("Initializing SDL")
if err := sdl.Init(sdl.INIT_VIDEO | sdl.INIT_AUDIO); err != nil { if err := sdl.Init(sdl.INIT_VIDEO | sdl.INIT_AUDIO); err != nil {
@@ -324,29 +324,38 @@ func (app *Application) initializeSDLAndOpenGL() (W, H int32) {
func (app *Application) initializeManagers() { func (app *Application) initializeManagers() {
logger.Info("Initializing ShaderManager") logger.Info("Initializing ShaderManager")
shaderDir := filepath.Join(app.Config.Load().AssetsDir, "shaders") config, err := db.GetConfig(app.DB)
if err != nil {
panic(err)
}
shaderDir := filepath.Join(config.AssetsDir, "shaders")
logger.Info("Loading shaders from %s", shaderDir) logger.Info("Loading shaders from %s", shaderDir)
if err := shaderManager.InitShaderManager(shaderDir); err != nil { if err := shaderManager.InitShaderManager(shaderDir); err != nil {
logger.Fatal("ShaderManager initialization failed: %v", err) logger.Fatal("ShaderManager initialization failed: %v", err)
} }
logger.Info("Initializing FontManager") logger.Info("Initializing FontManager")
if err := fontManager.InitFontManager(app.Config.Load()); err != nil { if err := fontManager.InitFontManager(config); err != nil {
logger.Fatal("FontManager initialization failed: %v", err) logger.Fatal("FontManager initialization failed: %v", err)
} }
} }
func (app *Application) initializeAudio() { func (app *Application) initializeAudio() {
logger.Info("Initializing audio") logger.Info("Initializing audio")
config, err := db.GetConfig(app.DB)
if err := mixer.Init(app.Config.Load(), app.refreshCache); err != nil { if err != nil {
logger.Error("Unable to initialize audio device '%s': %v", app.Config.Load().SelectedAudioDevice, err) panic(err)
}
if err := mixer.Init(config, app.refreshCache); err != nil {
logger.Error("Unable to initialize audio device '%s': %v", config.SelectedAudioDevice, err)
logger.Error("Audio will be unavailable until a device is configured") logger.Error("Audio will be unavailable until a device is configured")
} else { } else {
logger.Info("Audio initialized with device: '%s'", app.Config.Load().SelectedAudioDevice) logger.Info("Audio initialized with device: '%s'", config.SelectedAudioDevice)
} }
} }
func (app *Application) refreshCache() {}
func (app *Application) cleanup() { func (app *Application) cleanup() {
logger.Info("Cleaning up") logger.Info("Cleaning up")
+22 -21
View File
@@ -57,7 +57,7 @@ func InitFontManager(cfg *db.AppConfig) error {
// --- NEW: Create the scrambled font after loading normal fonts --- // --- NEW: Create the scrambled font after loading normal fonts ---
// We will use "DejaVuSans" as the base. If you use a different default font, // We will use "DejaVuSans" as the base. If you use a different default font,
// change this string. // change this string.
baseFontForScramble := "Roboto-Regular" baseFontForScramble := "SpaceMonoNerdFont-Regular"
if _, ok := fm.Fonts[baseFontForScramble]; ok { if _, ok := fm.Fonts[baseFontForScramble]; ok {
if err := fm.CreateScrambledFont(baseFontForScramble, "Scrambled"); err != nil { if err := fm.CreateScrambledFont(baseFontForScramble, "Scrambled"); err != nil {
// This is not a fatal error for the whole application, so we just log it. // This is not a fatal error for the whole application, so we just log it.
@@ -506,26 +506,27 @@ func (fm *FontManager) createFontMap(font *FontEntry, scramble bool, baseFont *F
126, // ~ 126, // ~
// Common Unicode punctuation // Common Unicode punctuation
161, // ¡ 161, // ¡
191, // ¿ 191, // ¿
8211, // – 8211, // –
8212, // — 8212, // —
8216, // ‘ 8216, // ‘
8217, // ’ 8217, // ’
8220, // “ 8220, // “
8221, // ” 8221, // ”
8226, // • 8226, // •
8230, // … 8230, // …
8240, // ‰ 8240, // ‰
8364, // € 8364, // €
162, // ¢ 162, // ¢
163, // £ 163, // £
165, // ¥ 165, // ¥
169, // © 169, // ©
174, // ® 174, // ®
8482, // ™ 8482, // ™
232, // è 232, // è
233, // é 233, // é
10003, // checkmark
} }
tempGlyphs := make([]TempGlyph, 0, len(chars)) tempGlyphs := make([]TempGlyph, 0, len(chars))
var totalWidth, maxHeight int32 var totalWidth, maxHeight int32
+1 -1
View File
@@ -70,7 +70,7 @@ func Init(cfg *db.AppConfig, onUpdate func()) error {
return err return err
} }
SetAllSinkVolumes(1.0) SetAllSinkVolumes(0.20)
inited = true inited = true
return nil return nil
+41 -11
View File
@@ -100,7 +100,7 @@ func (p *AudioOptions) Init(ctx *PageCtx) error {
// Font // Font
fm := fontManager.Get() fm := fontManager.Get()
font, ok := fm.GetFont("Roboto-Regular") font, ok := fm.GetFont("SpaceMonoNerdFont-Regular")
if !ok { if !ok {
return fmt.Errorf("failed to get font: %w", sdl.GetError()) return fmt.Errorf("failed to get font: %w", sdl.GetError())
} }
@@ -121,7 +121,12 @@ func (p *AudioOptions) Init(ctx *PageCtx) error {
return fmt.Errorf("base init failed: %w", err) return fmt.Errorf("base init failed: %w", err)
} }
baseFontSize := p.Context.CfgPtr.StandardFontSize config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
baseFontSize := config.StandardFontSize
titleScale := float32(64) / float32(baseFontSize) titleScale := float32(64) / float32(baseFontSize)
p.ItemScale = float32(32) / float32(baseFontSize) p.ItemScale = float32(32) / float32(baseFontSize)
@@ -211,7 +216,11 @@ func (p *AudioOptions) seedAudioFiles() error {
// ── Audio status toggle ─────────────────────────────────────────────────────── // ── Audio status toggle ───────────────────────────────────────────────────────
func (p *AudioOptions) CreateAudioStatusItem() error { func (p *AudioOptions) CreateAudioStatusItem() error {
on := p.Context.CfgPtr.AudioEnabled config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
on := config.AudioEnabled
statusString := "Turn Audio On" statusString := "Turn Audio On"
color := colors.Green color := colors.Green
if on { if on {
@@ -277,7 +286,11 @@ func (p *AudioOptions) CreateVolumePercentItem() error {
if p.volumePercent.ID != 0 { if p.volumePercent.ID != 0 {
gl.DeleteTextures(1, &p.volumePercent.ID) gl.DeleteTextures(1, &p.volumePercent.ID)
} }
pct := int(p.Context.CfgPtr.SavedVolume * 100) config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
pct := int(config.SavedVolume * 100)
item, err := NewStringItem(fmt.Sprintf("%d%%", pct), p.font, colors.White) item, err := NewStringItem(fmt.Sprintf("%d%%", pct), p.font, colors.White)
if err != nil { if err != nil {
return fmt.Errorf("failed to create volume percent item: %v", err) return fmt.Errorf("failed to create volume percent item: %v", err)
@@ -288,15 +301,19 @@ func (p *AudioOptions) CreateVolumePercentItem() error {
} }
func (p *AudioOptions) UpdateVolume(level int) { func (p *AudioOptions) UpdateVolume(level int) {
newVol := mixer.ConvertVolume(level) + p.Context.CfgPtr.SavedVolume config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
newVol := mixer.ConvertVolume(level) + config.SavedVolume
if newVol < 0.0 || newVol > 1.0 { if newVol < 0.0 || newVol > 1.0 {
return return
} }
if newVol > .99 { if newVol > .99 {
newVol = 1.0 newVol = 1.0
} }
p.Context.CfgPtr.SavedVolume = newVol config.SavedVolume = newVol
p.Context.DbPtr.Save(&p.Context.CfgPtr) p.Context.DbPtr.Save(&config)
if err := mixer.SetAllSinkVolumes(newVol); err != nil { if err := mixer.SetAllSinkVolumes(newVol); err != nil {
logger.Error("Error setting allSinkVolumes: %v", err) logger.Error("Error setting allSinkVolumes: %v", err)
} }
@@ -311,6 +328,11 @@ func (p *AudioOptions) UpdateVolume(level int) {
// ── Layout ──────────────────────────────────────────────────────────────────── // ── Layout ────────────────────────────────────────────────────────────────────
func (p *AudioOptions) layout() { func (p *AudioOptions) layout() {
config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
// Title // Title
p.title.X = p.ScreenCenterX - p.title.W/2 p.title.X = p.ScreenCenterX - p.title.W/2
p.title.Y = p.ScreenHeight - p.title.H - p.verticalMargin*2 p.title.Y = p.ScreenHeight - p.title.H - p.verticalMargin*2
@@ -320,7 +342,7 @@ func (p *AudioOptions) layout() {
p.prompt.Y = p.title.Y - p.verticalMargin - p.prompt.H p.prompt.Y = p.title.Y - p.verticalMargin - p.prompt.H
// Volume row // Volume row
barH := int32(float32(p.Context.CfgPtr.StandardFontSize) * p.ItemScale) barH := int32(float32(config.StandardFontSize) * p.ItemScale)
barW := int32(float32(p.ScreenWidth) * 0.30) barW := int32(float32(p.ScreenWidth) * 0.30)
barX := p.ScreenCenterX - barW/2 barX := p.ScreenCenterX - barW/2
barY := p.prompt.Y - p.verticalMargin*2 - barH barY := p.prompt.Y - p.verticalMargin*2 - barH
@@ -376,8 +398,12 @@ func (p *AudioOptions) updateCursorPosition() {
// ── Toggle ──────────────────────────────────────────────────────────────────── // ── Toggle ────────────────────────────────────────────────────────────────────
func (p *AudioOptions) ToggleAudio() { func (p *AudioOptions) ToggleAudio() {
p.Context.CfgPtr.AudioEnabled = !p.Context.CfgPtr.AudioEnabled config, err := db.GetConfig(p.Context.DbPtr)
p.Context.DbPtr.Save(p.Context.CfgPtr) if err != nil {
panic(err)
}
config.AudioEnabled = !config.AudioEnabled
p.Context.DbPtr.Save(config)
p.Context.RefreshConfig() p.Context.RefreshConfig()
p.CreateAudioStatusItem() p.CreateAudioStatusItem()
p.layout() p.layout()
@@ -447,7 +473,11 @@ func (p *AudioOptions) Update(dt float32) error {
// ── Render helpers ──────────────────────────────────────────────────────────── // ── Render helpers ────────────────────────────────────────────────────────────
func (p *AudioOptions) renderVolumeBar() error { func (p *AudioOptions) renderVolumeBar() error {
vol := p.Context.CfgPtr.SavedVolume config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
vol := config.SavedVolume
fillW := float32(p.volumeBarW) * float32(vol) fillW := float32(p.volumeBarW) * float32(vol)
if fillW > 0 { if fillW > 0 {
+19 -7
View File
@@ -8,6 +8,7 @@ import (
"github.com/go-gl/mathgl/mgl32" "github.com/go-gl/mathgl/mgl32"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
"rhemawavetech.com/rhemawave/internal/colors" "rhemawavetech.com/rhemawave/internal/colors"
"rhemawavetech.com/rhemawave/internal/db"
"rhemawavetech.com/rhemawave/internal/fontManager" "rhemawavetech.com/rhemawave/internal/fontManager"
"rhemawavetech.com/rhemawave/internal/shaderManager" "rhemawavetech.com/rhemawave/internal/shaderManager"
) )
@@ -59,7 +60,7 @@ func (p *EmailAddressPage) Init(ctx *PageCtx) error {
p.title = "Your Email Address" p.title = "Your Email Address"
applicationFont := "Roboto-Regular" applicationFont := "SpaceMonoNerdFont-Regular"
fm := fontManager.Get() fm := fontManager.Get()
textShader, ok := shaderManager.Get().Get("text") textShader, ok := shaderManager.Get().Get("text")
@@ -81,7 +82,11 @@ func (p *EmailAddressPage) Init(ctx *PageCtx) error {
p.titleTextureHeight = titleHeight p.titleTextureHeight = titleHeight
// Load existing email address from database // Load existing email address from database
emailAddress := ctx.CfgPtr.EmailAddress config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
emailAddress := config.EmailAddress
if emailAddress != "" { if emailAddress != "" {
p.inputText = emailAddress p.inputText = emailAddress
@@ -154,9 +159,12 @@ func (p *EmailAddressPage) HandleEvent(event *sdl.Event) error {
sdl.StopTextInput() sdl.StopTextInput()
// Save email address to database // Save email address to database
p.Context.CfgPtr.EmailAddress = p.inputText config, err := db.GetConfig(p.Context.DbPtr)
p.Context.DbPtr.Save(p.Context.CfgPtr) if err != nil {
p.Context.RefreshConfig() panic(err)
}
config.EmailAddress = p.inputText
p.Context.DbPtr.Save(config)
if p.isFirstTimeSetup { if p.isFirstTimeSetup {
// First-time setup: navigate to License Key page // First-time setup: navigate to License Key page
@@ -189,7 +197,7 @@ func (p *EmailAddressPage) HandleEvent(event *sdl.Event) error {
// Update updates animations and changing textures and other changes using `dt` which represents a time delta in float32. // Update updates animations and changing textures and other changes using `dt` which represents a time delta in float32.
func (p *EmailAddressPage) Update(dt float32) error { func (p *EmailAddressPage) Update(dt float32) error {
// Get application font from database // Get application font from database
applicationFont := "Roboto-Regular" applicationFont := "SpaceMonoNerdFont-Regular"
fm := fontManager.Get() fm := fontManager.Get()
sm := shaderManager.Get() sm := shaderManager.Get()
@@ -252,8 +260,12 @@ func (p *EmailAddressPage) Update(dt float32) error {
// Render performs page drawing logic. // Render performs page drawing logic.
func (p *EmailAddressPage) Render() error { func (p *EmailAddressPage) Render() error {
config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
// Get base font size from database // Get base font size from database
baseFontSize := p.Context.CfgPtr.StandardFontSize baseFontSize := config.StandardFontSize
sm := shaderManager.Get() sm := shaderManager.Get()
+13 -7
View File
@@ -7,6 +7,7 @@ import (
"github.com/go-gl/mathgl/mgl32" "github.com/go-gl/mathgl/mgl32"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
"rhemawavetech.com/rhemawave/internal/colors" "rhemawavetech.com/rhemawave/internal/colors"
"rhemawavetech.com/rhemawave/internal/db"
"rhemawavetech.com/rhemawave/internal/download" "rhemawavetech.com/rhemawave/internal/download"
"rhemawavetech.com/rhemawave/internal/fontManager" "rhemawavetech.com/rhemawave/internal/fontManager"
"rhemawavetech.com/rhemawave/internal/shaderManager" "rhemawavetech.com/rhemawave/internal/shaderManager"
@@ -45,7 +46,7 @@ func (p *Home) Init(ctx *PageCtx) error {
// Get font // Get font
p.fm = fontManager.Get() p.fm = fontManager.Get()
font, ok := p.fm.GetFont("Roboto-Regular") font, ok := p.fm.GetFont("SpaceMonoNerdFont-Regular")
if !ok { if !ok {
return fmt.Errorf("unable to fetch font") return fmt.Errorf("unable to fetch font")
} }
@@ -86,8 +87,13 @@ func (p *Home) Init(ctx *PageCtx) error {
return fmt.Errorf("unable to create initial message texture: %w", err) return fmt.Errorf("unable to create initial message texture: %w", err)
} }
config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
// Create license info textures if data exists // Create license info textures if data exists
if p.Context.CfgPtr.EmailAddress != "" || p.Context.CfgPtr.LicenseKey != "" { if config.EmailAddress != "" || config.LicenseKey != "" {
p.hasLicenseInfo = true p.hasLicenseInfo = true
// Create heading // Create heading
@@ -97,8 +103,8 @@ func (p *Home) Init(ctx *PageCtx) error {
} }
// Create email display if exists // Create email display if exists
if p.Context.CfgPtr.EmailAddress != "" { if config.EmailAddress != "" {
emailText := fmt.Sprintf("Email: %s", p.Context.CfgPtr.EmailAddress) emailText := fmt.Sprintf("Email: %s", config.EmailAddress)
p.emailDisplay, err = NewStringItem(emailText, font, colors.White) p.emailDisplay, err = NewStringItem(emailText, font, colors.White)
if err != nil { if err != nil {
return fmt.Errorf("unable to create email display texture: %w", err) return fmt.Errorf("unable to create email display texture: %w", err)
@@ -106,8 +112,8 @@ func (p *Home) Init(ctx *PageCtx) error {
} }
// Create license key display if exists // Create license key display if exists
if p.Context.CfgPtr.LicenseKey != "" { if config.LicenseKey != "" {
licenseKeyText := fmt.Sprintf("License Key: %s", p.Context.CfgPtr.LicenseKey) licenseKeyText := fmt.Sprintf("License Key: %s", config.LicenseKey)
p.licenseKeyDisplay, err = NewStringItem(licenseKeyText, font, colors.White) p.licenseKeyDisplay, err = NewStringItem(licenseKeyText, font, colors.White)
if err != nil { if err != nil {
return fmt.Errorf("unable to create license key display texture: %w", err) return fmt.Errorf("unable to create license key display texture: %w", err)
@@ -116,7 +122,7 @@ func (p *Home) Init(ctx *PageCtx) error {
} }
// Get base font size from database // Get base font size from database
baseFontSize := p.Context.CfgPtr.StandardFontSize baseFontSize := config.StandardFontSize
p.baseFontSize = baseFontSize p.baseFontSize = baseFontSize
// Scale the note text // Scale the note text
+19 -7
View File
@@ -10,6 +10,7 @@ import (
"github.com/go-gl/mathgl/mgl32" "github.com/go-gl/mathgl/mgl32"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
"rhemawavetech.com/rhemawave/internal/colors" "rhemawavetech.com/rhemawave/internal/colors"
"rhemawavetech.com/rhemawave/internal/db"
"rhemawavetech.com/rhemawave/internal/fontManager" "rhemawavetech.com/rhemawave/internal/fontManager"
"rhemawavetech.com/rhemawave/internal/logger" "rhemawavetech.com/rhemawave/internal/logger"
"rhemawavetech.com/rhemawave/internal/shaderManager" "rhemawavetech.com/rhemawave/internal/shaderManager"
@@ -55,7 +56,7 @@ func (p *LicenseKeyPage) Init(ctx *PageCtx) error {
p.title = "License Key" p.title = "License Key"
// Get application font from database // Get application font from database
applicationFont := "Roboto-Regular" applicationFont := "SpaceMonoNerdFont-Regular"
fm := fontManager.Get() fm := fontManager.Get()
textShader, ok := shaderManager.Get().Get("text") textShader, ok := shaderManager.Get().Get("text")
@@ -77,7 +78,11 @@ func (p *LicenseKeyPage) Init(ctx *PageCtx) error {
p.titleTextureHeight = titleHeight p.titleTextureHeight = titleHeight
// Load existing license key from database // Load existing license key from database
licenseKey := p.Context.CfgPtr.LicenseKey config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
licenseKey := config.LicenseKey
if licenseKey != "" { if licenseKey != "" {
p.inputText = licenseKey p.inputText = licenseKey
@@ -188,9 +193,12 @@ func (p *LicenseKeyPage) HandleEvent(event *sdl.Event) error {
sdl.StopTextInput() sdl.StopTextInput()
// Save license key to database // Save license key to database
p.Context.CfgPtr.LicenseKey = p.inputText config, err := db.GetConfig(p.Context.DbPtr)
p.Context.DbPtr.Save(p.Context.CfgPtr) if err != nil {
p.Context.RefreshConfig() panic(err)
}
config.LicenseKey = p.inputText
p.Context.DbPtr.Save(config)
// Write license key to plain text file for the updater // Write license key to plain text file for the updater
if homeDir, err := os.UserHomeDir(); err == nil { if homeDir, err := os.UserHomeDir(); err == nil {
@@ -239,7 +247,7 @@ func (p *LicenseKeyPage) HandleEvent(event *sdl.Event) error {
// Update updates animations and changing textures and other changes using `dt` which represents a time delta in float32. // Update updates animations and changing textures and other changes using `dt` which represents a time delta in float32.
func (p *LicenseKeyPage) Update(dt float32) error { func (p *LicenseKeyPage) Update(dt float32) error {
// Get application font from database // Get application font from database
applicationFont := "Roboto-Regular" applicationFont := "SpaceMonoNerdFont-Regular"
fm := fontManager.Get() fm := fontManager.Get()
sm := shaderManager.Get() sm := shaderManager.Get()
@@ -302,8 +310,12 @@ func (p *LicenseKeyPage) Update(dt float32) error {
// Render performs page drawing logic. // Render performs page drawing logic.
func (p *LicenseKeyPage) Render() error { func (p *LicenseKeyPage) Render() error {
config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
// Get base font size from database // Get base font size from database
baseFontSize := p.Context.CfgPtr.StandardFontSize baseFontSize := config.StandardFontSize
sm := shaderManager.Get() sm := shaderManager.Get()
+6 -2
View File
@@ -79,7 +79,7 @@ func (p *LogViewerPage) Init(ctx *PageCtx) error {
p.lineColors = append(p.lineColors, color) p.lineColors = append(p.lineColors, color)
} }
applicationFont := "Roboto-Regular" applicationFont := "SpaceMonoNerdFont-Regular"
fm := fontManager.Get() fm := fontManager.Get()
textShader, ok := shaderManager.Get().Get("text") textShader, ok := shaderManager.Get().Get("text")
@@ -177,7 +177,11 @@ func (p *LogViewerPage) Update(dt float32) error {
} }
func (p *LogViewerPage) Render() error { func (p *LogViewerPage) Render() error {
baseFontSize := p.Context.CfgPtr.StandardFontSize config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
baseFontSize := config.StandardFontSize
sm := shaderManager.Get() sm := shaderManager.Get()
textShader, _ := sm.Get("text") textShader, _ := sm.Get("text")
+24 -6
View File
@@ -105,7 +105,7 @@ func (p *ProclamationOptions) Init(ctx *PageCtx) error {
sync.SyncFrequencies(ctx.DbPtr) sync.SyncFrequencies(ctx.DbPtr)
fm := fontManager.Get() fm := fontManager.Get()
font, _ := fm.GetFont("Roboto-Regular") font, _ := fm.GetFont("SpaceMonoNerdFont-Regular")
p.font = font p.font = font
sm := shaderManager.Get() sm := shaderManager.Get()
@@ -114,9 +114,14 @@ func (p *ProclamationOptions) Init(ctx *PageCtx) error {
p.Base.Init(ctx) p.Base.Init(ctx)
itemScale := float32(32) / float32(ctx.CfgPtr.StandardFontSize) config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
itemScale := float32(32) / float32(config.StandardFontSize)
p.title, _ = NewStringItem("Proclamation Browser", p.font, colors.White) p.title, _ = NewStringItem("Proclamation Browser", p.font, colors.White)
p.title.Scale(float32(64) / float32(ctx.CfgPtr.StandardFontSize)) p.title.Scale(float32(64) / float32(config.StandardFontSize))
p.prompt, _ = NewStringItem("Type to Search Space to Select Enter to Save Escape to Exit", p.font, colors.White) p.prompt, _ = NewStringItem("Type to Search Space to Select Enter to Save Escape to Exit", p.font, colors.White)
p.prompt.Scale(itemScale * 0.8) p.prompt.Scale(itemScale * 0.8)
@@ -183,7 +188,11 @@ func (p *ProclamationOptions) createFlatItem(name, id string, isCat bool, depth
if isCat && depth == 0 { if isCat && depth == 0 {
text = "[+] " + name text = "[+] " + name
} }
itemScale := float32(32) / float32(p.Context.CfgPtr.StandardFontSize) config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
itemScale := float32(32) / float32(config.StandardFontSize)
lbl, _ := NewStringItem(text, p.font, colors.White) lbl, _ := NewStringItem(text, p.font, colors.White)
lbl.Scale(itemScale) lbl.Scale(itemScale)
return &FlatItem{ return &FlatItem{
@@ -277,7 +286,12 @@ func (p *ProclamationOptions) toggleExpansion(index int) {
pureName := item.Label.content[4:] pureName := item.Label.content[4:]
gl.DeleteTextures(1, &item.Label.ID) gl.DeleteTextures(1, &item.Label.ID)
itemScale := float32(32) / float32(p.Context.CfgPtr.StandardFontSize) config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
itemScale := float32(32) / float32(config.StandardFontSize)
if item.Expanded { if item.Expanded {
p.collapse(index) p.collapse(index)
item.Label, _ = NewStringItem("[+] "+pureName, p.font, colors.White) item.Label, _ = NewStringItem("[+] "+pureName, p.font, colors.White)
@@ -401,7 +415,11 @@ func (p *ProclamationOptions) rebuildSearchLabel() {
if p.searchQuery == "" { if p.searchQuery == "" {
return return
} }
itemScale := float32(32) / float32(p.Context.CfgPtr.StandardFontSize) config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
itemScale := float32(32) / float32(config.StandardFontSize)
p.searchInput, _ = NewStringItem("Search: "+p.searchQuery, p.font, colors.Yellow) p.searchInput, _ = NewStringItem("Search: "+p.searchQuery, p.font, colors.Yellow)
p.searchInput.Scale(itemScale * 1.1) p.searchInput.Scale(itemScale * 1.1)
} }
-2
View File
@@ -2,7 +2,6 @@ package page
import ( import (
"gorm.io/gorm" "gorm.io/gorm"
"rhemawavetech.com/rhemawave/internal/db"
"rhemawavetech.com/rhemawave/internal/download" "rhemawavetech.com/rhemawave/internal/download"
) )
@@ -23,7 +22,6 @@ const (
) )
type PageCtx struct { type PageCtx struct {
CfgPtr *db.AppConfig
DbPtr *gorm.DB DbPtr *gorm.DB
W, H int32 W, H int32
MessageChannel chan download.DownloadStatus MessageChannel chan download.DownloadStatus
+25 -10
View File
@@ -220,8 +220,11 @@ func (p *ScrollerPage) Init(ctx *PageCtx) error {
} }
// Get line pattern from database // Get line pattern from database
var err error config, err := db.GetConfig(p.Context.DbPtr)
linePatternName := p.Context.CfgPtr.LinePattern if err != nil {
panic(err)
}
linePatternName := config.LinePattern
p.activePattern, err = linePattern.Get(linePatternName) p.activePattern, err = linePattern.Get(linePatternName)
if err != nil { if err != nil {
@@ -241,7 +244,7 @@ func (p *ScrollerPage) Init(ctx *PageCtx) error {
p.colorPattern = colorPattern.NewColorPattern(baseColors) p.colorPattern = colorPattern.NewColorPattern(baseColors)
// Font setup // Font setup
p.fontScale = float32(32) / float32(p.Context.CfgPtr.StandardFontSize) p.fontScale = float32(32) / float32(config.StandardFontSize)
nLines := int(float32(p.ScreenHeight) / 32.0) nLines := int(float32(p.ScreenHeight) / 32.0)
p.lines = NewLineDeque(nLines * 2) p.lines = NewLineDeque(nLines * 2)
@@ -288,14 +291,14 @@ func (p *ScrollerPage) Init(ctx *PageCtx) error {
if err := p.Context.DbPtr.Where("Selected = ?", true).First(&selectedAudio).Error; err != nil { if err := p.Context.DbPtr.Where("Selected = ?", true).First(&selectedAudio).Error; err != nil {
selectedAudio = db.AudioFile{ selectedAudio = db.AudioFile{
Name: "OriginalAudio", Name: "OriginalAudio",
Path: filepath.Join(p.Context.CfgPtr.AssetsDir, "audio", "OriginalAudio.wav"), Path: filepath.Join(config.AssetsDir, "audio", "OriginalAudio.wav"),
Selected: true, Selected: true,
Order: 0, Order: 0,
} }
} }
logger.Info("Playing %s", selectedAudio.Path) logger.Info("Playing %s", selectedAudio.Path)
if err := mixer.Play(selectedAudio.Path, true, p.Context.CfgPtr); err != nil { if err := mixer.Play(selectedAudio.Path, true, config); err != nil {
logger.Error("Mixer returned an error: %v", err) logger.Error("Mixer returned an error: %v", err)
} }
} }
@@ -420,7 +423,11 @@ func (p *ScrollerPage) loadProclamations() error {
// audioPathForFrequency builds the on-disk path for a solfeggio frequency's // audioPathForFrequency builds the on-disk path for a solfeggio frequency's
// audio file, e.g. 396 -> "<AssetsDir>/audio/396Hz.wav". // audio file, e.g. 396 -> "<AssetsDir>/audio/396Hz.wav".
func (p *ScrollerPage) audioPathForFrequency(freq int) string { func (p *ScrollerPage) audioPathForFrequency(freq int) string {
return filepath.Join(p.Context.CfgPtr.AssetsDir, "audio", fmt.Sprintf("%dHz.wav", freq)) config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
return filepath.Join(config.AssetsDir, "audio", fmt.Sprintf("%dHz.wav", freq))
} }
// switchAudioForFrequency swaps the currently playing track to match freq. // switchAudioForFrequency swaps the currently playing track to match freq.
@@ -449,9 +456,12 @@ func (p *ScrollerPage) switchAudioForFrequency(freq int) bool {
logger.Warning("No audio file for %dHz (%s), keeping current track", freq, path) logger.Warning("No audio file for %dHz (%s), keeping current track", freq, path)
return false return false
} }
config, err := db.GetConfig(p.Context.DbPtr)
mixer.Stop(3000, p.Context.CfgPtr) if err != nil {
if err := mixer.Play(path, true, p.Context.CfgPtr); err != nil { panic(err)
}
mixer.Stop(3000, config)
if err := mixer.Play(path, true, config); err != nil {
logger.Error("Mixer returned an error: %v", err) logger.Error("Mixer returned an error: %v", err)
return false return false
} }
@@ -722,7 +732,12 @@ func (p *ScrollerPage) Destroy() error {
return nil return nil
} }
mixer.Stop(3000, p.Context.CfgPtr) config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
mixer.Stop(3000, config)
// Delete each cached word texture exactly once. // Delete each cached word texture exactly once.
for k, item := range p.wordTextureCache { for k, item := range p.wordTextureCache {
+7 -2
View File
@@ -6,6 +6,7 @@ import (
"github.com/go-gl/gl/v3.3-core/gl" "github.com/go-gl/gl/v3.3-core/gl"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
"rhemawavetech.com/rhemawave/internal/colors" "rhemawavetech.com/rhemawave/internal/colors"
"rhemawavetech.com/rhemawave/internal/db"
"rhemawavetech.com/rhemawave/internal/fontManager" "rhemawavetech.com/rhemawave/internal/fontManager"
"rhemawavetech.com/rhemawave/internal/shaderManager" "rhemawavetech.com/rhemawave/internal/shaderManager"
) )
@@ -34,7 +35,7 @@ func (p *Settings) Init(ctx *PageCtx) error {
// Get font // Get font
fm := fontManager.Get() fm := fontManager.Get()
font, ok := fm.GetFont("Roboto-Regular") font, ok := fm.GetFont("SpaceMonoNerdFont-Regular")
if !ok { if !ok {
return fmt.Errorf("failed to get font: %w", sdl.GetError()) return fmt.Errorf("failed to get font: %w", sdl.GetError())
} }
@@ -127,7 +128,11 @@ func (p *Settings) Init(ctx *PageCtx) error {
// TODO: Add entry for "About" // TODO: Add entry for "About"
// Get base font size from database // Get base font size from database
baseFontSize := p.Context.CfgPtr.StandardFontSize config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
baseFontSize := config.StandardFontSize
// Calculate scales // Calculate scales
titleScale := float32(64) / float32(baseFontSize) titleScale := float32(64) / float32(baseFontSize)
+29 -10
View File
@@ -8,6 +8,7 @@ import (
"github.com/go-gl/mathgl/mgl32" "github.com/go-gl/mathgl/mgl32"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
"rhemawavetech.com/rhemawave/internal/colors" "rhemawavetech.com/rhemawave/internal/colors"
"rhemawavetech.com/rhemawave/internal/db"
"rhemawavetech.com/rhemawave/internal/fontManager" "rhemawavetech.com/rhemawave/internal/fontManager"
"rhemawavetech.com/rhemawave/internal/keybinds" "rhemawavetech.com/rhemawave/internal/keybinds"
"rhemawavetech.com/rhemawave/internal/logger" "rhemawavetech.com/rhemawave/internal/logger"
@@ -39,6 +40,7 @@ type WiFiSetupPage struct {
applicationFont *fontManager.FontEntry applicationFont *fontManager.FontEntry
// UI elements // UI elements
BaseFontSize int
titleItem StringItem titleItem StringItem
scanningStatusItem StringItem scanningStatusItem StringItem
connectionStatusItem StringItem connectionStatusItem StringItem
@@ -75,9 +77,9 @@ func (p *WiFiSetupPage) Init(ctx *PageCtx) error {
} }
fm := fontManager.Get() fm := fontManager.Get()
font, ok := fm.GetFont("Roboto-Regular") font, ok := fm.GetFont("SpaceMonoNerdFont-Regular")
if !ok { if !ok {
return fmt.Errorf("could not find application font: Roboto-Regular") return fmt.Errorf("could not find application font: SpaceMonoNerdFont-Regular")
} }
p.applicationFont = font p.applicationFont = font
@@ -92,6 +94,12 @@ func (p *WiFiSetupPage) Init(ctx *PageCtx) error {
return fmt.Errorf("failed to create scanning status item: %w", err) return fmt.Errorf("failed to create scanning status item: %w", err)
} }
config, err := db.GetConfig(p.Context.DbPtr)
if err != nil {
panic(err)
}
p.BaseFontSize = config.StandardFontSize
p.connectResultCh = make(chan error, 1) p.connectResultCh = make(chan error, 1)
p.ctx, p.cancel = context.WithCancel(context.Background()) p.ctx, p.cancel = context.WithCancel(context.Background())
@@ -310,7 +318,20 @@ func (p *WiFiSetupPage) rebuildNetworkList(nets []network.Network) {
label += " ★" label += " ★"
} }
if n.Security != network.SecurityOpen { if n.Security != network.SecurityOpen {
label += " [" + string(n.Security) + "]" var secStr string
switch n.Security {
case network.SecurityWEP:
secStr = "WEP"
case network.SecurityWPA:
secStr = "WPA"
case network.SecurityWPA2:
secStr = "WPA2"
case network.SecurityWPA3:
secStr = "WPA3"
default:
secStr = "Secured"
}
label += " [" + secStr + "]"
} }
c := colors.White c := colors.White
@@ -417,8 +438,6 @@ func (p *WiFiSetupPage) rebuildPasswordErrorTexture() {
// ─── Render ─────────────────────────────────────────────────────────────────── // ─── Render ───────────────────────────────────────────────────────────────────
func (p *WiFiSetupPage) Render() error { func (p *WiFiSetupPage) Render() error {
baseFontSize := p.Context.CfgPtr.StandardFontSize
solidShader, _ := shaderManager.Get().Get("solid_color") solidShader, _ := shaderManager.Get().Get("solid_color")
textShader, _ := shaderManager.Get().Get("text") textShader, _ := shaderManager.Get().Get("text")
@@ -435,7 +454,7 @@ func (p *WiFiSetupPage) Render() error {
) )
// Title — 48pt // Title — 48pt
titleScale := float32(48) / float32(baseFontSize) titleScale := float32(48) / float32(p.BaseFontSize)
titleW := float32(p.titleItem.W) * titleScale titleW := float32(p.titleItem.W) * titleScale
titleH := float32(p.titleItem.H) * titleScale titleH := float32(p.titleItem.H) * titleScale
titleX := float32(p.ScreenCenterX) - titleW/2 titleX := float32(p.ScreenCenterX) - titleW/2
@@ -449,7 +468,7 @@ func (p *WiFiSetupPage) Render() error {
// Status message — 30pt (was 24pt, now 1.25x) // Status message — 30pt (was 24pt, now 1.25x)
statusY := titleY - titleH - vertMargin statusY := titleY - titleH - vertMargin
if p.status == wifiStatusScanning { if p.status == wifiStatusScanning {
scale := float32(30) / float32(baseFontSize) scale := float32(30) / float32(p.BaseFontSize)
w := float32(p.scanningStatusItem.W) * scale w := float32(p.scanningStatusItem.W) * scale
h := float32(p.scanningStatusItem.H) * scale h := float32(p.scanningStatusItem.H) * scale
x := float32(p.ScreenCenterX) - w/2 x := float32(p.ScreenCenterX) - w/2
@@ -459,7 +478,7 @@ func (p *WiFiSetupPage) Render() error {
white, white,
) )
} else if p.connectionStatusItem.ID != 0 { } else if p.connectionStatusItem.ID != 0 {
scale := float32(30) / float32(baseFontSize) scale := float32(30) / float32(p.BaseFontSize)
w := float32(p.connectionStatusItem.W) * scale w := float32(p.connectionStatusItem.W) * scale
h := float32(p.connectionStatusItem.H) * scale h := float32(p.connectionStatusItem.H) * scale
x := float32(p.ScreenCenterX) - w/2 x := float32(p.ScreenCenterX) - w/2
@@ -488,7 +507,7 @@ func (p *WiFiSetupPage) Render() error {
) )
} }
scale := float32(24) / float32(baseFontSize) scale := float32(24) / float32(p.BaseFontSize)
tw := float32(item.Item.W) * scale tw := float32(item.Item.W) * scale
th := float32(item.Item.H) * scale th := float32(item.Item.H) * scale
tx := listX + float32(listPad) tx := listX + float32(listPad)
@@ -502,7 +521,7 @@ func (p *WiFiSetupPage) Render() error {
// Password dialog // Password dialog
if p.isEnteringPassword { if p.isEnteringPassword {
if err := p.renderPasswordDialog(solidShader, textShader, float32(baseFontSize)); err != nil { if err := p.renderPasswordDialog(solidShader, textShader, float32(p.BaseFontSize)); err != nil {
return err return err
} }
} }
Binary file not shown.