201 lines
5.6 KiB
Go
201 lines
5.6 KiB
Go
package page
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"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/logger"
|
|
"rhemawavetech.com/rhemawave/internal/mixer"
|
|
"rhemawavetech.com/rhemawave/internal/shaderManager"
|
|
)
|
|
|
|
type AudioDevices struct {
|
|
Base
|
|
font *fontManager.FontEntry
|
|
textShader *shaderManager.Shader
|
|
solidShader *shaderManager.Shader
|
|
title StringItem
|
|
prompt StringItem
|
|
AudioStatusToggle StringItem
|
|
verticalMargin int32
|
|
ItemScale float32
|
|
CfgPtr *db.AppConfig
|
|
}
|
|
|
|
func (p *AudioDevices) Init(ctx *PageCtx) error {
|
|
var err error
|
|
p.verticalMargin = int32(20)
|
|
|
|
// Get font
|
|
fm := fontManager.Get()
|
|
font, ok := fm.GetFont("Roboto-Regular")
|
|
if !ok {
|
|
return fmt.Errorf("failed to get font: %w", sdl.GetError())
|
|
}
|
|
p.font = font
|
|
|
|
// Get shaders
|
|
sm := shaderManager.Get()
|
|
p.textShader, ok = sm.Get("text")
|
|
if !ok {
|
|
return fmt.Errorf("failed to get text shader: %w", sdl.GetError())
|
|
}
|
|
p.solidShader, ok = sm.Get("solid_color")
|
|
if !ok {
|
|
return fmt.Errorf("failed to get solid_color shader: %w", sdl.GetError())
|
|
}
|
|
|
|
// Initialize base
|
|
if err := p.Base.Init(ctx); err != nil {
|
|
return fmt.Errorf("base init failed: %w", err)
|
|
}
|
|
|
|
// Get font size from database
|
|
baseFontSize := p.Context.CfgPtr.StandardFontSize
|
|
|
|
// Calculate scales
|
|
titleScale := float32(64) / float32(baseFontSize)
|
|
p.ItemScale = float32(32) / float32(baseFontSize)
|
|
|
|
// Create title
|
|
p.title, err = NewStringItem("Toggle Audio", p.font, colors.White)
|
|
if err != nil {
|
|
return fmt.Errorf("creating title: %w", err)
|
|
}
|
|
p.title.Scale(titleScale)
|
|
|
|
// Create prompt
|
|
p.prompt, err = NewStringItem("Switch On/Off with Enter. Setting saves immediately.", p.font, colors.White)
|
|
if err != nil {
|
|
return fmt.Errorf("creating prompt: %w", err)
|
|
}
|
|
p.prompt.Scale(p.ItemScale)
|
|
|
|
if err = p.CreateAudioStatusItem(); err != nil {
|
|
logger.Error("failed to create AudioStatusItem in AudioDevices.Init()")
|
|
}
|
|
|
|
p.layout()
|
|
return nil
|
|
}
|
|
|
|
func (p *AudioDevices) CreateAudioStatusItem() error {
|
|
on := p.Context.CfgPtr.AudioEnabled
|
|
statusString := "Turn Audio Off" // The audio is ON
|
|
color := colors.Green
|
|
if !on {
|
|
statusString = "Turn Audio on"
|
|
color = colors.Red
|
|
}
|
|
audioStatusStringItem, err := NewStringItem(statusString, p.font, color)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create '%s' string item: %v", statusString, err)
|
|
}
|
|
fmt.Printf("AudioStatusToggle (%d)", audioStatusStringItem.ID)
|
|
audioStatusStringItem.Scale(p.ItemScale)
|
|
p.AudioStatusToggle = audioStatusStringItem
|
|
|
|
// Position the audio toggle
|
|
p.AudioStatusToggle.X = p.ScreenCenterX - p.AudioStatusToggle.W/2 // Horizontal Position
|
|
p.AudioStatusToggle.Y = p.ScreenCenterY + (p.AudioStatusToggle.H / 2) // Vertical Position
|
|
return nil
|
|
}
|
|
|
|
func (p *AudioDevices) layout() {
|
|
// Position title at the top
|
|
p.title.X = p.ScreenCenterX - p.title.W/2
|
|
p.title.Y = p.ScreenHeight - p.title.H - p.verticalMargin*2
|
|
|
|
// Position prompt below the title
|
|
p.prompt.X = p.ScreenCenterX - p.prompt.W/2
|
|
p.prompt.Y = p.title.Y - p.verticalMargin*2 - p.prompt.H
|
|
}
|
|
|
|
func (p *AudioDevices) ToggleAudio() {
|
|
// Toggle current setting
|
|
p.Context.CfgPtr.AudioEnabled = !p.Context.CfgPtr.AudioEnabled
|
|
|
|
// Update the database
|
|
p.Context.DbPtr.Save(p.Context.CfgPtr)
|
|
|
|
// Update the configuration
|
|
p.Context.RefreshConfig()
|
|
|
|
// Update AudioEnabled UI element
|
|
p.CreateAudioStatusItem()
|
|
}
|
|
|
|
// UpdateVolume increments or decrements system volume by level (percent change)
|
|
func (p *AudioDevices) UpdateVolume(level int) {
|
|
// Calculate the new volume
|
|
newVol := mixer.ConvertVolume(level) + p.Context.CfgPtr.SavedVolume
|
|
// Check that it's within bounds
|
|
if newVol < 0.0 || newVol > 1.0 {
|
|
return
|
|
}
|
|
// Force 1.0 rather than 0.99999999999999989
|
|
if newVol > .99 {
|
|
newVol = 1.0
|
|
}
|
|
// Update the config
|
|
p.Context.CfgPtr.SavedVolume = newVol
|
|
fmt.Printf("New Volume: %f", newVol)
|
|
// Update the database
|
|
p.Context.DbPtr.Save(&p.Context.CfgPtr)
|
|
// Set the new volume
|
|
err := mixer.SetAllSinkVolumes(newVol)
|
|
if err != nil {
|
|
fmt.Printf("Error setting allSinkVolumes: %v", err)
|
|
}
|
|
|
|
}
|
|
|
|
func (p *AudioDevices) HandleEvent(event *sdl.Event) error {
|
|
e, ok := (*event).(*sdl.KeyboardEvent)
|
|
if !ok || e.Type != sdl.KEYDOWN {
|
|
return nil
|
|
}
|
|
|
|
switch e.Keysym.Sym {
|
|
case sdl.K_RETURN:
|
|
p.ToggleAudio()
|
|
case sdl.K_RIGHT: // Right increase
|
|
p.UpdateVolume(10)
|
|
case sdl.K_LEFT: // Left decrease
|
|
p.UpdateVolume(-10)
|
|
case sdl.K_BACKSPACE:
|
|
p.Context.NavigateTo(SETTINGS)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *AudioDevices) Render() error {
|
|
if err := p.RenderTexture(p.textShader, p.title.ID, p.title.Position(), p.title.RawDimensions(), p.title.Color); err != nil {
|
|
logger.Error("unable to render title texture: %v", err)
|
|
return fmt.Errorf("unable to render title texture: %w", err)
|
|
}
|
|
|
|
if err := p.RenderTexture(p.textShader, p.prompt.ID, p.prompt.Position(), p.prompt.RawDimensions(), p.prompt.Color); err != nil {
|
|
logger.Error("unable to render prompt texture: %v", err)
|
|
return fmt.Errorf("unable to render prompt texture: %w", err)
|
|
}
|
|
|
|
if err := p.RenderTexture(p.textShader, p.AudioStatusToggle.ID, p.AudioStatusToggle.Position(), p.AudioStatusToggle.RawDimensions(), p.AudioStatusToggle.Color); err != nil {
|
|
logger.Error("unable to render audioStatusToggle texture: %v", err)
|
|
return fmt.Errorf("unable to render audioStatusToggle texture: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *AudioDevices) Destroy() error {
|
|
gl.DeleteTextures(1, &p.title.ID)
|
|
gl.DeleteTextures(1, &p.prompt.ID)
|
|
return nil
|
|
}
|