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 InitAudio uint32 = 0x00000010 AudioDeviceDefaultPlayback uint32 = 0xFFFFFFFF AudioF32 uint16 = 0x8120 ) 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 sdlOpenAudioDeviceStream func(devid uint32, spec unsafe.Pointer, callback uintptr, userdata uintptr) uintptr sdlResumeAudioStreamDevice func(stream uintptr) bool sdlPutAudioStreamData func(stream uintptr, buf unsafe.Pointer, length int32) bool sdlGetAudioStreamQueued func(stream uintptr) int32 sdlDestroyAudioStream func(stream uintptr) sdlGetAudioPlaybackDevices func(count *int32) uintptr sdlGetAudioDeviceName func(devid uint32) uintptr ) 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") // audio purego.RegisterLibFunc(&sdlOpenAudioDeviceStream, h, "SDL_OpenAudioDeviceStream") purego.RegisterLibFunc(&sdlResumeAudioStreamDevice, h, "SDL_ResumeAudioStreamDevice") purego.RegisterLibFunc(&sdlPutAudioStreamData, h, "SDL_PutAudioStreamData") purego.RegisterLibFunc(&sdlGetAudioStreamQueued, h, "SDL_GetAudioStreamQueued") purego.RegisterLibFunc(&sdlDestroyAudioStream, h, "SDL_DestroyAudioStream") purego.RegisterLibFunc(&sdlGetAudioPlaybackDevices, h, "SDL_GetAudioPlaybackDevices") purego.RegisterLibFunc(&sdlGetAudioDeviceName, h, "SDL_GetAudioDeviceName") 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] } type AudioSpec struct { Format uint16 Channels int32 Freq int32 } func OpenAudioDeviceStream(devid uint32, spec AudioSpec) uintptr { return sdlOpenAudioDeviceStream(devid, unsafe.Pointer(&spec), 0, 0) } func ResumeAudioStreamDevice(stream uintptr) bool { return sdlResumeAudioStreamDevice(stream) } func DestroyAudioStream(stream uintptr) { sdlDestroyAudioStream(stream) } func PutAudioStreamData(stream uintptr, buf []byte) bool { return sdlPutAudioStreamData(stream, unsafe.Pointer(&buf[0]), int32(len(buf))) } func GetAudioStreamQueued(stream uintptr) int32 { return sdlGetAudioStreamQueued(stream) } // playback device type AudioDevice struct { ID uint32 Name string } func GetAudioPlaybackDevices() []AudioDevice { var count int32 ptr := sdlGetAudioPlaybackDevices(&count) if ptr == 0 || count == 0 { return nil } ids := unsafe.Slice((*uint32)(unsafe.Pointer(ptr)), count) devs := make([]AudioDevice, 0, count) for _, id := range ids { name := cstr(sdlGetAudioDeviceName(id)) devs = append(devs, AudioDevice{ID: id, Name: name}) } return devs }