chore: Add basic config creation and reading

This commit is contained in:
Tera << 8 2025-06-22 12:28:00 -04:00
parent a88b75a4bc
commit d0a4d26082
Signed by: imterah
GPG key ID: 8FA7DD57BA6CEA37
9 changed files with 263 additions and 2 deletions

80
app/config.go Normal file
View file

@ -0,0 +1,80 @@
package main
import _ "embed"
//go:embed default_config.yml
var InitialConfig []byte
type DisplayConfig struct {
Angle *int `yaml:"angle"`
FOV *int `yaml:"fov"`
Spacing *int `yaml:"spacing"`
Count *int `yaml:"count"`
}
type AppOverrides struct {
AllowUnsupportedDevices *bool `yaml:"allow_unsupported_devices"`
OverrideWidth *int `yaml:"width"`
OverrideHeight *int `yaml:"height"`
OverrideRefreshRate *int `yaml:"refresh_rate"`
}
type Config struct {
DisplayConfig DisplayConfig `yaml:"display"`
Overrides AppOverrides `yaml:"overrides"`
}
func getPtrToInt(int int) *int {
return &int
}
func getPtrToBool(bool bool) *bool {
return &bool
}
var DefaultConfig = &Config{
DisplayConfig: DisplayConfig{
Angle: getPtrToInt(45),
FOV: getPtrToInt(45),
Spacing: getPtrToInt(1),
Count: getPtrToInt(3),
},
Overrides: AppOverrides{
AllowUnsupportedDevices: getPtrToBool(false),
},
}
func InitializePotentiallyMissingConfigValues(config *Config) {
// TODO: is there a better way to do this?
if config.DisplayConfig.Angle == nil {
config.DisplayConfig.Angle = DefaultConfig.DisplayConfig.Angle
}
if config.DisplayConfig.FOV == nil {
config.DisplayConfig.FOV = DefaultConfig.DisplayConfig.FOV
}
if config.DisplayConfig.Spacing == nil {
config.DisplayConfig.Spacing = DefaultConfig.DisplayConfig.Spacing
}
if config.DisplayConfig.Count == nil {
config.DisplayConfig.Count = DefaultConfig.DisplayConfig.Count
}
if config.Overrides.AllowUnsupportedDevices == nil {
config.Overrides.AllowUnsupportedDevices = DefaultConfig.Overrides.AllowUnsupportedDevices
}
if config.Overrides.OverrideWidth == nil {
config.Overrides.OverrideWidth = DefaultConfig.Overrides.OverrideWidth
}
if config.Overrides.OverrideHeight == nil {
config.Overrides.OverrideHeight = DefaultConfig.Overrides.OverrideHeight
}
if config.Overrides.OverrideRefreshRate == nil {
config.Overrides.OverrideRefreshRate = DefaultConfig.Overrides.OverrideRefreshRate
}
}

18
app/default_config.yml Normal file
View file

@ -0,0 +1,18 @@
# __ __ ___ __ ____
# / / / /___ ________ ____ _/ / |/ // __ \
# / / / / __ \/ ___/ _ \/ __ `/ /| // /_/ /
# / /_/ / / / / / / __/ /_/ / // |/ _, _/
# \____/_/ /_/_/ \___/\__,_/_//_/|_/_/ |_|
#
# Welcome to UnrealXR! This is the configuration file to configure various UnrealXR settings.
display:
angle: 45 # Angle of the virtual displays
fov: 45 # FOV of the 3D camera
spacing: 1 # Spacing between virtual displays
count: 3 # Count of virtual displays
overrides:
allow_unsupported_devices: false # If true, allows unsupported devices to be used as long as they're a compatible vendor (Xreal)
# width: 1920 # If set, overrides the width of the screen and virtual displays
# height: 1080 # If set, overrides the height of the screen and virtual displays
# refresh_rate: 120 # If set, overrides the refresh rate of the screen and the maximum refresh rate of the virtual displays

95
app/main.go Normal file
View file

@ -0,0 +1,95 @@
package main
import (
"context"
"fmt"
"os"
"path"
"github.com/charmbracelet/log"
"github.com/goccy/go-yaml"
"github.com/kirsle/configdir"
"github.com/urfave/cli/v3"
)
func mainEntrypoint(context.Context, *cli.Command) error {
if os.Geteuid() != 0 {
return fmt.Errorf("this program must be run as root")
}
log.Info("Initializing UnrealXR")
// Allow for overriding the config directory
configDir := os.Getenv("UNREALXR_CONFIG_PATH")
if configDir == "" {
configDir = configdir.LocalConfig("unrealxr")
err := configdir.MakePath(configDir)
if err != nil {
return fmt.Errorf("failed to ensure config directory exists: %w", err)
}
}
_, err := os.Stat(path.Join(configDir, "config.yml"))
if err != nil {
log.Debug("Creating default config file")
err := os.WriteFile(path.Join(configDir, "config.yml"), InitialConfig, 0644)
if err != nil {
return fmt.Errorf("failed to create initial config file: %w", err)
}
}
// Read and parse the config file
configBytes, err := os.ReadFile(path.Join(configDir, "config.yml"))
if err != nil {
return fmt.Errorf("failed to read config file: %w", err)
}
config := &Config{}
err = yaml.Unmarshal(configBytes, config)
if err != nil {
return fmt.Errorf("failed to parse config file: %w", err)
}
InitializePotentiallyMissingConfigValues(config)
return nil
}
func main() {
logLevel := os.Getenv("UNREALXR_LOG_LEVEL")
if logLevel != "" {
switch logLevel {
case "debug":
log.SetLevel(log.DebugLevel)
case "info":
log.SetLevel(log.InfoLevel)
case "warn":
log.SetLevel(log.WarnLevel)
case "error":
log.SetLevel(log.ErrorLevel)
case "fatal":
log.SetLevel(log.FatalLevel)
}
}
// Initialize the CLI
cmd := &cli.Command{
Name: "unrealxr",
Usage: "A spatial multi-display renderer for XR devices",
Action: mainEntrypoint,
}
if err := cmd.Run(context.Background(), os.Args); err != nil {
log.Fatal(err)
}
}