From a61767f6686a6533e0c3df0ea628febe9d7d7f25 Mon Sep 17 00:00:00 2001 From: Dmitry Sergeev Date: Thu, 3 Sep 2026 10:13:47 +0300 Subject: [PATCH] SDL3 loader fix for MacOS --- imgui.ini | 4 +-- internal/sdl/sdl.go | 73 ++++++++++++++++++++++++++++++++++++++-- internal/sdl/sdl_test.go | 56 ++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 internal/sdl/sdl_test.go diff --git a/imgui.ini b/imgui.ini index 4149602..87f5813 100644 --- a/imgui.ini +++ b/imgui.ini @@ -5,11 +5,11 @@ Collapsed=0 [Window][Settings & Info] Pos=0,0 -Size=700,1080 +Size=700,720 Collapsed=0 [Window][Stats] -Pos=1460,0 +Pos=820,0 Size=460,510 Collapsed=0 diff --git a/internal/sdl/sdl.go b/internal/sdl/sdl.go index aa951f2..71faa62 100644 --- a/internal/sdl/sdl.go +++ b/internal/sdl/sdl.go @@ -1,7 +1,9 @@ package sdl import ( + "errors" "fmt" + "os" "runtime" "unsafe" @@ -70,16 +72,82 @@ var ( sdlFree func(memory uintptr) ) +func libraryCandidates() ([]string, error) { + return libraryCandidatesForOS(runtime.GOOS) +} + +func libraryCandidatesForOS(goos string) ([]string, error) { + switch goos { + case "linux": + return []string{ + "libSDL3.so.0", + "libSDL3.so", + }, nil + + case "darwin": + return []string{ + "libSDL3.0.dylib", + "libSDL3.dylib", + }, nil + + case "windows": + return []string{ + "SDL3.dll", + }, nil + + default: + return nil, fmt.Errorf("SDL3: unsupported operating system %q", goos) + } +} + +func openLibrary() (uintptr, error) { + var candidates []string + + if explicit := os.Getenv("SDL3_LIBRARY"); explicit != "" { + candidates = append(candidates, explicit) + } + + defaults, err := libraryCandidates() + if err != nil { + return 0, err + } + candidates = append(candidates, defaults...) + + var attempts []error + + for _, name := range candidates { + handle, err := purego.Dlopen( + name, + purego.RTLD_NOW|purego.RTLD_GLOBAL, + ) + if err == nil && handle != 0 { + return handle, nil + } + if err == nil { + err = errors.New("loader returned a zero handle") + } + + attempts = append(attempts, fmt.Errorf("%s: %w", name, err)) + } + + return 0, fmt.Errorf( + "SDL3: unable to load shared library: %w", + errors.Join(attempts...), + ) +} + var loaded = false func Load() error { if loaded { return nil } - h, err := purego.Dlopen("libSDL3.so.0", purego.RTLD_NOW|purego.RTLD_GLOBAL) + + h, err := openLibrary() if err != nil { - return fmt.Errorf("win: load SDL3: %w", err) + return err } + purego.RegisterLibFunc(&sdlInit, h, "SDL_Init") purego.RegisterLibFunc(&sdlQuit, h, "SDL_Quit") purego.RegisterLibFunc(&sdlGetError, h, "SDL_GetError") @@ -104,6 +172,7 @@ func Load() error { purego.RegisterLibFunc(&sdlGetClipboardText, h, "SDL_GetClipboardText") purego.RegisterLibFunc(&sdlSetClipboardText, h, "SDL_SetClipboardText") purego.RegisterLibFunc(&sdlFree, h, "SDL_free") + loaded = true return nil } diff --git a/internal/sdl/sdl_test.go b/internal/sdl/sdl_test.go new file mode 100644 index 0000000..6c5349a --- /dev/null +++ b/internal/sdl/sdl_test.go @@ -0,0 +1,56 @@ +package sdl + +import ( + "reflect" + "strings" + "testing" +) + +func TestLibraryCandidatesForOS(t *testing.T) { + tests := []struct { + name string + goos string + want []string + }{ + { + name: "Linux", + goos: "linux", + want: []string{"libSDL3.so.0", "libSDL3.so"}, + }, + { + name: "macOS", + goos: "darwin", + want: []string{"libSDL3.0.dylib", "libSDL3.dylib"}, + }, + { + name: "Windows", + goos: "windows", + want: []string{"SDL3.dll"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := libraryCandidatesForOS(tt.goos) + if err != nil { + t.Fatalf("libraryCandidatesForOS(%q): %v", tt.goos, err) + } + if !reflect.DeepEqual(got, tt.want) { + t.Fatalf("libraryCandidatesForOS(%q) = %v, want %v", tt.goos, got, tt.want) + } + }) + } +} + +func TestLibraryCandidatesForOSRejectsUnsupportedOS(t *testing.T) { + got, err := libraryCandidatesForOS("plan9") + if err == nil { + t.Fatal("libraryCandidatesForOS(plan9) returned no error") + } + if got != nil { + t.Fatalf("libraryCandidatesForOS(plan9) = %v, want nil", got) + } + if !strings.Contains(err.Error(), `"plan9"`) { + t.Fatalf("error %q does not identify the unsupported OS", err) + } +}