From 30694431e69bba208f375e068943123e637107fd Mon Sep 17 00:00:00 2001 From: Louis Dalibard Date: Sat, 2 Nov 2024 17:50:15 +0100 Subject: [PATCH] feat: switch to ebiten --- config/config.go | 37 +++++++++++++- fonts/fonts.go | 42 +++------------- go.mod | 15 ++++-- go.sum | 36 +++++++++++--- main.go | 123 ++++++++++++++++++++++++++++++----------------- 5 files changed, 161 insertions(+), 92 deletions(-) diff --git a/config/config.go b/config/config.go index b3ee200..30021dc 100644 --- a/config/config.go +++ b/config/config.go @@ -8,6 +8,7 @@ import ( "path/filepath" "xyosc/utils" + "github.com/fsnotify/fsnotify" "github.com/kirsle/configdir" "gopkg.in/yaml.v2" ) @@ -50,6 +51,8 @@ var AccentColor color.RGBA var FirstColor color.RGBA var ThirdColor color.RGBA +var watcher *fsnotify.Watcher + func Init() { configPath := configdir.LocalConfig("ontake", "xyosc") err := configdir.MakePath(configPath) // Ensure it exists. @@ -78,9 +81,41 @@ func Init() { } // Get pywal accent color + watcher, err = fsnotify.NewWatcher() + utils.CheckError(err) + updatePywalColors() walPath := configdir.LocalCache("wal") walFile := filepath.Join(walPath, "colors") - if _, err = os.Stat(walFile); os.IsNotExist(err) { + // Start listening for events. + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + if event.Has(fsnotify.Write) { + updatePywalColors() + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + utils.CheckError(err) + } + } + }() + if _, err := os.Stat(walFile); os.IsNotExist(err) { + } else { + err = watcher.Add(walFile) + utils.CheckError(err) + } +} + +func updatePywalColors() { + walPath := configdir.LocalCache("wal") + walFile := filepath.Join(walPath, "colors") + if _, err := os.Stat(walFile); os.IsNotExist(err) { AccentColor = color.RGBA{255, 0, 0, Config.LineOpacity} FirstColor = color.RGBA{255, 120, 120, Config.LineOpacity} ThirdColor = color.RGBA{255, 0, 0, Config.LineOpacity} diff --git a/fonts/fonts.go b/fonts/fonts.go index 43c91f8..da94b4a 100644 --- a/fonts/fonts.go +++ b/fonts/fonts.go @@ -1,49 +1,21 @@ package fonts import ( - "unicode" + "os" "xyosc/utils" "github.com/flopp/go-findfont" - rl "github.com/gen2brain/raylib-go/raylib" + "github.com/hajimehoshi/ebiten/v2/text/v2" ) -func getAllChars(table *unicode.RangeTable) []rune { - res := make([]rune, 0) - - for _, r := range table.R16 { - for c := r.Lo; c <= r.Hi; c += r.Stride { - res = append(res, rune(c)) - } - } - for _, r := range table.R32 { - for c := r.Lo; c <= r.Hi; c += r.Stride { - res = append(res, rune(c)) - } - } - - return res -} - -var FontIosevka32 rl.Font -var FontIosevka16 rl.Font +var Font *text.GoTextFaceSource func Init() { // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) fontPath, err := findfont.Find("SourceHanSansJP-Heavy.otf") utils.CheckError(err) - runes := make([]rune, 0) - runes = append(runes, getAllChars(unicode.Katakana)...) - runes = append(runes, getAllChars(unicode.Hiragana)...) - runes = append(runes, getAllChars(unicode.Latin)...) - runes = append(runes, getAllChars(unicode.Digit)...) - runes = append(runes, getAllChars(unicode.Punct)...) - runes = append(runes, getAllChars(unicode.Han)...) - runes = append(runes, getAllChars(unicode.Symbol)...) - FontIosevka32 = rl.LoadFontEx(fontPath, 32, runes) - rl.GenTextureMipmaps(&FontIosevka32.Texture) - rl.SetTextureFilter(FontIosevka32.Texture, rl.FilterPoint) - FontIosevka16 = rl.LoadFontEx(fontPath, 16, runes) - rl.GenTextureMipmaps(&FontIosevka16.Texture) - rl.SetTextureFilter(FontIosevka16.Texture, rl.FilterPoint) + f, err := os.Open(fontPath) + utils.CheckError(err) + Font, err = text.NewGoTextFaceSource(f) + utils.CheckError(err) } diff --git a/go.mod b/go.mod index 0ea5f3e..2fb4c0f 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,10 @@ go 1.23.2 require ( github.com/flopp/go-findfont v0.1.0 + github.com/fsnotify/fsnotify v1.8.0 github.com/gen2brain/malgo v0.11.22 - github.com/gen2brain/raylib-go/raylib v0.0.0-20241019150900-b7833eeae8d0 github.com/godbus/dbus v4.1.0+incompatible + github.com/hajimehoshi/ebiten/v2 v2.8.3 github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f github.com/leberKleber/go-mpris v1.1.0 github.com/smallnest/ringbuffer v0.0.0-20240827114233-62e3c686e6c0 @@ -15,8 +16,14 @@ require ( ) require ( - github.com/ebitengine/purego v0.7.1 // indirect + github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect + github.com/ebitengine/hideconsole v1.0.0 // indirect + github.com/ebitengine/purego v0.8.0 // indirect + github.com/go-text/typesetting v0.2.0 // indirect github.com/godbus/dbus/v5 v5.0.6 // indirect - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/sys v0.20.0 // indirect + github.com/jezek/xgb v1.1.1 // indirect + golang.org/x/image v0.20.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect ) diff --git a/go.sum b/go.sum index ed0ab12..888f5a9 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,31 @@ 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.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= -github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 h1:Gk1XUEttOk0/hb6Tq3WkmutWa0ZLhNn/6fc6XZpM7tM= +github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325/go.mod h1:ulhSQcbPioQrallSuIzF8l1NKQoD7xmMZc5NxzibUMY= +github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= +github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= +github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= +github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/flopp/go-findfont v0.1.0 h1:lPn0BymDUtJo+ZkV01VS3661HL6F4qFlkhcJN55u6mU= github.com/flopp/go-findfont v0.1.0/go.mod h1:wKKxRDjD024Rh7VMwoU90i6ikQRCr+JTHB5n4Ejkqvw= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gen2brain/malgo v0.11.22 h1:fRtTbzVI9CDWnfEJGo/GxKxN7pXtCb0NsAeUVUjZk9U= github.com/gen2brain/malgo v0.11.22/go.mod h1:f9TtuN7DVrXMiV/yIceMeWpvanyVzJQMlBecJFVMxww= -github.com/gen2brain/raylib-go/raylib v0.0.0-20241019150900-b7833eeae8d0 h1:ssHgKQvc9AIHFSlpkfHr89lJU8nzGBdpKoQJBTPkvnQ= -github.com/gen2brain/raylib-go/raylib v0.0.0-20241019150900-b7833eeae8d0/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= +github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho= +github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/hajimehoshi/bitmapfont/v3 v3.2.0 h1:0DISQM/rseKIJhdF29AkhvdzIULqNIIlXAGWit4ez1Q= +github.com/hajimehoshi/bitmapfont/v3 v3.2.0/go.mod h1:8gLqGatKVu0pwcNCJguW3Igg9WQqVXF0zg/RvrGQWyg= +github.com/hajimehoshi/ebiten/v2 v2.8.3 h1:AKHqj3QbQMzNEhK33MMJeRwXm9UzftrUUo6AWwFV258= +github.com/hajimehoshi/ebiten/v2 v2.8.3/go.mod h1:SXx/whkvpfsavGo6lvZykprerakl+8Uo1X8d2U5aAnA= +github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= +github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f h1:dKccXx7xA56UNqOcFIbuqFjAWPVtP688j5QMgmo6OHU= github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f/go.mod h1:4rEELDSfUAlBSyUjPG0JnaNGjf13JySHFeRdD/3dLP0= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -20,6 +34,8 @@ github.com/leberKleber/go-mpris v1.1.0 h1:bHAnmUjVoxAs4uMHH9lfQ8bOm284UWtI7JhLvk github.com/leberKleber/go-mpris v1.1.0/go.mod h1:OwKywFZwFGC0p/8xBUTUXMIFZy0Rq/7C6EayfeASTA0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 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/smallnest/ringbuffer v0.0.0-20240827114233-62e3c686e6c0 h1:6wTlHUWggWb8Y5Q4f7xnIBHa3L7DgijNQP8eM6oTEhQ= @@ -28,10 +44,14 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/ztrue/tracerr v0.4.0 h1:vT5PFxwIGs7rCg9ZgJ/y0NmOpJkPCPFK8x0vVIYzd04= github.com/ztrue/tracerr v0.4.0/go.mod h1:PaFfYlas0DfmXNpo7Eay4MFhZUONqvXM+T2HyGPpngk= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= +golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/main.go b/main.go index 8141804..5e5959a 100644 --- a/main.go +++ b/main.go @@ -2,63 +2,98 @@ package main import ( "encoding/binary" + "log" "xyosc/audio" "xyosc/config" "xyosc/fonts" "xyosc/media" - rl "github.com/gen2brain/raylib-go/raylib" + "fmt" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" + "github.com/hajimehoshi/ebiten/v2/text/v2" + "github.com/hajimehoshi/ebiten/v2/vector" ) -func main() { - config.Init() - audio.Init() - go audio.Start() - go media.Start() +type Game struct { +} +func (g *Game) Update() error { + return nil +} + +func (g *Game) Draw(screen *ebiten.Image) { scale := min(config.Config.WindowWidth, config.Config.WindowHeight) / 2 - rl.InitWindow(config.Config.WindowWidth, config.Config.WindowHeight, "xyosc") - defer rl.CloseWindow() - rl.SetWindowOpacity(config.Config.WindowOpacity) - rl.SetConfigFlags(rl.FlagWindowTransparent) - rl.SetConfigFlags(rl.FlagMsaa4xHint) - rl.SetWindowState(rl.FlagWindowUndecorated) - rl.SetTargetFPS(config.Config.TargetFPS) - rl.SetWindowPosition(rl.GetMonitorWidth(rl.GetCurrentMonitor())/2, rl.GetMonitorHeight(rl.GetCurrentMonitor())/2) var AX float32 var AY float32 var BX float32 var BY float32 - - fonts.Init() - for !rl.WindowShouldClose() { - rl.BeginDrawing() - rl.ClearBackground(rl.Blank) - - binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &AX) - binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &AY) - for i := uint32(0); i < config.Config.ReadBufferSize/audio.SampleSizeInBytes/2; i++ { - binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &BX) - binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &BY) - fAX := float32(AX) * config.Config.Gain * float32(scale) - fAY := -float32(AY) * config.Config.Gain * float32(scale) - fBX := float32(BX) * config.Config.Gain * float32(scale) - fBY := -float32(BY) * config.Config.Gain * float32(scale) - //inv := fastsqrt.FastInvSqrt32((fBX-fAX)*(fBX-fAX) + (fBY-fBY)*(fBY-fBY)) - //colorAdjusted := color.RGBA{config.AccentColor.R, config.AccentColor.G, config.AccentColor.B, uint8(255 * inv * config.Config.LineOpacity)} - rl.DrawLineEx(rl.NewVector2(float32(config.Config.WindowWidth/2)+fAX, float32(config.Config.WindowWidth/2)+fAY), rl.NewVector2(float32(config.Config.WindowWidth/2)+fBX, float32(config.Config.WindowWidth/2)+fBY), config.Config.LineThickness, config.AccentColor) - AX = BX - AY = BY - } - //audio.SampleRingBuffer.Reset() - if config.Config.FPSCounter { - rl.DrawFPS(16, config.Config.WindowHeight-32) - } - rl.DrawTextEx(fonts.FontIosevka32, media.PlayingMediaInfo.Artist+" - "+media.PlayingMediaInfo.Title, rl.NewVector2(16, 16), 32, 2, config.AccentColor) - rl.DrawTextEx(fonts.FontIosevka16, media.PlayingMediaInfo.Album, rl.NewVector2(16, 48), 16, 1, config.ThirdColor) - rl.DrawTextEx(fonts.FontIosevka32, media.FmtDuration(media.PlayingMediaInfo.Position)+" / "+media.FmtDuration(media.PlayingMediaInfo.Duration), rl.NewVector2(16, 64), 32, 2, config.AccentColor) - rl.EndDrawing() + binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &AX) + binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &AY) + for i := uint32(0); i < config.Config.ReadBufferSize/audio.SampleSizeInBytes/2; i++ { + binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &BX) + binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &BY) + fAX := float32(AX) * config.Config.Gain * float32(scale) + fAY := -float32(AY) * config.Config.Gain * float32(scale) + fBX := float32(BX) * config.Config.Gain * float32(scale) + fBY := -float32(BY) * config.Config.Gain * float32(scale) + //inv := fastsqrt.FastInvSqrt32((fBX-fAX)*(fBX-fAX) + (fBY-fBY)*(fBY-fBY)) + //colorAdjusted := color.RGBA{config.AccentColor.R, config.AccentColor.G, config.AccentColor.B, uint8(255 * inv * config.Config.LineOpacity)} + vector.StrokeLine(screen, float32(config.Config.WindowWidth/2)+fAX, float32(config.Config.WindowWidth/2)+fAY, float32(config.Config.WindowWidth/2)+fBX, float32(config.Config.WindowWidth/2)+fBY, config.Config.LineThickness, config.AccentColor, true) + AX = BX + AY = BY + } + //audio.SampleRingBuffer.Reset() + if config.Config.FPSCounter { + ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f", ebiten.ActualTPS())) } - rl.CloseWindow() + if config.Config.ShowMPRIS { + op := &text.DrawOptions{} + op.GeoM.Translate(16, 16) + op.ColorScale.ScaleWithColor(config.AccentColor) + text.Draw(screen, media.PlayingMediaInfo.Artist+" - "+media.PlayingMediaInfo.Title, &text.GoTextFace{ + Source: fonts.Font, + Size: 32, + }, op) + + op = &text.DrawOptions{} + op.GeoM.Translate(16, 64) + op.ColorScale.ScaleWithColor(config.ThirdColor) + text.Draw(screen, media.PlayingMediaInfo.Album, &text.GoTextFace{ + Source: fonts.Font, + Size: 16, + }, op) + + op = &text.DrawOptions{} + op.GeoM.Translate(16, 80) + op.ColorScale.ScaleWithColor(config.AccentColor) + text.Draw(screen, media.FmtDuration(media.PlayingMediaInfo.Position)+" / "+media.FmtDuration(media.PlayingMediaInfo.Duration), &text.GoTextFace{ + Source: fonts.Font, + Size: 32, + }, op) + } +} + +func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { + return int(config.Config.WindowWidth), int(config.Config.WindowHeight) +} + +func main() { + config.Init() + audio.Init() + fonts.Init() + go audio.Start() + go media.Start() + ebiten.SetWindowSize(int(config.Config.WindowWidth), int(config.Config.WindowHeight)) + ebiten.SetWindowTitle("xyosc") + ebiten.SetTPS(int(config.Config.TargetFPS)) + ebiten.SetWindowDecorated(false) + screenW, screenH := ebiten.Monitor().Size() + ebiten.SetWindowPosition(screenW/2-int(config.Config.WindowWidth)/2, screenH/2-int(config.Config.WindowHeight)/2) + ebiten.SetVsyncEnabled(true) + if err := ebiten.RunGame(&Game{}); err != nil { + log.Fatal(err) + } }