diff --git a/audio/audio.go b/audio/audio.go index cef2fc4..dae4bc6 100644 --- a/audio/audio.go +++ b/audio/audio.go @@ -16,7 +16,7 @@ import ( const bufSize = 1024 * 8 -var audio struct { +var ( source al.Source buffers []al.Buffer rate int32 @@ -24,14 +24,14 @@ var audio struct { tmpBuf [bufSize]byte tmpBufPtr int32 resPtr int32 -} +) // Effects are sound effects var Effects map[string]*Effect // SetVolume sets the audio volume func SetVolume(vol float32) { - audio.source.SetGain(vol) + source.SetGain(vol) } // Init initializes the audio device @@ -54,19 +54,19 @@ func Init() { // Reconfigure initializes the audio package. It sets the number of buffers, the // volume and the source for the games. -func Reconfigure(rate int32) { - audio.rate = rate - audio.numBuffers = 4 +func Reconfigure(r int32) { + rate = r + numBuffers = 4 - log.Printf("[OpenAL]: Using %v buffers of %v bytes.\n", audio.numBuffers, bufSize) + log.Printf("[OpenAL]: Using %v buffers of %v bytes.\n", numBuffers, bufSize) - audio.source = al.GenSources(1)[0] - audio.buffers = al.GenBuffers(int(audio.numBuffers)) - audio.resPtr = audio.numBuffers - audio.tmpBufPtr = 0 - audio.tmpBuf = [bufSize]byte{} + source = al.GenSources(1)[0] + buffers = al.GenBuffers(int(numBuffers)) + resPtr = numBuffers + tmpBufPtr = 0 + tmpBuf = [bufSize]byte{} - audio.source.SetGain(settings.Current.AudioVolume) + source.SetGain(settings.Current.AudioVolume) } func min(a, b int32) int32 { @@ -77,19 +77,19 @@ func min(a, b int32) int32 { } func alUnqueueBuffers() bool { - val := audio.source.BuffersProcessed() + val := source.BuffersProcessed() if val <= 0 { return false } - audio.source.UnqueueBuffers(audio.buffers[audio.resPtr:val]...) - audio.resPtr += val + source.UnqueueBuffers(buffers[resPtr:val]...) + resPtr += val return true } func alGetBuffer() al.Buffer { - if audio.resPtr == 0 { + if resPtr == 0 { for { if alUnqueueBuffers() { break @@ -98,21 +98,21 @@ func alGetBuffer() al.Buffer { } } - audio.resPtr-- - return audio.buffers[audio.resPtr] + resPtr-- + return buffers[resPtr] } func fillInternalBuf(buf []byte) int32 { - readSize := min(bufSize-audio.tmpBufPtr, int32(len(buf))) - copy(audio.tmpBuf[audio.tmpBufPtr:], buf[:readSize]) - audio.tmpBufPtr += readSize + readSize := min(bufSize-tmpBufPtr, int32(len(buf))) + copy(tmpBuf[tmpBufPtr:], buf[:readSize]) + tmpBufPtr += readSize return readSize } func write(buf []byte, size int32) int32 { written := int32(0) - if state.Global.FastForward { + if state.FastForward { return size } @@ -123,18 +123,18 @@ func write(buf []byte, size int32) int32 { written += rc size -= rc - if audio.tmpBufPtr != bufSize { + if tmpBufPtr != bufSize { break } buffer := alGetBuffer() - buffer.BufferData(al.FormatStereo16, audio.tmpBuf[:], audio.rate) - audio.tmpBufPtr = 0 - audio.source.QueueBuffers(buffer) + buffer.BufferData(al.FormatStereo16, tmpBuf[:], rate) + tmpBufPtr = 0 + source.QueueBuffers(buffer) - if audio.source.State() != al.Playing { - al.PlaySources(audio.source) + if source.State() != al.Playing { + al.PlaySources(source) } } @@ -145,7 +145,7 @@ func write(buf []byte, size int32) int32 { // It is passed as a callback to the libretro implementation. func Sample(left int16, right int16) { // simulate the kind of raw byte array that would be provided from C via SampleBatch. - // (effectively typecasting int16 array to byte array) + // (effectively typecasting int16 array to byte array) buf := []int16{left, right} pi := (*[4]byte)(unsafe.Pointer(&buf[0])) write((*pi)[:], 4) diff --git a/core/core.go b/core/core.go index dee69fe..b5580c3 100644 --- a/core/core.go +++ b/core/core.go @@ -11,7 +11,6 @@ import ( "log" "os" "path/filepath" - "time" "github.com/libretro/ludo/audio" "github.com/libretro/ludo/input" @@ -32,17 +31,6 @@ var Options *options.Options // Call Init before calling other functions of this package. func Init(v *video.Video) { vid = v - ticker := time.NewTicker(time.Second * 10) - go func() { - for range ticker.C { - state.Global.Lock() - canSave := state.Global.CoreRunning && !state.Global.MenuActive - state.Global.Unlock() - if canSave { - savefiles.SaveSRAM() - } - } - }() } // Load loads a libretro core @@ -52,28 +40,26 @@ func Load(sofile string) error { Unload() // This must be set before the environment callback is called - state.Global.CorePath = sofile + state.CorePath = sofile var err error - state.Global.Core, err = libretro.Load(sofile) + state.Core, err = libretro.Load(sofile) if err != nil { return err } - state.Global.Core.SetEnvironment(environment) - state.Global.Core.Init() - state.Global.Core.SetVideoRefresh(vid.Refresh) - state.Global.Core.SetInputPoll(input.Poll) - state.Global.Core.SetInputState(input.State) - state.Global.Core.SetAudioSample(audio.Sample) - state.Global.Core.SetAudioSampleBatch(audio.SampleBatch) + state.Core.SetEnvironment(environment) + state.Core.Init() + state.Core.SetVideoRefresh(vid.Refresh) + state.Core.SetInputPoll(input.Poll) + state.Core.SetInputState(input.State) + state.Core.SetAudioSample(audio.Sample) + state.Core.SetAudioSampleBatch(audio.SampleBatch) // Append the library name to the window title. - si := state.Global.Core.GetSystemInfo() + si := state.Core.GetSystemInfo() if len(si.LibraryName) > 0 { - if vid.Window != nil { - vid.Window.SetTitle("Ludo - " + si.LibraryName) - } - if state.Global.Verbose { + vid.SetTitle("Ludo - " + si.LibraryName) + if state.Verbose { log.Println("[Core]: Name:", si.LibraryName) log.Println("[Core]: Version:", si.LibraryVersion) log.Println("[Core]: Valid extensions:", si.ValidExtensions) @@ -143,11 +129,11 @@ func LoadGame(gamePath string) error { // If we're loading a new game on the same core, save the RAM of the previous // game before closing it. - if state.Global.GamePath != gamePath { + if state.GamePath != gamePath { UnloadGame() } - si := state.Global.Core.GetSystemInfo() + si := state.Core.GetSystemInfo() gi, err := getGameInfo(gamePath, si.BlockExtract) if err != nil { @@ -168,38 +154,36 @@ func LoadGame(gamePath string) error { } } - ok := state.Global.Core.LoadGame(*gi) + ok := state.Core.LoadGame(*gi) if !ok { - state.Global.CoreRunning = false + state.CoreRunning = false return errors.New("failed to load the game") } - avi := state.Global.Core.GetSystemAVInfo() + avi := state.Core.GetSystemAVInfo() vid.Geom = avi.Geometry // Append the library name to the window title. if len(si.LibraryName) > 0 { - vid.Window.SetTitle("Ludo - " + si.LibraryName) + vid.SetTitle("Ludo - " + si.LibraryName) } input.Init(vid) audio.Reconfigure(int32(avi.Timing.SampleRate)) - if state.Global.Core.AudioCallback != nil { - state.Global.Core.AudioCallback.SetState(true) + if state.Core.AudioCallback != nil { + state.Core.AudioCallback.SetState(true) } - state.Global.Lock() - state.Global.CoreRunning = true - state.Global.FastForward = false - state.Global.GamePath = gamePath - state.Global.Unlock() + state.CoreRunning = true + state.FastForward = false + state.GamePath = gamePath - state.Global.Core.SetControllerPortDevice(0, libretro.DeviceJoypad) - state.Global.Core.SetControllerPortDevice(1, libretro.DeviceJoypad) - state.Global.Core.SetControllerPortDevice(2, libretro.DeviceJoypad) - state.Global.Core.SetControllerPortDevice(3, libretro.DeviceJoypad) - state.Global.Core.SetControllerPortDevice(4, libretro.DeviceJoypad) + state.Core.SetControllerPortDevice(0, libretro.DeviceJoypad) + state.Core.SetControllerPortDevice(1, libretro.DeviceJoypad) + state.Core.SetControllerPortDevice(2, libretro.DeviceJoypad) + state.Core.SetControllerPortDevice(3, libretro.DeviceJoypad) + state.Core.SetControllerPortDevice(4, libretro.DeviceJoypad) log.Println("[Core]: Game loaded: " + gamePath) savefiles.LoadSRAM() @@ -209,26 +193,22 @@ func LoadGame(gamePath string) error { // Unload unloads a libretro core func Unload() { - if state.Global.Core != nil { + if state.Core != nil { UnloadGame() - state.Global.Core.Deinit() - state.Global.Lock() - state.Global.CorePath = "" - state.Global.Core = nil + state.Core.Deinit() + state.CorePath = "" + state.Core = nil Options = nil - state.Global.Unlock() } } // UnloadGame unloads a game. func UnloadGame() { - if state.Global.CoreRunning { + if state.CoreRunning { savefiles.SaveSRAM() - state.Global.Core.UnloadGame() - state.Global.Lock() - state.Global.GamePath = "" - state.Global.CoreRunning = false - state.Global.Unlock() + state.Core.UnloadGame() + state.GamePath = "" + state.CoreRunning = false vid.ResetPitch() vid.ResetRot() } diff --git a/core/core_test.go b/core/core_test.go index 1ab6ad2..53dd6e3 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -29,17 +29,17 @@ func (m WindowMock) SetTitle(string) {} func (m WindowMock) SwapBuffers() {} func Test_coreLoad(t *testing.T) { - state.Global.Verbose = true + state.Verbose = true ext := utils.CoreExt() - Init(&video.Video{Window: &WindowMock{}}) + Init(&video.Video{}) out := utils.CaptureOutput(func() { Load("testdata/vecx_libretro" + ext) }) t.Run("The core is loaded", func(t *testing.T) { - if state.Global.Core == nil { - t.Errorf("got = %v, want not nil", state.Global.Core) + if state.Core == nil { + t.Errorf("got = %v, want not nil", state.Core) } }) @@ -56,10 +56,10 @@ func Test_coreLoad(t *testing.T) { } }) - state.Global.Core.UnloadGame() - state.Global.Core.Deinit() - state.Global.GamePath = "" - state.Global.Verbose = false + state.Core.UnloadGame() + state.Core.Deinit() + state.GamePath = "" + state.Verbose = false } func Test_getGameInfo(t *testing.T) { @@ -190,11 +190,11 @@ func Test_unzipGame(t *testing.T) { } func Test_coreLoadGame(t *testing.T) { - state.Global.Verbose = true + state.Verbose = true ext := utils.CoreExt() - Init(&video.Video{Window: &WindowMock{}}) + Init(&video.Video{}) if err := glfw.Init(); err != nil { log.Fatalln("failed to initialize glfw") @@ -212,14 +212,14 @@ func Test_coreLoadGame(t *testing.T) { }) t.Run("Global state should be set by Load", func(t *testing.T) { - if state.Global.Core == nil { - t.Errorf("got = %v, want %v", nil, state.Global.Core) + if state.Core == nil { + t.Errorf("got = %v, want %v", nil, state.Core) } - if state.Global.GamePath != "testdata/Polar Rescue (USA).vec" { - t.Errorf("got = %v, want %v", state.Global.GamePath, "testdata/Polar Rescue (USA).vec") + if state.GamePath != "testdata/Polar Rescue (USA).vec" { + t.Errorf("got = %v, want %v", state.GamePath, "testdata/Polar Rescue (USA).vec") } - if !state.Global.CoreRunning { - t.Errorf("got = %v, want %v", state.Global.CoreRunning, true) + if !state.CoreRunning { + t.Errorf("got = %v, want %v", state.CoreRunning, true) } }) @@ -227,14 +227,14 @@ func Test_coreLoadGame(t *testing.T) { Unload() t.Run("Global state should be cleared by Unload", func(t *testing.T) { - if state.Global.Core != nil { - t.Errorf("got = %v, want %v", state.Global.Core, nil) + if state.Core != nil { + t.Errorf("got = %v, want %v", state.Core, nil) } - if state.Global.GamePath != "" { - t.Errorf("got = %v, want %v", state.Global.GamePath, "") + if state.GamePath != "" { + t.Errorf("got = %v, want %v", state.GamePath, "") } - if state.Global.CoreRunning { - t.Errorf("got = %v, want %v", state.Global.CoreRunning, false) + if state.CoreRunning { + t.Errorf("got = %v, want %v", state.CoreRunning, false) } }) } diff --git a/core/environment.go b/core/environment.go index 12fc890..e45ae0a 100644 --- a/core/environment.go +++ b/core/environment.go @@ -136,13 +136,13 @@ func environment(cmd uint32, data unsafe.Pointer) bool { case libretro.EnvironmentGetUsername: return environmentGetUsername(data) case libretro.EnvironmentGetLogInterface: - state.Global.Core.BindLogCallback(data, logCallback) + state.Core.BindLogCallback(data, logCallback) case libretro.EnvironmentGetPerfInterface: - state.Global.Core.BindPerfCallback(data, getTimeUsec) + state.Core.BindPerfCallback(data, getTimeUsec) case libretro.EnvironmentSetFrameTimeCallback: - state.Global.Core.SetFrameTimeCallback(data) + state.Core.SetFrameTimeCallback(data) case libretro.EnvironmentSetAudioCallback: - state.Global.Core.SetAudioCallback(data) + state.Core.SetAudioCallback(data) case libretro.EnvironmentGetCanDupe: libretro.SetBool(data, true) case libretro.EnvironmentSetPixelFormat: @@ -152,7 +152,7 @@ func environment(cmd uint32, data unsafe.Pointer) bool { case libretro.EnvironmentGetSaveDirectory: return environmentGetSaveDirectory(data) case libretro.EnvironmentShutdown: - vid.Window.SetShouldClose(true) + vid.SetShouldClose(true) case libretro.EnvironmentGetCoreOptionsVersion: libretro.SetUint(data, 1) case libretro.EnvironmentSetCoreOptions: @@ -172,7 +172,7 @@ func environment(cmd uint32, data unsafe.Pointer) bool { avi := libretro.GetSystemAVInfo(data) vid.Geom = avi.Geometry case libretro.EnvironmentGetFastforwarding: - libretro.SetBool(data, state.Global.FastForward) + libretro.SetBool(data, state.FastForward) case libretro.EnvironmentGetLanguage: libretro.SetUint(data, 0) default: diff --git a/main.go b/main.go index cff657b..bf30758 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ import ( "github.com/libretro/ludo/menu" ntf "github.com/libretro/ludo/notifications" "github.com/libretro/ludo/playlists" + "github.com/libretro/ludo/savefiles" "github.com/libretro/ludo/scanner" "github.com/libretro/ludo/settings" "github.com/libretro/ludo/state" @@ -26,6 +27,8 @@ func init() { runtime.LockOSThread() } +var frame = 0 + func runLoop(vid *video.Video, m *menu.Menu) { currTime := time.Now() prevTime := time.Now() @@ -37,17 +40,21 @@ func runLoop(vid *video.Video, m *menu.Menu) { ntf.Process(dt) vid.ResizeViewport() m.UpdatePalette() - if !state.Global.MenuActive { - if state.Global.CoreRunning { - state.Global.Core.Run() - if state.Global.Core.FrameTimeCallback != nil { - state.Global.Core.FrameTimeCallback.Callback(state.Global.Core.FrameTimeCallback.Reference) + if !state.MenuActive { + if state.CoreRunning { + state.Core.Run() + if state.Core.FrameTimeCallback != nil { + state.Core.FrameTimeCallback.Callback(state.Core.FrameTimeCallback.Reference) } - if state.Global.Core.AudioCallback != nil { - state.Global.Core.AudioCallback.Callback() + if state.Core.AudioCallback != nil { + state.Core.AudioCallback.Callback() } } vid.Render() + frame++ + if frame%600 == 0 { // save sram about every 10 sec + savefiles.SaveSRAM() + } } else { input.Poll() m.Update(dt) @@ -55,7 +62,7 @@ func runLoop(vid *video.Video, m *menu.Menu) { m.Render(dt) } m.RenderNotifications() - if state.Global.FastForward { + if state.FastForward { glfw.SwapInterval(0) } else { glfw.SwapInterval(1) @@ -73,9 +80,9 @@ func main() { } flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) - flag.StringVar(&state.Global.CorePath, "L", "", "Path to the libretro core") - flag.BoolVar(&state.Global.Verbose, "v", false, "Verbose logs") - flag.BoolVar(&state.Global.LudOS, "ludos", false, "Expose the features related to LudOS") + flag.StringVar(&state.CorePath, "L", "", "Path to the libretro core") + flag.BoolVar(&state.Verbose, "v", false, "Verbose logs") + flag.BoolVar(&state.LudOS, "ludos", false, "Expose the features related to LudOS") flag.Parse() args := flag.Args() @@ -89,7 +96,7 @@ func main() { } defer glfw.Terminate() - state.Global.DB, err = scanner.LoadDB(settings.Current.DatabaseDirectory) + state.DB, err = scanner.LoadDB(settings.Current.DatabaseDirectory) if err != nil { log.Println("Can't load game database:", err) } @@ -108,8 +115,8 @@ func main() { input.Init(vid) - if len(state.Global.CorePath) > 0 { - err := core.Load(state.Global.CorePath) + if len(state.CorePath) > 0 { + err := core.Load(state.CorePath) if err == nil { if len(gamePath) > 0 { err := core.LoadGame(gamePath) @@ -125,7 +132,7 @@ func main() { } // No game running? display the menu - state.Global.MenuActive = !state.Global.CoreRunning + state.MenuActive = !state.CoreRunning runLoop(vid, m) diff --git a/menu/hints.go b/menu/hints.go index fc67d82..e2c4f35 100644 --- a/menu/hints.go +++ b/menu/hints.go @@ -7,12 +7,12 @@ import ( // Used to easily compose different hint bars based on the context. func stackHint(stack *float32, icon uint32, label string, h int) { - vid.Font.SetColor(darkGrey) + menu.Font.SetColor(darkGrey) *stack += 30 * menu.ratio - vid.DrawImage(icon, *stack, float32(h)-70*menu.ratio, 70*menu.ratio, 70*menu.ratio, 1.0, darkGrey) + menu.DrawImage(icon, *stack, float32(h)-70*menu.ratio, 70*menu.ratio, 70*menu.ratio, 1.0, darkGrey) *stack += 70 * menu.ratio - vid.Font.Printf(*stack, float32(h)-23*menu.ratio, 0.4*menu.ratio, label) - *stack += vid.Font.Width(0.4*menu.ratio, label) + menu.Font.Printf(*stack, float32(h)-23*menu.ratio, 0.4*menu.ratio, label) + *stack += menu.Font.Width(0.4*menu.ratio, label) } func hintIcons() (arrows, upDown, leftRight, a, b, x, y, start, slct, guide uint32) { diff --git a/menu/input.go b/menu/input.go index d879bf0..5bf5744 100644 --- a/menu/input.go +++ b/menu/input.go @@ -162,10 +162,10 @@ func (m *Menu) ProcessHotkeys() { combo := input.NewState[0][libretro.DeviceIDJoypadL3] == 1 && input.Pressed[0][libretro.DeviceIDJoypadR3] == 1 combo = combo || input.Pressed[0][libretro.DeviceIDJoypadL3] == 1 && input.NewState[0][libretro.DeviceIDJoypadR3] == 1 - if (input.Pressed[0][input.ActionMenuToggle] == 1 || combo) && state.Global.CoreRunning { - state.Global.MenuActive = !state.Global.MenuActive - state.Global.FastForward = false - if state.Global.MenuActive { + if (input.Pressed[0][input.ActionMenuToggle] == 1 || combo) && state.CoreRunning { + state.MenuActive = !state.MenuActive + state.FastForward = false + if state.MenuActive { audio.PlayEffect(audio.Effects["notice"]) } else { audio.PlayEffect(audio.Effects["notice_back"]) @@ -175,7 +175,7 @@ func (m *Menu) ProcessHotkeys() { // Toggle fullscreen if ActionFullscreenToggle is pressed if input.Pressed[0][input.ActionFullscreenToggle] == 1 { settings.Current.VideoFullscreen = !settings.Current.VideoFullscreen - vid.Reconfigure(settings.Current.VideoFullscreen) + m.Reconfigure(settings.Current.VideoFullscreen) m.ContextReset() err := settings.Save() if err != nil { @@ -183,9 +183,9 @@ func (m *Menu) ProcessHotkeys() { } } - if input.Pressed[0][input.ActionFastForwardToggle] == 1 && !state.Global.MenuActive { - state.Global.FastForward = !state.Global.FastForward - if state.Global.FastForward { + if input.Pressed[0][input.ActionFastForwardToggle] == 1 && !state.MenuActive { + state.FastForward = !state.FastForward + if state.FastForward { ntf.DisplayAndLog(ntf.Info, "Menu", "Fast forward ON") } else { ntf.DisplayAndLog(ntf.Info, "Menu", "Fast forward OFF") @@ -196,7 +196,7 @@ func (m *Menu) ProcessHotkeys() { // in case a game is running if input.Pressed[0][input.ActionShouldClose] == 1 { askConfirmation(func() { - vid.Window.SetShouldClose(true) + m.SetShouldClose(true) }) } } diff --git a/menu/menu.go b/menu/menu.go index b71b767..192b5f6 100644 --- a/menu/menu.go +++ b/menu/menu.go @@ -12,7 +12,6 @@ import ( "github.com/libretro/ludo/video" ) -var vid *video.Video var menu *Menu // Menu is a type holding the menu state, the stack of scenes, tweens, etc @@ -23,17 +22,18 @@ type Menu struct { scroll float32 ratio float32 t float64 + + *video.Video // we embbed video here to have direct access to drawing functions } // Init initializes the menu. // If a game is already running, it will warp the user to the quick menu. // If not, it will display the menu tabs. func Init(v *video.Video) *Menu { - vid = v - - w, _ := v.Window.GetFramebufferSize() + w, _ := v.GetFramebufferSize() menu = &Menu{} + menu.Video = v menu.stack = []Scene{} menu.tweens = make(Tweens) menu.ratio = float32(w) / 1920 @@ -56,18 +56,18 @@ func (m *Menu) Push(s Scene) { func (m *Menu) Render(dt float32) { // Early return to not render the menu, in case MenuActive is set to false // during the same mainloop iteration - if !state.Global.MenuActive { + if !state.MenuActive { return } m.t += float64(dt * 8) - w, h := vid.Window.GetFramebufferSize() + w, h := m.GetFramebufferSize() m.ratio = float32(w) / 1920 - if state.Global.CoreRunning { - vid.DrawRect(0, 0, float32(w), float32(h), 0, bgColor.Alpha(0.85)) + if state.CoreRunning { + m.DrawRect(0, 0, float32(w), float32(h), 0, bgColor.Alpha(0.85)) } else { - vid.DrawRect(0, 0, float32(w), float32(h), 0, bgColor) + m.DrawRect(0, 0, float32(w), float32(h), 0, bgColor) } m.tweens.Update(dt) diff --git a/menu/menu_test.go b/menu/menu_test.go index 25129ae..734796c 100644 --- a/menu/menu_test.go +++ b/menu/menu_test.go @@ -5,8 +5,6 @@ import ( "reflect" "testing" - "github.com/go-gl/glfw/v3.3/glfw" - "github.com/libretro/ludo/state" "github.com/libretro/ludo/video" @@ -14,26 +12,8 @@ import ( "github.com/tanema/gween/ease" ) -type WindowMock struct{} - -func (m WindowMock) GetFramebufferSize() (width, height int) { return 320, 240 } -func (m WindowMock) Destroy() {} -func (m WindowMock) MakeContextCurrent() {} -func (m WindowMock) SetSizeLimits(minw, minh, maxw, maxh int) {} -func (m WindowMock) SetInputMode(mode glfw.InputMode, value int) {} -func (m WindowMock) GetKey(key glfw.Key) glfw.Action { return 0 } -func (m WindowMock) SetShouldClose(bool) {} -func (m WindowMock) ShouldClose() bool { return false } -func (m WindowMock) SetTitle(string) {} -func (m WindowMock) SwapBuffers() {} - func Test_WarpToQuickMenu(t *testing.T) { - - var vid = &video.Video{ - Window: &WindowMock{}, - } - - m := Init(vid) + m := Init(&video.Video{}) t.Run("Starts with a single scene if no game is running", func(t *testing.T) { got := len(menu.stack) @@ -51,7 +31,7 @@ func Test_WarpToQuickMenu(t *testing.T) { } }) - state.Global.CoreRunning = true + state.CoreRunning = true m.WarpToQuickMenu() t.Run("Warps at the quick menu if a game is launched", func(t *testing.T) { diff --git a/menu/notifications.go b/menu/notifications.go index 8bf19f4..b71945c 100644 --- a/menu/notifications.go +++ b/menu/notifications.go @@ -21,8 +21,8 @@ var severityBgColor = map[ntf.Severity]video.Color{ // RenderNotifications draws the list of notification messages on the viewport func (m *Menu) RenderNotifications() { - fbw, fbh := vid.Window.GetFramebufferSize() - vid.Font.UpdateResolution(fbw, fbh) + fbw, fbh := m.GetFramebufferSize() + m.Font.UpdateResolution(fbw, fbh) var h float32 = 75 stack := h for _, n := range ntf.List() { @@ -31,10 +31,10 @@ func (m *Menu) RenderNotifications() { fading = 1 } offset := fading*h - h - lw := vid.Font.Width(0.5*m.ratio, n.Message) + lw := m.Font.Width(0.5*m.ratio, n.Message) fg := severityFgColor[n.Severity] bg := severityBgColor[n.Severity] - vid.DrawRect( + m.DrawRect( 25*m.ratio, (stack+offset-46)*m.ratio, lw+40*m.ratio, @@ -42,8 +42,8 @@ func (m *Menu) RenderNotifications() { 0.25, bg.Alpha(fading), ) - vid.Font.SetColor(fg.Alpha(fading)) - vid.Font.Printf( + m.Font.SetColor(fg.Alpha(fading)) + m.Font.Printf( 45*m.ratio, (stack+offset)*m.ratio, 0.5*m.ratio, diff --git a/menu/palette.go b/menu/palette.go index e3a8e59..c392ea3 100644 --- a/menu/palette.go +++ b/menu/palette.go @@ -40,7 +40,7 @@ func (m *Menu) UpdatePalette() { cursorBg = cyan textColor = black - if state.Global.CoreRunning || settings.Current.VideoDarkMode { + if state.CoreRunning || settings.Current.VideoDarkMode { bgColor = darkerGrey cursorBg = darkBlue textColor = white diff --git a/menu/scene.go b/menu/scene.go index 0548c61..06c8300 100644 --- a/menu/scene.go +++ b/menu/scene.go @@ -149,11 +149,11 @@ func genericSegueNext(list *entry) { // genericDrawCursor draws the blinking rectangular background of the active // menu entry func genericDrawCursor(list *entry) { - w, h := vid.Window.GetFramebufferSize() - vid.DrawImage(menu.icons["arrow"], + w, h := menu.GetFramebufferSize() + menu.DrawImage(menu.icons["arrow"], 530*menu.ratio, float32(h)*list.cursor.yp-35*menu.ratio, 70*menu.ratio, 70*menu.ratio, 1, cursorBg.Alpha(list.cursor.alpha)) - vid.DrawRect( + menu.DrawRect( 550*menu.ratio, float32(h)*list.cursor.yp-50*menu.ratio, float32(w)-630*menu.ratio, 100*menu.ratio, 1, cursorBg.Alpha(list.cursor.alpha)) } @@ -161,11 +161,11 @@ func genericDrawCursor(list *entry) { // thumbnailDrawCursor draws the blinking rectangular background of the active // menu entry when there is a thumbnail func thumbnailDrawCursor(list *entry) { - w, h := vid.Window.GetFramebufferSize() - vid.DrawImage(menu.icons["arrow"], + w, h := menu.GetFramebufferSize() + menu.DrawImage(menu.icons["arrow"], 500*menu.ratio, float32(h)*list.cursor.yp-50*menu.ratio, 100*menu.ratio, 100*menu.ratio, 1, cursorBg.Alpha(list.cursor.alpha)) - vid.DrawRect( + menu.DrawRect( 530*menu.ratio, float32(h)*list.cursor.yp-120*menu.ratio, float32(w)-630*menu.ratio, 240*menu.ratio, 0.2, cursorBg.Alpha(list.cursor.alpha)) } @@ -173,11 +173,11 @@ func thumbnailDrawCursor(list *entry) { // genericRender renders a vertical list of menu entries // It also display values of settings if we are displaying a settings scene func genericRender(list *entry) { - w, h := vid.Window.GetFramebufferSize() + w, h := menu.GetFramebufferSize() genericDrawCursor(list) - vid.ScissorStart(int32(530*menu.ratio), 0, int32(1310*menu.ratio), int32(h)) + menu.ScissorStart(int32(530*menu.ratio), 0, int32(1310*menu.ratio), int32(h)) for _, e := range list.children { if e.yp < -0.1 || e.yp > 1.1 { @@ -186,15 +186,15 @@ func genericRender(list *entry) { fontOffset := 64 * 0.7 * menu.ratio * 0.3 - vid.DrawImage(menu.icons[e.icon], + menu.DrawImage(menu.icons[e.icon], 610*menu.ratio-64*0.5*menu.ratio, float32(h)*e.yp-14*menu.ratio-64*0.5*menu.ratio+fontOffset, 128*menu.ratio, 128*menu.ratio, 0.5, textColor.Alpha(e.iconAlpha)) if e.labelAlpha > 0 { - vid.Font.SetColor(textColor.Alpha(e.labelAlpha)) - vid.Font.Printf( + menu.Font.SetColor(textColor.Alpha(e.labelAlpha)) + menu.Font.Printf( 670*menu.ratio, float32(h)*e.yp+fontOffset, 0.5*menu.ratio, e.label) @@ -202,8 +202,8 @@ func genericRender(list *entry) { if e.widget != nil { e.widget(&e) } else if e.stringValue != nil { - lw := vid.Font.Width(0.5*menu.ratio, e.stringValue()) - vid.Font.Printf( + lw := menu.Font.Width(0.5*menu.ratio, e.stringValue()) + menu.Font.Printf( float32(w)-lw-128*menu.ratio, float32(h)*e.yp+fontOffset, 0.5*menu.ratio, e.stringValue()) @@ -211,17 +211,17 @@ func genericRender(list *entry) { } } - vid.ScissorEnd() + menu.ScissorEnd() } func genericDrawHintBar() { - w, h := vid.Window.GetFramebufferSize() - vid.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) + w, h := menu.GetFramebufferSize() + menu.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) _, upDown, _, a, b, _, _, _, _, guide := hintIcons() var stack float32 - if state.Global.CoreRunning { + if state.CoreRunning { stackHint(&stack, guide, "RESUME", h) } stackHint(&stack, upDown, "NAVIGATE", h) diff --git a/menu/scene_core_options.go b/menu/scene_core_options.go index 7c11ae7..e347d7d 100644 --- a/menu/scene_core_options.go +++ b/menu/scene_core_options.go @@ -80,13 +80,13 @@ func (s *sceneCoreOptions) render() { } func (s *sceneCoreOptions) drawHintBar() { - w, h := vid.Window.GetFramebufferSize() - vid.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) + w, h := menu.GetFramebufferSize() + menu.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) _, upDown, leftRight, _, b, _, _, _, _, guide := hintIcons() var stack float32 - if state.Global.CoreRunning { + if state.CoreRunning { stackHint(&stack, guide, "RESUME", h) } stackHint(&stack, upDown, "NAVIGATE", h) diff --git a/menu/scene_dialog.go b/menu/scene_dialog.go index 0a199db..8d03653 100644 --- a/menu/scene_dialog.go +++ b/menu/scene_dialog.go @@ -47,15 +47,15 @@ func (s *sceneDialog) update(dt float32) { } func (s *sceneDialog) render() { - w, h := vid.Window.GetFramebufferSize() + w, h := menu.GetFramebufferSize() fw := float32(w) fh := float32(h) - vid.DrawRect(0, 0, fw, fh, 0, black.Alpha(0.85)) + menu.DrawRect(0, 0, fw, fh, 0, black.Alpha(0.85)) var width float32 = 1000 var height float32 = 400 - vid.DrawRect( + menu.DrawRect( fw/2-width/2*menu.ratio, fh/2-height/2*menu.ratio, width*menu.ratio, @@ -64,41 +64,41 @@ func (s *sceneDialog) render() { white, ) - vid.Font.SetColor(orange) + menu.Font.SetColor(orange) msg1 := "A game is currently running." - lw1 := vid.Font.Width(0.7*menu.ratio, msg1) - vid.Font.Printf(fw/2-lw1/2, fh/2-120*menu.ratio+20*menu.ratio, 0.7*menu.ratio, msg1) - vid.Font.SetColor(black) + lw1 := menu.Font.Width(0.7*menu.ratio, msg1) + menu.Font.Printf(fw/2-lw1/2, fh/2-120*menu.ratio+20*menu.ratio, 0.7*menu.ratio, msg1) + menu.Font.SetColor(black) msg2 := "If you have not saved yet, your progress will be lost." - lw2 := vid.Font.Width(0.5*menu.ratio, msg2) - vid.Font.Printf(fw/2-lw2/2, fh/2-30*menu.ratio+20*menu.ratio, 0.5*menu.ratio, msg2) + lw2 := menu.Font.Width(0.5*menu.ratio, msg2) + menu.Font.Printf(fw/2-lw2/2, fh/2-30*menu.ratio+20*menu.ratio, 0.5*menu.ratio, msg2) msg3 := "Do you want to exit Ludo anyway?" - lw3 := vid.Font.Width(0.5*menu.ratio, msg3) - vid.Font.Printf(fw/2-lw3/2, fh/2+30*menu.ratio+20*menu.ratio, 0.5*menu.ratio, msg3) + lw3 := menu.Font.Width(0.5*menu.ratio, msg3) + menu.Font.Printf(fw/2-lw3/2, fh/2+30*menu.ratio+20*menu.ratio, 0.5*menu.ratio, msg3) - vid.Font.SetColor(darkGrey) + menu.Font.SetColor(darkGrey) var margin float32 = 15 _, _, _, a, b, _, _, _, _, _ := hintIcons() - vid.DrawImage( + menu.DrawImage( b, fw/2-width/2*menu.ratio+margin*menu.ratio, fh/2+height/2*menu.ratio-70*menu.ratio-margin*menu.ratio, 70*menu.ratio, 70*menu.ratio, 1.0, darkGrey) - vid.Font.Printf( + menu.Font.Printf( fw/2-width/2*menu.ratio+margin*menu.ratio+70*menu.ratio, fh/2+height/2*menu.ratio-23*menu.ratio-margin*menu.ratio, 0.4*menu.ratio, "NO") - vid.DrawImage( + menu.DrawImage( a, fw/2+width/2*menu.ratio-150*menu.ratio-margin*menu.ratio, fh/2+height/2*menu.ratio-70*menu.ratio-margin*menu.ratio, 70*menu.ratio, 70*menu.ratio, 1.0, darkGrey) - vid.Font.Printf( + menu.Font.Printf( fw/2+width/2*menu.ratio-150*menu.ratio-margin*menu.ratio+70*menu.ratio, fh/2+height/2*menu.ratio-23*menu.ratio-margin*menu.ratio, 0.4*menu.ratio, diff --git a/menu/scene_history.go b/menu/scene_history.go index 5447cc2..cd79891 100644 --- a/menu/scene_history.go +++ b/menu/scene_history.go @@ -54,14 +54,14 @@ func loadHistoryEntry(list Scene, game history.Game) { ntf.DisplayAndLog(ntf.Error, "Menu", "Core not found: %s", filepath.Base(corePath)) return } - if state.Global.CorePath != corePath { + if state.CorePath != corePath { err := core.Load(corePath) if err != nil { ntf.DisplayAndLog(ntf.Error, "Menu", err.Error()) return } } - if state.Global.GamePath != game.Path { + if state.GamePath != game.Path { err := core.LoadGame(game.Path) if err != nil { ntf.DisplayAndLog(ntf.Error, "Menu", err.Error()) @@ -76,7 +76,7 @@ func loadHistoryEntry(list Scene, game history.Game) { list.segueNext() menu.Push(buildQuickMenu()) menu.tweens.FastForward() // position the elements without animating - state.Global.MenuActive = false + state.MenuActive = false } else { list.segueNext() menu.Push(buildQuickMenu()) @@ -108,11 +108,11 @@ func (s *sceneHistory) update(dt float32) { func (s *sceneHistory) render() { list := &s.entry - _, h := vid.Window.GetFramebufferSize() + _, h := menu.GetFramebufferSize() thumbnailDrawCursor(list) - vid.ScissorStart(int32(510*menu.ratio), 0, int32(1310*menu.ratio), int32(h)) + menu.ScissorStart(int32(510*menu.ratio), 0, int32(1310*menu.ratio), int32(h)) for i, e := range list.children { if e.yp < -0.1 || e.yp > 1.1 { @@ -131,18 +131,18 @@ func (s *sceneHistory) render() { 170*menu.ratio, 128*menu.ratio, e.scale, white.Alpha(e.iconAlpha), ) - vid.DrawBorder( + menu.DrawBorder( 680*menu.ratio-85*e.scale*menu.ratio, float32(h)*e.yp-14*menu.ratio-64*e.scale*menu.ratio+fontOffset, 170*menu.ratio*e.scale, 128*menu.ratio*e.scale, 0.02/e.scale, textColor.Alpha(e.iconAlpha)) - if e.path == state.Global.GamePath && e.path != "" { - vid.DrawCircle( + if e.path == state.GamePath && e.path != "" { + menu.DrawCircle( 680*menu.ratio, float32(h)*e.yp-14*menu.ratio+fontOffset, 90*menu.ratio*e.scale, black.Alpha(e.iconAlpha)) - vid.DrawImage(menu.icons["resume"], + menu.DrawImage(menu.icons["resume"], 680*menu.ratio-25*e.scale*menu.ratio, float32(h)*e.yp-14*menu.ratio-25*e.scale*menu.ratio+fontOffset, 50*menu.ratio, 50*menu.ratio, @@ -155,47 +155,47 @@ func (s *sceneHistory) render() { slOffset = 30 * menu.ratio * e.subLabelAlpha } - vid.Font.SetColor(textColor.Alpha(e.labelAlpha)) + menu.Font.SetColor(textColor.Alpha(e.labelAlpha)) stack := 840 * menu.ratio - vid.Font.Printf( + menu.Font.Printf( 840*menu.ratio, float32(h)*e.yp+fontOffset-slOffset, 0.5*menu.ratio, e.label) - stack += float32(int(vid.Font.Width(0.5*menu.ratio, e.label))) + stack += float32(int(menu.Font.Width(0.5*menu.ratio, e.label))) stack += 10 for _, tag := range e.tags { if _, ok := menu.icons[tag]; ok { stack += 20 - vid.DrawImage( + menu.DrawImage( menu.icons[tag], stack, float32(h)*e.yp-22*menu.ratio-slOffset, 60*menu.ratio, 44*menu.ratio, 1.0, white.Alpha(e.tagAlpha)) - vid.DrawBorder(stack, float32(h)*e.yp-22*menu.ratio-slOffset, + menu.DrawBorder(stack, float32(h)*e.yp-22*menu.ratio-slOffset, 60*menu.ratio, 44*menu.ratio, 0.05/menu.ratio, black.Alpha(e.tagAlpha/4)) stack += 60 * menu.ratio } } - vid.Font.SetColor(mediumGrey.Alpha(e.subLabelAlpha)) - vid.Font.Printf( + menu.Font.SetColor(mediumGrey.Alpha(e.subLabelAlpha)) + menu.Font.Printf( 840*menu.ratio, float32(h)*e.yp+fontOffset+60*menu.ratio-slOffset, 0.5*menu.ratio, e.subLabel) } } - vid.ScissorEnd() + menu.ScissorEnd() } func (s *sceneHistory) drawHintBar() { - w, h := vid.Window.GetFramebufferSize() - vid.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) + w, h := menu.GetFramebufferSize() + menu.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) _, upDown, _, a, b, _, _, _, _, guide := hintIcons() var stack float32 - if state.Global.CoreRunning { + if state.CoreRunning { stackHint(&stack, guide, "RESUME", h) } stackHint(&stack, upDown, "NAVIGATE", h) diff --git a/menu/scene_keyboard.go b/menu/scene_keyboard.go index 906ea47..96765b2 100644 --- a/menu/scene_keyboard.go +++ b/menu/scene_keyboard.go @@ -55,7 +55,7 @@ func (s *sceneKeyboard) Entry() *entry { } func (s *sceneKeyboard) segueMount() { - _, h := vid.Window.GetFramebufferSize() + _, h := menu.GetFramebufferSize() s.y = float32(h) s.alpha = 0 menu.tweens[&s.y] = gween.New(s.y, 0, 0.15, ease.OutSine) @@ -149,7 +149,7 @@ func (s *sceneKeyboard) update(dt float32) { } func (s *sceneKeyboard) render() { - w, h := vid.Window.GetFramebufferSize() + w, h := menu.GetFramebufferSize() lines := float32(4) kbh := float32(h) * 0.6 ksp := (kbh - (50 * menu.ratio)) / (lines + 1) @@ -157,34 +157,34 @@ func (s *sceneKeyboard) render() { ttw := 10 * ksp // Background - vid.DrawRect(0, 0, float32(w), float32(h), 0, + menu.DrawRect(0, 0, float32(w), float32(h), 0, white.Alpha(s.alpha)) // Label - vid.Font.SetColor(black) - vid.Font.Printf( + menu.Font.SetColor(black) + menu.Font.Printf( float32(w)/2-ttw/2, s.y+float32(h)*0.15-ksz/2+ksz*0.6, ksz/260, s.label) // Value - vid.DrawRect(float32(w)/2-ttw/2, s.y+float32(h)*0.25-ksz/2, ttw, ksz, 0, + menu.DrawRect(float32(w)/2-ttw/2, s.y+float32(h)*0.25-ksz/2, ttw, ksz, 0, video.Color{R: 0.95, G: 0.95, B: 0.95, A: 1}) - vid.Font.Printf( + menu.Font.Printf( float32(w)/2-ttw/2+ksz/4, s.y+float32(h)*0.25-ksz/2+ksz*0.62, ksz/200, s.value+"|") // Keyboard - vid.DrawRect(0, s.y+float32(h)-kbh, float32(w), kbh, 0, black) + menu.DrawRect(0, s.y+float32(h)-kbh, float32(w), kbh, 0, black) - vid.Font.SetColor(white) + menu.Font.SetColor(white) for i, key := range layouts[s.layout] { x := float32(i%10)*ksp - ttw/2 + float32(w)/2 y := s.y + float32(i/10)*ksp + ksp/2 + float32(h) - kbh - gw := vid.Font.Width(ksz/200, key) + gw := menu.Font.Width(ksz/200, key) c1 := video.Color{R: 0.15, G: 0.15, B: 0.15, A: 1} c2 := video.Color{R: 0.25, G: 0.25, B: 0.25, A: 1} @@ -193,10 +193,10 @@ func (s *sceneKeyboard) render() { c2 = video.Color{R: 0.45, G: 0.45, B: 0.45, A: 1} } - vid.DrawRect(x, y, ksz, ksz, 0.2, c1) - vid.DrawRect(x, y, ksz, ksz*0.95, 0.2, c2) + menu.DrawRect(x, y, ksz, ksz, 0.2, c1) + menu.DrawRect(x, y, ksz, ksz*0.95, 0.2, c2) - vid.Font.Printf( + menu.Font.Printf( x+ksz/2-gw/2, y+ksz*0.6, ksz/200, key) @@ -204,8 +204,8 @@ func (s *sceneKeyboard) render() { } func (s *sceneKeyboard) drawHintBar() { - w, h := vid.Window.GetFramebufferSize() - vid.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) + w, h := menu.GetFramebufferSize() + menu.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) arrows, _, _, a, b, x, y, start, _, _ := hintIcons() diff --git a/menu/scene_main.go b/menu/scene_main.go index e3182ea..b7b2ff2 100644 --- a/menu/scene_main.go +++ b/menu/scene_main.go @@ -23,7 +23,7 @@ func buildMainMenu() Scene { usr, _ := user.Current() - if state.Global.CoreRunning { + if state.CoreRunning { list.children = append(list.children, entry{ label: "Quick Menu", icon: "subsetting", @@ -52,7 +52,7 @@ func buildMainMenu() Scene { label: "Load Game", icon: "subsetting", callbackOK: func() { - if state.Global.Core != nil { + if state.Core != nil { list.segueNext() menu.Push(buildExplorer( usr.HomeDir, @@ -66,7 +66,7 @@ func buildMainMenu() Scene { }, }) - if state.Global.LudOS { + if state.LudOS { list.children = append(list.children, entry{ label: "Updater", icon: "subsetting", @@ -97,7 +97,7 @@ func buildMainMenu() Scene { icon: "subsetting", callbackOK: func() { askConfirmation(func() { - vid.Window.SetShouldClose(true) + menu.SetShouldClose(true) }) }, }) @@ -126,10 +126,10 @@ func gameExplorerCb(path string) { history.Push(history.Game{ Path: path, Name: utils.FileName(path), - CorePath: state.Global.CorePath, + CorePath: state.CorePath, }) menu.WarpToQuickMenu() - state.Global.MenuActive = false + state.MenuActive = false } // Shutdown the operating system @@ -150,9 +150,9 @@ func cleanReboot() { // Displays a confirmation dialog before performing an irreversible action func askConfirmation(cb func()) { - if state.Global.CoreRunning { - if !state.Global.MenuActive { - state.Global.MenuActive = true + if state.CoreRunning { + if !state.MenuActive { + state.MenuActive = true } menu.Push(buildDialog(func() { cb() diff --git a/menu/scene_playlist.go b/menu/scene_playlist.go index 6dbf072..04959f6 100644 --- a/menu/scene_playlist.go +++ b/menu/scene_playlist.go @@ -89,13 +89,13 @@ func loadPlaylistEntry(list *scenePlaylist, playlist string, game playlists.Game ntf.DisplayAndLog(ntf.Error, "Menu", "Core not found: %s", filepath.Base(corePath)) return } - if state.Global.CorePath != corePath { + if state.CorePath != corePath { if err := core.Load(corePath); err != nil { ntf.DisplayAndLog(ntf.Error, "Menu", err.Error()) return } } - if state.Global.GamePath != game.Path { + if state.GamePath != game.Path { if err := core.LoadGame(game.Path); err != nil { ntf.DisplayAndLog(ntf.Error, "Menu", err.Error()) return @@ -109,7 +109,7 @@ func loadPlaylistEntry(list *scenePlaylist, playlist string, game playlists.Game list.segueNext() menu.Push(buildQuickMenu()) menu.tweens.FastForward() // position the elements without animating - state.Global.MenuActive = false + state.MenuActive = false } else { list.segueNext() menu.Push(buildQuickMenu()) @@ -141,11 +141,11 @@ func (s *scenePlaylist) update(dt float32) { func (s *scenePlaylist) render() { list := &s.entry - _, h := vid.Window.GetFramebufferSize() + _, h := menu.GetFramebufferSize() thumbnailDrawCursor(list) - vid.ScissorStart(int32(510*menu.ratio), 0, int32(1310*menu.ratio), int32(h)) + menu.ScissorStart(int32(510*menu.ratio), 0, int32(1310*menu.ratio), int32(h)) for i, e := range list.children { if e.yp < -0.1 || e.yp > 1.1 { @@ -164,41 +164,41 @@ func (s *scenePlaylist) render() { 170*menu.ratio, 128*menu.ratio, e.scale, white.Alpha(e.iconAlpha), ) - vid.DrawBorder( + menu.DrawBorder( 680*menu.ratio-85*e.scale*menu.ratio, float32(h)*e.yp-14*menu.ratio-64*e.scale*menu.ratio+fontOffset, 170*menu.ratio*e.scale, 128*menu.ratio*e.scale, 0.02/e.scale, textColor.Alpha(e.iconAlpha)) - if e.path == state.Global.GamePath && e.path != "" { - vid.DrawCircle( + if e.path == state.GamePath && e.path != "" { + menu.DrawCircle( 680*menu.ratio, float32(h)*e.yp-14*menu.ratio+fontOffset, 90*menu.ratio*e.scale, black.Alpha(e.iconAlpha)) - vid.DrawImage(menu.icons["resume"], + menu.DrawImage(menu.icons["resume"], 680*menu.ratio-25*e.scale*menu.ratio, float32(h)*e.yp-14*menu.ratio-25*e.scale*menu.ratio+fontOffset, 50*menu.ratio, 50*menu.ratio, e.scale, white.Alpha(e.iconAlpha)) } - vid.Font.SetColor(textColor.Alpha(e.labelAlpha)) + menu.Font.SetColor(textColor.Alpha(e.labelAlpha)) stack := 840 * menu.ratio - vid.Font.Printf( + menu.Font.Printf( 840*menu.ratio, float32(h)*e.yp+fontOffset, 0.5*menu.ratio, e.label) - stack += float32(int(vid.Font.Width(0.5*menu.ratio, e.label))) + stack += float32(int(menu.Font.Width(0.5*menu.ratio, e.label))) stack += 10 for _, tag := range e.tags { if _, ok := menu.icons[tag]; ok { stack += 20 - vid.DrawImage( + menu.DrawImage( menu.icons[tag], stack, float32(h)*e.yp-22*menu.ratio, 60*menu.ratio, 44*menu.ratio, 1.0, white.Alpha(e.tagAlpha)) - vid.DrawBorder(stack, float32(h)*e.yp-22*menu.ratio, + menu.DrawBorder(stack, float32(h)*e.yp-22*menu.ratio, 60*menu.ratio, 44*menu.ratio, 0.05/menu.ratio, black.Alpha(e.tagAlpha/4)) stack += 60 * menu.ratio } @@ -206,17 +206,17 @@ func (s *scenePlaylist) render() { } } - vid.ScissorEnd() + menu.ScissorEnd() } func (s *scenePlaylist) drawHintBar() { - w, h := vid.Window.GetFramebufferSize() - vid.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) + w, h := menu.GetFramebufferSize() + menu.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) _, upDown, _, a, b, _, _, _, _, guide := hintIcons() var stack float32 - if state.Global.CoreRunning { + if state.CoreRunning { stackHint(&stack, guide, "RESUME", h) } stackHint(&stack, upDown, "NAVIGATE", h) diff --git a/menu/scene_quick.go b/menu/scene_quick.go index 5263e5a..7dbd2c1 100644 --- a/menu/scene_quick.go +++ b/menu/scene_quick.go @@ -18,8 +18,8 @@ func buildQuickMenu() Scene { label: "Resume", icon: "resume", callbackOK: func() { - state.Global.MenuActive = false - state.Global.FastForward = false + state.MenuActive = false + state.FastForward = false }, }) @@ -27,9 +27,9 @@ func buildQuickMenu() Scene { label: "Reset", icon: "reset", callbackOK: func() { - state.Global.Core.Reset() - state.Global.MenuActive = false - state.Global.FastForward = false + state.Core.Reset() + state.MenuActive = false + state.FastForward = false }, }) @@ -46,8 +46,8 @@ func buildQuickMenu() Scene { label: "Take Screenshot", icon: "screenshot", callbackOK: func() { - name := utils.DatedName(state.Global.GamePath) - err := vid.TakeScreenshot(name) + name := utils.DatedName(state.GamePath) + err := menu.TakeScreenshot(name) if err != nil { ntf.DisplayAndLog(ntf.Error, "Menu", err.Error()) } else { diff --git a/menu/scene_savestates.go b/menu/scene_savestates.go index 82fe791..8c12c73 100644 --- a/menu/scene_savestates.go +++ b/menu/scene_savestates.go @@ -24,8 +24,8 @@ func buildSavestates() Scene { label: "Save State", icon: "savestate", callbackOK: func() { - name := utils.DatedName(state.Global.GamePath) - err := vid.TakeScreenshot(name) + name := utils.DatedName(state.GamePath) + err := menu.TakeScreenshot(name) if err != nil { ntf.DisplayAndLog(ntf.Error, "Menu", err.Error()) } @@ -40,7 +40,7 @@ func buildSavestates() Scene { }, }) - gameName := utils.FileName(state.Global.GamePath) + gameName := utils.FileName(state.GamePath) paths, _ := filepath.Glob(settings.Current.SavestatesDirectory + "/" + gameName + "@*.state") sort.Sort(sort.Reverse(sort.StringSlice(paths))) for _, path := range paths { @@ -55,7 +55,7 @@ func buildSavestates() Scene { if err != nil { ntf.DisplayAndLog(ntf.Error, "Menu", err.Error()) } else { - state.Global.MenuActive = false + state.MenuActive = false ntf.DisplayAndLog(ntf.Success, "Menu", "State loaded.") } @@ -92,7 +92,7 @@ func (s *sceneSavestates) update(dt float32) { func (s *sceneSavestates) render() { list := &s.entry - _, h := vid.Window.GetFramebufferSize() + _, h := menu.GetFramebufferSize() thumbnailDrawCursor(list) @@ -112,21 +112,21 @@ func (s *sceneSavestates) render() { 170*menu.ratio, 128*menu.ratio, e.scale, textColor.Alpha(e.iconAlpha), ) - vid.DrawBorder( + menu.DrawBorder( 680*menu.ratio-85*e.scale*menu.ratio, float32(h)*e.yp-14*menu.ratio-64*e.scale*menu.ratio+fontOffset, 170*menu.ratio*e.scale, 128*menu.ratio*e.scale, 0.02/e.scale, textColor.Alpha(e.iconAlpha)) if i == 0 { - vid.DrawImage(menu.icons["savestate"], + menu.DrawImage(menu.icons["savestate"], 680*menu.ratio-25*e.scale*menu.ratio, float32(h)*e.yp-14*menu.ratio-25*e.scale*menu.ratio+fontOffset, 50*menu.ratio, 50*menu.ratio, e.scale, textColor.Alpha(e.iconAlpha)) } - vid.Font.SetColor(textColor.Alpha(e.labelAlpha)) - vid.Font.Printf( + menu.Font.SetColor(textColor.Alpha(e.labelAlpha)) + menu.Font.Printf( 840*menu.ratio, float32(h)*e.yp+fontOffset, 0.5*menu.ratio, e.label) @@ -135,15 +135,15 @@ func (s *sceneSavestates) render() { } func (s *sceneSavestates) drawHintBar() { - w, h := vid.Window.GetFramebufferSize() - vid.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) + w, h := menu.GetFramebufferSize() + menu.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) ptr := menu.stack[len(menu.stack)-1].Entry().ptr _, upDown, _, a, b, _, _, _, _, guide := hintIcons() var stack float32 - if state.Global.CoreRunning { + if state.CoreRunning { stackHint(&stack, guide, "RESUME", h) } stackHint(&stack, upDown, "NAVIGATE", h) diff --git a/menu/scene_settings.go b/menu/scene_settings.go index 290f880..5038644 100644 --- a/menu/scene_settings.go +++ b/menu/scene_settings.go @@ -25,15 +25,15 @@ type sceneSettings struct { // If we're in program mode, hide settings flagged with hide:"program" func isHidden(f *structs.Field) bool { return f.Tag("hide") == "always" || - (state.Global.LudOS && f.Tag("hide") == "ludos") || - (!state.Global.LudOS && f.Tag("hide") == "app") + (state.LudOS && f.Tag("hide") == "ludos") || + (!state.LudOS && f.Tag("hide") == "app") } func buildSettings() Scene { var list sceneSettings list.label = "Settings" - if state.Global.LudOS { + if state.LudOS { list.children = append(list.children, entry{ label: "Wi-Fi", icon: "subsetting", @@ -133,8 +133,8 @@ var widgets = map[string]func(*entry){ if e.value().(bool) { icon = "on" } - w, h := vid.Window.GetFramebufferSize() - vid.DrawImage(menu.icons[icon], + w, h := menu.GetFramebufferSize() + menu.DrawImage(menu.icons[icon], float32(w)-128*menu.ratio-128*menu.ratio, float32(h)*e.yp-64*1.25*menu.ratio, 128*menu.ratio, 128*menu.ratio, @@ -143,15 +143,15 @@ var widgets = map[string]func(*entry){ // Range widget for audio volume and similat float settings "range": func(e *entry) { - fbw, fbh := vid.Window.GetFramebufferSize() + fbw, fbh := menu.GetFramebufferSize() x := float32(fbw) - 128*menu.ratio - 175*menu.ratio y := float32(fbh)*e.yp - 4*menu.ratio w := 175 * menu.ratio h := 8 * menu.ratio - vid.DrawRect(x, y, w, h, 0.9, textColor.Alpha(e.iconAlpha/4)) + menu.DrawRect(x, y, w, h, 0.9, textColor.Alpha(e.iconAlpha/4)) w = 175 * menu.ratio * e.value().(float32) - vid.DrawRect(x, y, w, h, 0.9, textColor.Alpha(e.iconAlpha)) - vid.DrawCircle(x+w, y+4*menu.ratio, 38*menu.ratio, textColor.Alpha(e.iconAlpha)) + menu.DrawRect(x, y, w, h, 0.9, textColor.Alpha(e.iconAlpha)) + menu.DrawCircle(x+w, y+4*menu.ratio, 38*menu.ratio, textColor.Alpha(e.iconAlpha)) }, } @@ -163,7 +163,7 @@ var incrCallbacks = map[string]callbackIncrement{ v := f.Value().(bool) v = !v f.Set(v) - vid.Reconfigure(settings.Current.VideoFullscreen) + menu.Reconfigure(settings.Current.VideoFullscreen) menu.ContextReset() settings.Save() }, @@ -177,7 +177,7 @@ var incrCallbacks = map[string]callbackIncrement{ v = len(glfw.GetMonitors()) - 1 } f.Set(v) - vid.Reconfigure(settings.Current.VideoFullscreen) + menu.Reconfigure(settings.Current.VideoFullscreen) menu.ContextReset() settings.Save() }, @@ -193,7 +193,7 @@ var incrCallbacks = map[string]callbackIncrement{ i = 0 } f.Set(filters[i]) - vid.UpdateFilter(filters[i]) + menu.UpdateFilter(filters[i]) settings.Save() }, "VideoDarkMode": func(f *structs.Field, direction int) { @@ -272,14 +272,14 @@ func (s *sceneSettings) render() { } func (s *sceneSettings) drawHintBar() { - w, h := vid.Window.GetFramebufferSize() - vid.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) + w, h := menu.GetFramebufferSize() + menu.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) _, upDown, leftRight, a, b, _, _, _, _, guide := hintIcons() var stack float32 list := menu.stack[len(menu.stack)-1].Entry() - if state.Global.CoreRunning { + if state.CoreRunning { stackHint(&stack, guide, "RESUME", h) } stackHint(&stack, upDown, "NAVIGATE", h) diff --git a/menu/scene_tabs.go b/menu/scene_tabs.go index 964f01d..bbcfc3f 100644 --- a/menu/scene_tabs.go +++ b/menu/scene_tabs.go @@ -257,7 +257,7 @@ func (tabs *sceneTabs) update(dt float32) { } func (tabs sceneTabs) render() { - _, h := vid.Window.GetFramebufferSize() + _, h := menu.GetFramebufferSize() stackWidth := 710 * menu.ratio for i, e := range tabs.children { @@ -270,31 +270,31 @@ func (tabs sceneTabs) render() { stackWidth += e.width*menu.ratio + e.margin*menu.ratio if e.labelAlpha > 0 { - vid.Font.SetColor(c.Alpha(e.labelAlpha)) - lw := vid.Font.Width(0.5*menu.ratio, e.label) - vid.Font.Printf(x-lw/2, float32(int(float32(h)/2+250*menu.ratio)), 0.5*menu.ratio, e.label) - lw = vid.Font.Width(0.4*menu.ratio, e.subLabel) - vid.Font.Printf(x-lw/2, float32(int(float32(h)/2+330*menu.ratio)), 0.4*menu.ratio, e.subLabel) + menu.Font.SetColor(c.Alpha(e.labelAlpha)) + lw := menu.Font.Width(0.5*menu.ratio, e.label) + menu.Font.Printf(x-lw/2, float32(int(float32(h)/2+250*menu.ratio)), 0.5*menu.ratio, e.label) + lw = menu.Font.Width(0.4*menu.ratio, e.subLabel) + menu.Font.Printf(x-lw/2, float32(int(float32(h)/2+330*menu.ratio)), 0.4*menu.ratio, e.subLabel) } - vid.DrawImage(menu.icons["hexagon"], + menu.DrawImage(menu.icons["hexagon"], x-220*e.scale*menu.ratio, float32(h)/2-220*e.scale*menu.ratio, 440*menu.ratio, 440*menu.ratio, e.scale, c) - vid.DrawImage(menu.icons[e.icon], + menu.DrawImage(menu.icons[e.icon], x-128*e.scale*menu.ratio, float32(h)/2-128*e.scale*menu.ratio, 256*menu.ratio, 256*menu.ratio, e.scale, white.Alpha(e.iconAlpha)) } } func (tabs sceneTabs) drawHintBar() { - w, h := vid.Window.GetFramebufferSize() - vid.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) + w, h := menu.GetFramebufferSize() + menu.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) _, _, leftRight, a, _, _, _, _, _, guide := hintIcons() var stack float32 - if state.Global.CoreRunning { + if state.CoreRunning { stackHint(&stack, guide, "RESUME", h) } stackHint(&stack, leftRight, "NAVIGATE", h) diff --git a/menu/scene_wifi.go b/menu/scene_wifi.go index ec78295..0af9d25 100644 --- a/menu/scene_wifi.go +++ b/menu/scene_wifi.go @@ -85,8 +85,8 @@ func (s *sceneWiFi) render() { } func (s *sceneWiFi) drawHintBar() { - w, h := vid.Window.GetFramebufferSize() - vid.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) + w, h := menu.GetFramebufferSize() + menu.DrawRect(0, float32(h)-70*menu.ratio, float32(w), 70*menu.ratio, 0, lightGrey) _, upDown, _, a, b, _, _, _, _, _ := hintIcons() diff --git a/menu/thumbnail.go b/menu/thumbnail.go index a97c883..280fda3 100644 --- a/menu/thumbnail.go +++ b/menu/thumbnail.go @@ -76,7 +76,7 @@ func drawThumbnail(list *entry, i int, system, gameName string, x, y, w, h, scal } } - vid.DrawImage( + menu.DrawImage( list.children[i].thumbnail, x, y, w, h, scale, color, @@ -91,7 +91,7 @@ func drawSavestateThumbnail(list *entry, i int, path string, x, y, w, h, scale f } } - vid.DrawImage( + menu.DrawImage( list.children[i].thumbnail, x, y, w, h, scale, color, diff --git a/notifications/notifications.go b/notifications/notifications.go index ec23882..380527f 100644 --- a/notifications/notifications.go +++ b/notifications/notifications.go @@ -60,7 +60,7 @@ func Display(severity Severity, message string, duration float32) *Notification // DisplayAndLog creates a new notification and also logs the message to stdout. func DisplayAndLog(severity Severity, prefix, message string, vars ...interface{}) *Notification { msg := fmt.Sprintf(message, vars...) - if state.Global.Verbose { + if state.Verbose { log.Print("[" + prefix + "]: " + msg + "\n") } return Display(severity, msg, Medium) diff --git a/notifications/notifications_test.go b/notifications/notifications_test.go index 39b1572..9803039 100644 --- a/notifications/notifications_test.go +++ b/notifications/notifications_test.go @@ -67,7 +67,7 @@ func Test_DisplayAndLog(t *testing.T) { Clear() t.Run("Logs to stdout if verbose", func(t *testing.T) { - state.Global.Verbose = true + state.Verbose = true got := utils.CaptureOutput(func() { DisplayAndLog(Info, "Test", "Joypad #%d loaded with name %s.", 3, "Foo") }) want := "[Test]: Joypad #3 loaded with name Foo.\n" if !reflect.DeepEqual(got, want) { @@ -77,7 +77,7 @@ func Test_DisplayAndLog(t *testing.T) { Clear() t.Run("Logs nothing if not verbose", func(t *testing.T) { - state.Global.Verbose = false + state.Verbose = false got := utils.CaptureOutput(func() { DisplayAndLog(Info, "Test", "Joypad #%d loaded with name %s.", 3, "Foo") }) want := "" if !reflect.DeepEqual(got, want) { diff --git a/options/options.go b/options/options.go index 40a6bc0..333abdd 100644 --- a/options/options.go +++ b/options/options.go @@ -83,7 +83,7 @@ func (o *Options) Save() error { return err } - name := utils.FileName(state.Global.CorePath) + name := utils.FileName(state.CorePath) fd, err := os.Create(filepath.Join(home, ".ludo", name+".toml")) if err != nil { return err @@ -108,7 +108,7 @@ func (o *Options) load() error { return err } - name := utils.FileName(state.Global.CorePath) + name := utils.FileName(state.CorePath) b, err := ioutil.ReadFile(filepath.Join(home, ".ludo", name+".toml")) if err != nil { return err diff --git a/savefiles/savefiles.go b/savefiles/savefiles.go index 3b39324..fad9552 100644 --- a/savefiles/savefiles.go +++ b/savefiles/savefiles.go @@ -22,7 +22,7 @@ var mutex sync.Mutex func path() string { return filepath.Join( settings.Current.SavefilesDirectory, - utils.FileName(state.Global.GamePath) + ".srm") + utils.FileName(state.GamePath)+".srm") } // SaveSRAM saves the game SRAM to the filesystem @@ -30,12 +30,12 @@ func SaveSRAM() error { mutex.Lock() defer mutex.Unlock() - if !state.Global.CoreRunning { + if !state.CoreRunning { return errors.New("core not running") } - len := state.Global.Core.GetMemorySize(libretro.MemorySaveRAM) - ptr := state.Global.Core.GetMemoryData(libretro.MemorySaveRAM) + len := state.Core.GetMemorySize(libretro.MemorySaveRAM) + ptr := state.Core.GetMemoryData(libretro.MemorySaveRAM) if ptr == nil || len == 0 { return errors.New("unable to get SRAM address") } @@ -71,12 +71,12 @@ func LoadSRAM() error { mutex.Lock() defer mutex.Unlock() - if !state.Global.CoreRunning { + if !state.CoreRunning { return errors.New("core not running") } - len := state.Global.Core.GetMemorySize(libretro.MemorySaveRAM) - ptr := state.Global.Core.GetMemoryData(libretro.MemorySaveRAM) + len := state.Core.GetMemorySize(libretro.MemorySaveRAM) + ptr := state.Core.GetMemoryData(libretro.MemorySaveRAM) if ptr == nil || len == 0 { return errors.New("unable to get SRAM address") } diff --git a/savestates/savestates.go b/savestates/savestates.go index 5e89492..9440b86 100644 --- a/savestates/savestates.go +++ b/savestates/savestates.go @@ -14,8 +14,8 @@ import ( // Save the current state to the filesystem. name is the name of the // savestate file to save to, without extension. func Save(name string) error { - s := state.Global.Core.SerializeSize() - bytes, err := state.Global.Core.Serialize(s) + s := state.Core.SerializeSize() + bytes, err := state.Core.Serialize(s) if err != nil { return err } @@ -29,11 +29,11 @@ func Save(name string) error { // Load the state from the filesystem func Load(path string) error { - s := state.Global.Core.SerializeSize() + s := state.Core.SerializeSize() bytes, err := ioutil.ReadFile(path) if err != nil { return err } - err = state.Global.Core.Unserialize(bytes, s) + err = state.Core.Unserialize(bytes, s) return err } diff --git a/scanner/scanner.go b/scanner/scanner.go index 71cc085..70a9f89 100644 --- a/scanner/scanner.go +++ b/scanner/scanner.go @@ -121,19 +121,19 @@ func Scan(dir string, roms []string, games chan (dat.Game), n *ntf.Notification) n.Update(ntf.Error, err.Error()) continue } - state.Global.DB.FindByCRC(f, rom.Name, crc, games) - state.Global.DB.FindByCRC(f, rom.Name, crcHeaderless, games) + state.DB.FindByCRC(f, rom.Name, crc, games) + state.DB.FindByCRC(f, rom.Name, crcHeaderless, games) n.Update(ntf.Info, strconv.Itoa(i)+"/"+strconv.Itoa(len(roms))+" "+f) } else if rom.CRC32 > 0 { // Look for a matching game entry in the database - state.Global.DB.FindByCRC(f, rom.Name, rom.CRC32, games) + state.DB.FindByCRC(f, rom.Name, rom.CRC32, games) n.Update(ntf.Info, strconv.Itoa(i)+"/"+strconv.Itoa(len(roms))+" "+f) } } z.Close() case ".cue": // Look for a matching game entry in the database - state.Global.DB.FindByROMName(f, filepath.Base(f), 0, games) + state.DB.FindByROMName(f, filepath.Base(f), 0, games) n.Update(ntf.Info, strconv.Itoa(i)+"/"+strconv.Itoa(len(roms))+" "+f) case ".32x", "a52", ".a78", ".col", ".crt", ".d64", ".pce", ".fds", ".gb", ".gba", ".gbc", ".gen", ".gg", ".ipf", ".j64", ".jag", ".lnx", ".md", ".n64", ".nes", ".ngc", ".nds", ".rom", ".sfc", ".sg", ".smc", ".smd", ".sms", ".ws", ".wsc": bytes, err := ioutil.ReadFile(f) @@ -142,7 +142,7 @@ func Scan(dir string, roms []string, games chan (dat.Game), n *ntf.Notification) continue } CRC32 := crc32.ChecksumIEEE(bytes) - state.Global.DB.FindByCRC(f, utils.FileName(f), CRC32, games) + state.DB.FindByCRC(f, utils.FileName(f), CRC32, games) n.Update(ntf.Info, strconv.Itoa(i)+"/"+strconv.Itoa(len(roms))+" "+f) } } diff --git a/state/state.go b/state/state.go index 5fe9aac..8b0e6d6 100644 --- a/state/state.go +++ b/state/state.go @@ -3,26 +3,33 @@ package state import ( - "sync" - "github.com/libretro/ludo/dat" "github.com/libretro/ludo/libretro" ) -// State is a type for the global state of the app -type State struct { - Core *libretro.Core // Current libretro core - CoreRunning bool // Should be true if a game or a gameless core is loaded - MenuActive bool // When set to true, will display the menu layer - Verbose bool // When set to true, will output a lots of logs - CorePath string // Path of the current libretro core - GamePath string // Path of the current game - DB dat.DB // The game database loaded on startup - LudOS bool // Run Ludo as a unix desktop environment - FastForward bool // Run the core as fast as possible +// Core is the current libretro core, if any is loaded +var Core *libretro.Core - sync.Mutex -} +// CoreRunning is true if a game or a gameless core is loaded +var CoreRunning bool -// Global state -var Global State +// MenuActive is whether to display the menu layer +var MenuActive bool + +// Verbose will output more logs +var Verbose bool + +// CorePath is the path of the current libretro core +var CorePath string + +// GamePath is the path of the current game +var GamePath string + +// DB is the game database loaded on startup +var DB dat.DB + +// LudOS is whether run Ludo as a unix desktop environment +var LudOS bool + +// FastForward will run the core as fast as possible +var FastForward bool diff --git a/video/gfx_test.go b/video/gfx_test.go index 19504c8..2d9abfa 100644 --- a/video/gfx_test.go +++ b/video/gfx_test.go @@ -1,11 +1,7 @@ package video import ( - "reflect" "testing" - - "github.com/go-gl/glfw/v3.3/glfw" - "github.com/libretro/ludo/libretro" ) func TestXYWHTo4points(t *testing.T) { @@ -71,95 +67,3 @@ func TestXYWHTo4points(t *testing.T) { }) } } - -type WindowMock struct{} - -func (m WindowMock) GetFramebufferSize() (width, height int) { return 320, 240 } -func (m WindowMock) Destroy() {} -func (m WindowMock) MakeContextCurrent() {} -func (m WindowMock) SetSizeLimits(minw, minh, maxw, maxh int) {} -func (m WindowMock) SetInputMode(mode glfw.InputMode, value int) {} -func (m WindowMock) GetKey(key glfw.Key) glfw.Action { return 0 } -func (m WindowMock) SetShouldClose(bool) {} -func (m WindowMock) ShouldClose() bool { return false } -func (m WindowMock) SetTitle(string) {} -func (m WindowMock) SwapBuffers() {} - -func TestVideo_vertexArray(t *testing.T) { - - var myWindowMock WindowMock - - type fields struct { - Window WindowInterface - Geom libretro.GameGeometry - Font *Font - program uint32 - roundedProgram uint32 - borderProgram uint32 - circleProgram uint32 - demulProgram uint32 - vao uint32 - vbo uint32 - texID uint32 - pitch int32 - pixFmt uint32 - pixType uint32 - bpp int32 - } - type args struct { - x float32 - y float32 - w float32 - h float32 - scale float32 - } - tests := []struct { - name string - fields fields - args args - want []float32 - }{ - { - name: "Works", - fields: fields{ - Window: myWindowMock, - }, - args: args{ - x: 10, - y: 11, - w: 300, - h: 400, - scale: 2, - }, - want: []float32{ - -0.9375, -5.758333, 0, 1, - -0.9375, 0.9083333, 0, 0, - 2.8125, -5.758333, 1, 1, - 2.8125, 0.9083333, 1, 0}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - video := &Video{ - Window: tt.fields.Window, - Geom: tt.fields.Geom, - Font: tt.fields.Font, - program: tt.fields.program, - roundedProgram: tt.fields.roundedProgram, - borderProgram: tt.fields.borderProgram, - circleProgram: tt.fields.circleProgram, - demulProgram: tt.fields.demulProgram, - vao: tt.fields.vao, - vbo: tt.fields.vbo, - texID: tt.fields.texID, - pitch: tt.fields.pitch, - pixFmt: tt.fields.pixFmt, - pixType: tt.fields.pixType, - bpp: tt.fields.bpp, - } - if got := video.vertexArray(tt.args.x, tt.args.y, tt.args.w, tt.args.h, tt.args.scale); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Video.vertexArray() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/video/screenshot.go b/video/screenshot.go index 9b76537..ce64095 100644 --- a/video/screenshot.go +++ b/video/screenshot.go @@ -31,8 +31,8 @@ func (video *Video) renderScreenshot() { // TakeScreenshot captures the ouput of video.Render and writes it to a file func (video *Video) TakeScreenshot(name string) error { - state.Global.MenuActive = false - defer func() { state.Global.MenuActive = true }() + state.MenuActive = false + defer func() { state.MenuActive = true }() gl.UseProgram(video.defaultProgram) diff --git a/video/video.go b/video/video.go index fef2974..dfd20a0 100644 --- a/video/video.go +++ b/video/video.go @@ -5,6 +5,7 @@ package video import ( "log" + "path/filepath" "unsafe" "github.com/go-gl/gl/v2.1/gl" @@ -14,24 +15,9 @@ import ( "github.com/libretro/ludo/state" ) -// WindowInterface lists all the methods from glfw.Window that we are using. -// It is there only to allow mocking during tests. -type WindowInterface interface { - GetFramebufferSize() (width, height int) - Destroy() - MakeContextCurrent() - SetSizeLimits(minw, minh, maxw, maxh int) - SetInputMode(mode glfw.InputMode, value int) - GetKey(key glfw.Key) glfw.Action - SetShouldClose(bool) - ShouldClose() bool - SetTitle(string) - SwapBuffers() -} - // Video holds the state of the video package type Video struct { - Window WindowInterface + Window *glfw.Window Geom libretro.GameGeometry Font *Font @@ -70,6 +56,30 @@ func (video *Video) Reconfigure(fullscreen bool) { video.Configure(fullscreen) } +// GetFramebufferSize retrieves the size, in pixels, of the framebuffer of the specified window. +func (video *Video) GetFramebufferSize() (int, int) { + if video.Window == nil { + return 0, 0 + } + return video.Window.GetFramebufferSize() +} + +// SetTitle sets the window title, encoded as UTF-8, of the window. +func (video *Video) SetTitle(title string) { + if video.Window == nil { + return + } + video.Window.SetTitle(title) +} + +// SetShouldClose sets the value of the close flag of the window. +func (video *Video) SetShouldClose(b bool) { + if video.Window == nil { + return + } + video.Window.SetShouldClose(b) +} + // Configure instanciates the video package func (video *Video) Configure(fullscreen bool) { var width, height int @@ -107,8 +117,8 @@ func (video *Video) Configure(fullscreen bool) { fbw, fbh := video.Window.GetFramebufferSize() // LoadFont (fontfile, font scale, window width, window height) - assets := settings.Current.AssetsDirectory - video.Font, err = LoadFont(assets+"/font.ttf", int32(36*2), fbw, fbh) + fontPath := filepath.Join(settings.Current.AssetsDirectory, "font.ttf") + video.Font, err = LoadFont(fontPath, int32(36*2), fbw, fbh) if err != nil { panic(err) } @@ -180,7 +190,7 @@ func (video *Video) Configure(fullscreen bool) { gl.GenTextures(1, &video.texID) gl.ActiveTexture(gl.TEXTURE0) - if video.texID == 0 && state.Global.Verbose { + if video.texID == 0 && state.Verbose { log.Println("[Video]: Failed to create the vid texture") } @@ -233,7 +243,7 @@ func (video *Video) UpdateFilter(filter string) { // SetPixelFormat is a callback passed to the libretro implementation. // It allows the core or the game to tell us which pixel format should be used for the display. func (video *Video) SetPixelFormat(format uint32) bool { - if state.Global.Verbose { + if state.Verbose { log.Printf("[Video]: Set Pixel Format: %v\n", format) } @@ -315,7 +325,7 @@ func (video *Video) ResizeViewport() { // Render the current frame func (video *Video) Render() { - if !state.Global.CoreRunning { + if !state.CoreRunning { gl.ClearColor(1, 1, 1, 1) gl.Clear(gl.COLOR_BUFFER_BIT) return @@ -367,7 +377,7 @@ func (video *Video) SetRotation(rot uint) bool { // limit to valid values (0, 1, 2, 3, which rotates screen by 0, 90, 180 270 degrees counter-clockwise) video.rot = rot % 4 - if state.Global.Verbose { + if state.Verbose { log.Printf("[Video]: Set Rotation: %v", video.rot) }