From a9156f7ec218d817cad45d9e00f20a4a0f121882 Mon Sep 17 00:00:00 2001 From: imterah Date: Mon, 23 Jun 2025 13:17:44 -0400 Subject: [PATCH] feature: Add EVDI display initialization --- app/main.go | 74 +++++++++++++++++++++++++++++++++++++++++++---------- go.mod | 3 ++- go.sum | 10 ++++++++ 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/app/main.go b/app/main.go index 11b66ad..8a6c89c 100644 --- a/app/main.go +++ b/app/main.go @@ -5,20 +5,36 @@ import ( "context" "fmt" "os" + "os/signal" "path" + "syscall" libconfig "git.terah.dev/UnrealXR/unrealxr/app/config" "git.terah.dev/UnrealXR/unrealxr/app/edidtools" + "git.terah.dev/UnrealXR/unrealxr/app/renderer" "git.terah.dev/UnrealXR/unrealxr/edidpatcher" + "git.terah.dev/imterah/goevdi/libevdi" "github.com/charmbracelet/log" "github.com/goccy/go-yaml" "github.com/kirsle/configdir" + "github.com/tebeka/atexit" "github.com/urfave/cli/v3" rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func mainEntrypoint(context.Context, *cli.Command) error { + // Allow for clean exits + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + + go func() { + <-c + log.Info("Exiting...") + atexit.Exit(1) + }() + + // TODO: add built-in privesc if os.Geteuid() != 0 { return fmt.Errorf("this program must be run as root") } @@ -63,7 +79,7 @@ func mainEntrypoint(context.Context, *cli.Command) error { } libconfig.InitializePotentiallyMissingConfigValues(config) - log.Info("Attempting to read display EDID file and fetch metadata") + log.Debug("Attempting to read display EDID file and fetch metadata") displayMetadata, err := edidtools.FetchXRGlassEDID(*config.Overrides.AllowUnsupportedDevices) @@ -71,8 +87,8 @@ func mainEntrypoint(context.Context, *cli.Command) error { return fmt.Errorf("failed to fetch EDID or get metadata: %w", err) } - log.Info("Got EDID file and metadata") - log.Info("Patching EDID firmware to be specialized") + log.Debug("Got EDID file and metadata") + log.Debug("Patching EDID firmware to be specialized") patchedFirmware, err := edidpatcher.PatchEDIDToBeSpecialized(displayMetadata.EDID) @@ -87,7 +103,7 @@ func mainEntrypoint(context.Context, *cli.Command) error { return fmt.Errorf("failed to upload patched EDID firmware: %w", err) } - defer func() { + atexit.Register(func() { err := edidtools.UnloadCustomEDIDFirmware(displayMetadata) if err != nil { @@ -95,27 +111,59 @@ func mainEntrypoint(context.Context, *cli.Command) error { } log.Info("Please unplug and plug in your XR device to restore it back to normal settings.") - }() + }) fmt.Print("Press the Enter key to continue loading after you unplug and plug in your XR device.") bufio.NewReader(os.Stdin).ReadBytes('\n') // Wait for Enter key press before continuing log.Info("Initializing XR headset") + rl.SetTargetFPS(int32(displayMetadata.MaxRefreshRate * 2)) + rl.InitWindow(int32(displayMetadata.MaxWidth), int32(displayMetadata.MaxHeight), "UnrealXR") - rl.InitWindow(800, 450, "raylib [core] example - basic window") - defer rl.CloseWindow() + atexit.Register(func() { + rl.CloseWindow() + }) - rl.SetTargetFPS(60) + log.Info("Initializing virtual displays") - for !rl.WindowShouldClose() { - rl.BeginDrawing() + libevdi.SetupLogger(&libevdi.EvdiLogger{ + Log: func(msg string) { + log.Debugf("EVDI: %s", msg) + }, + }) - rl.ClearBackground(rl.RayWhite) - rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LightGray) + displayMetadataBlock := make([]*renderer.EvdiDisplayMetadata, *config.DisplayConfig.Count) - rl.EndDrawing() + for currentDisplay := range *config.DisplayConfig.Count { + openedDevice, err := libevdi.Open(nil) + + if err != nil { + log.Errorf("Failed to open EVDI device: %s", err.Error()) + } + + openedDevice.Connect(displayMetadata.EDID, uint(displayMetadata.MaxWidth), uint(displayMetadata.MaxHeight), uint(displayMetadata.MaxRefreshRate)) + + atexit.Register(func() { + openedDevice.Disconnect() + }) + + displayRect := &libevdi.EvdiDisplayRect{ + X1: 0, + Y1: 0, + X2: displayMetadata.MaxWidth, + Y2: displayMetadata.MaxHeight, + } + + displayBuffer := openedDevice.CreateBuffer(displayMetadata.MaxWidth, displayMetadata.MaxHeight, 4, displayRect) + + displayMetadataBlock[currentDisplay] = &renderer.EvdiDisplayMetadata{ + EvdiNode: openedDevice, + Rect: displayRect, + Buffer: displayBuffer, + } } + atexit.Exit(0) return nil } diff --git a/go.mod b/go.mod index 4df2f71..aa4a6e5 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.3 require ( git.terah.dev/UnrealXR/raylib-go/raylib v0.55.2-0.20250623002739-1468af2636e1 // indirect - git.terah.dev/imterah/goevdi v0.0.0-20250211014657-96d98ad0735c // indirect + git.terah.dev/imterah/goevdi v1.14.10 // indirect github.com/anoopengineer/edidparser v0.0.0-20240602223913-86ca9ed3d2b0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect @@ -23,6 +23,7 @@ require ( github.com/mattn/go-runewidth v0.0.16 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/tebeka/atexit v0.3.0 // indirect github.com/urfave/cli/v3 v3.3.8 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect diff --git a/go.sum b/go.sum index 4040ae3..72d89f4 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ git.terah.dev/UnrealXR/raylib-go/raylib v0.55.2-0.20250623002739-1468af2636e1 h1 git.terah.dev/UnrealXR/raylib-go/raylib v0.55.2-0.20250623002739-1468af2636e1/go.mod h1:ZRirF2UuVWSbl2ux7oyHwXcinni9msejCvtIsXbT8yY= git.terah.dev/imterah/goevdi v0.0.0-20250211014657-96d98ad0735c h1:Ys33r6kjCyCs8N3ofSzCGxxL1gLIfetIiO1uL5UIsAM= git.terah.dev/imterah/goevdi v0.0.0-20250211014657-96d98ad0735c/go.mod h1:RmxqlNaEJ+6qbHpyw6ivJhXlRYT1RcsZ9gzNOsFXfOA= +git.terah.dev/imterah/goevdi v1.14.10 h1:3zr3E/WRkSQ//+Gcrh0wfy0LVgm4Pcglc5K4MVcZ7n8= +git.terah.dev/imterah/goevdi v1.14.10/go.mod h1:z6GnBn2qHVV5N6fKZXPgUka8H9g5mZA5BRxCzUxLaNk= github.com/anoopengineer/edidparser v0.0.0-20240602223913-86ca9ed3d2b0 h1:rTfysyBCL7LPbq9GFpQbllvKT8vEI93lQUwksMMxHMI= github.com/anoopengineer/edidparser v0.0.0-20240602223913-86ca9ed3d2b0/go.mod h1:fEt61NePh3ZMxA+g3iC4CaGzY9lEsHRUkYJY2x0lBAw= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -18,6 +20,7 @@ github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0G github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= @@ -36,9 +39,14 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tebeka/atexit v0.3.0 h1:jleL99H7Ywt80oJKR+VWmJNnezcCOG0CuzcN3CIpsdI= +github.com/tebeka/atexit v0.3.0/go.mod h1:WJmSUSmMT7WoR7etUOaGBVXk+f5/ZJ+67qwuedq7Fbs= github.com/urfave/cli/v3 v3.3.8 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E= github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= @@ -52,3 +60,5 @@ golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=