diff --git a/cmd/mxl-player/main.go b/cmd/mxl-player/main.go index ab2bd8d..a68b0ac 100644 --- a/cmd/mxl-player/main.go +++ b/cmd/mxl-player/main.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "log" + "mxl-player/internal/imgui" "mxl-player/internal/renderer" "mxl-player/internal/sdl" "mxl-player/internal/source" @@ -137,6 +138,13 @@ func main() { return } + // ImGui init + gui := imgui.New() + defer gui.Destroy() + sdl.StartTextInput(windowHandler) + defer sdl.StopTextInput(windowHandler) + // fin on ImGui init + if err := vk.Load(); err != nil { panic(err) } @@ -542,6 +550,7 @@ func main() { resized = true } } + gui.ProcessEvent(&event) } if !running { break diff --git a/go.mod b/go.mod index 2f15b17..475a75a 100644 --- a/go.mod +++ b/go.mod @@ -11,4 +11,4 @@ require github.com/qvest-digital/go-mxl v1.1.0-rc.1 require github.com/spf13/pflag v1.0.10 -require github.com/AllenDang/cimgui-go v1.5.0 // indirect +require github.com/AllenDang/cimgui-go v1.5.0 diff --git a/internal/imgui/backend_vulkan.go b/internal/imgui/backend_vulkan.go new file mode 100644 index 0000000..e69de29 diff --git a/internal/imgui/input_sdl3.go b/internal/imgui/input_sdl3.go new file mode 100644 index 0000000..cc63ee7 --- /dev/null +++ b/internal/imgui/input_sdl3.go @@ -0,0 +1,108 @@ +package imgui + +import ( + "mxl-player/internal/sdl" + "unsafe" + + cimgui "github.com/AllenDang/cimgui-go/imgui" +) + +// SDL3 event (128 byte raw buffer) -> imgui +func (c *Context) ProcessEvent(event *[128]byte) { + eventType := *(*uint32)(unsafe.Pointer(&event[0])) + + // CommonEvent (16 bytes: type uint32 + reserved uint32 + timestamp uint64) + switch eventType { + case sdl.EventKeyDown, sdl.EventKeyUp: + scancode := *(*uint32)(unsafe.Pointer(&event[24])) + down := eventType == sdl.EventKeyDown + key := sdlScancodeToImGuiKey(scancode) + if key >= 0 { + c.io.AddKeyEvent(key, down) + } + // MouseMotionEvent: +16 windowID(4), +20 which(4), +24 state(4), +28 x(float32), +32 y(float32) + case sdl.EventMouseMotion: + x := *(*float32)(unsafe.Pointer(&event[28])) + y := *(*float32)(unsafe.Pointer(&event[32])) + c.io.AddMousePosEvent(x, y) + // MouseButtonEvent: +16 windowID(4), +20 which(4), +24 button(1), +25 down(bool) + case sdl.EventMouseButtonDown, sdl.EventMouseButtonUp: + button := *(*uint8)(unsafe.Pointer(&event[24])) + down := eventType == sdl.EventMouseButtonDown + c.io.AddMouseButtonEvent(int32(button-1), down) + // MouseWheelEvent: +16 windowID(4), +20 which(4), +24 x(float32), +28 y(float32) + case sdl.EventMouseWheel: + x := *(*float32)(unsafe.Pointer(&event[24])) + y := *(*float32)(unsafe.Pointer(&event[28])) + c.io.AddMouseWheelEvent(x, y) + // TextInputEvent: +16 windowID(4), +20 text(*char pointer, 8 bytes on 64-bit) + case sdl.EventTextInput: + // text pointer is at offset 24, 8 bytes (pointer) + ptr := *(*uintptr)(unsafe.Pointer(&event[24])) + if ptr != 0 { + text := cstr(ptr) + c.io.AddInputCharactersUTF8(text) + } + + } +} + +func sdlScancodeToImGuiKey(scancode uint32) cimgui.Key { + switch scancode { + case 40: // SDL_SCANCODE_RETURN + return cimgui.KeyEnter + case 42: // SDL_SCANCODE_BACKSPACE + return cimgui.KeyBackspace + case 41: // SDL_SCANCODE_ESCAPE + return cimgui.KeyEscape + case 43: // SDL_SCANCODE_TAB + return cimgui.KeyTab + case 44: // SDL_SCANCODE_SPACE + return cimgui.KeySpace + case 80: // SDL_SCANCODE_LEFT + return cimgui.KeyLeftArrow + case 79: // SDL_SCANCODE_RIGHT + return cimgui.KeyRightArrow + case 82: // SDL_SCANCODE_UP + return cimgui.KeyUpArrow + case 81: // SDL_SCANCODE_DOWN + return cimgui.KeyDownArrow + case 225: // SDL_SCANCODE_LSHIFT + return cimgui.KeyLeftShift + case 229: // SDL_SCANCODE_RSHIFT + return cimgui.KeyRightShift + case 224: // SDL_SCANCODE_LCTRL + return cimgui.KeyLeftCtrl + case 228: // SDL_SCANCODE_RCTRL + return cimgui.KeyRightCtrl + case 226: // SDL_SCANCODE_LALT + return cimgui.KeyLeftAlt + case 230: // SDL_SCANCODE_RALT + return cimgui.KeyRightAlt + default: + // Letters A-Z: scancodes 4-29 map to ImGui KeyA-KeyZ + if scancode >= 4 && scancode <= 29 { + return cimgui.Key(int(cimgui.KeyA) + int(scancode-4)) + } + // Numbers 0-9: scancodes 30-39 + if scancode >= 30 && scancode <= 39 { + return cimgui.Key(int(cimgui.Key0) + int(scancode-30)) + } + return -1 + } +} + +// cstr reads a NUL-terminated C string at p. +// TODO: same func used in Vulkan +// Maybe i should create something like "package common" +// for funcs like this +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)) +} diff --git a/internal/imgui/shaders.go b/internal/imgui/shaders.go new file mode 100644 index 0000000..b0d59e3 --- /dev/null +++ b/internal/imgui/shaders.go @@ -0,0 +1,9 @@ +package imgui + +import _ "embed" + +//go:embed shaders/imgui.vert.spv +var vertSPV []byte + +//go:embed shaders/imgui.frag.spv +var fragSPV []byte diff --git a/internal/imgui/shaders/imgui.frag b/internal/imgui/shaders/imgui.frag new file mode 100644 index 0000000..ce7e6f7 --- /dev/null +++ b/internal/imgui/shaders/imgui.frag @@ -0,0 +1,14 @@ +#version 450 core +layout(location = 0) out vec4 fColor; + +layout(set=0, binding=0) uniform sampler2D sTexture; + +layout(location = 0) in struct { + vec4 Color; + vec2 UV; +} In; + +void main() +{ + fColor = In.Color * texture(sTexture, In.UV.st); +} diff --git a/internal/imgui/shaders/imgui.frag.spv b/internal/imgui/shaders/imgui.frag.spv new file mode 100644 index 0000000..87b9aea Binary files /dev/null and b/internal/imgui/shaders/imgui.frag.spv differ diff --git a/internal/imgui/shaders/imgui.vert b/internal/imgui/shaders/imgui.vert new file mode 100644 index 0000000..9425365 --- /dev/null +++ b/internal/imgui/shaders/imgui.vert @@ -0,0 +1,25 @@ +#version 450 core +layout(location = 0) in vec2 aPos; +layout(location = 1) in vec2 aUV; +layout(location = 2) in vec4 aColor; + +layout(push_constant) uniform uPushConstant { + vec2 uScale; + vec2 uTranslate; +} pc; + +out gl_PerVertex { + vec4 gl_Position; +}; + +layout(location = 0) out struct { + vec4 Color; + vec2 UV; +} Out; + +void main() +{ + Out.Color = aColor; + Out.UV = aUV; + gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1); +} diff --git a/internal/imgui/shaders/imgui.vert.spv b/internal/imgui/shaders/imgui.vert.spv new file mode 100644 index 0000000..14bcce0 Binary files /dev/null and b/internal/imgui/shaders/imgui.vert.spv differ diff --git a/internal/sdl/sdl.go b/internal/sdl/sdl.go index bcbdb41..74f3d4c 100644 --- a/internal/sdl/sdl.go +++ b/internal/sdl/sdl.go @@ -16,7 +16,16 @@ const ( EventQuit uint32 = 0x100 EventWindowResized uint32 = 0x206 EventPixelSizeChanged uint32 = 0x207 - EventKeyDown uint32 = 0x300 + + EventKeyDown uint32 = 0x300 + EventKeyUp uint32 = 0x301 + + EventMouseMotion uint32 = 0x400 + EventMouseButtonDown uint32 = 0x401 + EventMouseButtonUp uint32 = 0x402 + EventMouseWheel uint32 = 0x403 + + EventTextInput uint32 = 0x303 KeyEscape uint32 = 0x1B KeyF uint32 = 0x66 @@ -49,6 +58,9 @@ var ( sdlDestroyAudioStream func(stream uintptr) sdlGetAudioPlaybackDevices func(count *int32) uintptr sdlGetAudioDeviceName func(devid uint32) uintptr + + sdlStartTextInput func(window uintptr) + sdlStopTextInput func(window uintptr) ) var loaded = false @@ -79,6 +91,9 @@ func Load() error { 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") loaded = true return nil } @@ -174,3 +189,7 @@ func GetAudioPlaybackDevices() []AudioDevice { } return devs } + +// Input wrappers +func StartTextInput(window uintptr) { sdlStartTextInput(window) } +func StopTextInput(window uintptr) { sdlStopTextInput(window) }