feature: Get initial multi-display working in Go
This commit is contained in:
parent
243d595a35
commit
47e693a7b9
5 changed files with 261 additions and 35 deletions
24
app/main.go
24
app/main.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
libconfig "git.terah.dev/UnrealXR/unrealxr/app/config"
|
libconfig "git.terah.dev/UnrealXR/unrealxr/app/config"
|
||||||
"git.terah.dev/UnrealXR/unrealxr/app/edidtools"
|
"git.terah.dev/UnrealXR/unrealxr/app/edidtools"
|
||||||
|
@ -154,15 +155,28 @@ func mainEntrypoint(context.Context, *cli.Command) error {
|
||||||
Y2: displayMetadata.MaxHeight,
|
Y2: displayMetadata.MaxHeight,
|
||||||
}
|
}
|
||||||
|
|
||||||
displayBuffer := openedDevice.CreateBuffer(displayMetadata.MaxWidth, displayMetadata.MaxHeight, 4, displayRect)
|
displayBuffer := openedDevice.CreateBuffer(displayMetadata.MaxWidth, displayMetadata.MaxHeight, libevdi.StridePixelFormatRGBA32, displayRect)
|
||||||
|
|
||||||
evdiCards[currentDisplay] = &renderer.EvdiDisplayMetadata{
|
displayMetadata := &renderer.EvdiDisplayMetadata{
|
||||||
EvdiNode: openedDevice,
|
EvdiNode: openedDevice,
|
||||||
Rect: displayRect,
|
Rect: displayRect,
|
||||||
Buffer: displayBuffer,
|
Buffer: displayBuffer,
|
||||||
|
ShouldRequestUpdate: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
displayMetadata.EventContext = &libevdi.EvdiEventContext{
|
||||||
|
UpdateReadyHandler: func(bufferToBeUpdated int) {
|
||||||
|
displayMetadata.IsUpdateReady = true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
openedDevice.RegisterEventHandler(displayMetadata.EventContext)
|
||||||
|
evdiCards[currentDisplay] = displayMetadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK: sometimes the buffer doesn't get initialized properly if we don't wait a bit...
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
|
||||||
log.Info("Initialized displays. Entering rendering loop")
|
log.Info("Initialized displays. Entering rendering loop")
|
||||||
renderer.EnterRenderLoop(config, displayMetadata, evdiCards)
|
renderer.EnterRenderLoop(config, displayMetadata, evdiCards)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package renderer
|
package renderer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
libconfig "git.terah.dev/UnrealXR/unrealxr/app/config"
|
libconfig "git.terah.dev/UnrealXR/unrealxr/app/config"
|
||||||
"git.terah.dev/UnrealXR/unrealxr/app/edidtools"
|
"git.terah.dev/UnrealXR/unrealxr/app/edidtools"
|
||||||
|
@ -9,8 +12,27 @@ import (
|
||||||
arcommons "git.terah.dev/UnrealXR/unrealxr/ardriver/commons"
|
arcommons "git.terah.dev/UnrealXR/unrealxr/ardriver/commons"
|
||||||
"github.com/charmbracelet/log"
|
"github.com/charmbracelet/log"
|
||||||
"github.com/tebeka/atexit"
|
"github.com/tebeka/atexit"
|
||||||
|
|
||||||
|
rl "git.terah.dev/UnrealXR/raylib-go/raylib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TextureModelPair struct {
|
||||||
|
Texture rl.Texture2D
|
||||||
|
Model rl.Model
|
||||||
|
}
|
||||||
|
|
||||||
|
func findMaxVerticalSize(fovyDeg float32, distance float32) float32 {
|
||||||
|
fovyRad := float64(fovyDeg * math.Pi / 180.0)
|
||||||
|
return 2 * distance * float32(math.Tan(fovyRad/2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func findOptimalHorizontalRes(verticalDisplayRes float32, horizontalDisplayRes float32, verticalSize float32) float32 {
|
||||||
|
aspectRatio := horizontalDisplayRes / verticalDisplayRes
|
||||||
|
horizontalSize := verticalSize * aspectRatio
|
||||||
|
|
||||||
|
return horizontalSize
|
||||||
|
}
|
||||||
|
|
||||||
func EnterRenderLoop(config *libconfig.Config, displayMetadata *edidtools.DisplayMetadata, evdiCards []*EvdiDisplayMetadata) {
|
func EnterRenderLoop(config *libconfig.Config, displayMetadata *edidtools.DisplayMetadata, evdiCards []*EvdiDisplayMetadata) {
|
||||||
log.Info("Initializing AR driver")
|
log.Info("Initializing AR driver")
|
||||||
headset, err := ardriver.GetDevice()
|
headset, err := ardriver.GetDevice()
|
||||||
|
@ -19,21 +41,52 @@ func EnterRenderLoop(config *libconfig.Config, displayMetadata *edidtools.Displa
|
||||||
log.Errorf("Failed to get device: %s", err.Error())
|
log.Errorf("Failed to get device: %s", err.Error())
|
||||||
atexit.Exit(1)
|
atexit.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Initialized")
|
log.Info("Initialized")
|
||||||
|
|
||||||
var pitch float32
|
var (
|
||||||
var yaw float32
|
currentPitch float32
|
||||||
var roll float32
|
previousPitch float32
|
||||||
|
currentYaw float32
|
||||||
|
previousYaw float32
|
||||||
|
currentRoll float32
|
||||||
|
previousRoll float32
|
||||||
|
|
||||||
|
hasGottenPitchCallbackBefore bool
|
||||||
|
hasGottenYawCallbackBefore bool
|
||||||
|
hasGottenRollCallbackBefore bool
|
||||||
|
)
|
||||||
|
|
||||||
arEventListner := &arcommons.AREventListener{
|
arEventListner := &arcommons.AREventListener{
|
||||||
PitchCallback: func(newPitch float32) {
|
PitchCallback: func(newPitch float32) {
|
||||||
pitch = newPitch
|
if !hasGottenPitchCallbackBefore {
|
||||||
|
hasGottenPitchCallbackBefore = true
|
||||||
|
currentPitch = newPitch
|
||||||
|
previousPitch = newPitch
|
||||||
|
} else {
|
||||||
|
previousPitch = currentPitch
|
||||||
|
currentPitch = newPitch
|
||||||
|
}
|
||||||
},
|
},
|
||||||
YawCallback: func(newYaw float32) {
|
YawCallback: func(newYaw float32) {
|
||||||
yaw = newYaw
|
if !hasGottenYawCallbackBefore {
|
||||||
|
hasGottenYawCallbackBefore = true
|
||||||
|
currentYaw = newYaw
|
||||||
|
previousYaw = newYaw
|
||||||
|
} else {
|
||||||
|
previousYaw = currentYaw
|
||||||
|
currentYaw = newYaw
|
||||||
|
}
|
||||||
},
|
},
|
||||||
RollCallback: func(newRoll float32) {
|
RollCallback: func(newRoll float32) {
|
||||||
roll = newRoll
|
if !hasGottenRollCallbackBefore {
|
||||||
|
hasGottenRollCallbackBefore = true
|
||||||
|
currentRoll = newRoll
|
||||||
|
previousRoll = newRoll
|
||||||
|
} else {
|
||||||
|
previousRoll = currentRoll
|
||||||
|
currentRoll = newRoll
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,8 +97,158 @@ func EnterRenderLoop(config *libconfig.Config, displayMetadata *edidtools.Displa
|
||||||
|
|
||||||
headset.RegisterEventListeners(arEventListner)
|
headset.RegisterEventListeners(arEventListner)
|
||||||
|
|
||||||
for {
|
fovY := float32(45.0)
|
||||||
log.Debugf("pitch: %f, yaw: %f, roll: %f", pitch, yaw, roll)
|
verticalSize := findMaxVerticalSize(fovY, 5.0)
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
|
camera := rl.NewCamera3D(
|
||||||
|
rl.Vector3{
|
||||||
|
X: 0.0,
|
||||||
|
Y: verticalSize / 2,
|
||||||
|
Z: 5.0,
|
||||||
|
},
|
||||||
|
rl.Vector3{
|
||||||
|
X: 0.0,
|
||||||
|
Y: verticalSize / 2,
|
||||||
|
Z: 0.0,
|
||||||
|
},
|
||||||
|
rl.Vector3{
|
||||||
|
X: 0.0,
|
||||||
|
Y: 1.0,
|
||||||
|
Z: 0.0,
|
||||||
|
},
|
||||||
|
fovY,
|
||||||
|
rl.CameraPerspective,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Disable front and back face culling. It caused issues involving the entire virtual display dissappearing
|
||||||
|
// If this issue still happens I *am* going to cry
|
||||||
|
rl.DisableBackfaceCulling()
|
||||||
|
rl.DisableDepthTest()
|
||||||
|
|
||||||
|
coreMesh := rl.GenMeshPlane(findOptimalHorizontalRes(float32(displayMetadata.MaxHeight), float32(displayMetadata.MaxWidth), verticalSize), verticalSize, 1, 1)
|
||||||
|
|
||||||
|
movementVector := rl.Vector3{
|
||||||
|
X: 0.0,
|
||||||
|
Y: 0.0,
|
||||||
|
Z: 0.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lookVector := rl.Vector3{
|
||||||
|
X: 0.0,
|
||||||
|
Y: 0.0,
|
||||||
|
Z: 0.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
hasZVectorDisabledQuirk := false
|
||||||
|
hasSensorInitDelayQuirk := false
|
||||||
|
sensorInitStartTime := time.Now()
|
||||||
|
|
||||||
|
if displayMetadata.DeviceQuirks.ZVectorDisabled {
|
||||||
|
log.Warn("QUIRK: The Z vector has been disabled for your specific device")
|
||||||
|
hasZVectorDisabledQuirk = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if displayMetadata.DeviceQuirks.SensorInitDelay != 0 {
|
||||||
|
log.Warnf("QUIRK: Waiting %d second(s) before reading sensors", displayMetadata.DeviceQuirks.SensorInitDelay)
|
||||||
|
log.Warn("|| MOVEMENT WILL NOT BE OPERATIONAL DURING THIS TIME. ||")
|
||||||
|
hasSensorInitDelayQuirk = true
|
||||||
|
}
|
||||||
|
|
||||||
|
rects := make([]*TextureModelPair, len(evdiCards))
|
||||||
|
|
||||||
|
for i, card := range evdiCards {
|
||||||
|
image := rl.NewImage(card.Buffer.Buffer, int32(displayMetadata.MaxWidth), int32(displayMetadata.MaxHeight), 1, rl.UncompressedR8g8b8a8)
|
||||||
|
|
||||||
|
texture := rl.LoadTextureFromImage(image)
|
||||||
|
model := rl.LoadModelFromMesh(coreMesh)
|
||||||
|
|
||||||
|
rl.SetMaterialTexture(model.Materials, rl.MapAlbedo, texture)
|
||||||
|
|
||||||
|
rects[i] = &TextureModelPair{
|
||||||
|
Texture: texture,
|
||||||
|
Model: model,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eventTimeoutDuration := 0 * time.Millisecond
|
||||||
|
|
||||||
|
for !rl.WindowShouldClose() {
|
||||||
|
if hasSensorInitDelayQuirk {
|
||||||
|
if time.Now().Sub(sensorInitStartTime) > time.Duration(displayMetadata.DeviceQuirks.SensorInitDelay)*time.Second {
|
||||||
|
log.Info("Movement is now enabled.")
|
||||||
|
hasSensorInitDelayQuirk = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lookVector.X = (currentYaw - previousYaw) * 6.5
|
||||||
|
lookVector.Y = -(currentPitch - previousPitch) * 6.5
|
||||||
|
|
||||||
|
if !hasZVectorDisabledQuirk {
|
||||||
|
lookVector.Z = (currentRoll - previousRoll) * 6.5
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.UpdateCameraPro(&camera, movementVector, lookVector, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.BeginDrawing()
|
||||||
|
rl.ClearBackground(rl.Black)
|
||||||
|
rl.BeginMode3D(camera)
|
||||||
|
|
||||||
|
for rectPos, rect := range rects {
|
||||||
|
card := evdiCards[rectPos]
|
||||||
|
|
||||||
|
ready, err := card.EvdiNode.WaitUntilEventsAreReadyToHandle(eventTimeoutDuration)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to wait for display events: %s", err.Error())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if ready {
|
||||||
|
if err := card.EvdiNode.HandleEvents(card.EventContext); err != nil {
|
||||||
|
log.Errorf("Failed to handle display events: %s", err.Error())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
card.EvdiNode.GrabPixels(card.Rect)
|
||||||
|
|
||||||
|
pixels := unsafe.Slice(
|
||||||
|
(*color.RGBA)(unsafe.Pointer(&card.Buffer.Buffer[0])),
|
||||||
|
len(card.Buffer.Buffer)/4,
|
||||||
|
)
|
||||||
|
|
||||||
|
rl.UpdateTexture(rect.Texture, pixels)
|
||||||
|
card.EvdiNode.RequestUpdate(card.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.DrawModelEx(
|
||||||
|
rect.Model,
|
||||||
|
rl.Vector3{
|
||||||
|
X: 0,
|
||||||
|
Y: verticalSize / 2,
|
||||||
|
Z: 0,
|
||||||
|
},
|
||||||
|
// rotate around X to make it vertical
|
||||||
|
rl.Vector3{
|
||||||
|
X: 1,
|
||||||
|
Y: 0,
|
||||||
|
Z: 0,
|
||||||
|
},
|
||||||
|
90,
|
||||||
|
rl.Vector3{
|
||||||
|
X: 1,
|
||||||
|
Y: 1,
|
||||||
|
Z: 1,
|
||||||
|
},
|
||||||
|
rl.White,
|
||||||
|
)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.EndMode3D()
|
||||||
|
rl.EndDrawing()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Goodbye!")
|
||||||
|
rl.CloseWindow()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
package renderer
|
package renderer
|
||||||
|
|
||||||
import "git.terah.dev/imterah/goevdi/libevdi"
|
import (
|
||||||
|
"git.terah.dev/imterah/goevdi/libevdi"
|
||||||
|
)
|
||||||
|
|
||||||
type EvdiDisplayMetadata struct {
|
type EvdiDisplayMetadata struct {
|
||||||
EvdiNode *libevdi.EvdiNode
|
EvdiNode *libevdi.EvdiNode
|
||||||
Rect *libevdi.EvdiDisplayRect
|
Rect *libevdi.EvdiDisplayRect
|
||||||
Buffer *libevdi.EvdiBuffer
|
Buffer *libevdi.EvdiBuffer
|
||||||
|
EventContext *libevdi.EvdiEventContext
|
||||||
|
ShouldRequestUpdate bool
|
||||||
|
IsUpdateReady bool
|
||||||
}
|
}
|
||||||
|
|
21
go.mod
21
go.mod
|
@ -3,28 +3,31 @@ module git.terah.dev/UnrealXR/unrealxr
|
||||||
go 1.24.3
|
go 1.24.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.terah.dev/UnrealXR/raylib-go/raylib v0.55.2-0.20250623002739-1468af2636e1 // indirect
|
git.terah.dev/UnrealXR/raylib-go/raylib v0.55.2-0.20250623002739-1468af2636e1
|
||||||
git.terah.dev/imterah/goevdi v1.14.10 // indirect
|
git.terah.dev/imterah/goevdi v1.14.11-0.20250626004148-bdbef2a68ff9
|
||||||
github.com/anoopengineer/edidparser v0.0.0-20240602223913-86ca9ed3d2b0 // indirect
|
github.com/anoopengineer/edidparser v0.0.0-20240602223913-86ca9ed3d2b0
|
||||||
|
github.com/charmbracelet/log v0.4.2
|
||||||
|
github.com/goccy/go-yaml v1.18.0
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
|
||||||
|
github.com/tebeka/atexit v0.3.0
|
||||||
|
github.com/urfave/cli/v3 v3.3.8
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||||
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
||||||
github.com/charmbracelet/log v0.4.2 // indirect
|
|
||||||
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||||
github.com/ebitengine/purego v0.8.4 // indirect
|
github.com/ebitengine/purego v0.8.4 // indirect
|
||||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f // indirect
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/muesli/termenv v0.16.0 // indirect
|
github.com/muesli/termenv v0.16.0 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // 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
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
|
|
17
go.sum
17
go.sum
|
@ -1,9 +1,7 @@
|
||||||
git.terah.dev/UnrealXR/raylib-go/raylib v0.55.2-0.20250623002739-1468af2636e1 h1:cO/veKc8mon3Zlueq1sEc047B04fk/hoBJSn+btMLDI=
|
git.terah.dev/UnrealXR/raylib-go/raylib v0.55.2-0.20250623002739-1468af2636e1 h1:cO/veKc8mon3Zlueq1sEc047B04fk/hoBJSn+btMLDI=
|
||||||
git.terah.dev/UnrealXR/raylib-go/raylib v0.55.2-0.20250623002739-1468af2636e1/go.mod h1:ZRirF2UuVWSbl2ux7oyHwXcinni9msejCvtIsXbT8yY=
|
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 v1.14.11-0.20250626004148-bdbef2a68ff9 h1:TYcPZ62CR3keYf/dE9KyV5X5krh+riDyZ3fnhkeSRyA=
|
||||||
git.terah.dev/imterah/goevdi v0.0.0-20250211014657-96d98ad0735c/go.mod h1:RmxqlNaEJ+6qbHpyw6ivJhXlRYT1RcsZ9gzNOsFXfOA=
|
git.terah.dev/imterah/goevdi v1.14.11-0.20250626004148-bdbef2a68ff9/go.mod h1:4scjAuFakx/2gTRSeCtTNHnj1v9FdF3XiOMmWsz4FDs=
|
||||||
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 h1:rTfysyBCL7LPbq9GFpQbllvKT8vEI93lQUwksMMxHMI=
|
||||||
github.com/anoopengineer/edidparser v0.0.0-20240602223913-86ca9ed3d2b0/go.mod h1:fEt61NePh3ZMxA+g3iC4CaGzY9lEsHRUkYJY2x0lBAw=
|
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=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
|
@ -21,6 +19,8 @@ github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod
|
||||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/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 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||||
|
@ -39,26 +39,27 @@ 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/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 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
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/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/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tebeka/atexit v0.3.0 h1:jleL99H7Ywt80oJKR+VWmJNnezcCOG0CuzcN3CIpsdI=
|
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/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 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E=
|
||||||
github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
|
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=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
|
||||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
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 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
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/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=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue