diff --git a/cmd/mxl-player/main.go b/cmd/mxl-player/main.go index 405a865..5e27429 100644 --- a/cmd/mxl-player/main.go +++ b/cmd/mxl-player/main.go @@ -6,13 +6,13 @@ import ( "flag" "fmt" "log" + "mxl-player/internal/sdl" "mxl-player/internal/source" "runtime" "time" "unsafe" vk "github.com/christerso/vulkan-go/vk" - "github.com/ebitengine/purego" "github.com/qvest-digital/go-mxl/mxl" ) @@ -23,77 +23,6 @@ const ( WIN_HEIGHT int32 = 720 ) -const ( - initVideo uint32 = 0x00000020 - windowVulkan uint64 = 0x0000000010000000 - windowResizable uint64 = 0x0000000000000020 - - sdlEventQuit uint32 = 0x100 - sdlEventWindowResized uint32 = 0x206 - sdlEventPixelSizeChanged uint32 = 0x207 - sdlEventKeyDown uint32 = 0x300 - keyEscape uint32 = 0x1B -) - -var ( - sdlInit func(flags uint32) bool - sdlQuit func() - sdlGetError func() uintptr - sdlCreateWindow func(title *byte, w, h int32, flags uint64) uintptr - sdlDestroyWindow func(window uintptr) - sdlVulkanGetInstanceExtensions func(count *uint32) uintptr - sdlVulkanCreateSurface func(window, instance, allocator uintptr, surface *uint64) bool - sdlPollEvent func(event unsafe.Pointer) bool - sdlGetWindowSizeInPixels func(window uintptr, w, h *int32) bool - sdlSetWindowFullscreen func(windows uintptr, fullscreen bool) bool -) - -func sdlError() string { return cstr(sdlGetError()) } - -var sdlLoaded = false - -func loadSDLMissing() error { - if sdlLoaded { - return nil - } - h, err := purego.Dlopen("libSDL3.so.0", purego.RTLD_NOW|purego.RTLD_GLOBAL) - if err != nil { - return fmt.Errorf("win: load SDL3: %w", err) - } - purego.RegisterLibFunc(&sdlInit, h, "SDL_Init") - purego.RegisterLibFunc(&sdlQuit, h, "SDL_Quit") - purego.RegisterLibFunc(&sdlGetError, h, "SDL_GetError") - purego.RegisterLibFunc(&sdlCreateWindow, h, "SDL_CreateWindow") - purego.RegisterLibFunc(&sdlDestroyWindow, h, "SDL_DestroyWindow") - purego.RegisterLibFunc(&sdlVulkanGetInstanceExtensions, h, "SDL_Vulkan_GetInstanceExtensions") - purego.RegisterLibFunc(&sdlVulkanCreateSurface, h, "SDL_Vulkan_CreateSurface") - purego.RegisterLibFunc(&sdlPollEvent, h, "SDL_PollEvent") - purego.RegisterLibFunc(&sdlGetWindowSizeInPixels, h, "SDL_GetWindowSizeInPixels") - purego.RegisterLibFunc(&sdlSetWindowFullscreen, h, "SDL_SetWindowFullscreen") - sdlLoaded = true - return nil -} - -// cstr reads a NUL-terminated C string at p. -func cstr(p uintptr) string { - if p == 0 { - return "" - } - var n int - for *(*byte)(unsafe.Pointer(p + uintptr(n))) != 0 { - n++ - } - return string(unsafe.Slice((*byte)(unsafe.Pointer(p)), n)) -} - -// cbytes returns a NUL-terminated copy of s as *byte, kept alive by the caller. -func cbytes(s string) *byte { - b := make([]byte, len(s)+1) - copy(b, s) - runtime.KeepAlive(b) - return &b[0] -} - func main() { var errMinimized = errors.New("Window minimized") // TODO: remove flags defaults @@ -102,18 +31,18 @@ func main() { flag.Parse() runtime.LockOSThread() - if err := loadSDLMissing(); err != nil { + if err := sdl.Load(); err != nil { panic(err) } - if !sdlInit(initVideo) { - log.Fatalf("SDL_Init: %s", sdlError()) + if !sdl.Init(sdl.InitVideo) { + log.Fatalf("SDL_Init: %s", sdl.GetError()) return } - windowHandler := sdlCreateWindow(cbytes(fmt.Sprintf("%s %s", APP_NAME, APP_VER)), WIN_WIDTH, WIN_HEIGHT, windowVulkan|windowResizable) + windowHandler := sdl.CreateWindow(fmt.Sprintf("%s %s", APP_NAME, APP_VER), WIN_WIDTH, WIN_HEIGHT, sdl.WindowVulkan|sdl.WindowResizable) if windowHandler == 0 { - sdlQuit() - log.Fatalf("SDL_CreateWindow: %s", sdlError()) + sdl.Quit() + log.Fatalf("SDL_CreateWindow: %s", sdl.GetError()) return } @@ -123,17 +52,11 @@ func main() { } // Vulkan instance extensions SDL needs. - var sdlExtCount uint32 - arr := sdlVulkanGetInstanceExtensions(&sdlExtCount) - if arr == 0 { + sdlExtensions := sdl.VulkanGetInstanceExtensions() + if len(sdlExtensions) == 0 { log.Fatal("sdlVulkanGetInstanceExtensions is 0") return } - sdlExtensions := make([]string, sdlExtCount) - for i := uint32(0); i < sdlExtCount; i++ { - p := *(*uintptr)(unsafe.Pointer(arr + uintptr(i)*unsafe.Sizeof(uintptr(0)))) - sdlExtensions[i] = cstr(p) - } // Vulkan Instance creation var vkLayers []string vkExtensions := append(sdlExtensions, vk.ExtDebugUtils) @@ -150,8 +73,8 @@ func main() { defer vkInstance.Destroy() // Vulkan surface var vkSurface uint64 - if !sdlVulkanCreateSurface(windowHandler, uintptr(vkInstance), 0, &vkSurface) { - log.Fatalf("SDL_Vulkan_CreateSurface: %s", sdlError()) + if !sdl.VulkanCreateSurface(windowHandler, uintptr(vkInstance), 0, &vkSurface) { + log.Fatalf("SDL_Vulkan_CreateSurface: %s", sdl.GetError()) return } vkSurf := vk.SurfaceKHR(vkSurface) @@ -355,8 +278,8 @@ func main() { } //compute swapchain extent var w, h int32 = WIN_WIDTH, WIN_HEIGHT - if !sdlGetWindowSizeInPixels(windowHandler, &w, &h) { - return fmt.Errorf("SDL_GetWindowSizeInPixels: %s", sdlError()) + if !sdl.GetWindowSizeInPixels(windowHandler, &w, &h) { + return fmt.Errorf("SDL_GetWindowSizeInPixels: %s", sdl.GetError()) } if w < int32(caps.MinImageExtent.Width) { w = int32(caps.MinImageExtent.Width) @@ -575,22 +498,21 @@ func main() { for running { frameStart := time.Now() var event [128]byte - for sdlPollEvent(unsafe.Pointer(&event[0])) { + for sdl.PollEvent(unsafe.Pointer(&event[0])) { eventType := *(*uint32)(unsafe.Pointer(&event[0])) switch eventType { - case sdlEventQuit: + case sdl.EventQuit: running = false - case sdlEventWindowResized, sdlEventPixelSizeChanged: + case sdl.EventWindowResized, sdl.EventPixelSizeChanged: resized = true - case sdlEventKeyDown: + case sdl.EventKeyDown: key := *(*int32)(unsafe.Pointer(&event[28])) switch uint32(key) { - case keyEscape: + case sdl.KeyEscape: running = false - // f - case 0x66: + case sdl.KeyF: fullscreen = !fullscreen - sdlSetWindowFullscreen(windowHandler, fullscreen) + sdl.SetWindowFullscreen(windowHandler, fullscreen) resized = true } } diff --git a/internal/sdl/sdl.go b/internal/sdl/sdl.go new file mode 100644 index 0000000..dd760f5 --- /dev/null +++ b/internal/sdl/sdl.go @@ -0,0 +1,115 @@ +package sdl + +import ( + "fmt" + "runtime" + "unsafe" + + "github.com/ebitengine/purego" +) + +const ( + InitVideo uint32 = 0x00000020 + WindowVulkan uint64 = 0x0000000010000000 + WindowResizable uint64 = 0x0000000000000020 + + EventQuit uint32 = 0x100 + EventWindowResized uint32 = 0x206 + EventPixelSizeChanged uint32 = 0x207 + EventKeyDown uint32 = 0x300 + + KeyEscape uint32 = 0x1B + KeyF uint32 = 0x66 +) + +var ( + sdlInit func(flags uint32) bool + sdlQuit func() + sdlGetError func() uintptr + sdlCreateWindow func(title *byte, w, h int32, flags uint64) uintptr + sdlDestroyWindow func(window uintptr) + sdlVulkanGetInstanceExtensions func(count *uint32) uintptr + sdlVulkanCreateSurface func(window, instance, allocator uintptr, surface *uint64) bool + sdlPollEvent func(event unsafe.Pointer) bool + sdlGetWindowSizeInPixels func(window uintptr, w, h *int32) bool + sdlSetWindowFullscreen func(window uintptr, fullscreen bool) bool +) + +var loaded = false + +func Load() error { + if loaded { + return nil + } + h, err := purego.Dlopen("libSDL3.so.0", purego.RTLD_NOW|purego.RTLD_GLOBAL) + if err != nil { + return fmt.Errorf("win: load SDL3: %w", err) + } + purego.RegisterLibFunc(&sdlInit, h, "SDL_Init") + purego.RegisterLibFunc(&sdlQuit, h, "SDL_Quit") + purego.RegisterLibFunc(&sdlGetError, h, "SDL_GetError") + purego.RegisterLibFunc(&sdlCreateWindow, h, "SDL_CreateWindow") + purego.RegisterLibFunc(&sdlDestroyWindow, h, "SDL_DestroyWindow") + purego.RegisterLibFunc(&sdlVulkanGetInstanceExtensions, h, "SDL_Vulkan_GetInstanceExtensions") + purego.RegisterLibFunc(&sdlVulkanCreateSurface, h, "SDL_Vulkan_CreateSurface") + purego.RegisterLibFunc(&sdlPollEvent, h, "SDL_PollEvent") + purego.RegisterLibFunc(&sdlGetWindowSizeInPixels, h, "SDL_GetWindowSizeInPixels") + purego.RegisterLibFunc(&sdlSetWindowFullscreen, h, "SDL_SetWindowFullscreen") + loaded = true + return nil +} + +func Init(flags uint32) bool { return sdlInit(flags) } +func Quit() { sdlQuit() } +func GetError() string { return cstr(sdlGetError()) } + +func CreateWindow(title string, w, h int32, flags uint64) uintptr { + return sdlCreateWindow(cbytes(title), w, h, flags) +} +func DestroyWindow(window uintptr) { sdlDestroyWindow(window) } + +func VulkanGetInstanceExtensions() []string { + var count uint32 + vkExts := sdlVulkanGetInstanceExtensions(&count) + if vkExts == 0 { + return nil + } + exts := make([]string, count) + for i := uint32(0); i < count; i++ { + p := *(*uintptr)(unsafe.Pointer(vkExts + uintptr(i)*unsafe.Sizeof(uintptr(0)))) + exts[i] = cstr(p) + } + return exts +} +func VulkanCreateSurface(window, instance, allocator uintptr, surface *uint64) bool { + return sdlVulkanCreateSurface(window, instance, allocator, surface) +} + +func PollEvent(event unsafe.Pointer) bool { return sdlPollEvent(event) } + +func GetWindowSizeInPixels(window uintptr, w, h *int32) bool { + return sdlGetWindowSizeInPixels(window, w, h) +} +func SetWindowFullscreen(window uintptr, fullscreen bool) bool { + return sdlSetWindowFullscreen(window, fullscreen) +} + +// cstr reads a NUL-terminated C string at p. +func cstr(p uintptr) string { + if p == 0 { + return "" + } + var n int + for *(*byte)(unsafe.Pointer(p + uintptr(n))) != 0 { + n++ + } + return string(unsafe.Slice((*byte)(unsafe.Pointer(p)), n)) +} + +// cbytes returns a NUL-terminated copy of s as *byte, kept alive by the caller. +func cbytes(s string) *byte { + b := make([]byte, len(s)+1) + copy(b, s) + runtime.KeepAlive(b) + return &b[0] +}