Files
go-mxl-player/internal/sdl/sdl.go
T
2026-09-02 00:23:03 +03:00

224 lines
7.0 KiB
Go

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
EventKeyUp uint32 = 0x301
EventMouseMotion uint32 = 0x400
EventMouseButtonDown uint32 = 0x401
EventMouseButtonUp uint32 = 0x402
EventMouseWheel uint32 = 0x403
EventTextInput uint32 = 0x303
// it's about SDL_keycode, not SDL_scancode
KeyEscape uint32 = 0x1B
KeyF uint32 = 0x66
KeyQ uint32 = 0x71
KeyF1 uint32 = 0x4000003A
KeyF2 uint32 = 0x4000003B
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
sdlStartTextInput func(window uintptr)
sdlStopTextInput func(window uintptr)
sdlGetClipboardText func() uintptr
sdlSetClipboardText func(text *byte) bool
sdlFree func(memory 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")
// input
purego.RegisterLibFunc(&sdlStartTextInput, h, "SDL_StartTextInput")
purego.RegisterLibFunc(&sdlStopTextInput, h, "SDL_StopTextInput")
purego.RegisterLibFunc(&sdlGetClipboardText, h, "SDL_GetClipboardText")
purego.RegisterLibFunc(&sdlSetClipboardText, h, "SDL_SetClipboardText")
purego.RegisterLibFunc(&sdlFree, h, "SDL_free")
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
}
// Input wrappers
func StartTextInput(window uintptr) { sdlStartTextInput(window) }
func StopTextInput(window uintptr) { sdlStopTextInput(window) }
func GetClipboardText() string {
text := sdlGetClipboardText()
if text == 0 {
return ""
}
result := cstr(text)
sdlFree(text)
return result
}
func SetClipboardText(text string) bool {
bytes := make([]byte, len(text)+1)
copy(bytes, text)
result := sdlSetClipboardText(&bytes[0])
runtime.KeepAlive(bytes)
return result
}