├── .docker ├── emscripten-build │ └── Dockerfile ├── ffi │ └── Dockerfile └── wiki-annotations │ ├── Dockerfile │ └── main.go ├── .gitattributes ├── .github └── workflows │ ├── sdl-build.yml │ ├── sdl-image-build.yml │ ├── sdl-mixer-build.yml │ └── sdl-ttf-build.yml ├── .gitignore ├── CONTRIBUTE.md ├── COVERAGE.md ├── LICENSE ├── README.md ├── Taskfile.yml ├── bin ├── binimg │ ├── assets │ │ ├── img.so │ │ ├── img_amd64.dll │ │ ├── img_amd64.dylib │ │ ├── img_arm64.dll │ │ └── img_arm64.dylib │ ├── binary_darwin_amd64.go │ ├── binary_darwin_arm64.go │ ├── binary_linux.go │ ├── binary_windows_amd64.go │ ├── binary_windows_arm64.go │ ├── load_js.go │ └── load_notjs.go ├── binmix │ ├── assets │ │ ├── mix.so │ │ ├── mix_amd64.dylib │ │ └── mix_arm64.dylib │ ├── binary_darwin_amd64.go │ ├── binary_darwin_arm64.go │ ├── binary_linux.go │ ├── binary_windows_amd64.go │ ├── binary_windows_arm64.go │ ├── load_js.go │ └── load_notjs.go ├── binsdl │ ├── assets │ │ ├── sdl.so │ │ ├── sdl_amd64.dll │ │ ├── sdl_amd64.dylib │ │ ├── sdl_arm64.dll │ │ └── sdl_arm64.dylib │ ├── binary_darwin_amd64.go │ ├── binary_darwin_arm64.go │ ├── binary_linux.go │ ├── binary_windows_amd64.go │ ├── binary_windows_arm64.go │ ├── load_js.go │ └── load_notjs.go └── binttf │ ├── assets │ ├── ttf.so │ ├── ttf_amd64.dll │ ├── ttf_amd64.dylib │ ├── ttf_arm64.dll │ └── ttf_arm64.dylib │ ├── binary_darwin_amd64.go │ ├── binary_darwin_arm64.go │ ├── binary_linux.go │ ├── binary_windows_amd64.go │ ├── binary_windows_arm64.go │ ├── load_js.go │ └── load_notjs.go ├── cmd ├── coverage │ └── main.go ├── docgen │ └── main.go ├── ffi2go │ └── main.go ├── internal │ └── assets │ │ ├── assets.go │ │ ├── config │ │ ├── img.json │ │ ├── mixer.json │ │ ├── sdl.json │ │ └── ttf.json │ │ ├── ffi │ │ ├── img.json │ │ ├── mixer.json │ │ ├── sdl.json │ │ └── ttf.json │ │ └── wiki │ │ └── annotations.csv ├── jsgen │ └── main.go ├── methodgen │ └── main.go └── wasmsdl │ ├── README.md │ ├── assets │ ├── index.html │ ├── sdl.js │ └── sdl.wasm │ └── main.go ├── examples ├── helloworld │ └── main.go ├── input │ ├── 01-joystick-polling │ │ └── main.go │ ├── 02-joystick-events │ │ └── main.go │ └── README.md └── renderer │ ├── 01-clear │ └── main.go │ ├── 02-primitives │ └── main.go │ ├── 03-lines │ └── main.go │ ├── 04-points │ └── main.go │ ├── 05-rectangles │ └── main.go │ ├── 06-textures │ └── main.go │ ├── 07-streaming-textures │ └── main.go │ ├── 08-rotating-textures │ └── main.go │ ├── 09-scaling-textures │ └── main.go │ ├── 10-geometry │ └── main.go │ ├── 11-color-mods │ └── main.go │ ├── 14-viewport │ └── main.go │ ├── 15-cliprect │ └── main.go │ ├── 17-read-pixels │ └── main.go │ ├── 18-debug-text │ └── main.go │ ├── README.md │ └── _assets │ ├── images.go │ └── images │ └── sample.bmp ├── go.mod ├── go.sum ├── img ├── functions.go ├── glue.go ├── glue_js.go ├── glue_notjs.go ├── img_enums.gen.go ├── img_functions.gen.go ├── img_functions.gen_impl.go ├── img_functions_js.go ├── img_structs.gen.go ├── img_types.gen.go ├── init_js.go ├── init_notjs.go └── methods.go ├── internal ├── memory_js.go ├── memory_notjs.go ├── sdl_functions.go ├── slice.go ├── string_js.go ├── string_notjs.go └── tmp_directory.go ├── mixer ├── functions.go ├── glue.go ├── glue_js.go ├── glue_notjs.go ├── init_js.go ├── init_notjs.go ├── macros.go ├── methods.go ├── mixer_enums.gen.go ├── mixer_functions.gen.go ├── mixer_functions.gen_impl.go ├── mixer_functions_js.go ├── mixer_structs.gen.go └── mixer_types.gen.go ├── sdl ├── functions.go ├── glue.go ├── glue_js.go ├── glue_notjs.go ├── init_js.go ├── init_notjs.go ├── macros.go ├── methods.go ├── sdl_enums.gen.go ├── sdl_functions.gen.go ├── sdl_functions.gen_impl.go ├── sdl_functions_ex.go ├── sdl_functions_ex_impl.go ├── sdl_functions_js.go ├── sdl_structs.gen.go └── sdl_types.gen.go └── ttf ├── functions.go ├── glue.go ├── init_js.go ├── init_notjs.go ├── macros.go ├── methods.go ├── ttf_enums.gen.go ├── ttf_functions.gen.go ├── ttf_functions.gen_impl.go ├── ttf_functions_ex.go ├── ttf_functions_ex_impl.go ├── ttf_functions_js.go ├── ttf_structs.gen.go └── ttf_types.gen.go /.docker/emscripten-build/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | SHELL ["/bin/bash", "-c"] 4 | 5 | # Install dependencies 6 | RUN apt update -y 7 | RUN apt-get install -y wget 8 | RUN wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc 9 | RUN apt-get install -y software-properties-common 10 | RUN apt-get install -y git 11 | RUN apt-get install -y cmake 12 | RUN add-apt-repository -s -y "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-18 main" 13 | RUN apt-get install -y llvm-18 clang-18 libclang-18-dev libclang-cpp18-dev python3-pip 14 | 15 | # Clone and install emscripten 16 | RUN git clone https://github.com/emscripten-core/emsdk.git --depth=1 && \ 17 | cd emsdk && \ 18 | ./emsdk install tot && \ 19 | ./emsdk activate tot 20 | 21 | ENV SDL3_VERSION=3.2.8 22 | ENV TTF_VERSION=3.2.0 23 | ENV IMAGE_VERSION=3.2.4 24 | ENV MIXER_VERSION=main 25 | 26 | # Clone SDL and satellite libraries 27 | RUN git clone --depth 1 --branch release-$SDL3_VERSION https://github.com/libsdl-org/SDL.git 28 | RUN git clone --recurse-submodules --branch release-$TTF_VERSION https://github.com/libsdl-org/SDL_ttf.git 29 | RUN git clone --recurse-submodules --branch release-$IMAGE_VERSION https://github.com/libsdl-org/SDL_image.git 30 | RUN git clone --recurse-submodules https://github.com/libsdl-org/SDL_mixer.git 31 | 32 | # Build SDL 33 | RUN source emsdk/emsdk_env.sh && \ 34 | mkdir build_sdl && cd build_sdl && \ 35 | emcmake cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON ../SDL && \ 36 | emmake make -j4 37 | 38 | # Build SDL_ttf 39 | RUN source emsdk/emsdk_env.sh && \ 40 | mkdir build_ttf && cd build_ttf && \ 41 | emcmake cmake -DSDLTTF_SAMPLES=OFF -DSDLTTF_VENDORED=ON -DSDL3_DIR=/build_sdl -DCMAKE_BUILD_TYPE=Release ../SDL_ttf && \ 42 | emmake make -j4 43 | 44 | # Build SDL_image 45 | RUN source emsdk/emsdk_env.sh && \ 46 | mkdir build_image && cd build_image && \ 47 | emcmake cmake -DSDLIMAGE_AVIF=OFF -DSDLIMAGE_WEBP=OFF -DSDLIMAGE_TIF=OFF -DSDLIMAGE_BUILD_SHARED_LIBS=OFF -DSDLIMAGE_TESTS=OFF -DHAVE_POSIX_MEMALIGN=OFF -DDAV1D_ASM=OFF -DAOM_TARGET_CPU=generic -DSDLIMAGE_SAMPLES=OFF -DSDLIMAGE_VENDORED=ON -DSDL_SHARED=OFF -DSDL3_DIR=/build_sdl -DCMAKE_BUILD_TYPE=Release ../SDL_image && \ 48 | emmake make -j4 49 | 50 | # Build SDL_mixer 51 | RUN source emsdk/emsdk_env.sh && \ 52 | mkdir build_mixer && cd build_mixer && \ 53 | emcmake cmake -DSDLMIXER_MOD_XMP=OFF -DSDLMIXER_MOD=OFF -DSDLMIXER_WAVPACK=OFF -DSDLMIXER_GME=OFF -DSDLMIXER_OPUS=OFF -DSDLMIXER_SAMPLES=OFF -DSDLMIXER_VENDORED=ON -DSDL_SHARED=OFF -DSDL3_DIR=/build_sdl -DCMAKE_BUILD_TYPE=Release ../SDL_mixer && \ 54 | emmake make -j4 55 | 56 | # Build final wasm library 57 | RUN source emsdk/emsdk_env.sh && \ 58 | emcc --no-entry -s LINKABLE=1 -s EXPORTED_RUNTIME_METHODS="['addFunction', 'allocateUTF8','UTF8ToString', 'stringToUTF8', 'getValue', 'setValue']" -s ALLOW_MEMORY_GROWTH=1 -s ALLOW_TABLE_GROWTH=1 -s EXPORT_ALL -s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE='$addFunction' build_image/libSDL3_image.a build_ttf/libSDL3_ttf.a build_mixer/libSDL3_mixer.a build_sdl/libSDL3.a -o sdl.html 59 | 60 | RUN mkdir _out && \ 61 | mv /build_sdl _out/build_sdl && \ 62 | mv /build_ttf _out/build_ttf && \ 63 | mv /build_image _out/build_image && \ 64 | mv /build_mixer _out/build_mixer && \ 65 | mv sdl* _out 66 | -------------------------------------------------------------------------------- /.docker/ffi/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | 4 | RUN apt update -y 5 | 6 | # Apparently wget is not installed by default 7 | RUN apt-get install -y wget 8 | # Add llvm pub key 9 | RUN wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc 10 | # Install add-apt-repository 11 | RUN apt-get install -y software-properties-common 12 | # Add llvm 18 repository with sources 13 | RUN add-apt-repository -s -y "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-18 main" 14 | RUN echo apt-get update && apt-get install -y llvm-18 clang-18 libclang-18-dev libclang-cpp18-dev python3-pip 15 | # Easiest way to get sufficiently new cmake appears to be using pip - Ubuntu 20.10 version is too old 16 | RUN pip3 install --upgrade cmake 17 | 18 | # Clone c2ffi repository 19 | RUN apt-get install -y git 20 | RUN git clone https://github.com/rpav/c2ffi.git --depth 1 21 | RUN cd c2ffi && git checkout llvm-18.1.0 22 | 23 | # Build c2ffi 24 | RUN cd c2ffi && mkdir -p build && cd build && \ 25 | cmake -DBUILD_CONFIG=Release .. && make 26 | 27 | # Clone SDL repository 28 | RUN git clone https://github.com/libsdl-org/SDL.git && \ 29 | cd SDL && git checkout tags/release-3.2.8 && \ 30 | cp -r include/SDL3 /usr/include/SDL3 31 | 32 | # Generate SDL ffi 33 | RUN echo "#include \n#include " > sdl.c 34 | RUN /c2ffi/build/bin/c2ffi sdl.c > sdl_ffi.json 35 | 36 | # Clone SDL_ttf repository 37 | RUN git clone https://github.com/libsdl-org/SDL_ttf.git && \ 38 | cd SDL_ttf && git checkout tags/release-3.2.0 && \ 39 | cp -r include/SDL3_ttf /usr/include/SDL3_ttf 40 | 41 | # Generate SDL_ttf ffi 42 | RUN echo "#include \n#include " > ttf.c 43 | RUN /c2ffi/build/bin/c2ffi ttf.c > ttf_ffi.json 44 | 45 | # Clone SDL_mixer repository 46 | RUN git clone https://github.com/libsdl-org/SDL_mixer.git && \ 47 | cd SDL_mixer && \ 48 | cp -r include/SDL3_mixer/ /usr/include/SDL3_mixer/ 49 | 50 | # Generate SDL_mixer ffi 51 | RUN echo "#include \n" > mixer.c 52 | RUN /c2ffi/build/bin/c2ffi mixer.c > mixer_ffi.json 53 | 54 | # Clone SDL_image repository 55 | RUN git clone https://github.com/libsdl-org/SDL_image.git && \ 56 | cd SDL_image && git checkout tags/release-3.2.4 && \ 57 | cp -r include/SDL3_image/ /usr/include/SDL3_image/ 58 | 59 | # Generate SDL_image ffi 60 | RUN echo "#include \n" > image.c 61 | RUN /c2ffi/build/bin/c2ffi image.c > image_ffi.json -------------------------------------------------------------------------------- /.docker/wiki-annotations/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.24 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | 5 | RUN apt update -y 6 | 7 | # Apparently wget is not installed by default 8 | RUN apt-get install -y wget 9 | 10 | # Install add-apt-repository 11 | RUN apt-get install -y software-properties-common 12 | # Clone libsdl-org/sdlwiki repository 13 | RUN apt-get install -y git 14 | RUN git clone https://github.com/libsdl-org/sdlwiki.git --depth 1 15 | 16 | COPY *.go ./ 17 | RUN go mod init sdlwiki-annotations 18 | 19 | # Run the annotations extraction script 20 | RUN go run . -------------------------------------------------------------------------------- /.docker/wiki-annotations/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/csv" 5 | "fmt" 6 | "io/fs" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | type Entry struct { 14 | Id string 15 | Description string 16 | URL string 17 | } 18 | 19 | func main() { 20 | type config struct { 21 | Library string 22 | Prefix string 23 | } 24 | cfgs := []*config{ 25 | {"SDL3", "SDL_"}, 26 | {"SDL3_image", "IMG_"}, 27 | {"SDL3_mixer", "Mix_"}, 28 | {"SDL3_ttf", "TTF_"}, 29 | } 30 | 31 | var entries []*Entry 32 | for _, c := range cfgs { 33 | path := filepath.Join("sdlwiki", c.Library) 34 | files, err := fs.ReadDir(os.DirFS("."), path) 35 | if err != nil { 36 | log.Fatalf("couldn't read dir sdlwiki: %v\n", err) 37 | } 38 | for _, file := range files { 39 | if file.IsDir() || !strings.HasPrefix(file.Name(), c.Prefix) { 40 | continue 41 | } 42 | 43 | fullpath := filepath.Join(path, file.Name()) 44 | content, err := os.ReadFile(fullpath) 45 | if err != nil { 46 | log.Fatalf("couldn't read file '%s': %v\n", fullpath, err) 47 | } 48 | 49 | lines := strings.Split(string(content), "\n") 50 | var description string 51 | var hasTitle bool 52 | for i := 0; i < len(lines); i++ { 53 | if lines[i] == "" { 54 | // Documentation content ended 55 | if description != "" { 56 | break 57 | } 58 | continue 59 | } 60 | if strings.HasPrefix(lines[i], "# ") { 61 | hasTitle = true 62 | continue 63 | } 64 | if hasTitle { 65 | if strings.HasPrefix(lines[i], "Please refer to [") { 66 | break 67 | } 68 | // Documentation line 69 | description += lines[i] + "\n" 70 | } 71 | } 72 | // If no content was found, skip this file 73 | if description == "" { 74 | continue 75 | } 76 | 77 | id := strings.TrimSuffix(file.Name(), ".md") 78 | entries = append(entries, &Entry{ 79 | Id: id, 80 | Description: description, 81 | URL: fmt.Sprintf("https://wiki.libsdl.org/%s/%s", c.Library, id), 82 | }) 83 | } 84 | } 85 | 86 | f, err := os.Create("annotations.csv") 87 | if err != nil { 88 | log.Fatalf("couldn't create 'annotations.csv': %v\n", err) 89 | } 90 | defer f.Close() 91 | 92 | w := csv.NewWriter(f) 93 | w.Write([]string{"id", "description", "url"}) 94 | for _, e := range entries { 95 | if err := w.Write([]string{e.Id, e.Description, e.URL}); err != nil { 96 | log.Fatalf("couldn't write entry to csv writer: %v\n", err) 97 | } 98 | } 99 | w.Flush() 100 | 101 | err = w.Error() 102 | if err != nil { 103 | log.Fatalf("error flushing csv writer: %v\n", err) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html linguist-detectable=false 2 | *.js linguist-detectable=false -------------------------------------------------------------------------------- /.github/workflows/sdl-build.yml: -------------------------------------------------------------------------------- 1 | name: Build SDL 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | SDL3_VERSION: 3.2.8 8 | 9 | jobs: 10 | build-windows: 11 | name: Download Windows 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | arch: [x64, x86, arm64] 17 | 18 | steps: 19 | - name: Download release build 20 | run: wget https://github.com/libsdl-org/SDL/releases/download/release-${{ env.SDL3_VERSION }}/SDL3-${{ env.SDL3_VERSION }}-win32-${{ matrix.arch }}.zip 21 | - name: Upload Build 22 | uses: actions/upload-artifact@v4 23 | with: 24 | name: sdl-windows-${{ matrix.arch }} 25 | path: SDL3-${{ env.SDL3_VERSION }}-win32-${{ matrix.arch }}.zip 26 | 27 | build-linux: 28 | name: Build Linux 29 | runs-on: ubuntu-latest 30 | container: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:beta 31 | defaults: 32 | run: 33 | working-directory: SDL 34 | steps: 35 | - name: Clone SDL3 36 | working-directory: . 37 | run: git clone --depth 1 --branch release-${{ env.SDL3_VERSION }} https://github.com/libsdl-org/SDL.git 38 | - name: Configure 39 | run: > 40 | cmake -G Ninja 41 | -DCMAKE_BUILD_TYPE=Release 42 | -DSDL_SHARED=ON 43 | -DSDL_STATIC=OFF 44 | -DSDL_DISABLE_INSTALL=ON 45 | -DSDL_DISABLE_INSTALL_DOCS=ON 46 | -DSDL_INSTALL_TESTS=OFF 47 | -S . -B build 48 | - name: Build 49 | run: cmake --build build --config Release 50 | - name: Upload Build 51 | uses: actions/upload-artifact@v4 52 | with: 53 | name: sdl-linux 54 | path: SDL/build/*.so.0 55 | 56 | build-macos: 57 | name: Build for Apple 58 | runs-on: macos-14 59 | defaults: 60 | run: 61 | working-directory: SDL 62 | strategy: 63 | fail-fast: false 64 | matrix: 65 | target: [iOS, ''] 66 | steps: 67 | - name: Clone SDL3 68 | working-directory: . 69 | run: git clone --depth 1 --branch release-${{ env.SDL3_VERSION }} https://github.com/libsdl-org/SDL.git 70 | - name: Setup Xcode version 71 | uses: maxim-lobanov/setup-xcode@v1.6.0 72 | with: 73 | xcode-version: "16.1" 74 | - name: Configure 75 | run: > 76 | cmake -G "Xcode" 77 | -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=NO 78 | -DCMAKE_SYSTEM_NAME=${{ matrix.target }} 79 | -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" 80 | -DCMAKE_BUILD_TYPE=Release 81 | -DSDL_SHARED=ON 82 | -DSDL_STATIC=OFF 83 | -DSDL_DISABLE_INSTALL=ON 84 | -DSDL_DISABLE_INSTALL_DOCS=ON 85 | -DSDL_INSTALL_TESTS=OFF 86 | -S . -B build 87 | - name: Build 88 | run: cmake --build build --config Release 89 | - name: Upload Build 90 | uses: actions/upload-artifact@v4 91 | with: 92 | name: sdl-apple-${{ matrix.target }} 93 | path: SDL/build/* 94 | -------------------------------------------------------------------------------- /.github/workflows/sdl-image-build.yml: -------------------------------------------------------------------------------- 1 | name: Build SDL_image 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | IMAGE_VERSION: 3.2.0 8 | 9 | jobs: 10 | build-windows: 11 | name: Download Windows 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | arch: [x64, x86, arm64] 17 | steps: 18 | - name: Download release build 19 | run: wget https://github.com/libsdl-org/SDL_image/releases/download/release-${{ env.IMAGE_VERSION }}/SDL3_image-${{ env.IMAGE_VERSION }}-win32-${{ matrix.arch }}.zip 20 | - name: Upload Build 21 | uses: actions/upload-artifact@v4 22 | with: 23 | name: sdl-image-windows-${{ matrix.arch }} 24 | path: SDL3_image-${{ env.IMAGE_VERSION }}-win32-${{ matrix.arch }}.zip 25 | 26 | build-linux: 27 | name: Build Linux 28 | runs-on: ubuntu-latest 29 | container: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:beta 30 | defaults: 31 | run: 32 | working-directory: SDL_image 33 | steps: 34 | - name: Set up Ninja 35 | uses: aseprite/get-ninja@main 36 | - name: Set up SDL 37 | uses: libsdl-org/setup-sdl@main 38 | id: sdl 39 | with: 40 | cmake-generator: Ninja 41 | version: 3-head 42 | shell: sh 43 | - name: Set up Linux dependencies 44 | working-directory: . 45 | run: | 46 | sudo apt-get update 47 | sudo apt-get -y install \ 48 | libavif-dev \ 49 | libjpeg-dev \ 50 | libpng-dev \ 51 | libtiff-dev \ 52 | libwebp-dev \ 53 | zlib1g-dev \ 54 | ${NULL+} 55 | - name: Set up NASM 56 | uses: ilammy/setup-nasm@v1 57 | - name: Clone SDL_image 58 | working-directory: . 59 | run: git clone --recurse-submodules --branch release-${{ env.IMAGE_VERSION }} https://github.com/libsdl-org/SDL_image.git 60 | - name: Check that versioning is consistent 61 | # We only need to run this once: arbitrarily use the Linux build 62 | run: ./build-scripts/test-versioning.sh 63 | - name: Configure 64 | run: > 65 | cmake -G Ninja 66 | -DBUILD_SHARED_LIBS=ON 67 | -DCMAKE_BUILD_TYPE=Release 68 | -DSDL_SHARED=ON 69 | -DSDL_STATIC=OFF 70 | -DSDL_DISABLE_INSTALL=ON 71 | -DSDL_DISABLE_INSTALL_DOCS=ON 72 | -DSDLIMAGE_TESTS=OFF 73 | -DSDLIMAGE_TESTS_INSTALL=OFF 74 | -DSDLIMAGE_SAMPLES=OFF 75 | -DSDLIMAGE_INSTALL_MAN=OFF 76 | -DSDLIMAGE_VENDORED=ON 77 | -S . -B build 78 | - name: Build 79 | run: cmake --build build --config Release 80 | - name: Upload Build 81 | uses: actions/upload-artifact@v4 82 | with: 83 | name: sdl-image-linux 84 | path: SDL_image/build/*.so.0 85 | 86 | build-macos: 87 | name: Build for Apple (${{ matrix.platform.arch }}) 88 | runs-on: macos-latest 89 | strategy: 90 | fail-fast: false 91 | matrix: 92 | platform: 93 | - {arch: 'arm64', flags: '-DSDL_CPU_ARM64=ON'} 94 | - {arch: 'x86_64', flags: '-DSDL_CPU_X64=ON'} 95 | defaults: 96 | run: 97 | working-directory: SDL_image 98 | steps: 99 | - name: Set up Ninja 100 | uses: aseprite/get-ninja@main 101 | - name: Set up SDL 102 | id: sdl 103 | uses: libsdl-org/setup-sdl@main 104 | with: 105 | cmake-generator: Ninja 106 | cmake-arguments: | 107 | -DCMAKE_OSX_ARCHITECTURES="${{ matrix.platform.arch }}" \ 108 | -DSDL_DISABLE_INSTALL_DOCS=ON \ 109 | -DSDL_INSTALL_TESTS=OFF \ 110 | ${{ matrix.platform.flags }} 111 | version: 3-head 112 | shell: sh 113 | - name: Setup MacOS dependencies 114 | working-directory: . 115 | run: | 116 | brew install \ 117 | jpeg-xl \ 118 | libavif \ 119 | webp \ 120 | ${NULL+} 121 | - name: Set up NASM 122 | uses: ilammy/setup-nasm@v1 123 | - name: Clone SDL_image 124 | working-directory: . 125 | run: git clone --recurse-submodules --branch release-${{ env.IMAGE_VERSION }} https://github.com/libsdl-org/SDL_image.git 126 | - name: Configure 127 | run: > 128 | cmake -G Ninja 129 | -DBUILD_SHARED_LIBS=ON 130 | -DCMAKE_OSX_ARCHITECTURES="${{ matrix.platform.arch }}" 131 | -DCMAKE_BUILD_TYPE=Release 132 | -DSDL_SHARED=ON 133 | -DSDL_STATIC=OFF 134 | -DSDLIMAGE_TESTS=OFF 135 | -DSDLIMAGE_TESTS_INSTALL=OFF 136 | -DSDLIMAGE_SAMPLES=OFF 137 | -DSDLIMAGE_INSTALL_MAN=OFF 138 | -DSDLIMAGE_VENDORED=ON 139 | ${{ matrix.platform.flags }} 140 | -S . -B build 141 | - name: Build 142 | run: cmake --build build --config Release 143 | - name: Upload Build 144 | uses: actions/upload-artifact@v4 145 | with: 146 | name: sdl-image-apple-macOS-${{ matrix.platform.arch }} 147 | path: SDL_image/build/* 148 | -------------------------------------------------------------------------------- /.github/workflows/sdl-mixer-build.yml: -------------------------------------------------------------------------------- 1 | name: Build SDL_mixer 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | MIXER_BRANCH: main 8 | 9 | jobs: 10 | # Windows only has binaries to download for the SDL2 version it seems 11 | # build-windows: 12 | # name: Download Windows 13 | # runs-on: ubuntu-latest 14 | # strategy: 15 | # fail-fast: false 16 | # matrix: 17 | # arch: [x64, x86] 18 | # steps: 19 | # - name: Download release build 20 | # run: wget https://github.com/libsdl-org/SDL_mixer/releases/download/release-${{ env.MIXER_VERSION }}/SDL2_mixer-${{ env.MIXER_VERSION }}-win32-${{ matrix.arch }}.zip 21 | # - name: Upload Build 22 | # uses: actions/upload-artifact@v4 23 | # with: 24 | # name: sdl-mixer-windows-${{ matrix.arch }} 25 | # path: SDL2_mixer-${{ env.MIXER_VERSION }}-win32-${{ matrix.arch }}.zip 26 | 27 | build-linux: 28 | name: Build Linux 29 | runs-on: ubuntu-latest 30 | container: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:beta 31 | defaults: 32 | run: 33 | working-directory: SDL_mixer 34 | steps: 35 | - name: Set up Ninja 36 | uses: aseprite/get-ninja@main 37 | - name: Set up SDL 38 | uses: libsdl-org/setup-sdl@main 39 | id: sdl 40 | with: 41 | cmake-generator: Ninja 42 | version: 3-head 43 | shell: sh 44 | - name: Set up Linux dependencies 45 | working-directory: . 46 | run: | 47 | sudo apt-get update 48 | sudo apt-get -y install \ 49 | cmake \ 50 | libasound2-dev \ 51 | libflac-dev \ 52 | libfluidsynth-dev \ 53 | libgme-dev \ 54 | libmpg123-dev \ 55 | libopusfile-dev \ 56 | libvorbis-dev \ 57 | libxmp-dev \ 58 | libwavpack-dev \ 59 | ninja-build \ 60 | pkg-config \ 61 | ${NULL+} 62 | - name: Clone SDL_mixer 63 | working-directory: . 64 | run: git clone --recurse-submodules --branch ${{ env.MIXER_BRANCH }} https://github.com/libsdl-org/SDL_mixer.git 65 | - name: Check that versioning is consistent 66 | # We only need to run this once: arbitrarily use the Linux build 67 | run: ./build-scripts/test-versioning.sh 68 | - name: Configure 69 | run: > 70 | cmake -G Ninja 71 | -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ 72 | -DCMAKE_BUILD_TYPE=Release \ 73 | -DSDLMIXER_FLAC=ON \ 74 | -DSDLMIXER_FLAC_LIBFLAC=ON \ 75 | -DSDLMIXER_GME=ON \ 76 | -DSDLMIXER_MOD_XMP=ON \ 77 | -DSDLMIXER_MP3_MPG123=ON \ 78 | -DSDLMIXER_OPUS=ON \ 79 | -DSDLMIXER_VORBIS=VORBISFILE \ 80 | -DBUILD_SHARED_LIBS=ON 81 | -DSDL_SHARED=ON 82 | -DSDL_STATIC=OFF 83 | -DSDL_DISABLE_INSTALL=ON 84 | -DSDL_DISABLE_INSTALL_DOCS=ON 85 | -DSDLMIXER_SAMPLES=OFF 86 | -DSDLMIXER_INSTALL=OFF 87 | -DSDLMIXER_VENDORED=ON 88 | -S . -B build 89 | - name: Build 90 | run: cmake --build build --config Release 91 | - name: Upload Build 92 | uses: actions/upload-artifact@v4 93 | with: 94 | name: sdl-mixer-linux 95 | path: SDL_mixer/build/*.so.0 96 | 97 | build-macos: 98 | name: Build for Apple (${{ matrix.platform.arch }}) 99 | runs-on: macos-latest 100 | strategy: 101 | fail-fast: false 102 | matrix: 103 | platform: 104 | - {arch: 'arm64', flags: '-DSDL_CPU_ARM64=ON'} 105 | - {arch: 'x86_64', flags: '-DSDL_CPU_X64=ON'} 106 | defaults: 107 | run: 108 | working-directory: SDL_mixer 109 | steps: 110 | - name: Set up Ninja 111 | uses: aseprite/get-ninja@main 112 | - name: Set up SDL 113 | id: sdl 114 | uses: libsdl-org/setup-sdl@main 115 | with: 116 | cmake-generator: Ninja 117 | cmake-arguments: | 118 | -DCMAKE_OSX_ARCHITECTURES="${{ matrix.platform.arch }}" \ 119 | -DSDL_DISABLE_INSTALL_DOCS=ON \ 120 | -DSDL_INSTALL_TESTS=OFF \ 121 | ${{ matrix.platform.flags }} 122 | version: 3-head 123 | shell: sh 124 | - name: Setup MacOS dependencies 125 | working-directory: . 126 | run: | 127 | brew install \ 128 | libtool \ 129 | ninja \ 130 | flac \ 131 | fluidsynth \ 132 | game-music-emu \ 133 | libvorbis \ 134 | libxmp \ 135 | mpg123 \ 136 | opusfile \ 137 | wavpack \ 138 | ${NULL+} 139 | - name: Clone SDL_mixer 140 | working-directory: . 141 | run: git clone --recurse-submodules --branch ${{ env.MIXER_BRANCH }} https://github.com/libsdl-org/SDL_mixer.git 142 | - name: Configure 143 | run: > 144 | cmake -G Ninja 145 | -DCMAKE_OSX_ARCHITECTURES="${{ matrix.platform.arch }}" 146 | -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ 147 | -DCMAKE_BUILD_TYPE=Release \ 148 | -DSDLMIXER_FLAC=ON \ 149 | -DSDLMIXER_FLAC_LIBFLAC=ON \ 150 | -DSDLMIXER_GME=ON \ 151 | -DSDLMIXER_MOD_XMP=ON \ 152 | -DSDLMIXER_MP3_MPG123=ON \ 153 | -DSDLMIXER_OPUS=ON \ 154 | -DSDLMIXER_VORBIS=VORBISFILE \ 155 | -DBUILD_SHARED_LIBS=ON 156 | -DSDL_SHARED=ON 157 | -DSDL_STATIC=OFF 158 | -DSDL_DISABLE_INSTALL=ON 159 | -DSDL_DISABLE_INSTALL_DOCS=ON 160 | -DSDLMIXER_SAMPLES=OFF 161 | -DSDLMIXER_INSTALL=OFF 162 | -DSDLMIXER_VENDORED=ON 163 | ${{ matrix.platform.flags }} 164 | -S . -B build 165 | - name: Build 166 | run: cmake --build build --config Release 167 | - name: Upload Build 168 | uses: actions/upload-artifact@v4 169 | with: 170 | name: sdl-mixer-apple-macOS-${{ matrix.platform.arch }} 171 | path: SDL_mixer/build/* 172 | -------------------------------------------------------------------------------- /.github/workflows/sdl-ttf-build.yml: -------------------------------------------------------------------------------- 1 | name: Build SDL_ttf 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | TTF_VERSION: 3.2.0 8 | 9 | jobs: 10 | build-windows: 11 | name: Download Windows 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | arch: [x64, x86, arm64] 17 | steps: 18 | - name: Download release build 19 | run: wget https://github.com/libsdl-org/SDL_ttf/releases/download/release-${{ env.TTF_VERSION }}/SDL3_ttf-${{ env.TTF_VERSION }}-win32-${{ matrix.arch }}.zip 20 | - name: Upload Build 21 | uses: actions/upload-artifact@v4 22 | with: 23 | name: sdl-ttf-windows-${{ matrix.arch }} 24 | path: SDL3_ttf-${{ env.TTF_VERSION }}-win32-${{ matrix.arch }}.zip 25 | 26 | build-linux: 27 | name: Build Linux 28 | runs-on: ubuntu-latest 29 | container: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:beta 30 | defaults: 31 | run: 32 | working-directory: SDL_ttf 33 | steps: 34 | - name: Set up Ninja 35 | uses: aseprite/get-ninja@main 36 | - name: Set up SDL 37 | uses: libsdl-org/setup-sdl@main 38 | id: sdl 39 | with: 40 | cmake-generator: Ninja 41 | version: 3-head 42 | shell: sh 43 | - name: Set up Linux dependencies 44 | working-directory: . 45 | run: | 46 | sudo apt-get update 47 | sudo apt-get -y install \ 48 | cmake \ 49 | file \ 50 | fonts-dejavu-core \ 51 | libfreetype-dev \ 52 | libharfbuzz-dev \ 53 | pkg-config \ 54 | ${NULL+} 55 | - name: Clone SDL_ttf 56 | working-directory: . 57 | run: git clone --recurse-submodules --branch release-${{ env.TTF_VERSION }} https://github.com/libsdl-org/SDL_ttf.git 58 | - name: Check that versioning is consistent 59 | # We only need to run this once: arbitrarily use the Linux build 60 | run: ./build-scripts/test-versioning.sh 61 | - name: Configure 62 | run: > 63 | cmake -G Ninja 64 | -DBUILD_SHARED_LIBS=ON 65 | -DSDLTTF_HARFBUZZ=ON 66 | -DSDLTTF_WERROR=ON 67 | -DCMAKE_BUILD_TYPE=Release 68 | -DSDL_SHARED=ON 69 | -DSDL_STATIC=OFF 70 | -DSDL_DISABLE_INSTALL=ON 71 | -DSDL_DISABLE_INSTALL_DOCS=ON 72 | -DSDL_INSTALL_TESTS=OFF 73 | -DSDLTTF_INSTALL_MAN=OFF 74 | -DSDLTTF_VENDORED=ON 75 | -S . -B build 76 | - name: Build 77 | run: cmake --build build --config Release 78 | - name: Upload Build 79 | uses: actions/upload-artifact@v4 80 | with: 81 | name: sdl-ttf-linux 82 | path: SDL_ttf/build/*.so.0 83 | 84 | build-macos: 85 | name: Build for Apple 86 | runs-on: macos-latest 87 | strategy: 88 | fail-fast: false 89 | matrix: 90 | arch: [arm64, x86_64] 91 | defaults: 92 | run: 93 | working-directory: SDL_ttf 94 | steps: 95 | - name: Set up Ninja 96 | uses: aseprite/get-ninja@main 97 | - name: Set up SDL 98 | id: sdl 99 | uses: libsdl-org/setup-sdl@main 100 | with: 101 | cmake-generator: Ninja 102 | cmake-arguments: | 103 | -DCMAKE_OSX_ARCHITECTURES="${{ matrix.arch }}" \ 104 | -DSDL_DISABLE_INSTALL_DOCS=ON \ 105 | -DSDL_INSTALL_TESTS=OFF 106 | version: 3-head 107 | shell: sh 108 | - name: Setup MacOS dependencies 109 | working-directory: . 110 | run: | 111 | brew install \ 112 | freetype \ 113 | harfbuzz \ 114 | ${NULL+} 115 | - name: Clone SDL_ttf 116 | working-directory: . 117 | run: git clone --recurse-submodules --branch release-${{ env.TTF_VERSION }} https://github.com/libsdl-org/SDL_ttf.git 118 | - name: Configure 119 | run: > 120 | cmake -G Ninja 121 | -DBUILD_SHARED_LIBS=ON 122 | -DCMAKE_OSX_ARCHITECTURES="${{ matrix.arch }}" 123 | -DCMAKE_POSITION_INDEPENDENT_CODE=ON 124 | -DSDLTTF_HARFBUZZ=ON 125 | -DSDLTTF_WERROR=ON 126 | -DCMAKE_BUILD_TYPE=Release 127 | -DSDL_SHARED=ON 128 | -DSDL_STATIC=OFF 129 | -DSDL_DISABLE_INSTALL=ON 130 | -DSDL_DISABLE_INSTALL_DOCS=ON 131 | -DSDL_INSTALL_TESTS=OFF 132 | -DSDLTTF_INSTALL_MAN=OFF 133 | -DSDLTTF_VENDORED=ON 134 | -S . -B build 135 | - name: Build 136 | run: cmake --build build --config Release 137 | - name: Upload Build 138 | uses: actions/upload-artifact@v4 139 | with: 140 | name: sdl-ttf-apple-macOS-${{ matrix.arch }} 141 | path: SDL_ttf/build/* 142 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | TODO.md -------------------------------------------------------------------------------- /CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | See [COVERAGE.md](COVERAGE.md) for the list of fonctions that are and are not implemented yet. 4 | 5 | ## Structure 6 | 7 | - A json description of SDL3 API is generated ([`ffi/sdl.json`](cmd/internal/assets/ffi/sdl.json)) using [c2ffi](https://github.com/rpav/c2ffi) from [docker](Dockerfile). 8 | - The [API quick reference](https://wiki.libsdl.org/SDL3/QuickReference) is downloaded within [cmd/ffi2go](cmd/ffi2go/) and serves as a base to determine the list of API functions. 9 | - All `*.gen.go` files are generated by running [cmd/ffi2go](cmd/ffi2go/), it uses both `ffi.json` and the quick api reference file. 10 | - Function wrapped implementations can be found in [sdl/functions.go](sdl/functions.go) and [sdl/methods.go](sdl/methods.go). I'm not sure about the names, or this organization at all, suggestions are welcome! 11 | - [sdl/methods.go](sdl/methods.go) contains pointer receiver functions, some don't make sense as they have been generated as a starting point by [cmd/methodgen](cmd/methodgen/) and need tweaking. Most of these methods `panic` and need a developer input to be sanitized (idiomatic Go) and checked against SDL3 spec. 12 | - [sdl/sdl_functions_js.go](sdl/sdl_functions_js.go) is in a similar state as methods.go, most of the code has been generated as a starting point but need to be fixed, sanitized and mostly tested. For this reason most of the functions panic by default. 13 | - Functions are defined unexposed (with a `i` prefix) and registered to [purego](https://github.com/ebitengine/purego), they have a comment when they require a call to `internal.Free` (you can also find the list of functions requiring sdlFree, in ffi2go/config json files). 14 | - [sdl/glue.go](sdl/glue.go) contains glue code that couldn't be generated efficiently. 15 | 16 | Feel free, to help for testing, examples, expose some functions. 17 | Also to help re-organize, rename, refactor, as some directions have been taken arbitrarily. 18 | 19 | ## Generate 20 | 21 | `go install github.com/go-task/task/v3/cmd/task@latest` 22 | 23 | ### Commands 24 | 25 | `task --list-all` 26 | 27 | ### Bindings 28 | - `task gen-bindings LIBRARY="sdl"` 29 | - `task gen-bindings LIBRARY="ttf"` 30 | - `task gen-bindings LIBRARY="img"` 31 | - `task gen-bindings LIBRARY="mixer"` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Zyko0 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-sdl3 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/zyko0/go-sdl3.svg)](https://pkg.go.dev/github.com/zyko0/go-sdl3) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/Zyko0/go-sdl3)](https://goreportcard.com/report/github.com/Zyko0/go-sdl3) 5 | 6 | [SDL3](https://wiki.libsdl.org/SDL3/FrontPage) bindings for Go in pure Go (thanks to [ebitengine/purego](https://github.com/ebitengine/purego)). 7 | 8 | ## About 9 | 10 | This library wraps SDL3 to more idiomatic go and: 11 | - Changes return values from `bool` to `error` (`SDL_GetError`). 12 | - Trims `SDL_` prefix from all types, variables, function names. 13 | - Make methods from global functions when it is possible. 14 | - Turn some pointer function parameters into return values. 15 | 16 | If you are looking for pure Go bindings that are closer to the original API, please have a look at https://github.com/JupiterRider/purego-sdl3. 17 | 18 | ## Status 19 | 20 | > [!NOTE] 21 | > The API is currently subject to many changes / refactors, many method functions are also exposed but not implemented yet (need human validation). 22 | 23 | The list of currently supported functions can be found in [COVERAGE.md](COVERAGE.md). 24 | 25 | Platforms: 26 | - Windows (amd64, arm64) 27 | - Linux (amd64) 28 | - MacOS (amd64, arm64) 29 | - WebAssembly 30 | 31 | ## Usage 32 | 33 | The library is linked dynamically with [purego](https://github.com/ebitengine/purego) (does not require CGo). 34 | 35 | Embedded: The code below will write the library to a temporary folder, and remove it when the `main` function returns. 36 | ```go 37 | defer binsdl.Load().Unload() 38 | ``` 39 | Manual: If the library is installed or if the location is known (e.g: same folder), it can be loaded by its path. 40 | ```go 41 | sdl.LoadLibrary(sdl.Path()) // "SDL3.dll", "libSDL3.so.0", "libSDL3.dylib" 42 | ``` 43 | 44 | **Example:** 45 | > Note that you do not have to pass your update function `sdl.RunLoop`, however doing so allows you to target `GOOS=js`/`GOARCH=wasm`, see [wasmsdl](cmd/wasmsdl/). 46 | ```go 47 | package main 48 | 49 | import ( 50 | "github.com/Zyko0/go-sdl3/sdl" 51 | "github.com/Zyko0/go-sdl3/bin/binsdl" 52 | ) 53 | 54 | func main() { 55 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 56 | defer sdl.Quit() 57 | 58 | if err := sdl.Init(sdl.INIT_VIDEO); err != nil { 59 | panic(err) 60 | } 61 | 62 | window, renderer, err := sdl.CreateWindowAndRenderer("Hello world", 500, 500, 0) 63 | if err != nil { 64 | panic(err) 65 | } 66 | defer renderer.Destroy() 67 | defer window.Destroy() 68 | 69 | renderer.SetDrawColor(255, 255, 255, 255) 70 | 71 | sdl.RunLoop(func() error { 72 | var event sdl.Event 73 | 74 | for sdl.PollEvent(&event) { 75 | if event.Type == sdl.EVENT_QUIT { 76 | return sdl.EndLoop 77 | } 78 | } 79 | 80 | renderer.DebugText(50, 50, "Hello world") 81 | renderer.Present() 82 | 83 | return nil 84 | }) 85 | } 86 | ``` 87 | 88 | ## Examples 89 | 90 | The [examples](./examples/) folder contains the offical examples that can be found here https://examples.libsdl.org/SDL3, and a few more. 91 | 92 | - Clay UI renderer: https://github.com/TotallyGamerJet/clay/tree/main/examples/sdl3 93 | 94 | ## Libraries 95 | 96 | - SDL3 97 | - SDL3_ttf 98 | - SDL3_image 99 | - SDL3_mixer 100 | - SDL3_shadercross (TBD) -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | tasks: 4 | tools: 5 | desc: Install required tools 6 | cmds: 7 | # Install purego-gen 8 | - go install github.com/Zyko0/purego-gen/cmd/purego-gen@latest 9 | 10 | gen-ffi: 11 | desc: Generate ffi.json files for SDL3 libraries 12 | cmds: 13 | # Generate ffi.json files 14 | - docker build ./.docker/ffi/ -t sdl3ffi 15 | - docker create --name tmp sdl3ffi 16 | - docker cp tmp:/sdl_ffi.json ./cmd/internal/assets/ffi/sdl.json 17 | - docker cp tmp:/ttf_ffi.json ./cmd/internal/assets/ffi/ttf.json 18 | - docker cp tmp:/mixer_ffi.json ./cmd/internal/assets/ffi/mixer.json 19 | - docker cp tmp:/image_ffi.json ./cmd/internal/assets/ffi/img.json 20 | - docker rm -f tmp 21 | 22 | extract-wiki: 23 | desc: Extract documentation annotations from SDL3 wiki repository 24 | cmds: 25 | # Extract SDL3 wiki annotations 26 | - docker build ./.docker/wiki-annotations/ -t sdl3wiki 27 | - docker create --name tmp sdl3wiki 28 | - docker cp tmp:/go/annotations.csv ./cmd/internal/assets/wiki/annotations.csv 29 | - docker rm -f tmp 30 | 31 | build-wasm-lib: 32 | desc: Build SDL3 library as a wasm binary file 33 | cmds: 34 | # Generate emscripten wasm binary file 35 | - docker build ./.docker/emscripten-build/ -t sdl3wasm 36 | - docker create --name tmp sdl3wasm 37 | - docker cp tmp:/_out ./out_test 38 | - docker rm -f tmp 39 | 40 | gen-bindings: 41 | desc: Generate Go bindings for a chosen SDL3 library 42 | deps: [tools, gen-ffi] 43 | requires: 44 | vars: [LIBRARY] 45 | vars: 46 | DIR: '{{.LIBRARY}}' 47 | INPUT: 48 | '{{.DIR}}/{{.LIBRARY}}_functions.gen.go' 49 | EXTRA: 50 | - '{{.DIR}}/{{.LIBRARY}}_structs.gen.go' 51 | - '{{.DIR}}/{{.LIBRARY}}_types.gen.go' 52 | - '{{.DIR}}/{{.LIBRARY}}_enums.gen.go' 53 | - '{{.DIR}}/glue.go' 54 | cmds: 55 | # Generate SDL3 bindings 56 | - go run ./cmd/ffi2go --config "cmd/internal/assets/config/{{.LIBRARY}}.json" --ffi "cmd/internal/assets/ffi/{{.LIBRARY}}.json" 57 | - purego-gen --no-warnings --function-name "initialize" --input {{.INPUT}} --extra {{.EXTRA | join ","}} 58 | - '{{if eq .LIBRARY "sdl"}}purego-gen --no-library-handle --no-warnings --function-name "initialize_ex" --input {{.DIR}}/{{.LIBRARY}}_functions_ex.go --extra {{.EXTRA | join ","}}{{end}}' 59 | - '{{if eq .LIBRARY "ttf"}}purego-gen --no-library-handle --no-warnings --function-name "initialize_ex" --input {{.DIR}}/{{.LIBRARY}}_functions_ex.go --extra {{.EXTRA | join ","}}{{end}}' 60 | 61 | gen-methods: 62 | desc: Generate methods for SDL3 library (first pass) 63 | requires: 64 | vars: [LIBRARY] 65 | vars: 66 | DIR: '{{.LIBRARY"}}' 67 | cmds: 68 | # Generate SDL3 methods heuristically as a starting point 69 | - go run ./cmd/methodgen --dir {{ .DIR }} --library {{ .LIBRARY }} 70 | 71 | gen-doc: 72 | deps: [extract-wiki] 73 | desc: Generate documentation for functions and types of the chosen SDL3 library 74 | requires: 75 | vars: [LIBRARY] 76 | vars: 77 | DIR: '{{.LIBRARY}}' 78 | cmds: 79 | # Generate SDL3 documentation on top of functions and types 80 | - go run ./cmd/docgen --config "cmd/internal/assets/config/{{.LIBRARY}}.json" --dir {{ .DIR }} --annotations "cmd/internal/assets/wiki/annotations.csv" 81 | 82 | coverage: 83 | desc: Update the COVERAGE.md file with functions covered by the bindings for SDL3 libraries 84 | vars: 85 | LIBS: [sdl, ttf, img, mixer] 86 | cmds: 87 | - for: 88 | var: LIBS 89 | cmd: go run ./cmd/coverage --config "cmd/internal/assets/config/{{.ITEM}}.json" --dir {{.ITEM}} 90 | 91 | fmt: 92 | desc: Format go code 93 | cmds: 94 | # go fmt code 95 | - go fmt github.com/Zyko0/go-sdl3/... -------------------------------------------------------------------------------- /bin/binimg/assets/img.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binimg/assets/img.so -------------------------------------------------------------------------------- /bin/binimg/assets/img_amd64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binimg/assets/img_amd64.dll -------------------------------------------------------------------------------- /bin/binimg/assets/img_amd64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binimg/assets/img_amd64.dylib -------------------------------------------------------------------------------- /bin/binimg/assets/img_arm64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binimg/assets/img_arm64.dll -------------------------------------------------------------------------------- /bin/binimg/assets/img_arm64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binimg/assets/img_arm64.dylib -------------------------------------------------------------------------------- /bin/binimg/binary_darwin_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && amd64 2 | 3 | package binimg 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/img_amd64.dylib 11 | imgBlob []byte 12 | imgLibName = "libSDL3_image.dylib" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binimg/binary_darwin_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && arm64 2 | 3 | package binimg 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/img_arm64.dylib 11 | imgBlob []byte 12 | imgLibName = "libSDL3_image.dylib" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binimg/binary_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package binimg 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/img.so 11 | imgBlob []byte 12 | imgLibName = "libSDL3_image.so.0" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binimg/binary_windows_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && amd64 2 | 3 | package binimg 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/img_amd64.dll 11 | imgBlob []byte 12 | imgLibName = "SDL3_image.dll" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binimg/binary_windows_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && arm64 2 | 3 | package binimg 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/img_arm64.dll 11 | imgBlob []byte 12 | imgLibName = "SDL3_image.dll" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binimg/load_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package binimg 4 | 5 | import "github.com/Zyko0/go-sdl3/img" 6 | 7 | type library struct{} 8 | 9 | func Load() library { 10 | return library{} 11 | } 12 | 13 | func (l library) Unload() { 14 | img.CloseLibrary() 15 | } 16 | -------------------------------------------------------------------------------- /bin/binimg/load_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package binimg 4 | 5 | import ( 6 | "log" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/Zyko0/go-sdl3/img" 11 | "github.com/Zyko0/go-sdl3/internal" 12 | ) 13 | 14 | type library struct { 15 | dir string 16 | } 17 | 18 | func Load() library { 19 | tmp, err := internal.TmpDir() 20 | if err != nil { 21 | log.Fatal("binimg: couldn't create a temporary directory: " + err.Error()) 22 | } 23 | 24 | imgPath := filepath.Join(tmp, imgLibName) 25 | err = os.WriteFile(imgPath, imgBlob, 0666) 26 | if err != nil { 27 | log.Fatal("binimg: couldn't write img library to disk: " + err.Error()) 28 | } 29 | 30 | err = img.LoadLibrary(imgPath) 31 | if err != nil { 32 | log.Fatal("binimg: couldn't img.LoadLibrary: ", err.Error()) 33 | } 34 | 35 | return library{ 36 | dir: tmp, 37 | } 38 | } 39 | 40 | func (l library) Unload() { 41 | err := img.CloseLibrary() 42 | if err != nil { 43 | log.Fatal("binimg: couldn't close library: ", err.Error()) 44 | } 45 | internal.RemoveTmpDir() 46 | } 47 | -------------------------------------------------------------------------------- /bin/binmix/assets/mix.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binmix/assets/mix.so -------------------------------------------------------------------------------- /bin/binmix/assets/mix_amd64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binmix/assets/mix_amd64.dylib -------------------------------------------------------------------------------- /bin/binmix/assets/mix_arm64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binmix/assets/mix_arm64.dylib -------------------------------------------------------------------------------- /bin/binmix/binary_darwin_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && amd64 2 | 3 | package binmix 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/mix_amd64.dylib 11 | mixBlob []byte 12 | mixLibName = "libSDL3_mixer.dylib" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binmix/binary_darwin_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && arm64 2 | 3 | package binmix 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/mix_arm64.dylib 11 | mixBlob []byte 12 | mixLibName = "libSDL3_mixer.dylib" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binmix/binary_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package binmix 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/mix.so 11 | mixBlob []byte 12 | mixLibName = "libSDL3_mixer.so.0" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binmix/binary_windows_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && amd64 2 | 3 | package binmix 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | // //go:embed assets/mix_amd64.dll 11 | mixBlob []byte 12 | mixLibName = "SDL3_mixer.dll" 13 | ) 14 | 15 | func init() { 16 | panic("unsupported mixer binary for windows amd64") 17 | } 18 | -------------------------------------------------------------------------------- /bin/binmix/binary_windows_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && arm64 2 | 3 | package binmix 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | // TODO: not available for downloads apparently 11 | // //go:embed assets/img_arm64.dll 12 | 13 | mixBlob []byte 14 | mixLibName = "SDL3_mixer.dll" 15 | ) 16 | 17 | func init() { 18 | panic("unsupported mixer binary for windows arm64") 19 | } 20 | -------------------------------------------------------------------------------- /bin/binmix/load_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package binmix 4 | 5 | import "github.com/Zyko0/go-sdl3/mixer" 6 | 7 | type library struct{} 8 | 9 | func Load() library { 10 | return library{} 11 | } 12 | 13 | func (l library) Unload() { 14 | mixer.CloseLibrary() 15 | } 16 | -------------------------------------------------------------------------------- /bin/binmix/load_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package binmix 4 | 5 | import ( 6 | "log" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/Zyko0/go-sdl3/internal" 11 | "github.com/Zyko0/go-sdl3/mixer" 12 | ) 13 | 14 | type library struct { 15 | dir string 16 | } 17 | 18 | func Load() library { 19 | tmp, err := internal.TmpDir() 20 | if err != nil { 21 | log.Fatal("binmix: couldn't create a temporary directory: " + err.Error()) 22 | } 23 | 24 | mixPath := filepath.Join(tmp, mixLibName) 25 | err = os.WriteFile(mixPath, mixBlob, 0666) 26 | if err != nil { 27 | log.Fatal("binmix: couldn't write mixer library to disk: " + err.Error()) 28 | } 29 | 30 | err = mixer.LoadLibrary(mixPath) 31 | if err != nil { 32 | log.Fatal("binmix: couldn't mixer.LoadLibrary: ", err.Error()) 33 | } 34 | 35 | return library{ 36 | dir: tmp, 37 | } 38 | } 39 | 40 | func (l library) Unload() { 41 | err := mixer.CloseLibrary() 42 | if err != nil { 43 | log.Fatal("binmix: couldn't close library: ", err.Error()) 44 | } 45 | internal.RemoveTmpDir() 46 | } 47 | -------------------------------------------------------------------------------- /bin/binsdl/assets/sdl.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binsdl/assets/sdl.so -------------------------------------------------------------------------------- /bin/binsdl/assets/sdl_amd64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binsdl/assets/sdl_amd64.dll -------------------------------------------------------------------------------- /bin/binsdl/assets/sdl_amd64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binsdl/assets/sdl_amd64.dylib -------------------------------------------------------------------------------- /bin/binsdl/assets/sdl_arm64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binsdl/assets/sdl_arm64.dll -------------------------------------------------------------------------------- /bin/binsdl/assets/sdl_arm64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binsdl/assets/sdl_arm64.dylib -------------------------------------------------------------------------------- /bin/binsdl/binary_darwin_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && amd64 2 | 3 | package binsdl 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/sdl_amd64.dylib 11 | sdlBlob []byte 12 | sdlLibName = "libSDL3.dylib" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binsdl/binary_darwin_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && arm64 2 | 3 | package binsdl 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/sdl_arm64.dylib 11 | sdlBlob []byte 12 | sdlLibName = "libSDL3.dylib" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binsdl/binary_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package binsdl 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/sdl.so 11 | sdlBlob []byte 12 | sdlLibName = "libSDL3.so.0" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binsdl/binary_windows_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && amd64 2 | 3 | package binsdl 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/sdl_amd64.dll 11 | sdlBlob []byte 12 | sdlLibName = "SDL3.dll" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binsdl/binary_windows_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && arm64 2 | 3 | package binsdl 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/sdl_arm64.dll 11 | sdlBlob []byte 12 | sdlLibName = "SDL3.dll" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binsdl/load_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package binsdl 4 | 5 | type library struct{} 6 | 7 | func Load() library { 8 | return library{} 9 | } 10 | 11 | func (l library) Unload() { 12 | } 13 | -------------------------------------------------------------------------------- /bin/binsdl/load_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package binsdl 4 | 5 | import ( 6 | "log" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/Zyko0/go-sdl3/internal" 11 | "github.com/Zyko0/go-sdl3/sdl" 12 | ) 13 | 14 | type library struct { 15 | dir string 16 | } 17 | 18 | func Load() library { 19 | tmp, err := internal.TmpDir() 20 | if err != nil { 21 | log.Fatal("binsdl: couldn't create a temporary directory: " + err.Error()) 22 | } 23 | 24 | sdlPath := filepath.Join(tmp, sdlLibName) 25 | err = os.WriteFile(sdlPath, sdlBlob, 0666) 26 | if err != nil { 27 | log.Fatal("binsdl: couldn't write sdl library to disk: " + err.Error()) 28 | } 29 | 30 | err = sdl.LoadLibrary(sdlPath) 31 | if err != nil { 32 | log.Fatal("binsdl: couldn't sdl.LoadLibrary: ", err.Error()) 33 | } 34 | 35 | return library{ 36 | dir: tmp, 37 | } 38 | } 39 | 40 | func (l library) Unload() { 41 | err := sdl.CloseLibrary() 42 | if err != nil { 43 | log.Fatal("binsdl: couldn't close library: ", err.Error()) 44 | } 45 | internal.RemoveTmpDir() 46 | } 47 | -------------------------------------------------------------------------------- /bin/binttf/assets/ttf.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binttf/assets/ttf.so -------------------------------------------------------------------------------- /bin/binttf/assets/ttf_amd64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binttf/assets/ttf_amd64.dll -------------------------------------------------------------------------------- /bin/binttf/assets/ttf_amd64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binttf/assets/ttf_amd64.dylib -------------------------------------------------------------------------------- /bin/binttf/assets/ttf_arm64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binttf/assets/ttf_arm64.dll -------------------------------------------------------------------------------- /bin/binttf/assets/ttf_arm64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/bin/binttf/assets/ttf_arm64.dylib -------------------------------------------------------------------------------- /bin/binttf/binary_darwin_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && amd64 2 | 3 | package binttf 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/ttf_amd64.dylib 11 | ttfBlob []byte 12 | ttfLibName = "libSDL3_ttf.dylib" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binttf/binary_darwin_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && arm64 2 | 3 | package binttf 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/ttf_arm64.dylib 11 | ttfBlob []byte 12 | ttfLibName = "libSDL3_ttf.dylib" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binttf/binary_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package binttf 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/ttf.so 11 | ttfBlob []byte 12 | ttfLibName = "libSDL3_ttf.so.0" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binttf/binary_windows_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && amd64 2 | 3 | package binttf 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/ttf_amd64.dll 11 | ttfBlob []byte 12 | ttfLibName = "SDL3_ttf.dll" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binttf/binary_windows_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && arm64 2 | 3 | package binttf 4 | 5 | import ( 6 | _ "embed" 7 | ) 8 | 9 | var ( 10 | //go:embed assets/ttf_arm64.dll 11 | ttfBlob []byte 12 | ttfLibName = "SDL3_ttf.dll" 13 | ) 14 | -------------------------------------------------------------------------------- /bin/binttf/load_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package binttf 4 | 5 | import "github.com/Zyko0/go-sdl3/ttf" 6 | 7 | type library struct{} 8 | 9 | func Load() library { 10 | return library{} 11 | } 12 | 13 | func (l library) Unload() { 14 | ttf.CloseLibrary() 15 | } 16 | -------------------------------------------------------------------------------- /bin/binttf/load_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package binttf 4 | 5 | import ( 6 | "log" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/Zyko0/go-sdl3/internal" 11 | "github.com/Zyko0/go-sdl3/ttf" 12 | ) 13 | 14 | type library struct { 15 | dir string 16 | } 17 | 18 | func Load() library { 19 | tmp, err := internal.TmpDir() 20 | if err != nil { 21 | log.Fatal("binttf: couldn't create a temporary directory: " + err.Error()) 22 | } 23 | 24 | ttfPath := filepath.Join(tmp, ttfLibName) 25 | err = os.WriteFile(ttfPath, ttfBlob, 0666) 26 | if err != nil { 27 | log.Fatal("binttf: couldn't write ttf library to disk: " + err.Error()) 28 | } 29 | 30 | err = ttf.LoadLibrary(ttfPath) 31 | if err != nil { 32 | log.Fatal("binttf: couldn't ttf.LoadLibrary: ", err.Error()) 33 | } 34 | 35 | return library{ 36 | dir: tmp, 37 | } 38 | } 39 | 40 | func (l library) Unload() { 41 | err := ttf.CloseLibrary() 42 | if err != nil { 43 | log.Fatal("binttf: couldn't close library: ", err.Error()) 44 | } 45 | internal.RemoveTmpDir() 46 | } 47 | -------------------------------------------------------------------------------- /cmd/docgen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/csv" 6 | "encoding/json" 7 | "flag" 8 | "fmt" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | "regexp" 13 | "strings" 14 | 15 | "github.com/Zyko0/go-sdl3/cmd/internal/assets" 16 | ) 17 | 18 | var ( 19 | reg = regexp.MustCompile(`i([A-Za-z][A-Za-z_0-9]+)\(`) 20 | 21 | cfg assets.Config 22 | ) 23 | 24 | type Entry struct { 25 | Id string 26 | Description string 27 | URL string 28 | } 29 | 30 | var ( 31 | uniqueAPIIdentifiers = make(map[string]*Entry) 32 | ) 33 | 34 | func main() { 35 | var ( 36 | configPath string 37 | annotationsPath string 38 | dir string 39 | ) 40 | 41 | flag.StringVar(&configPath, "config", "", "path to config.json file") 42 | flag.StringVar(&annotationsPath, "annotations", "", "path to annotations.csv file") 43 | flag.StringVar(&dir, "dir", "", "base directory to generate from/to") 44 | flag.Parse() 45 | 46 | // Parse config 47 | b, err := os.ReadFile(configPath) 48 | if err != nil { 49 | log.Fatal("couldn't read config.json file: ", err) 50 | } 51 | err = json.Unmarshal(b, &cfg) 52 | if err != nil { 53 | log.Fatal("couldn't unmarshal config file: ", err) 54 | } 55 | 56 | // Parse annotations 57 | b, err = os.ReadFile(annotationsPath) 58 | if err != nil { 59 | log.Fatal("couldn't read annotations.csv file: ", err) 60 | } 61 | csvr := csv.NewReader(bytes.NewReader(b)) 62 | records, err := csvr.ReadAll() 63 | if err != nil { 64 | log.Fatal("couldn't read csv records: ", err) 65 | } 66 | 67 | for _, record := range records[1:] { // Skip header 68 | uniqueAPIIdentifiers[record[0]] = &Entry{ 69 | Id: record[0], 70 | Description: strings.ReplaceAll(record[1], "\n", ""), 71 | URL: record[2], 72 | } 73 | } 74 | 75 | path, err := os.Getwd() 76 | if err != nil { 77 | log.Fatal("err: ", err) 78 | } 79 | path = filepath.Join(path, dir) 80 | 81 | files, err := os.ReadDir(path) 82 | if err != nil { 83 | log.Fatal("err: ", err) 84 | } 85 | 86 | var fnAnnotations int 87 | var typesAnnotations int 88 | 89 | for _, e := range files { 90 | if !strings.HasSuffix(e.Name(), ".go") { 91 | continue 92 | } 93 | 94 | b, err := os.ReadFile(filepath.Join(path, e.Name())) 95 | if err != nil { 96 | log.Fatalf("couldn't read file %s: %v\n", e.Name(), err) 97 | } 98 | 99 | var outLines []string 100 | var edited bool 101 | 102 | var inFunc bool 103 | var braces int 104 | var fnName string 105 | var fnLineIndex int 106 | 107 | lines := strings.Split(string(b), "\n") 108 | for i, l := range lines { 109 | // Type 110 | if strings.HasPrefix(l, "type ") { 111 | if i > 0 && strings.TrimSpace(lines[i-1]) == "" { 112 | parts := strings.Split(l, " ") 113 | if len(parts) > 2 { 114 | name := parts[1] 115 | entry, found := uniqueAPIIdentifiers[cfg.Prefix+name] 116 | if found { 117 | // Add comments on top of the type 118 | outLines = append(outLines, fmt.Sprintf( 119 | "// %s - %s", entry.Id, entry.Description, 120 | )) 121 | outLines = append(outLines, fmt.Sprintf( 122 | "// (%s)", entry.URL, 123 | )) 124 | typesAnnotations++ 125 | edited = true 126 | } 127 | } 128 | } 129 | outLines = append(outLines, l) 130 | continue 131 | } 132 | // Function 133 | if inFunc { 134 | braces += strings.Count(l, "{") 135 | braces -= strings.Count(l, "}") 136 | if braces > 0 { 137 | if reg.MatchString(l) { 138 | matches := reg.FindAll([]byte(l), -1) 139 | for _, m := range matches { 140 | name := string(m[1 : len(m)-1]) 141 | _, found := uniqueAPIIdentifiers[name] 142 | if !found { 143 | name = cfg.Prefix + name 144 | _, found = uniqueAPIIdentifiers[name] 145 | } 146 | if found { 147 | fnName = name 148 | } 149 | } 150 | } 151 | } else { 152 | inFunc = false 153 | braces = 0 154 | // Add comments + whole function 155 | entry, found := uniqueAPIIdentifiers[fnName] 156 | if found { 157 | outLines = append(outLines, fmt.Sprintf( 158 | "// %s - %s", entry.Id, entry.Description, 159 | )) 160 | outLines = append(outLines, fmt.Sprintf( 161 | "// (%s)", entry.URL, 162 | )) 163 | fnAnnotations++ 164 | edited = true 165 | } 166 | // Function lines 167 | for fnLineIndex <= i { 168 | outLines = append(outLines, lines[fnLineIndex]) 169 | fnLineIndex++ 170 | } 171 | } 172 | continue 173 | } 174 | 175 | if !strings.HasPrefix(l, "func ") { 176 | outLines = append(outLines, l) 177 | continue 178 | } 179 | // Skip if there's something written on top of the function 180 | // (assuming a comment already) 181 | if strings.TrimSpace(lines[i-1]) != "" { 182 | outLines = append(outLines, l) 183 | continue 184 | } 185 | inFunc = true 186 | braces = 1 187 | fnName = "" 188 | fnLineIndex = i 189 | } 190 | 191 | // Write file if there has been some changes 192 | if edited { 193 | err = os.WriteFile(filepath.Join(dir, e.Name()), []byte(strings.Join(outLines, "\n")), 0666) 194 | if err != nil { 195 | log.Fatalf("couldn't write file %s: %v\n", e.Name(), err) 196 | } 197 | } 198 | } 199 | 200 | fmt.Println("Total functions annotated:", fnAnnotations) 201 | fmt.Println("Total types annotated:", typesAnnotations) 202 | } 203 | -------------------------------------------------------------------------------- /cmd/internal/assets/assets.go: -------------------------------------------------------------------------------- 1 | package assets 2 | 3 | type Config struct { 4 | OutDir string `json:"out_dir"` 5 | LibraryName string `json:"library_name"` 6 | URLLibrarySuffix string `json:"url_library_suffix"` 7 | Prefix string `json:"prefix"` 8 | QuickAPIRefURL string `json:"quick_api_ref_url"` 9 | AllowedInclude string `json:"allowed_include"` 10 | IgnoredHeaders []string `json:"ignored_headers"` 11 | IgnoredTypes []string `json:"ignored_types"` 12 | IgnoredFunctions []string `json:"ignored_functions"` 13 | AllowlistedFunctions []string `json:"allowlisted_functions"` 14 | AllowlistedTypePrefixes []string `json:"allowlisted_type_prefixes"` 15 | BaseTypes []string `json:"base_types"` 16 | SDLFreeFunctions []string `json:"sdl_free_functions"` 17 | } 18 | 19 | type FFIEntry struct { 20 | Name string `json:"name"` 21 | Ns int `json:"ns"` 22 | Tag string `json:"tag"` 23 | Type *FFIEntry `json:"type"` 24 | Value int `json:"value"` 25 | Size int `json:"size"` 26 | Fields []*FFIEntry `json:"fields"` 27 | StorageClass string `json:"storage-class"` 28 | Variadic bool `json:"variadic"` 29 | Inline bool `json:"inline"` 30 | ReturnType *FFIEntry `json:"return-type"` 31 | Parameters []*FFIEntry `json:"parameters"` 32 | BitOffset int `json:"bit-offset"` 33 | BitSize int `json:"bit-size"` 34 | BitAlignment int `json:"bit-alignment"` 35 | ID int `json:"id"` 36 | Location string `json:"location"` 37 | 38 | SymbolHasPrefix bool 39 | } 40 | -------------------------------------------------------------------------------- /cmd/internal/assets/config/img.json: -------------------------------------------------------------------------------- 1 | { 2 | "out_dir": "img", 3 | "library_name": "img", 4 | "url_library_suffix": "_image", 5 | "prefix": "IMG_", 6 | "quick_api_ref_url": "https://raw.githubusercontent.com/libsdl-org/sdlwiki/refs/heads/main/SDL3_image/QuickReference.md", 7 | "allowed_include": "/usr/include/SDL3_image/", 8 | "ignored_headers": [ 9 | ], 10 | "ignored_types": [ 11 | "Animation" 12 | ], 13 | "ignored_functions": [ 14 | ], 15 | "allowlisted_functions": [ 16 | "IMG_isAVIF", 17 | "IMG_isICO", 18 | "IMG_isCUR", 19 | "IMG_isBMP", 20 | "IMG_isGIF", 21 | "IMG_isJPG", 22 | "IMG_isJXL", 23 | "IMG_isLBM", 24 | "IMG_isPCX", 25 | "IMG_isPNG", 26 | "IMG_isPNM", 27 | "IMG_isSVG", 28 | "IMG_isQOI", 29 | "IMG_isTIF", 30 | "IMG_isXCF", 31 | "IMG_isXPM", 32 | "IMG_isXV", 33 | "IMG_isWEBP" 34 | ], 35 | "allowlisted_type_prefixes": [ 36 | "IMG_" 37 | ], 38 | "base_types": [ 39 | ], 40 | "sdl_free_functions": [ 41 | ] 42 | } -------------------------------------------------------------------------------- /cmd/internal/assets/config/mixer.json: -------------------------------------------------------------------------------- 1 | { 2 | "out_dir": "mixer", 3 | "library_name": "mixer", 4 | "url_library_suffix": "_mixer", 5 | "prefix": "Mix_", 6 | "quick_api_ref_url": "https://raw.githubusercontent.com/libsdl-org/sdlwiki/refs/heads/main/SDL3_mixer/QuickReference.md", 7 | "allowed_include": "/usr/include/SDL3_mixer/", 8 | "ignored_headers": [ 9 | ], 10 | "ignored_types": [ 11 | "Chunk" 12 | ], 13 | "ignored_functions": [ 14 | ], 15 | "allowlisted_functions": [ 16 | ], 17 | "allowlisted_type_prefixes": [ 18 | "Mix_", "MIX_" 19 | ], 20 | "base_types": [ 21 | ], 22 | "sdl_free_functions": [ 23 | 24 | ] 25 | } -------------------------------------------------------------------------------- /cmd/internal/assets/config/sdl.json: -------------------------------------------------------------------------------- 1 | { 2 | "out_dir": "sdl", 3 | "library_name": "sdl", 4 | "url_library_suffix": "", 5 | "prefix": "SDL_", 6 | "quick_api_ref_url": "https://raw.githubusercontent.com/libsdl-org/sdlwiki/refs/heads/main/SDL3/QuickReference.md", 7 | "allowed_include": "/usr/include/SDL3/", 8 | "ignored_headers": [ 9 | "SDL_oldnames.h", 10 | "SDL_bits.h", 11 | "SDL_intrin.h", 12 | "SDL_main_impl.h", 13 | "SDL_assert.h", 14 | "SDL_stdinc.h", 15 | "SDL_endian.h" 16 | ], 17 | "ignored_types": [ 18 | "GamepadBinding", 19 | "GUID", 20 | "Surface", 21 | "MessageBoxData", 22 | "GPUShaderCreateInfo", 23 | "GPUComputePipelineCreateInfo", 24 | "TextEditingCandidatesEvent", 25 | "ClipboardEvent", 26 | "Palette" 27 | ], 28 | "ignored_functions": [ 29 | "SDL_SetLinuxThreadPriority", 30 | "SDL_SetLinuxThreadPriorityAndPolicy", 31 | "SDL_SetGPUBlendConstants", 32 | "SDL_ShowMessageBox", 33 | "SDL_CreateGPUShader", 34 | "SDL_CreateGPUComputePipeline" 35 | ], 36 | "allowlisted_functions": [ 37 | "SDL_free" 38 | ], 39 | "allowlisted_type_prefixes": [ 40 | "SDL_", 41 | "Vk", 42 | "X" 43 | ], 44 | "base_types": [ 45 | "SDL_Event" 46 | ], 47 | "sdl_free_functions": [ 48 | "SDL_GetDisplays", 49 | "SDL_GetFullscreenDisplayModes", 50 | "SDL_GetWindowICCProfile", 51 | "SDL_GetWindows", 52 | "SDL_GetKeyboards", 53 | "SDL_GetMice", 54 | "SDL_GetTouchDevices", 55 | "SDL_GetTouchFingers", 56 | "SDL_GetGamepads", 57 | "SDL_GetGamepadMappings", 58 | "SDL_GetGamepadMappingForGUID", 59 | "SDL_GetGamepadMapping", 60 | "SDL_GetGamepadMappingForID", 61 | "SDL_GetGamepadBindings", 62 | "SDL_GetJoysticks", 63 | "SDL_GetHaptics", 64 | "SDL_GetAudioPlaybackDevices", 65 | "SDL_GetAudioRecordingDevices", 66 | "SDL_GetAudioDeviceChannelMap", 67 | "SDL_GetAudioStreamInputChannelMap", 68 | "SDL_GetAudioStreamOutputChannelMap", 69 | "SDL_GetPrefPath", 70 | "SDL_GlobDirectory", 71 | "SDL_GetCurrentDirectory", 72 | "SDL_LoadFile_IO", 73 | "SDL_LoadFile", 74 | "SDL_GlobStorageDirectory", 75 | "SDL_GetSurfaceImages", 76 | "SDL_GetCameras", 77 | "SDL_GetCameraSupportedFormats", 78 | "SDL_GetClipboardText", 79 | "SDL_GetPrimarySelectionText", 80 | "SDL_GetClipboardData", 81 | "SDL_GetClipboardMimeTypes", 82 | "SDL_GetSensors", 83 | "SDL_GetPreferredLocales" 84 | ] 85 | } -------------------------------------------------------------------------------- /cmd/internal/assets/config/ttf.json: -------------------------------------------------------------------------------- 1 | { 2 | "out_dir": "ttf", 3 | "library_name": "ttf", 4 | "url_library_suffix": "_ttf", 5 | "prefix": "TTF_", 6 | "quick_api_ref_url": "https://raw.githubusercontent.com/libsdl-org/sdlwiki/refs/heads/main/SDL3_ttf/QuickReference.md", 7 | "allowed_include": "/usr/include/SDL3_ttf/", 8 | "ignored_headers": [ 9 | ], 10 | "ignored_types": [ 11 | "TextData", 12 | "TextEngine" 13 | ], 14 | "ignored_functions": [ 15 | "TTF_RenderText_Solid", 16 | "TTF_RenderText_Solid_Wrapped", 17 | "TTF_RenderGlyph_Solid", 18 | "TTF_RenderText_Shaded", 19 | "TTF_RenderText_Shaded_Wrapped", 20 | "TTF_RenderGlyph_Shaded", 21 | "TTF_RenderText_Blended", 22 | "TTF_RenderText_Blended_Wrapped", 23 | "TTF_RenderGlyph_Blended", 24 | "TTF_RenderText_LCD", 25 | "TTF_RenderText_LCD_Wrapped", 26 | "TTF_RenderGlyph_LCD", 27 | "TTF_TagToString" 28 | ], 29 | "allowlisted_functions": [ 30 | ], 31 | "allowlisted_type_prefixes": [ 32 | "TTF_" 33 | ], 34 | "base_types": [ 35 | "TTF_DrawOperation" 36 | ], 37 | "sdl_free_functions": [ 38 | 39 | ] 40 | } -------------------------------------------------------------------------------- /cmd/methodgen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go/ast" 7 | "go/parser" 8 | "go/token" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | 14 | "github.com/dave/jennifer/jen" 15 | ) 16 | 17 | type FuncArg struct { 18 | Name string 19 | Type string 20 | } 21 | 22 | type Function struct { 23 | Name string 24 | Receiver *FuncArg 25 | Params []*FuncArg 26 | Return *FuncArg 27 | } 28 | 29 | func main() { 30 | var ( 31 | libraryName string 32 | dir string 33 | ) 34 | 35 | flag.StringVar(&dir, "dir", "", "base directory to generate from/to") 36 | flag.StringVar(&libraryName, "library", "", "library name (e.g: sdl)") 37 | flag.Parse() 38 | 39 | path, err := os.Getwd() 40 | if err != nil { 41 | log.Fatal("err: ", err) 42 | } 43 | path = filepath.Join(path, dir, libraryName+"_functions.gen.go") 44 | 45 | fset := token.NewFileSet() 46 | root, err := parser.ParseFile(fset, path, nil, parser.AllErrors) 47 | if err != nil { 48 | log.Fatal("cannot parse "+path+": ", err) 49 | } 50 | 51 | funcsByReceiver := map[string][]*Function{} 52 | ast.Inspect(root, func(n ast.Node) bool { 53 | if n == nil { 54 | return true 55 | } 56 | switch tn := n.(type) { 57 | case *ast.GenDecl: 58 | if tn.Tok == token.VAR { 59 | for _, s := range tn.Specs { 60 | switch ts := s.(type) { 61 | case *ast.ValueSpec: 62 | name := ts.Names[0].Name[1:] // Remove "i" prefix 63 | switch tt := ts.Type.(type) { 64 | case *ast.FuncType: 65 | fn := &Function{ 66 | Name: name, 67 | } 68 | if tt.Results != nil && len(tt.Results.List) > 0 { 69 | for _, f := range tt.Results.List { 70 | typ := fmt.Sprintf("%v", f.Type) 71 | switch ft := f.Type.(type) { 72 | case *ast.StarExpr: 73 | switch xt := ft.X.(type) { 74 | case *ast.StarExpr: 75 | typ = fmt.Sprintf("**%v", xt.X) 76 | case *ast.SelectorExpr: 77 | typ = fmt.Sprintf("*%v.%v", xt.X, xt.Sel) 78 | default: 79 | raw := fmt.Sprintf("%v", xt) 80 | typ = "*" + raw 81 | } 82 | case *ast.SliceExpr: 83 | typ = fmt.Sprintf("[]%v", ft.X) 84 | case *ast.SelectorExpr: 85 | typ = fmt.Sprintf("%v.%v", ft.X, ft.Sel) 86 | } 87 | fn.Return = &FuncArg{ 88 | Type: typ, 89 | } 90 | } 91 | } 92 | if len(tt.Params.List) > 0 { 93 | for i, f := range tt.Params.List { 94 | typ := fmt.Sprintf("%v", f.Type) 95 | switch ft := f.Type.(type) { 96 | case *ast.StarExpr: 97 | switch xt := ft.X.(type) { 98 | case *ast.StarExpr: 99 | typ = fmt.Sprintf("**%v", xt.X) 100 | case *ast.SelectorExpr: 101 | typ = fmt.Sprintf("*%v.%v", xt.X, xt.Sel) 102 | default: 103 | raw := fmt.Sprintf("%v", xt) 104 | typ = "*" + raw 105 | if i == 0 && raw[0] >= 'A' && raw[0] <= 'Z' { 106 | fn.Receiver = &FuncArg{ 107 | Name: f.Names[0].Name, 108 | Type: typ, 109 | } 110 | } 111 | } 112 | case *ast.SliceExpr: 113 | typ = fmt.Sprintf("[]%v", ft.X) 114 | case *ast.SelectorExpr: 115 | typ = fmt.Sprintf("*%v.%v", ft.X, ft.Sel) 116 | default: 117 | if i == 0 && typ[0] >= 'A' && typ[0] <= 'Z' { 118 | fn.Receiver = &FuncArg{ 119 | Name: f.Names[0].Name, 120 | Type: typ, 121 | } 122 | } 123 | } 124 | if i != 0 || fn.Receiver == nil { 125 | fn.Params = append(fn.Params, &FuncArg{ 126 | Name: f.Names[0].Name, 127 | Type: typ, 128 | }) 129 | } 130 | } 131 | } 132 | if fn.Receiver != nil { 133 | funcsByReceiver[fn.Receiver.Type] = append(funcsByReceiver[fn.Receiver.Type], fn) 134 | } 135 | } 136 | } 137 | } 138 | } 139 | } 140 | return true 141 | }) 142 | 143 | f := jen.NewFile(libraryName) 144 | for receiver, funcs := range funcsByReceiver { 145 | receiver = strings.ReplaceAll(receiver, "*", "") 146 | f.Comment("// " + receiver).Line() 147 | for _, fn := range funcs { 148 | newFnName := strings.Replace(fn.Name, receiver, "", 1) 149 | if newFnName != "Get" && strings.HasPrefix(newFnName, "Get") { 150 | newFnName = newFnName[3:] 151 | } 152 | f.Func(). 153 | Params( 154 | jen.Id(fn.Receiver.Name).Id(fn.Receiver.Type), 155 | ). 156 | Id(newFnName). 157 | ParamsFunc(func(g *jen.Group) { 158 | for _, arg := range fn.Params { 159 | g.Add(jen.Id(arg.Name).Id(arg.Type)) 160 | } 161 | }). 162 | ParamsFunc(func(g *jen.Group) { 163 | if fn.Return != nil { 164 | g.Add(jen.Id(fn.Return.Name).Id(fn.Return.Type)) 165 | } 166 | }). 167 | BlockFunc(func(g *jen.Group) { 168 | g.Add(jen.Panic(jen.Lit("not implemented"))) 169 | call := jen.Id("i" + fn.Name).CallFunc(func(g *jen.Group) { 170 | g.Add(jen.Id(fn.Receiver.Name)) 171 | for _, arg := range fn.Params { 172 | g.Add(jen.Id(arg.Name)) 173 | } 174 | }) 175 | if fn.Return != nil { 176 | g.Add(jen.Return(call)) 177 | } else { 178 | g.Add(call) 179 | } 180 | }). 181 | Line() 182 | } 183 | } 184 | 185 | err = os.WriteFile("methods_generated_please_rename.go", []byte(f.GoString()), 0666) 186 | if err != nil { 187 | log.Fatal("err: ", err) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /cmd/wasmsdl/README.md: -------------------------------------------------------------------------------- 1 | # wasmsdl 2 | 3 | wasmsdl is a tool to build or serve an application built with go-sdl3 for wasm/js targets. 4 | 5 | ## Usage 6 | 7 | ``` 8 | Usage of wasmsdl: 9 | 10 | wasmsdl [arguments] 11 | 12 | Commands: 13 | 14 | build build the application on disk 15 | build-archive build the application and pack all files in an archive 16 | serve serve the application on localhost:8080 17 | ``` 18 | 19 | ## Credits 20 | 21 | Heavily inspired from the more general purpose: https://github.com/hajimehoshi/wasmserve/ -------------------------------------------------------------------------------- /cmd/wasmsdl/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 51 | 52 | 53 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /cmd/wasmsdl/assets/sdl.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/cmd/wasmsdl/assets/sdl.wasm -------------------------------------------------------------------------------- /cmd/wasmsdl/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "archive/zip" 5 | "bytes" 6 | _ "embed" 7 | "encoding/base64" 8 | "errors" 9 | "flag" 10 | "fmt" 11 | "go/version" 12 | "io" 13 | "log" 14 | "net/http" 15 | "os" 16 | "os/exec" 17 | "path" 18 | "path/filepath" 19 | "strings" 20 | "time" 21 | ) 22 | 23 | var ( 24 | //go:embed assets/index.html 25 | indexHTML []byte 26 | 27 | //go:embed assets/sdl.js 28 | sdlJS []byte 29 | 30 | //go:embed assets/sdl.wasm 31 | sdlWASM []byte 32 | ) 33 | 34 | // Get the go version of the user module 35 | func goVersion(buildDir string) (string, error) { 36 | cmd := exec.Command("go", "list", "-f", "go{{.Module.GoVersion}}") 37 | cmd.Dir = buildDir 38 | cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm") 39 | out, err := cmd.Output() 40 | if err != nil { 41 | if exitError, ok := err.(*exec.ExitError); ok { 42 | return "", fmt.Errorf("couldn't get go version: %s", exitError.Stderr) 43 | } 44 | return "", err 45 | } 46 | v := strings.TrimSpace(string(out)) 47 | if !version.IsValid(v) { 48 | return "", errors.New("could not determine go version") 49 | } 50 | 51 | return v, nil 52 | } 53 | 54 | func getWasmExecJS(buildDir string) ([]byte, error) { 55 | // Try finding it locally 56 | dir, ok := os.LookupEnv("GOROOT") 57 | if ok { 58 | // go1.23.0 and before were under "misc" folder 59 | for _, d := range []string{"lib", "misc"} { 60 | b, err := os.ReadFile(filepath.Join(dir, d, "wasm/wasm_exec.js")) 61 | if err == nil { 62 | return b, nil 63 | } 64 | if !errors.Is(err, os.ErrNotExist) { 65 | return nil, err 66 | } 67 | } 68 | } 69 | // Try fetching it online 70 | v, err := goVersion(buildDir) 71 | if err != nil { 72 | return nil, err 73 | } 74 | // Build path 75 | path := "lib/wasm/wasm_exec.js" 76 | if version.Compare(v, "go1.24.0") < 0 { 77 | path = "misc/wasm/wasm_exec.js" 78 | } 79 | // Fetch wasm_exec.js content 80 | resp, err := http.Get(fmt.Sprintf("https://go.googlesource.com/go/+/refs/tags/%s/%s?format=TEXT", v, path)) 81 | if err != nil { 82 | return nil, err 83 | } 84 | defer resp.Body.Close() 85 | 86 | content, err := io.ReadAll(base64.NewDecoder(base64.StdEncoding, resp.Body)) 87 | if err != nil { 88 | return nil, err 89 | } 90 | 91 | return content, nil 92 | } 93 | 94 | type Files map[string][]byte 95 | 96 | func GetFiles(buildDir, htmlPath string) (Files, error) { 97 | // index.html content 98 | htmlContent := indexHTML 99 | if htmlPath != "" { 100 | b, err := os.ReadFile(htmlPath) 101 | if err != nil { 102 | log.Fatal("couldn't read index.html file: ", err) 103 | } 104 | htmlContent = b 105 | } 106 | // wasm_exec.js content 107 | wasmExecContent, err := getWasmExecJS(buildDir) 108 | if err != nil { 109 | log.Fatal("couldn't get wasm_exec.js content: ", err) 110 | } 111 | // Build 112 | wasmFileName := fmt.Sprintf("main_%d.wasm", time.Now().UnixNano()) 113 | cmd := exec.Command("go", "build", "-o", wasmFileName) 114 | cmd.Dir = buildDir 115 | cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm") 116 | 117 | var stderr bytes.Buffer 118 | cmd.Stderr = &stderr 119 | if err := cmd.Run(); err != nil { 120 | return nil, fmt.Errorf("%s%w", stderr.String(), err) 121 | } 122 | // Read wasm binary and remove it from disk 123 | wasmBytes, err := os.ReadFile(filepath.Join(buildDir, wasmFileName)) 124 | if err != nil { 125 | return nil, err 126 | } 127 | if err := os.Remove(filepath.Join(buildDir, wasmFileName)); err != nil { 128 | fmt.Fprintf(os.Stderr, "warn: couldn't remove wasm file '%s': %v\n", wasmFileName, err) 129 | } 130 | 131 | return Files{ 132 | "index.html": htmlContent, 133 | "wasm_exec.js": wasmExecContent, 134 | "main.wasm": wasmBytes, 135 | "sdl.js": sdlJS, 136 | "sdl.wasm": sdlWASM, 137 | }, nil 138 | } 139 | 140 | func main() { 141 | const usage = ` 142 | Usage of wasmsdl: 143 | 144 | wasmsdl [arguments] 145 | 146 | Commands: 147 | 148 | build build the application on disk 149 | build-archive build the application and pack all files in an archive 150 | serve serve the application on localhost:8080 151 | ` 152 | var htmlPath string 153 | 154 | if len(os.Args) <= 1 { 155 | log.Fatal(usage) 156 | } 157 | dir, err := os.Getwd() 158 | if err != nil { 159 | log.Fatal(err) 160 | } 161 | cmd := os.Args[1] 162 | cmdFlags := flag.NewFlagSet("wasmsdl "+cmd, flag.ContinueOnError) 163 | cmdFlags.StringVar(&htmlPath, "html", "", "optional index.html file") 164 | switch cmd { 165 | case "build": 166 | var out string 167 | 168 | cmdFlags.StringVar(&out, "out", "", "output directory") 169 | err := cmdFlags.Parse(os.Args[2:]) 170 | if err != nil { 171 | os.Exit(1) 172 | } 173 | if cmdFlags.Arg(0) != "" { 174 | dir, _ = filepath.Abs(cmdFlags.Arg(0)) 175 | } 176 | files, err := GetFiles(dir, htmlPath) 177 | if err != nil { 178 | log.Fatal(err) 179 | } 180 | if out == "" { 181 | out = fmt.Sprintf("out_%d", time.Now().UnixNano()) 182 | } 183 | // Create out directory 184 | err = os.MkdirAll(out, 0700) 185 | if err != nil { 186 | log.Fatal("couldn't create output directory: ", err) 187 | } 188 | // Write files 189 | for name, data := range files { 190 | if err := os.WriteFile(filepath.Join(out, name), data, 0666); err != nil { 191 | log.Fatalf("couldn't write '%s': %v", name, err) 192 | } 193 | } 194 | fmt.Printf("Created directory '%s'\n", out) 195 | case "build-archive": 196 | var out string 197 | 198 | cmdFlags.StringVar(&out, "out", "", "output filename") 199 | err := cmdFlags.Parse(os.Args[2:]) 200 | if err != nil { 201 | os.Exit(1) 202 | } 203 | if cmdFlags.Arg(0) != "" { 204 | dir, _ = filepath.Abs(cmdFlags.Arg(0)) 205 | } 206 | files, err := GetFiles(dir, htmlPath) 207 | if err != nil { 208 | log.Fatal(err) 209 | } 210 | if out == "" { 211 | out = fmt.Sprintf("out_%d.zip", time.Now().UnixNano()) 212 | } 213 | // Create out zip f 214 | f, err := os.Create(out) 215 | if err != nil { 216 | log.Fatal(err) 217 | } 218 | defer f.Close() 219 | // Write files to archive 220 | zipw := zip.NewWriter(f) 221 | for name, data := range files { 222 | w, err := zipw.Create(name) 223 | if err != nil { 224 | log.Fatalf("couldn't add '%s' to archive: %v", name, err) 225 | } 226 | _, err = w.Write(data) 227 | if err != nil { 228 | log.Fatalf("couldn't write '%s' data to archive: %v", name, err) 229 | } 230 | } 231 | err = zipw.Flush() 232 | if err != nil { 233 | log.Fatalf("couldn't flush remaining data to archive: %v", err) 234 | } 235 | fmt.Printf("Created archive '%s'\n", out) 236 | case "serve": 237 | err := cmdFlags.Parse(os.Args[2:]) 238 | if err != nil { 239 | os.Exit(1) 240 | } 241 | if cmdFlags.Arg(0) != "" { 242 | dir, _ = filepath.Abs(cmdFlags.Arg(0)) 243 | } 244 | files, err := GetFiles(dir, htmlPath) 245 | if err != nil { 246 | log.Fatal(err) 247 | } 248 | 249 | var server http.Server 250 | 251 | mux := http.NewServeMux() 252 | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 253 | name := path.Base(r.URL.Path) 254 | 255 | switch name { 256 | case "/", "", ".", "index.html": 257 | name = "index.html" 258 | fallthrough 259 | default: 260 | if data, ok := files[name]; ok { 261 | http.ServeContent(w, r, name, time.Now(), bytes.NewReader(data)) 262 | return 263 | } 264 | } 265 | http.ServeFile(w, r, filepath.Join(".", r.URL.Path)) 266 | }) 267 | server.Handler = mux 268 | server.Addr = "localhost:8080" 269 | 270 | fmt.Println("Listening on localhost:8080...") 271 | err = server.ListenAndServe() 272 | if err != nil && !errors.Is(err, http.ErrServerClosed) { 273 | fmt.Printf("couldn't listen and serve: %v\n", err) 274 | } 275 | default: 276 | fmt.Fprint(os.Stderr, usage) 277 | os.Exit(2) 278 | } 279 | 280 | fmt.Println("Done") 281 | } 282 | -------------------------------------------------------------------------------- /examples/helloworld/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Zyko0/go-sdl3/bin/binsdl" 5 | "github.com/Zyko0/go-sdl3/sdl" 6 | ) 7 | 8 | func main() { 9 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 10 | defer sdl.Quit() 11 | 12 | if err := sdl.Init(sdl.INIT_VIDEO); err != nil { 13 | panic(err) 14 | } 15 | 16 | window, renderer, err := sdl.CreateWindowAndRenderer("Hello world", 500, 500, 0) 17 | if err != nil { 18 | panic(err) 19 | } 20 | defer renderer.Destroy() 21 | defer window.Destroy() 22 | 23 | renderer.SetDrawColor(255, 255, 255, 255) 24 | 25 | sdl.RunLoop(func() error { 26 | var event sdl.Event 27 | 28 | for sdl.PollEvent(&event) { 29 | if event.Type == sdl.EVENT_QUIT { 30 | return sdl.EndLoop 31 | } 32 | } 33 | 34 | renderer.DebugText(50, 50, "Hello world") 35 | renderer.Present() 36 | 37 | return nil 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /examples/input/01-joystick-polling/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/input/01-joystick-polling/ 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "math" 8 | "math/rand/v2" 9 | "os" 10 | 11 | "github.com/Zyko0/go-sdl3/bin/binsdl" 12 | "github.com/Zyko0/go-sdl3/sdl" 13 | ) 14 | 15 | func main() { 16 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 17 | defer sdl.Quit() 18 | err := sdl.Init(sdl.INIT_VIDEO | sdl.INIT_JOYSTICK) 19 | if err != nil { 20 | panic(err) 21 | } 22 | 23 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/input/01-joystick-polling", 640, 480, 0) 24 | if err != nil { 25 | panic(err) 26 | } 27 | defer window.Destroy() 28 | defer renderer.Destroy() 29 | 30 | var joystick *sdl.Joystick 31 | 32 | var colors [64]sdl.Color 33 | for i := range colors { 34 | colors[i].R = uint8(rand.IntN(255)) 35 | colors[i].G = uint8(rand.IntN(255)) 36 | colors[i].B = uint8(rand.IntN(255)) 37 | colors[i].A = 255 38 | } 39 | 40 | sdl.RunLoop(func() error { 41 | var event sdl.Event 42 | var err error 43 | 44 | for sdl.PollEvent(&event) { 45 | switch event.Type { 46 | case sdl.EVENT_QUIT: 47 | return sdl.EndLoop 48 | case sdl.EVENT_JOYSTICK_ADDED: 49 | /* this event is sent for each hotplugged stick, but also each already-connected joystick during SDL_Init(). */ 50 | evt := event.JoyDeviceEvent() 51 | if joystick == nil { /* we don't have a stick yet and one was added, open it! */ 52 | joystick, err = evt.Which.OpenJoystick() 53 | if err != nil { 54 | fmt.Fprintf(os.Stderr, "failed to open joystick ID %d: %s\n", evt.Which, err.Error()) 55 | } 56 | } 57 | case sdl.EVENT_JOYSTICK_REMOVED: 58 | evt := event.JoyDeviceEvent() 59 | if joystick != nil { 60 | id, _ := joystick.ID() 61 | if id == evt.Which { 62 | joystick.Close() /* our joystick was unplugged. */ 63 | joystick = nil 64 | } 65 | } 66 | } 67 | } 68 | 69 | // Rendering 70 | 71 | text := "Plug in a joystick, please." 72 | if joystick != nil { /* we have a stick opened? */ 73 | text, _ = joystick.Name() 74 | } 75 | 76 | renderer.SetDrawColor(0, 0, 0, 255) 77 | renderer.Clear() 78 | winw, winh, _ := window.Size() 79 | 80 | /* note that you can get input as events, instead of polling, which is 81 | better since it won't miss button presses if the system is lagging, 82 | but often times checking the current state per-frame is good enough, 83 | and maybe better if you'd rather _drop_ inputs due to lag. */ 84 | 85 | if joystick != nil { /* we have a stick opened? */ 86 | size := float32(30) 87 | 88 | /* draw axes as bars going across middle of screen. We don't know if it's an X or Y or whatever axis, so we can't do more than this. */ 89 | total, _ := joystick.NumAxes() 90 | y := (float32(winh) - (float32(total) * size)) / 2 91 | x := float32(winw) / 2 92 | for i := 0; i < int(total); i++ { 93 | color := colors[i%len(colors)] 94 | axis, _ := joystick.Axis(int32(i)) 95 | dx := x + (float32(axis) / 32767 * x) /* make it -1.0f to 1.0f */ 96 | dst := sdl.FRect{ 97 | X: dx, 98 | Y: y, 99 | W: x - float32(math.Abs(float64(dx))), 100 | H: size, 101 | } 102 | renderer.SetDrawColor(color.R, color.G, color.B, color.A) 103 | renderer.RenderFillRect(&dst) 104 | y += size 105 | } 106 | 107 | /* draw buttons as blocks across top of window. We only know the button numbers, but not where they are on the device. */ 108 | total, _ = joystick.NumButtons() 109 | x = float32((float32(winw) - (float32(total) * size)) / 2) 110 | for i := 0; i < int(total); i++ { 111 | color := colors[i%len(colors)] 112 | dst := sdl.FRect{ 113 | X: x, 114 | Y: 0, 115 | W: size, 116 | H: size, 117 | } 118 | if joystick.Button(int32(i)) { 119 | renderer.SetDrawColor(color.R, color.G, color.B, color.A) 120 | } else { 121 | renderer.SetDrawColor(0, 0, 0, 255) 122 | } 123 | renderer.RenderFillRect(&dst) 124 | renderer.SetDrawColor(255, 255, 255, color.A) 125 | renderer.RenderRect(&dst) /* outline it */ 126 | x += size 127 | } 128 | 129 | /* draw hats across the bottom of the screen. */ 130 | total, _ = joystick.NumHats() 131 | x = (float32(winw) - (float32(total)*(size*2))/2) + size/2 132 | y = float32(winh) - size 133 | for i := 0; i < int(total); i++ { 134 | color := colors[i%len(colors)] 135 | thirdsize := size / 3 136 | cross := []sdl.FRect{ 137 | { 138 | X: x, 139 | Y: y + float32(thirdsize), 140 | W: float32(size), 141 | H: float32(thirdsize), 142 | }, 143 | { 144 | X: x + float32(thirdsize), 145 | Y: y, 146 | W: float32(thirdsize), 147 | H: float32(size), 148 | }, 149 | } 150 | hat := joystick.Hat(int32(i)) 151 | 152 | renderer.SetDrawColor(90, 90, 90, 255) 153 | renderer.RenderFillRects(cross) 154 | 155 | renderer.SetDrawColor(color.R, color.G, color.B, color.A) 156 | 157 | if hat&sdl.HAT_UP != 0 { 158 | dst := sdl.FRect{ 159 | X: x + float32(thirdsize), 160 | Y: y, 161 | W: float32(thirdsize), 162 | H: float32(thirdsize), 163 | } 164 | renderer.RenderFillRect(&dst) 165 | } 166 | if hat&sdl.HAT_RIGHT != 0 { 167 | dst := sdl.FRect{ 168 | X: x + float32(thirdsize*2), 169 | Y: y + float32(thirdsize), 170 | W: float32(thirdsize), 171 | H: float32(thirdsize), 172 | } 173 | renderer.RenderFillRect(&dst) 174 | } 175 | 176 | if hat&sdl.HAT_DOWN != 0 { 177 | dst := sdl.FRect{ 178 | X: x + float32(thirdsize), 179 | Y: y + float32(thirdsize*2), 180 | W: float32(thirdsize), 181 | H: float32(thirdsize), 182 | } 183 | renderer.RenderFillRect(&dst) 184 | } 185 | 186 | if hat&sdl.HAT_LEFT != 0 { 187 | dst := sdl.FRect{ 188 | X: x, 189 | Y: y + float32(thirdsize), 190 | W: float32(thirdsize), 191 | H: float32(thirdsize), 192 | } 193 | renderer.RenderFillRect(&dst) 194 | } 195 | 196 | x += size * 2 197 | } 198 | } 199 | 200 | x := (float32(winw) - float32(len(text)*sdl.DEBUG_TEXT_FONT_CHARACTER_SIZE)) / 2 201 | y := (float32(winh) - sdl.DEBUG_TEXT_FONT_CHARACTER_SIZE) / 2 202 | renderer.SetDrawColor(255, 255, 255, 255) 203 | renderer.DebugText(x, y, text) 204 | 205 | /* put the newly-cleared rendering on the screen. */ 206 | renderer.Present() 207 | 208 | return nil 209 | }) 210 | } 211 | -------------------------------------------------------------------------------- /examples/input/02-joystick-events/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/input/02-joystick-events/ 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "math/rand/v2" 8 | 9 | "github.com/Zyko0/go-sdl3/bin/binsdl" 10 | "github.com/Zyko0/go-sdl3/sdl" 11 | ) 12 | 13 | const ( 14 | MotionEventCooldown = 40 15 | ) 16 | 17 | type EventMessage struct { 18 | str string 19 | color sdl.Color 20 | startTicks uint64 21 | } 22 | 23 | var ( 24 | colors [64]sdl.Color 25 | messages []EventMessage 26 | ) 27 | 28 | func hatStateString(state uint8) string { 29 | switch state { 30 | case sdl.HAT_CENTERED: 31 | return "CENTERED" 32 | case sdl.HAT_UP: 33 | return "UP" 34 | case sdl.HAT_RIGHT: 35 | return "RIGHT" 36 | case sdl.HAT_DOWN: 37 | return "DOWN" 38 | case sdl.HAT_LEFT: 39 | return "LEFT" 40 | case sdl.HAT_RIGHTUP: 41 | return "RIGHT+UP" 42 | case sdl.HAT_RIGHTDOWN: 43 | return "RIGHT+DOWN" 44 | case sdl.HAT_LEFTUP: 45 | return "LEFT+UP" 46 | case sdl.HAT_LEFTDOWN: 47 | return "LEFT+DOWN" 48 | default: 49 | return "UNKNOWN" 50 | } 51 | } 52 | 53 | func batteryStateString(state sdl.PowerState) string { 54 | switch state { 55 | case sdl.POWERSTATE_ERROR: 56 | return "ERROR" 57 | case sdl.POWERSTATE_UNKNOWN: 58 | return "UNKNOWN" 59 | case sdl.POWERSTATE_ON_BATTERY: 60 | return "ON BATTERY" 61 | case sdl.POWERSTATE_NO_BATTERY: 62 | return "NO BATTERY" 63 | case sdl.POWERSTATE_CHARGING: 64 | return "CHARGING" 65 | case sdl.POWERSTATE_CHARGED: 66 | return "CHARGED" 67 | default: 68 | return "UNKNOWN" 69 | } 70 | } 71 | 72 | func AddMessage(jid sdl.JoystickID, format string, values ...any) { 73 | var msg EventMessage 74 | 75 | color := colors[int(jid)%len(colors)] 76 | msg.str = fmt.Sprintf(format, values...) 77 | msg.color = color 78 | msg.startTicks = sdl.Ticks() 79 | messages = append(messages, msg) 80 | } 81 | 82 | func main() { 83 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 84 | defer sdl.Quit() 85 | err := sdl.Init(sdl.INIT_VIDEO | sdl.INIT_JOYSTICK) 86 | if err != nil { 87 | panic(err) 88 | } 89 | 90 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/input/02-joystick-events", 640, 480, 0) 91 | if err != nil { 92 | panic(err) 93 | } 94 | defer window.Destroy() 95 | defer renderer.Destroy() 96 | 97 | for i := range colors { 98 | colors[i].R = uint8(rand.IntN(255)) 99 | colors[i].G = uint8(rand.IntN(255)) 100 | colors[i].B = uint8(rand.IntN(255)) 101 | colors[i].A = 255 102 | } 103 | colors[0].R, colors[0].G, colors[0].B, colors[0].A = 255, 255, 255, 255 104 | 105 | AddMessage(0, "Please plug in a joystick.") 106 | 107 | var ( 108 | axisMotionCooldownTime uint64 109 | ballMotionCooldownTime uint64 110 | ) 111 | 112 | sdl.RunLoop(func() error { 113 | var event sdl.Event 114 | 115 | for sdl.PollEvent(&event) { 116 | switch event.Type { 117 | case sdl.EVENT_QUIT: 118 | return sdl.EndLoop 119 | case sdl.EVENT_JOYSTICK_ADDED: 120 | /* this event is sent for each hotplugged stick, but also each already-connected joystick during SDL_Init(). */ 121 | evt := event.JoyDeviceEvent() 122 | joystick, err := evt.Which.OpenJoystick() 123 | if err != nil { 124 | AddMessage(evt.Which, "Joystick %d add, but not opened: %v", evt.Which, err) 125 | } else { 126 | name, _ := joystick.Name() 127 | AddMessage(evt.Which, "Joystick %d ('%s') added", evt.Which, name) 128 | } 129 | case sdl.EVENT_JOYSTICK_REMOVED: 130 | evt := event.JoyDeviceEvent() 131 | joystick, _ := evt.Which.Joystick() 132 | if joystick != nil { 133 | joystick.Close() /* our joystick was unplugged. */ 134 | } 135 | AddMessage(evt.Which, "Joystick %d removed", evt.Which) 136 | case sdl.EVENT_JOYSTICK_AXIS_MOTION: 137 | evt := event.JoyAxisEvent() 138 | now := sdl.Ticks() 139 | if now >= axisMotionCooldownTime { 140 | axisMotionCooldownTime = now + MotionEventCooldown 141 | AddMessage(evt.Which, "Joystick %d axis %d -> %d", evt.Which, evt.Axis, evt.Value) 142 | } 143 | case sdl.EVENT_JOYSTICK_BALL_MOTION: 144 | evt := event.JoyBallEvent() 145 | now := sdl.Ticks() 146 | if now >= ballMotionCooldownTime { 147 | ballMotionCooldownTime = now + MotionEventCooldown 148 | AddMessage(evt.Which, "Joystick %d ball %d -> %d, %d", evt.Which, evt.Ball, evt.Xrel, evt.Yrel) 149 | } 150 | case sdl.EVENT_JOYSTICK_HAT_MOTION: 151 | evt := event.JoyHatEvent() 152 | AddMessage(evt.Which, "Joystick %d hat %d -> %s", evt.Which, evt.Hat, hatStateString(evt.Value)) 153 | case sdl.EVENT_JOYSTICK_BUTTON_UP, sdl.EVENT_JOYSTICK_BUTTON_DOWN: 154 | evt := event.JoyButtonEvent() 155 | state := "RELEASED" 156 | if evt.Down { 157 | state = "PRESSED" 158 | } 159 | AddMessage(evt.Which, "Joystick %d button %d -> %s", evt.Which, evt.Button, state) 160 | case sdl.EVENT_JOYSTICK_BATTERY_UPDATED: 161 | evt := event.JoyBatteryEvent() 162 | AddMessage(evt.Which, "Joystick %d batter -> %s - %d%%", evt.Which, batteryStateString(evt.State), evt.Percent) 163 | } 164 | } 165 | 166 | // Rendering 167 | 168 | now := sdl.Ticks() 169 | msgLifetime := 3500. 170 | prevY := float32(0) 171 | 172 | renderer.SetDrawColor(0, 0, 0, 255) 173 | renderer.Clear() 174 | winw, winh, _ := window.Size() 175 | 176 | start := 0 177 | for i, msg := range messages { 178 | lifePercent := float32(now-msg.startTicks) / float32(msgLifetime) 179 | if lifePercent >= 1 { /* msg is done. */ 180 | start++ 181 | continue 182 | } 183 | x := (float32(winw) - float32(len(msg.str)*sdl.DEBUG_TEXT_FONT_CHARACTER_SIZE)) / 2 184 | y := float32(winh) * lifePercent 185 | if prevY != 0 && (prevY-y) < sdl.DEBUG_TEXT_FONT_CHARACTER_SIZE { 186 | messages[i].startTicks = now 187 | break // wait for the previous message to tick up a little. 188 | } 189 | 190 | renderer.SetDrawColor(msg.color.R, msg.color.G, msg.color.B, uint8(float32(msg.color.A)*(1-lifePercent))) 191 | renderer.DebugText(x, y, msg.str) 192 | 193 | prevY = y 194 | } 195 | messages = messages[start:] 196 | 197 | /* put the newly-cleared rendering on the screen. */ 198 | renderer.Present() 199 | 200 | return nil 201 | }) 202 | } 203 | -------------------------------------------------------------------------------- /examples/input/README.md: -------------------------------------------------------------------------------- 1 | # Input 2 | 3 | In this folder are the input official examples seen here https://examples.libsdl.org/SDL3/input/. -------------------------------------------------------------------------------- /examples/renderer/01-clear/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/01-clear/ 2 | 3 | package main 4 | 5 | import ( 6 | "math" 7 | 8 | "github.com/Zyko0/go-sdl3/bin/binsdl" 9 | "github.com/Zyko0/go-sdl3/sdl" 10 | ) 11 | 12 | func main() { 13 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 14 | defer sdl.Quit() 15 | 16 | err := sdl.Init(sdl.INIT_VIDEO) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/01-clear", 640, 480, 0) 22 | if err != nil { 23 | panic(err) 24 | } 25 | defer window.Destroy() 26 | defer renderer.Destroy() 27 | 28 | sdl.RunLoop(func() error { 29 | var event sdl.Event 30 | 31 | for sdl.PollEvent(&event) { 32 | if event.Type == sdl.EVENT_QUIT { 33 | return sdl.EndLoop 34 | } 35 | } 36 | 37 | // Rendering 38 | 39 | now := float64(sdl.Ticks()) / 1000 /* convert from milliseconds to seconds. */ 40 | /* choose the color for the frame we will draw. The sine wave trick makes it fade between colors smoothly. */ 41 | red := 0.5 + 0.5*float32(math.Sin(now)) 42 | green := 0.5 + 0.5*float32(math.Sin(now+math.Pi*2/3)) 43 | blue := 0.5 + 0.5*float32(math.Sin(now+math.Pi*4/3)) 44 | renderer.SetDrawColorFloat(red, green, blue, 1) /* new color, full alpha. */ 45 | 46 | /* clear the window to the draw color. */ 47 | renderer.Clear() 48 | 49 | /* put the newly-cleared rendering on the screen. */ 50 | renderer.Present() 51 | 52 | return nil 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /examples/renderer/02-primitives/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/02-primitives/ 2 | 3 | package main 4 | 5 | import ( 6 | "math/rand/v2" 7 | 8 | "github.com/Zyko0/go-sdl3/bin/binsdl" 9 | "github.com/Zyko0/go-sdl3/sdl" 10 | ) 11 | 12 | var ( 13 | points = [500]sdl.FPoint{} 14 | ) 15 | 16 | func main() { 17 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 18 | defer sdl.Quit() 19 | 20 | err := sdl.Init(sdl.INIT_VIDEO) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/02-primitives", 640, 480, 0) 26 | if err != nil { 27 | panic(err) 28 | } 29 | defer window.Destroy() 30 | defer renderer.Destroy() 31 | 32 | /* set up some random points */ 33 | for i := range len(points) { 34 | points[i].X = (rand.Float32() * 440) + 100 35 | points[i].Y = (rand.Float32() * 280) + 100 36 | } 37 | 38 | sdl.RunLoop(func() error { 39 | var event sdl.Event 40 | 41 | for sdl.PollEvent(&event) { 42 | if event.Type == sdl.EVENT_QUIT { 43 | return sdl.EndLoop 44 | } 45 | } 46 | 47 | // Rendering 48 | 49 | var rect sdl.FRect 50 | 51 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 52 | renderer.SetDrawColor(33, 33, 33, 255) /* dark gray, full alpha */ 53 | renderer.Clear() /* start with a blank canvas. */ 54 | 55 | /* draw a filled rectangle in the middle of the canvas. */ 56 | renderer.SetDrawColor(0, 0, 255, 255) /* blue, full alpha */ 57 | rect.X, rect.Y = 100, 100 58 | rect.W = 440 59 | rect.H = 280 60 | renderer.RenderFillRect(&rect) 61 | 62 | /* draw some points across the canvas. */ 63 | renderer.SetDrawColor(255, 0, 0, 255) /* red, full alpha */ 64 | renderer.RenderPoints(points[:]) 65 | 66 | /* draw a unfilled rectangle in-set a little bit. */ 67 | renderer.SetDrawColor(0, 255, 0, 255) /* green, full alpha */ 68 | rect.X += 30 69 | rect.Y += 30 70 | rect.W -= 60 71 | rect.H -= 60 72 | renderer.RenderRect(&rect) 73 | 74 | /* draw two lines in an X across the whole canvas. */ 75 | renderer.SetDrawColor(255, 255, 0, 255) /* yellow, full alpha */ 76 | renderer.RenderLine(0, 0, 640, 480) 77 | renderer.RenderLine(0, 480, 640, 0) 78 | 79 | renderer.Present() /* put it all on the screen! */ 80 | 81 | return nil 82 | }) 83 | } 84 | -------------------------------------------------------------------------------- /examples/renderer/03-lines/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/03-lines/ 2 | 3 | package main 4 | 5 | import ( 6 | "math" 7 | "math/rand/v2" 8 | 9 | "github.com/Zyko0/go-sdl3/bin/binsdl" 10 | "github.com/Zyko0/go-sdl3/sdl" 11 | ) 12 | 13 | var ( 14 | line_points = []sdl.FPoint{ 15 | {100, 354}, {220, 230}, {140, 230}, {320, 100}, {500, 230}, 16 | {420, 230}, {540, 354}, {400, 354}, {100, 354}, 17 | } 18 | ) 19 | 20 | func main() { 21 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 22 | defer sdl.Quit() 23 | 24 | err := sdl.Init(sdl.INIT_VIDEO) 25 | if err != nil { 26 | panic(err) 27 | } 28 | 29 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/03-lines", 640, 480, 0) 30 | if err != nil { 31 | panic(err) 32 | } 33 | defer window.Destroy() 34 | defer renderer.Destroy() 35 | 36 | sdl.RunLoop(func() error { 37 | var event sdl.Event 38 | 39 | for sdl.PollEvent(&event) { 40 | if event.Type == sdl.EVENT_QUIT { 41 | return sdl.EndLoop 42 | } 43 | } 44 | 45 | // Rendering 46 | 47 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 48 | renderer.SetDrawColor(100, 100, 100, 255) /* grey, full alpha */ 49 | renderer.Clear() /* start with a blank canvas. */ 50 | 51 | /* You can draw lines, one at a time, like these brown ones... */ 52 | renderer.SetDrawColor(127, 49, 32, 255) 53 | renderer.RenderLine(240, 450, 400, 450) 54 | renderer.RenderLine(240, 356, 400, 356) 55 | renderer.RenderLine(240, 356, 240, 450) 56 | renderer.RenderLine(400, 356, 400, 450) 57 | 58 | /* You can also draw a series of connected lines in a single batch... */ 59 | renderer.SetDrawColor(0, 255, 0, 255) 60 | renderer.RenderLines(line_points) 61 | 62 | /* here's a bunch of lines drawn out from a center point in a circle. */ 63 | /* we randomize the color of each line, so it functions as animation. */ 64 | for i := range 360 { 65 | const ( 66 | size = 30. 67 | x = 320. 68 | y = 95. - (size / 2.) 69 | ) 70 | renderer.SetDrawColor( 71 | uint8(rand.IntN(256)), 72 | uint8(rand.IntN(256)), 73 | uint8(rand.IntN(256)), 74 | 255, 75 | ) 76 | x2 := x + float32(math.Sin(float64(i))*size) 77 | y2 := y + float32(math.Cos(float64(i))*size) 78 | renderer.RenderLine(x, y, x2, y2) 79 | } 80 | 81 | renderer.Present() /* put it all on the screen! */ 82 | 83 | return nil 84 | }) 85 | } 86 | -------------------------------------------------------------------------------- /examples/renderer/04-points/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/04-points/ 2 | 3 | package main 4 | 5 | import ( 6 | "math/rand/v2" 7 | 8 | "github.com/Zyko0/go-sdl3/bin/binsdl" 9 | "github.com/Zyko0/go-sdl3/sdl" 10 | ) 11 | 12 | const ( 13 | WindowWidth = 640 14 | WindowHeight = 480 15 | 16 | NumPoints = 500 17 | MinPixelsPerSecond = 30 18 | MaxPixelsPerSecond = 60 19 | ) 20 | 21 | var ( 22 | points [NumPoints]sdl.FPoint 23 | pointSpeeds [NumPoints]float32 24 | lastTime uint64 25 | ) 26 | 27 | func main() { 28 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 29 | defer sdl.Quit() 30 | 31 | err := sdl.Init(sdl.INIT_VIDEO) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/04-points", WindowWidth, WindowHeight, 0) 37 | if err != nil { 38 | panic(err) 39 | } 40 | defer window.Destroy() 41 | defer renderer.Destroy() 42 | 43 | /* set up some random points */ 44 | for i := range len(points) { 45 | points[i].X = (rand.Float32() * WindowWidth) + 100 46 | points[i].Y = (rand.Float32() * WindowHeight) + 100 47 | pointSpeeds[i] = MinPixelsPerSecond + rand.Float32()*(MaxPixelsPerSecond-MinPixelsPerSecond) 48 | } 49 | 50 | lastTime = sdl.Ticks() 51 | 52 | sdl.RunLoop(func() error { 53 | var event sdl.Event 54 | 55 | for sdl.PollEvent(&event) { 56 | if event.Type == sdl.EVENT_QUIT { 57 | return sdl.EndLoop 58 | } 59 | } 60 | 61 | // Rendering 62 | 63 | now := sdl.Ticks() 64 | elapsed := float32(now-lastTime) / 1000 65 | 66 | /* let's move all our points a little for a new frame. */ 67 | for i := range len(points) { 68 | distance := elapsed * pointSpeeds[i] 69 | points[i].X += distance 70 | points[i].Y += distance 71 | if points[i].X >= WindowWidth || points[i].Y >= WindowHeight { 72 | /* off the screen; restart it elsewhere! */ 73 | if rand.IntN(2) != 0 { 74 | points[i].X = rand.Float32() * float32(WindowWidth) 75 | points[i].Y = 0 76 | } else { 77 | points[i].X = 0 78 | points[i].Y = rand.Float32() * float32(WindowHeight) 79 | } 80 | pointSpeeds[i] = MinPixelsPerSecond + rand.Float32()*(MaxPixelsPerSecond-MinPixelsPerSecond) 81 | } 82 | } 83 | 84 | lastTime = now 85 | 86 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 87 | renderer.SetDrawColor(0, 0, 0, 255) /* black, full alpha */ 88 | renderer.Clear() /* start with a blank canvas. */ 89 | renderer.SetDrawColor(255, 255, 255, 255) /* white, full alpha */ 90 | renderer.RenderPoints(points[:]) /* draw all the points! */ 91 | 92 | /* You can also draw single points with SDL_RenderPoint(), but it's 93 | cheaper (sometimes significantly so) to do them all at once. */ 94 | 95 | renderer.Present() /* put it all on the screen! */ 96 | 97 | return nil 98 | }) 99 | } 100 | -------------------------------------------------------------------------------- /examples/renderer/05-rectangles/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/05-rectangles/ 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/Zyko0/go-sdl3/bin/binsdl" 7 | "github.com/Zyko0/go-sdl3/sdl" 8 | ) 9 | 10 | const ( 11 | WindowWidth = 640 12 | WindowHeight = 480 13 | ) 14 | 15 | func main() { 16 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 17 | defer sdl.Quit() 18 | 19 | err := sdl.Init(sdl.INIT_VIDEO) 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/05-rectangles", WindowWidth, WindowHeight, 0) 25 | if err != nil { 26 | panic(err) 27 | } 28 | defer window.Destroy() 29 | defer renderer.Destroy() 30 | 31 | sdl.RunLoop(func() error { 32 | var event sdl.Event 33 | 34 | for sdl.PollEvent(&event) { 35 | if event.Type == sdl.EVENT_QUIT { 36 | return sdl.EndLoop 37 | } 38 | } 39 | 40 | // Rendering 41 | 42 | var rects [16]sdl.FRect 43 | now := sdl.Ticks() 44 | 45 | /* we'll have the rectangles grow and shrink over a few seconds. */ 46 | var direction float32 47 | if now%2000 >= 1000 { 48 | direction = 1 49 | } else { 50 | direction = -1 51 | } 52 | scale := (float32(int(now%1000)-500) / 500) * direction 53 | 54 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 55 | renderer.SetDrawColor(0, 0, 0, 255) /* black, full alpha */ 56 | renderer.Clear() /* start with a blank canvas. */ 57 | 58 | /* Rectangles are comprised of set of X and Y coordinates, plus width and 59 | height. (0, 0) is the top left of the window, and larger numbers go 60 | down and to the right. This isn't how geometry works, but this is 61 | pretty standard in 2D graphics. */ 62 | 63 | /* Let's draw a single rectangle (square, really). */ 64 | rects[0].X, rects[0].Y = 100, 100 65 | rects[0].W, rects[0].H = 100+(100*scale), 100+(100*scale) 66 | renderer.SetDrawColor(255, 0, 0, 255) /* red, full alpha */ 67 | renderer.RenderRect(&rects[0]) 68 | 69 | /* Now let's draw several rectangles with one function call. */ 70 | for i := 0; i < 3; i++ { 71 | size := float32(i+1) * 50 72 | rects[i].W, rects[i].H = size+(size*scale), size+(size*scale) 73 | rects[i].X = (WindowWidth - rects[i].W) / 2 /* center it. */ 74 | rects[i].Y = (WindowHeight - rects[i].H) / 2 /* center it. */ 75 | } 76 | renderer.SetDrawColor(0, 255, 0, 255) /* green, full alpha */ 77 | renderer.RenderRects(rects[:3]) /* draw three rectangles at once */ 78 | 79 | /* those were rectangle _outlines_, really. You can also draw _filled_ rectangles! */ 80 | rects[0].X = 400 81 | rects[0].Y = 50 82 | rects[0].W = 100 + (100 * scale) 83 | rects[0].H = 50 + (50 * scale) 84 | renderer.SetDrawColor(0, 0, 255, 255) /* blue, full alpha */ 85 | renderer.RenderFillRect(&rects[0]) 86 | 87 | /* ...and also fill a bunch of rectangles at once... */ 88 | for i := 0; i < len(rects); i++ { 89 | w := float32(WindowWidth / len(rects)) 90 | h := float32(i * 8) 91 | rects[i].X = float32(i) * w 92 | rects[i].Y = WindowHeight - h 93 | rects[i].W = w 94 | rects[i].H = h 95 | } 96 | renderer.SetDrawColor(255, 255, 255, 255) /* white, full alpha */ 97 | renderer.RenderFillRects(rects[:]) 98 | 99 | renderer.Present() /* put it all on the screen! */ 100 | 101 | return nil 102 | }) 103 | } 104 | -------------------------------------------------------------------------------- /examples/renderer/06-textures/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/06-textures/ 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/Zyko0/go-sdl3/bin/binsdl" 9 | assets "github.com/Zyko0/go-sdl3/examples/renderer/_assets" 10 | "github.com/Zyko0/go-sdl3/sdl" 11 | ) 12 | 13 | const ( 14 | WindowWidth = 640 15 | WindowHeight = 480 16 | ) 17 | 18 | func main() { 19 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 20 | defer sdl.Quit() 21 | 22 | err := sdl.Init(sdl.INIT_VIDEO) 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/06-textures", WindowWidth, WindowHeight, 0) 28 | if err != nil { 29 | panic(err) 30 | } 31 | defer window.Destroy() 32 | defer renderer.Destroy() 33 | 34 | /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D 35 | engines refer to these as "sprites." We'll do a static texture (upload once, draw many 36 | times) with data from a bitmap file. */ 37 | bmpStream, err := sdl.IOFromConstMem(assets.SampleBMP) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access. 43 | Load a .bmp into a surface, move it to a texture from there. */ 44 | surface, err := sdl.LoadBMP_IO(bmpStream, true) 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | texture, err := renderer.CreateTextureFromSurface(surface) 50 | if err != nil { 51 | panic(err) 52 | } 53 | 54 | surface.Destroy() 55 | 56 | sdl.RunLoop(func() error { 57 | var event sdl.Event 58 | 59 | for sdl.PollEvent(&event) { 60 | if event.Type == sdl.EVENT_QUIT { 61 | return sdl.EndLoop 62 | } 63 | } 64 | 65 | // Rendering 66 | 67 | var dstRect sdl.FRect 68 | now := sdl.Ticks() 69 | 70 | /* we'll have some textures move around over a few seconds. */ 71 | var direction float32 72 | if now%2000 >= 1000 { 73 | direction = 1 74 | } else { 75 | direction = -1 76 | } 77 | scale := (float32(int(now%1000)-500) / 500) * direction 78 | 79 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 80 | renderer.SetDrawColor(0, 0, 0, 255) /* black, full alpha */ 81 | renderer.Clear() /* start with a blank canvas. */ 82 | 83 | /* Just draw the static texture a few times. You can think of it like a 84 | stamp, there isn't a limit to the number of times you can draw with it. */ 85 | 86 | /* top left */ 87 | dstRect.X = 100 * scale 88 | dstRect.Y = 0 89 | dstRect.W = float32(texture.W) 90 | dstRect.H = float32(texture.H) 91 | err := renderer.RenderTexture(texture, nil, &dstRect) 92 | if err != nil { 93 | fmt.Println("err:", err) 94 | } 95 | 96 | /* center this one. */ 97 | dstRect.X = float32(WindowWidth-texture.W) / 2 98 | dstRect.Y = float32(WindowHeight-texture.H) / 2 99 | dstRect.W = float32(texture.W) 100 | dstRect.H = float32(texture.H) 101 | renderer.RenderTexture(texture, nil, &dstRect) 102 | 103 | /* bottom right. */ 104 | dstRect.X = float32(WindowWidth-texture.W) - (100. * scale) 105 | dstRect.Y = float32(WindowHeight - texture.H) 106 | dstRect.W = float32(texture.W) 107 | dstRect.H = float32(texture.H) 108 | renderer.RenderTexture(texture, nil, &dstRect) 109 | 110 | renderer.Present() /* put it all on the screen! */ 111 | 112 | return nil 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /examples/renderer/07-streaming-textures/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/07-streaming-textures/ 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/Zyko0/go-sdl3/bin/binsdl" 7 | "github.com/Zyko0/go-sdl3/sdl" 8 | ) 9 | 10 | const ( 11 | WindowWidth = 640 12 | WindowHeight = 480 13 | 14 | TextureSize = 150 15 | ) 16 | 17 | func main() { 18 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 19 | defer sdl.Quit() 20 | 21 | err := sdl.Init(sdl.INIT_VIDEO) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/07-streaming-textures", WindowWidth, WindowHeight, 0) 27 | if err != nil { 28 | panic(err) 29 | } 30 | defer window.Destroy() 31 | defer renderer.Destroy() 32 | 33 | texture, err := renderer.CreateTexture(sdl.PIXELFORMAT_RGBA8888, sdl.TEXTUREACCESS_STREAMING, TextureSize, TextureSize) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | sdl.RunLoop(func() error { 39 | var event sdl.Event 40 | 41 | for sdl.PollEvent(&event) { 42 | if event.Type == sdl.EVENT_QUIT { 43 | return sdl.EndLoop 44 | } 45 | } 46 | 47 | // Rendering 48 | 49 | var dstRect sdl.FRect 50 | now := sdl.Ticks() 51 | var surface *sdl.Surface 52 | 53 | /* we'll have some color move around over a few seconds. */ 54 | var direction float32 55 | if now%2000 >= 1000 { 56 | direction = 1 57 | } else { 58 | direction = -1 59 | } 60 | scale := (float32(int(now%1000)-500) / 500) * direction 61 | 62 | /* To update a streaming texture, you need to lock it first. This gets you access to the pixels. 63 | Note that this is considered a _write-only_ operation: the buffer you get from locking 64 | might not actually have the existing contents of the texture, and you have to write to every 65 | locked pixel! */ 66 | 67 | /* You can use SDL_LockTexture() to get an array of raw pixels, but we're going to use 68 | SDL_LockTextureToSurface() here, because it wraps that array in a temporary SDL_Surface, 69 | letting us use the surface drawing functions instead of lighting up individual pixels. */ 70 | err := texture.LockToSurface(nil, &surface) 71 | if err == nil { 72 | var r sdl.Rect 73 | 74 | details, _ := surface.Format.Details() 75 | surface.FillRect(nil, sdl.MapRGB(details, nil, 0, 0, 0)) /* make the whole surface black */ 76 | r.W = TextureSize 77 | r.H = TextureSize / 10 78 | r.X = 0 79 | r.Y = int32(float32(TextureSize-r.H) * (scale + 1) / 2) 80 | surface.FillRect(&r, sdl.MapRGB(details, nil, 0, 255, 0)) /* make a strip of the surface green */ 81 | texture.Unlock() /* upload the changes (and frees the temporary surface)! */ 82 | } 83 | 84 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 85 | renderer.SetDrawColor(66, 66, 66, 255) /* grey, full alpha */ 86 | renderer.Clear() /* start with a blank canvas. */ 87 | 88 | /* Just draw the static texture a few times. You can think of it like a 89 | stamp, there isn't a limit to the number of times you can draw with it. */ 90 | 91 | /* Center this one. It'll draw the latest version of the texture we drew while it was locked. */ 92 | dstRect.X = float32(WindowWidth-TextureSize) / 2 93 | dstRect.Y = float32(WindowHeight-TextureSize) / 2 94 | dstRect.W, dstRect.H = TextureSize, TextureSize 95 | renderer.RenderTexture(texture, nil, &dstRect) 96 | 97 | renderer.Present() /* put it all on the screen! */ 98 | 99 | return nil 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /examples/renderer/08-rotating-textures/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/08-rotating-textures/ 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/Zyko0/go-sdl3/bin/binsdl" 7 | assets "github.com/Zyko0/go-sdl3/examples/renderer/_assets" 8 | "github.com/Zyko0/go-sdl3/sdl" 9 | ) 10 | 11 | const ( 12 | WindowWidth = 640 13 | WindowHeight = 480 14 | ) 15 | 16 | func main() { 17 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 18 | defer sdl.Quit() 19 | 20 | err := sdl.Init(sdl.INIT_VIDEO) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/08-rotating-textures", WindowWidth, WindowHeight, 0) 26 | if err != nil { 27 | panic(err) 28 | } 29 | defer window.Destroy() 30 | defer renderer.Destroy() 31 | 32 | /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D 33 | engines refer to these as "sprites." We'll do a static texture (upload once, draw many 34 | times) with data from a bitmap file. */ 35 | bmpStream, err := sdl.IOFromConstMem(assets.SampleBMP) 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access. 41 | Load a .bmp into a surface, move it to a texture from there. */ 42 | surface, err := sdl.LoadBMP_IO(bmpStream, true) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | texture, err := renderer.CreateTextureFromSurface(surface) 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | surface.Destroy() 53 | 54 | sdl.RunLoop(func() error { 55 | var event sdl.Event 56 | 57 | for sdl.PollEvent(&event) { 58 | if event.Type == sdl.EVENT_QUIT { 59 | return sdl.EndLoop 60 | } 61 | } 62 | 63 | // Rendering 64 | 65 | var center sdl.FPoint 66 | var dstRect sdl.FRect 67 | now := sdl.Ticks() 68 | 69 | /* we'll have a texture rotate around over 2 seconds (2000 milliseconds). 360 degrees in a circle! */ 70 | rotation := float32(now%2000) / 2000 * 360 71 | 72 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 73 | renderer.SetDrawColor(0, 0, 0, 255) /* black, full alpha */ 74 | renderer.Clear() /* start with a blank canvas. */ 75 | 76 | /* Center this one, and draw it with some rotation so it spins! */ 77 | dstRect.X = float32(WindowWidth-texture.W) / 2 78 | dstRect.Y = float32(WindowHeight-texture.H) / 2 79 | dstRect.W = float32(texture.W) 80 | dstRect.H = float32(texture.H) 81 | /* rotate it around the center of the texture; you can rotate it from a different point, too! */ 82 | center.X = float32(texture.W) / 2 83 | center.Y = float32(texture.H) / 2 84 | 85 | renderer.RenderTextureRotated(texture, nil, &dstRect, float64(rotation), ¢er, sdl.FLIP_NONE) 86 | 87 | renderer.Present() /* put it all on the screen! */ 88 | 89 | return nil 90 | }) 91 | } 92 | -------------------------------------------------------------------------------- /examples/renderer/09-scaling-textures/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/09-scaling-textures/ 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/Zyko0/go-sdl3/bin/binsdl" 7 | assets "github.com/Zyko0/go-sdl3/examples/renderer/_assets" 8 | "github.com/Zyko0/go-sdl3/sdl" 9 | ) 10 | 11 | const ( 12 | WindowWidth = 640 13 | WindowHeight = 480 14 | ) 15 | 16 | func main() { 17 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 18 | defer sdl.Quit() 19 | 20 | err := sdl.Init(sdl.INIT_VIDEO) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/09-scaling-textures", WindowWidth, WindowHeight, 0) 26 | if err != nil { 27 | panic(err) 28 | } 29 | defer window.Destroy() 30 | defer renderer.Destroy() 31 | 32 | /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D 33 | engines refer to these as "sprites." We'll do a static texture (upload once, draw many 34 | times) with data from a bitmap file. */ 35 | bmpStream, err := sdl.IOFromConstMem(assets.SampleBMP) 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access. 41 | Load a .bmp into a surface, move it to a texture from there. */ 42 | surface, err := sdl.LoadBMP_IO(bmpStream, true) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | texture, err := renderer.CreateTextureFromSurface(surface) 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | surface.Destroy() 53 | 54 | sdl.RunLoop(func() error { 55 | var event sdl.Event 56 | 57 | for sdl.PollEvent(&event) { 58 | if event.Type == sdl.EVENT_QUIT { 59 | return sdl.EndLoop 60 | } 61 | } 62 | 63 | // Rendering 64 | 65 | var dstRect sdl.FRect 66 | now := sdl.Ticks() 67 | 68 | /* we'll have the texture grow and shrink over a few seconds. */ 69 | var direction float32 70 | if now%2000 >= 1000 { 71 | direction = 1 72 | } else { 73 | direction = -1 74 | } 75 | scale := (float32(int(now%1000)-500) / 500) * direction 76 | 77 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 78 | renderer.SetDrawColor(0, 0, 0, 255) /* black, full alpha */ 79 | renderer.Clear() /* start with a blank canvas. */ 80 | 81 | /* center this one and make it grow and shrink. */ 82 | dstRect.W = float32(texture.W) + float32(texture.W)*scale 83 | dstRect.H = float32(texture.H) + float32(texture.H)*scale 84 | dstRect.X = float32(WindowWidth-dstRect.W) / 2 85 | dstRect.Y = float32(WindowHeight-dstRect.H) / 2 86 | 87 | renderer.RenderTexture(texture, nil, &dstRect) 88 | 89 | renderer.Present() /* put it all on the screen! */ 90 | 91 | return nil 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /examples/renderer/10-geometry/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/10-geometry/ 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/Zyko0/go-sdl3/bin/binsdl" 7 | assets "github.com/Zyko0/go-sdl3/examples/renderer/_assets" 8 | "github.com/Zyko0/go-sdl3/sdl" 9 | ) 10 | 11 | const ( 12 | WindowWidth = 640 13 | WindowHeight = 480 14 | ) 15 | 16 | func main() { 17 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 18 | defer sdl.Quit() 19 | 20 | err := sdl.Init(sdl.INIT_VIDEO) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/10-geometry", WindowWidth, WindowHeight, 0) 26 | if err != nil { 27 | panic(err) 28 | } 29 | defer window.Destroy() 30 | defer renderer.Destroy() 31 | 32 | /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D 33 | engines refer to these as "sprites." We'll do a static texture (upload once, draw many 34 | times) with data from a bitmap file. */ 35 | bmpStream, err := sdl.IOFromConstMem(assets.SampleBMP) 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access. 41 | Load a .bmp into a surface, move it to a texture from there. */ 42 | surface, err := sdl.LoadBMP_IO(bmpStream, true) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | texture, err := renderer.CreateTextureFromSurface(surface) 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | surface.Destroy() 53 | 54 | sdl.RunLoop(func() error { 55 | var event sdl.Event 56 | 57 | for sdl.PollEvent(&event) { 58 | if event.Type == sdl.EVENT_QUIT { 59 | return sdl.EndLoop 60 | } 61 | } 62 | 63 | // Rendering 64 | 65 | now := sdl.Ticks() 66 | 67 | /* we'll have the texture grow and shrink over a few seconds. */ 68 | var direction float32 69 | if now%2000 >= 1000 { 70 | direction = 1 71 | } else { 72 | direction = -1 73 | } 74 | scale := (float32(int(now%1000)-500) / 500) * direction 75 | size := 200 + (200 * scale) 76 | 77 | var vertices [4]sdl.Vertex 78 | 79 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 80 | renderer.SetDrawColor(0, 0, 0, 255) /* black, full alpha */ 81 | renderer.Clear() /* start with a blank canvas. */ 82 | 83 | /* Draw a single triangle with a different color at each vertex. Center this one and make it grow and shrink. */ 84 | /* You always draw triangles with this, but you can string triangles together to form polygons. */ 85 | vertices[0].Position.X = float32(WindowWidth) / 2 86 | vertices[0].Position.Y = float32(WindowHeight-size) / 2 87 | vertices[0].Color.R = 1 88 | vertices[0].Color.A = 1 89 | vertices[1].Position.X = float32(WindowWidth+size) / 2 90 | vertices[1].Position.Y = float32(WindowHeight+size) / 2 91 | vertices[1].Color.G = 1 92 | vertices[1].Color.A = 1 93 | vertices[2].Position.X = float32(WindowWidth-size) / 2 94 | vertices[2].Position.Y = float32(WindowHeight+size) / 2 95 | vertices[2].Color.B = 1 96 | vertices[2].Color.A = 1 97 | 98 | renderer.RenderGeometry(nil, vertices[:3], nil) 99 | 100 | /* you can also map a texture to the geometry! Texture coordinates go from 0.0f to 1.0f. That will be the location 101 | in the texture bound to this vertex. */ 102 | vertices = [4]sdl.Vertex{} 103 | vertices[0].Position.X = 10 104 | vertices[0].Position.Y = 10 105 | vertices[0].Color.R = 1 106 | vertices[0].Color.G = 1 107 | vertices[0].Color.B = 1 108 | vertices[0].Color.A = 1 109 | vertices[0].TexCoord.X = 0 110 | vertices[0].TexCoord.Y = 0 111 | vertices[1].Position.X = 150 112 | vertices[1].Position.Y = 10 113 | vertices[1].Color.R = 1 114 | vertices[1].Color.G = 1 115 | vertices[1].Color.B = 1 116 | vertices[1].Color.A = 1 117 | vertices[1].TexCoord.X = 1 118 | vertices[1].TexCoord.Y = 0 119 | vertices[2].Position.X = 10 120 | vertices[2].Position.Y = 150 121 | vertices[2].Color.R = 1 122 | vertices[2].Color.G = 1 123 | vertices[2].Color.B = 1 124 | vertices[2].Color.A = 1 125 | vertices[2].TexCoord.X = 0 126 | vertices[2].TexCoord.Y = 1 127 | 128 | renderer.RenderGeometry(texture, vertices[:3], nil) 129 | 130 | /* Did that only draw half of the texture? You can do multiple triangles sharing some vertices, 131 | using indices, to get the whole thing on the screen: */ 132 | 133 | /* Let's just move this over so it doesn't overlap... */ 134 | for i := range 3 { 135 | vertices[i].Position.X += 450 136 | } 137 | 138 | /* we need one more vertex, since the two triangles can share two of them. */ 139 | vertices[3].Position.X = 600 140 | vertices[3].Position.Y = 150 141 | vertices[3].Color.R = 1 142 | vertices[3].Color.G = 1 143 | vertices[3].Color.B = 1 144 | vertices[3].Color.A = 1 145 | vertices[3].TexCoord.X = 1 146 | vertices[3].TexCoord.Y = 1 147 | 148 | /* And an index to tell it to reuse some of the vertices between triangles... */ 149 | /* 4 vertices, but 6 actual places they used. Indices need less bandwidth to transfer and can reorder vertices easily! */ 150 | indices := []int32{0, 1, 2, 1, 2, 3} 151 | renderer.RenderGeometry(texture, vertices[:], indices) 152 | 153 | renderer.Present() /* put it all on the screen! */ 154 | 155 | return nil 156 | }) 157 | } 158 | -------------------------------------------------------------------------------- /examples/renderer/11-color-mods/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/11-color-mods/ 2 | 3 | package main 4 | 5 | import ( 6 | "math" 7 | 8 | "github.com/Zyko0/go-sdl3/bin/binsdl" 9 | assets "github.com/Zyko0/go-sdl3/examples/renderer/_assets" 10 | "github.com/Zyko0/go-sdl3/sdl" 11 | ) 12 | 13 | const ( 14 | WindowWidth = 640 15 | WindowHeight = 480 16 | ) 17 | 18 | func main() { 19 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 20 | defer sdl.Quit() 21 | 22 | err := sdl.Init(sdl.INIT_VIDEO) 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/11-color-mods", WindowWidth, WindowHeight, 0) 28 | if err != nil { 29 | panic(err) 30 | } 31 | defer window.Destroy() 32 | defer renderer.Destroy() 33 | 34 | /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D 35 | engines refer to these as "sprites." We'll do a static texture (upload once, draw many 36 | times) with data from a bitmap file. */ 37 | bmpStream, err := sdl.IOFromConstMem(assets.SampleBMP) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access. 43 | Load a .bmp into a surface, move it to a texture from there. */ 44 | surface, err := sdl.LoadBMP_IO(bmpStream, true) 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | texture, err := renderer.CreateTextureFromSurface(surface) 50 | if err != nil { 51 | panic(err) 52 | } 53 | 54 | surface.Destroy() 55 | 56 | sdl.RunLoop(func() error { 57 | var event sdl.Event 58 | 59 | for sdl.PollEvent(&event) { 60 | if event.Type == sdl.EVENT_QUIT { 61 | return sdl.EndLoop 62 | } 63 | } 64 | 65 | // Rendering 66 | 67 | var dstRect sdl.FRect 68 | now := float64(sdl.Ticks()) / 1000 /* convert from milliseconds to seconds. */ 69 | /* choose the modulation values for the center texture. The sine wave trick makes it fade between colors smoothly. */ 70 | red := float32(0.5 + 0.5*math.Sin(now)) 71 | green := float32(0.5 + 0.5*math.Sin(now+math.Pi*2/3)) 72 | blue := float32(0.5 + 0.5*math.Sin(now+math.Pi*4/3)) 73 | 74 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 75 | renderer.SetDrawColor(0, 0, 0, 255) /* black, full alpha */ 76 | renderer.Clear() /* start with a blank canvas. */ 77 | 78 | /* Just draw the static texture a few times. You can think of it like a 79 | stamp, there isn't a limit to the number of times you can draw with it. */ 80 | 81 | /* Color modulation multiplies each pixel's red, green, and blue intensities by the mod values, 82 | so multiplying by 1.0f will leave a color intensity alone, 0.0f will shut off that color 83 | completely, etc. */ 84 | 85 | /* top left; let's make this one blue! */ 86 | dstRect.X = 0 87 | dstRect.Y = 0 88 | dstRect.W = float32(texture.W) 89 | dstRect.H = float32(texture.H) 90 | texture.SetColorModFloat(red, green, blue) 91 | renderer.RenderTexture(texture, nil, &dstRect) 92 | 93 | /* center this one, and have it cycle through red/green/blue modulations. */ 94 | dstRect.X = float32(WindowWidth-texture.W) / 2 95 | dstRect.Y = float32(WindowHeight-texture.H) / 2 96 | dstRect.W = float32(texture.W) 97 | dstRect.H = float32(texture.H) 98 | texture.SetColorModFloat(red, green, blue) 99 | renderer.RenderTexture(texture, nil, &dstRect) 100 | 101 | /* bottom right; let's make this one red! */ 102 | dstRect.X = float32(WindowWidth - texture.W) 103 | dstRect.Y = float32(WindowHeight - texture.H) 104 | dstRect.W = float32(texture.W) 105 | dstRect.H = float32(texture.H) 106 | texture.SetColorModFloat(1, 0, 0) /* kill all green and blue. */ 107 | renderer.RenderTexture(texture, nil, &dstRect) 108 | 109 | renderer.Present() /* put it all on the screen! */ 110 | 111 | return nil 112 | }) 113 | } 114 | -------------------------------------------------------------------------------- /examples/renderer/14-viewport/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/14-viewport/ 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/Zyko0/go-sdl3/bin/binsdl" 7 | assets "github.com/Zyko0/go-sdl3/examples/renderer/_assets" 8 | "github.com/Zyko0/go-sdl3/sdl" 9 | ) 10 | 11 | const ( 12 | WindowWidth = 640 13 | WindowHeight = 480 14 | ) 15 | 16 | func main() { 17 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 18 | defer sdl.Quit() 19 | 20 | err := sdl.Init(sdl.INIT_VIDEO) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/14-viewport", WindowWidth, WindowHeight, 0) 26 | if err != nil { 27 | panic(err) 28 | } 29 | defer window.Destroy() 30 | defer renderer.Destroy() 31 | 32 | /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D 33 | engines refer to these as "sprites." We'll do a static texture (upload once, draw many 34 | times) with data from a bitmap file. */ 35 | bmpStream, err := sdl.IOFromConstMem(assets.SampleBMP) 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access. 41 | Load a .bmp into a surface, move it to a texture from there. */ 42 | surface, err := sdl.LoadBMP_IO(bmpStream, true) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | texture, err := renderer.CreateTextureFromSurface(surface) 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | surface.Destroy() 53 | 54 | sdl.RunLoop(func() error { 55 | var event sdl.Event 56 | 57 | for sdl.PollEvent(&event) { 58 | if event.Type == sdl.EVENT_QUIT { 59 | return sdl.EndLoop 60 | } 61 | } 62 | 63 | // Rendering 64 | 65 | var viewport sdl.Rect 66 | dstRect := sdl.FRect{ 67 | X: 0, 68 | Y: 0, 69 | W: float32(texture.W), 70 | H: float32(texture.H), 71 | } 72 | 73 | /* Setting a viewport has the effect of limiting the area that rendering 74 | can happen, and making coordinate (0, 0) live somewhere else in the 75 | window. It does _not_ scale rendering to fit the viewport. */ 76 | 77 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 78 | renderer.SetDrawColor(0, 0, 0, 255) /* black, full alpha */ 79 | renderer.Clear() /* start with a blank canvas. */ 80 | 81 | /* Draw once with the whole window as the viewport. */ 82 | viewport.X = 0 83 | viewport.Y = 0 84 | viewport.W = WindowWidth / 2 85 | viewport.H = WindowHeight / 2 86 | renderer.SetViewport(nil) /* NULL means "use the whole window" */ 87 | renderer.RenderTexture(texture, nil, &dstRect) 88 | 89 | /* top right quarter of the window. */ 90 | viewport.X = WindowWidth / 2 91 | viewport.Y = WindowHeight / 2 92 | viewport.W = WindowWidth / 2 93 | viewport.H = WindowHeight / 2 94 | renderer.SetViewport(&viewport) 95 | renderer.RenderTexture(texture, nil, &dstRect) 96 | 97 | /* bottom 20% of the window. Note it clips the width! */ 98 | viewport.X = 0 99 | viewport.Y = WindowHeight - (WindowHeight / 5) 100 | viewport.W = WindowWidth / 5 101 | viewport.H = WindowHeight / 5 102 | renderer.SetViewport(&viewport) 103 | renderer.RenderTexture(texture, nil, &dstRect) 104 | 105 | /* what happens if you try to draw above the viewport? It should clip! */ 106 | viewport.X = 100 107 | viewport.Y = 200 108 | viewport.W = WindowWidth 109 | viewport.H = WindowHeight 110 | renderer.SetViewport(&viewport) 111 | dstRect.Y = -50 112 | renderer.RenderTexture(texture, nil, &dstRect) 113 | 114 | renderer.Present() /* put it all on the screen! */ 115 | 116 | return nil 117 | }) 118 | } 119 | -------------------------------------------------------------------------------- /examples/renderer/15-cliprect/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/15-cliprect/ 2 | 3 | package main 4 | 5 | import ( 6 | "math" 7 | 8 | "github.com/Zyko0/go-sdl3/bin/binsdl" 9 | assets "github.com/Zyko0/go-sdl3/examples/renderer/_assets" 10 | "github.com/Zyko0/go-sdl3/sdl" 11 | ) 12 | 13 | const ( 14 | WindowWidth = 640 15 | WindowHeight = 480 16 | ClipRectSize = 250 17 | ClipRectSpeed = 200 18 | ) 19 | 20 | func main() { 21 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 22 | defer sdl.Quit() 23 | 24 | err := sdl.Init(sdl.INIT_VIDEO) 25 | if err != nil { 26 | panic(err) 27 | } 28 | 29 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/15-cliprect", WindowWidth, WindowHeight, 0) 30 | if err != nil { 31 | panic(err) 32 | } 33 | defer window.Destroy() 34 | defer renderer.Destroy() 35 | 36 | var clipRectPosition sdl.FPoint 37 | 38 | clipRectDirection := sdl.FPoint{ 39 | X: 1, 40 | Y: 1, 41 | } 42 | lastTime := sdl.Ticks() 43 | 44 | /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D 45 | engines refer to these as "sprites." We'll do a static texture (upload once, draw many 46 | times) with data from a bitmap file. */ 47 | bmpStream, err := sdl.IOFromConstMem(assets.SampleBMP) 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access. 53 | Load a .bmp into a surface, move it to a texture from there. */ 54 | surface, err := sdl.LoadBMP_IO(bmpStream, true) 55 | if err != nil { 56 | panic(err) 57 | } 58 | 59 | texture, err := renderer.CreateTextureFromSurface(surface) 60 | if err != nil { 61 | panic(err) 62 | } 63 | 64 | surface.Destroy() 65 | 66 | sdl.RunLoop(func() error { 67 | var event sdl.Event 68 | 69 | for sdl.PollEvent(&event) { 70 | if event.Type == sdl.EVENT_QUIT { 71 | return sdl.EndLoop 72 | } 73 | } 74 | 75 | // Rendering 76 | 77 | cliprect := sdl.Rect{ 78 | X: int32(math.Round(float64(clipRectPosition.X))), 79 | Y: int32(math.Round(float64(clipRectPosition.Y))), 80 | W: ClipRectSize, 81 | H: ClipRectSize, 82 | } 83 | now := sdl.Ticks() 84 | elapsed := float32(now-lastTime) / 1000 /* seconds since last iteration */ 85 | distance := elapsed * ClipRectSpeed 86 | 87 | /* Set a new clipping rectangle position */ 88 | clipRectPosition.X += distance * clipRectDirection.X 89 | if clipRectPosition.X < 0 { 90 | clipRectPosition.X = 0 91 | clipRectDirection.X = 1 92 | } else if clipRectPosition.X >= (WindowWidth - ClipRectSize) { 93 | clipRectPosition.X = (WindowWidth - ClipRectSize) - 1 94 | clipRectDirection.X = -1 95 | } 96 | 97 | clipRectPosition.Y += distance * clipRectDirection.Y 98 | if clipRectPosition.Y < 0 { 99 | clipRectPosition.Y = 0 100 | clipRectDirection.Y = 1 101 | } else if clipRectPosition.Y >= (WindowHeight - ClipRectSize) { 102 | clipRectPosition.Y = (WindowHeight - ClipRectSize) - 1 103 | clipRectDirection.Y = -1 104 | } 105 | renderer.SetClipRect(&cliprect) 106 | 107 | lastTime = now 108 | 109 | /* okay, now draw! */ 110 | 111 | /* Note that SDL_RenderClear is _not_ affected by the clipping rectangle! */ 112 | renderer.SetDrawColor(33, 33, 33, 255) /* grey, full alpha */ 113 | renderer.Clear() /* start with a blank canvas. */ 114 | 115 | /* stretch the texture across the entire window. Only the piece in the 116 | clipping rectangle will actually render, though! */ 117 | renderer.RenderTexture(texture, nil, nil) 118 | 119 | renderer.Present() /* put it all on the screen! */ 120 | 121 | return nil 122 | }) 123 | } 124 | -------------------------------------------------------------------------------- /examples/renderer/17-read-pixels/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/17-read-pixels/ 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/Zyko0/go-sdl3/bin/binsdl" 7 | assets "github.com/Zyko0/go-sdl3/examples/renderer/_assets" 8 | "github.com/Zyko0/go-sdl3/sdl" 9 | ) 10 | 11 | const ( 12 | WindowWidth = 640 13 | WindowHeight = 480 14 | ) 15 | 16 | func main() { 17 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 18 | defer sdl.Quit() 19 | 20 | err := sdl.Init(sdl.INIT_VIDEO) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/17-read-pixels", WindowWidth, WindowHeight, 0) 26 | if err != nil { 27 | panic(err) 28 | } 29 | defer window.Destroy() 30 | defer renderer.Destroy() 31 | 32 | var convertedTexture *sdl.Texture 33 | var convertedTextureWidth, convertedTextureHeight int32 34 | 35 | /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D 36 | engines refer to these as "sprites." We'll do a static texture (upload once, draw many 37 | times) with data from a bitmap file. */ 38 | bmpStream, err := sdl.IOFromConstMem(assets.SampleBMP) 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access. 44 | Load a .bmp into a surface, move it to a texture from there. */ 45 | surface, err := sdl.LoadBMP_IO(bmpStream, true) 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | texture, err := renderer.CreateTextureFromSurface(surface) 51 | if err != nil { 52 | panic(err) 53 | } 54 | 55 | surface.Destroy() 56 | 57 | sdl.RunLoop(func() error { 58 | var event sdl.Event 59 | 60 | for sdl.PollEvent(&event) { 61 | if event.Type == sdl.EVENT_QUIT { 62 | return sdl.EndLoop 63 | } 64 | } 65 | 66 | // Rendering 67 | 68 | now := sdl.Ticks() 69 | var center sdl.FPoint 70 | var dstRect sdl.FRect 71 | 72 | /* we'll have a texture rotate around over 2 seconds (2000 milliseconds). 360 degrees in a circle! */ 73 | rotation := float64(now%2000) / 2000 * 360 74 | 75 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 76 | renderer.SetDrawColor(0, 0, 0, 255) /* black, full alpha */ 77 | renderer.Clear() /* start with a blank canvas. */ 78 | 79 | /* Center this one, and draw it with some rotation so it spins! */ 80 | dstRect.X = float32(WindowWidth-texture.W) / 2 81 | dstRect.Y = float32(WindowHeight-texture.H) / 2 82 | dstRect.W = float32(texture.W) 83 | dstRect.H = float32(texture.H) 84 | /* rotate it around the center of the texture; you can rotate it from a different point, too! */ 85 | center.X = float32(texture.W) / 2 86 | center.Y = float32(texture.H) / 2 87 | renderer.RenderTextureRotated(texture, nil, &dstRect, rotation, ¢er, sdl.FLIP_NONE) 88 | 89 | /* this next whole thing is _super_ expensive. Seriously, don't do this in real life. */ 90 | 91 | /* Download the pixels of what has just been rendered. This has to wait for the GPU to finish rendering it and everything before it, 92 | and then make an expensive copy from the GPU to system RAM! */ 93 | surface, _ := renderer.ReadPixels(nil) 94 | 95 | /* This is also expensive, but easier: convert the pixels to a format we want. */ 96 | if surface != nil && surface.Format != sdl.PIXELFORMAT_RGBA8888 && surface.Format != sdl.PIXELFORMAT_BGRA8888 { 97 | converted, _ := surface.Convert(sdl.PIXELFORMAT_RGBA8888) 98 | surface.Destroy() 99 | surface = converted 100 | } 101 | 102 | if surface != nil { 103 | /* Rebuild converted_texture if the dimensions have changed (window resized, etc). */ 104 | if surface.W != convertedTextureWidth || surface.H != convertedTextureHeight { 105 | convertedTexture.Destroy() 106 | convertedTexture, err = renderer.CreateTexture(sdl.PIXELFORMAT_RGBA8888, sdl.TEXTUREACCESS_STREAMING, int(surface.W), int(surface.H)) 107 | if err != nil { 108 | panic(err) 109 | } 110 | convertedTextureWidth = surface.W 111 | convertedTextureHeight = surface.H 112 | } 113 | /* Turn each pixel into either black or white. This is a lousy technique but it works here. 114 | In real life, something like Floyd-Steinberg dithering might work 115 | better: https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering*/ 116 | pixels := surface.Pixels() 117 | for y := range surface.H { 118 | row := pixels[y*surface.Pitch:] 119 | for x := range surface.W { 120 | p := row[x*4 : x*4+4] 121 | average := (uint32(p[1]) + uint32(p[2]) + uint32(p[3])) / 3 122 | if average == 0 { 123 | /* make pure black pixels red. */ 124 | p[0] = 0xFF 125 | p[1] = 0 126 | p[2] = 0 127 | p[3] = 0xFF 128 | } else { 129 | /* make everything else either black or white. */ 130 | var v byte 131 | if average > 50 { 132 | v = 0xFF 133 | } 134 | p[1] = v 135 | p[2] = v 136 | p[3] = v 137 | } 138 | } 139 | } 140 | /* upload the processed pixels back into a texture. */ 141 | convertedTexture.Update(nil, pixels, surface.Pitch) 142 | surface.Destroy() 143 | 144 | /* draw the texture to the top-left of the screen. */ 145 | dstRect.X, dstRect.Y = 0, 0 146 | dstRect.W = float32(WindowWidth) / 4 147 | dstRect.H = float32(WindowHeight) / 4 148 | renderer.RenderTexture(convertedTexture, nil, &dstRect) 149 | } 150 | 151 | renderer.Present() /* put it all on the screen! */ 152 | 153 | return nil 154 | }) 155 | } 156 | -------------------------------------------------------------------------------- /examples/renderer/18-debug-text/main.go: -------------------------------------------------------------------------------- 1 | // https://examples.libsdl.org/SDL3/renderer/18-debug-text/ 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/Zyko0/go-sdl3/bin/binsdl" 9 | "github.com/Zyko0/go-sdl3/sdl" 10 | ) 11 | 12 | const ( 13 | WindowWidth = 640 14 | WindowHeight = 480 15 | ) 16 | 17 | func main() { 18 | defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) 19 | defer sdl.Quit() 20 | 21 | err := sdl.Init(sdl.INIT_VIDEO) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | window, renderer, err := sdl.CreateWindowAndRenderer("examples/renderer/18-debug-text", WindowWidth, WindowHeight, 0) 27 | if err != nil { 28 | panic(err) 29 | } 30 | defer window.Destroy() 31 | defer renderer.Destroy() 32 | 33 | sdl.RunLoop(func() error { 34 | var event sdl.Event 35 | 36 | for sdl.PollEvent(&event) { 37 | if event.Type == sdl.EVENT_QUIT { 38 | return sdl.EndLoop 39 | } 40 | } 41 | 42 | // Rendering 43 | 44 | const charSize = sdl.DEBUG_TEXT_FONT_CHARACTER_SIZE 45 | 46 | /* as you can see from this, rendering draws over whatever was drawn before it. */ 47 | renderer.SetDrawColor(0, 0, 0, 255) /* black, full alpha */ 48 | renderer.Clear() /* start with a blank canvas. */ 49 | 50 | renderer.SetDrawColor(255, 255, 255, 255) /* white, full alpha */ 51 | renderer.DebugText(272, 100, "Hello world!") 52 | renderer.DebugText(224, 150, "This is some debug text.") 53 | 54 | renderer.SetDrawColor(51, 102, 255, 255) /* light blue, full alpha */ 55 | renderer.DebugText(184, 200, "You can do it in different colors.") 56 | renderer.SetDrawColor(255, 255, 255, 255) /* white, full alpha */ 57 | 58 | renderer.SetScale(4, 4) 59 | renderer.DebugText(14, 65, "It can be scaled.") 60 | renderer.SetScale(1, 1) 61 | renderer.DebugText(64, 350, "This only does ASCII chars. So this laughing emoji won't draw: 🤣") 62 | 63 | renderer.DebugText(float32(WindowWidth-charSize*46)/2, 400, fmt.Sprintf("(This program has been running for %d seconds.)", sdl.Ticks()/1000)) 64 | 65 | renderer.Present() /* put it all on the screen! */ 66 | 67 | return nil 68 | }) 69 | } 70 | -------------------------------------------------------------------------------- /examples/renderer/README.md: -------------------------------------------------------------------------------- 1 | # Renderer 2 | 3 | In this folder are the renderer official examples seen here https://examples.libsdl.org/SDL3/renderer/. -------------------------------------------------------------------------------- /examples/renderer/_assets/images.go: -------------------------------------------------------------------------------- 1 | package assets 2 | 3 | import ( 4 | _ "embed" 5 | ) 6 | 7 | var ( 8 | //go:embed images/sample.bmp 9 | SampleBMP []byte 10 | ) 11 | -------------------------------------------------------------------------------- /examples/renderer/_assets/images/sample.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zyko0/go-sdl3/2fefbd8ac5cd4db9306f0736a85582d88b50842f/examples/renderer/_assets/images/sample.bmp -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Zyko0/go-sdl3 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/Zyko0/purego-gen v0.0.0-20250601142424-aec919327f6e 7 | github.com/dave/jennifer v1.7.0 8 | github.com/ebitengine/purego v0.9.0-alpha.2.0.20250124174847-29f0104e3c2b 9 | ) 10 | 11 | require golang.org/x/sys v0.7.0 // indirect 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Zyko0/purego-gen v0.0.0-20250308152853-097c3ba1e28a h1:N5RnQ3/z1/HjCivGNfRbID/3QreOQ6Dulr3FAOgULqc= 2 | github.com/Zyko0/purego-gen v0.0.0-20250308152853-097c3ba1e28a/go.mod h1:yJn8avSZmgophSwHMUDhlEIzdkB9uUTEctszmQmjxDo= 3 | github.com/Zyko0/purego-gen v0.0.0-20250601142424-aec919327f6e h1:IUgp9j8VuemBZx/uFGvCkmt2HGmmccVl+NP3oD+gSmU= 4 | github.com/Zyko0/purego-gen v0.0.0-20250601142424-aec919327f6e/go.mod h1:hhfgmPL6oQvMnADoaY/LAfeMqIEaNDIN2RrpVMD+zwk= 5 | github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE= 6 | github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= 7 | github.com/ebitengine/purego v0.9.0-alpha.2.0.20250124174847-29f0104e3c2b h1:/KAOJuXR4cWaQIiA9xBMDSQJ1JXq5gZHdSK8prrtUqQ= 8 | github.com/ebitengine/purego v0.9.0-alpha.2.0.20250124174847-29f0104e3c2b/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= 9 | golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= 10 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 11 | -------------------------------------------------------------------------------- /img/glue.go: -------------------------------------------------------------------------------- 1 | package img 2 | 3 | import "github.com/Zyko0/go-sdl3/internal" 4 | 5 | // Types 6 | 7 | type Pointer = internal.Pointer 8 | 9 | // IMG_Animation - Animated image support 10 | // (https://wiki.libsdl.org/SDL3_image/IMG_Animation) 11 | type Animation struct { 12 | W int32 13 | H int32 14 | count int32 15 | frames Pointer 16 | delays Pointer 17 | } 18 | -------------------------------------------------------------------------------- /img/glue_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package img 4 | 5 | import ( 6 | "syscall/js" 7 | 8 | "github.com/Zyko0/go-sdl3/internal" 9 | "github.com/Zyko0/go-sdl3/sdl" 10 | ) 11 | 12 | func (a *Animation) Frames() []*sdl.Surface { 13 | return internal.GetObjectSliceFromJSPtr[sdl.Surface](js.ValueOf(a.frames), int(a.count)) 14 | } 15 | 16 | func (a *Animation) Delays() []int32 { 17 | return internal.GetNumericSliceFromJSPtr[int32](js.ValueOf(a.delays), int(a.count)) 18 | } 19 | -------------------------------------------------------------------------------- /img/glue_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package img 4 | 5 | import ( 6 | "github.com/Zyko0/go-sdl3/internal" 7 | "github.com/Zyko0/go-sdl3/sdl" 8 | ) 9 | 10 | func (a *Animation) Frames() []*sdl.Surface { 11 | return internal.PtrToSlice[*sdl.Surface](uintptr(a.frames), int(a.count)) 12 | } 13 | 14 | func (a *Animation) Delays() []int32 { 15 | return internal.PtrToSlice[int32](uintptr(a.delays), int(a.count)) 16 | } 17 | -------------------------------------------------------------------------------- /img/img_enums.gen.go: -------------------------------------------------------------------------------- 1 | package img 2 | 3 | // Code generated by cmd/ffi2go. DO NOT EDIT. 4 | -------------------------------------------------------------------------------- /img/img_structs.gen.go: -------------------------------------------------------------------------------- 1 | package img 2 | 3 | // Code generated by cmd/ffi2go. DO NOT EDIT. 4 | -------------------------------------------------------------------------------- /img/img_types.gen.go: -------------------------------------------------------------------------------- 1 | package img 2 | 3 | // Code generated by cmd/ffi2go. DO NOT EDIT. 4 | -------------------------------------------------------------------------------- /img/init_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package img 4 | 5 | // We can just initialize everything here in js/wasm env 6 | func init() { 7 | initialize() 8 | } 9 | 10 | // Path returns an empty string in js/wasm environment. 11 | func Path() string { 12 | return "" 13 | } 14 | 15 | // LoadLibrary does nothing in js/wasm environment. 16 | func LoadLibrary(path string) error { 17 | return nil 18 | } 19 | 20 | // CloseLibrary does nothing in js/wasm environment. 21 | func CloseLibrary() error { 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /img/init_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package img 4 | 5 | import ( 6 | "runtime" 7 | 8 | puregogen "github.com/Zyko0/purego-gen" 9 | ) 10 | 11 | // Path returns the library installation path based on the operating 12 | // system 13 | func Path() string { 14 | switch runtime.GOOS { 15 | case "windows": 16 | return "SDL3_image.dll" 17 | case "linux", "freebsd": 18 | return "libSDL3_image.so.0" 19 | case "darwin": 20 | return "libSDL3_image.dylib" 21 | default: 22 | return "" 23 | } 24 | } 25 | 26 | // LoadLibrary loads SDL_image library and initializes all functions. 27 | func LoadLibrary(path string) error { 28 | var err error 29 | 30 | runtime.LockOSThread() 31 | 32 | _hnd_img, err = puregogen.OpenLibrary(path) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | initialize() 38 | 39 | return nil 40 | } 41 | 42 | // CloseLibrary releases resources associated with the library. 43 | func CloseLibrary() error { 44 | return puregogen.CloseLibrary(_hnd_img) 45 | } 46 | -------------------------------------------------------------------------------- /img/methods.go: -------------------------------------------------------------------------------- 1 | package img 2 | 3 | // Animation 4 | 5 | // IMG_FreeAnimation - Dispose of an IMG_Animation and free its resources. 6 | // (https://wiki.libsdl.org/SDL3_img/IMG_FreeAnimation) 7 | func (anim *Animation) Free() { 8 | iFreeAnimation(anim) 9 | } 10 | -------------------------------------------------------------------------------- /internal/memory_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package internal 4 | 5 | import ( 6 | "runtime" 7 | "slices" 8 | "strconv" 9 | "strings" 10 | "sync" 11 | "syscall/js" 12 | "time" 13 | "unsafe" 14 | ) 15 | 16 | type Pointer = int32 17 | 18 | var ( 19 | heapU8 js.Value 20 | 21 | stackAlloc js.Value 22 | stackString js.Value 23 | utf8String js.Value 24 | getValue js.Value 25 | setValue js.Value 26 | stackSave js.Value 27 | stackRestore js.Value 28 | 29 | bigInt js.Value 30 | boolean js.Value 31 | 32 | lastStackPtr js.Value 33 | 34 | lock sync.Mutex 35 | ) 36 | 37 | func init() { 38 | // Wait for runtime initialization 39 | // TODO: find a cleaner way than loop+sleep? 40 | for !js.Global().Get("runtimeInitialized").Bool() { 41 | time.Sleep(1 * time.Millisecond) 42 | } 43 | for js.Global().Get("Module").Get("HEAPU8").IsUndefined() { 44 | time.Sleep(1 * time.Millisecond) 45 | } 46 | 47 | heapU8 = js.Global().Get("Module").Get("HEAPU8") 48 | 49 | stackAlloc = js.Global().Get("Module").Get("stackAlloc") 50 | stackString = js.Global().Get("Module").Get("stringToUTF8OnStack") 51 | utf8String = js.Global().Get("Module").Get("UTF8ToString") 52 | getValue = js.Global().Get("getValue") 53 | setValue = js.Global().Get("setValue") 54 | stackSave = js.Global().Get("stackSave") 55 | stackRestore = js.Global().Get("stackRestore") 56 | 57 | bigInt = js.Global().Get("BigInt") 58 | boolean = js.Global().Get("Boolean") 59 | } 60 | 61 | type object struct { 62 | data []byte 63 | value js.Value 64 | finalizers []func() 65 | } 66 | 67 | func (o *object) Map(size uintptr) { 68 | o.data = slices.Grow(o.data, int(size))[:size] 69 | o.finalizers = nil 70 | o.value = js.Value{} 71 | for i := range size { 72 | o.data[i] = 0 73 | } 74 | } 75 | 76 | func (o *object) ExtractSelf() { 77 | arr := heapU8.Call("slice", o.value.Int(), o.value.Int()+len(o.data)) 78 | js.CopyBytesToGo(o.data, arr) 79 | } 80 | 81 | var ( 82 | jsPtrsByObject = map[uintptr]*object{} 83 | pool = sync.Pool{ 84 | New: func() any { 85 | return new(object) 86 | }, 87 | } 88 | ) 89 | 90 | func StackSave() { 91 | lock.Lock() 92 | lastStackPtr = stackSave.Invoke() 93 | } 94 | 95 | func StackRestore() { 96 | stackRestore.Invoke(lastStackPtr) 97 | lock.Unlock() 98 | } 99 | 100 | func StackAlloc(n int) js.Value { 101 | return stackAlloc.Invoke(int32(n)) 102 | } 103 | 104 | func CloneObjectToJSStack[T any](obj *T) js.Value { 105 | if obj == nil { 106 | return js.Null() 107 | } 108 | size := unsafe.Sizeof(*obj) 109 | ptr := stackAlloc.Invoke(int32(size)) 110 | arr := heapU8.Call("subarray", ptr.Int(), ptr.Int()+int(size)) 111 | js.CopyBytesToJS(arr, unsafe.Slice((*byte)(unsafe.Pointer(obj)), size)) 112 | 113 | return ptr 114 | } 115 | 116 | func CopyJSToObject[T any](obj *T, ptr js.Value) { 117 | size := unsafe.Sizeof(*obj) 118 | arr := heapU8.Call("slice", ptr.Int(), ptr.Int()+int(size)) 119 | js.CopyBytesToGo(unsafe.Slice((*byte)(unsafe.Pointer(obj)), size), arr) 120 | } 121 | 122 | func CloneByteSliceToJSHeap(s []byte) js.Value { 123 | ptr := js.Global().Call("_malloc", len(s)) 124 | arr := heapU8.Call("subarray", ptr.Int(), ptr.Int()+len(s)) 125 | js.CopyBytesToJS(arr, s) 126 | 127 | return ptr 128 | } 129 | 130 | func ClonePtrArrayToJSHeap[T any](ptr *T, count int) (js.Value, func()) { 131 | if ptr == nil || count <= 0 { 132 | return js.Null(), func() {} 133 | } 134 | size := int(count) * int(unsafe.Sizeof(*ptr)) 135 | s := unsafe.Slice((*byte)(unsafe.Pointer(ptr)), size) 136 | arr := CloneByteSliceToJSHeap(s) 137 | runtime.KeepAlive(s) 138 | return arr, func() { 139 | js.Global().Call("_free", arr) 140 | } 141 | } 142 | 143 | func StringOnJSStack(str string) js.Value { 144 | return stackString.Invoke(str) 145 | } 146 | 147 | func UTF8JSToString(ptr js.Value) string { 148 | return utf8String.Invoke(ptr).String() 149 | } 150 | 151 | func GetValue(ptr js.Value, typ string) js.Value { 152 | if strings.HasPrefix(typ, "*") { 153 | typ = "*" 154 | } 155 | switch typ { 156 | case "i1", "i8", "i16", "i32", "*", "float", "double": 157 | return getValue.Invoke(ptr, typ) 158 | default: 159 | panic("unknown incoming js type") 160 | } 161 | } 162 | 163 | func SetValue(ptr js.Value, value js.Value, typ string) { 164 | if strings.HasPrefix(typ, "*") { 165 | typ = "*" 166 | } 167 | switch typ { 168 | case "i1", "i8", "i16", "i32", "*", "float", "double": 169 | setValue.Invoke(ptr, value, typ) 170 | default: 171 | panic("unknown incoming js type") 172 | } 173 | } 174 | 175 | func GetJSPointerFromUintptr(ptr uintptr) (js.Value, bool) { 176 | v, ok := jsPtrsByObject[ptr] 177 | if ok { 178 | return v.value, true 179 | } 180 | return js.Null(), false 181 | } 182 | 183 | func GetJSPointer[T any](obj *T) (js.Value, bool) { 184 | v, ok := jsPtrsByObject[uintptr(unsafe.Pointer(obj))] 185 | if ok { 186 | return v.value, true 187 | } 188 | return js.Null(), false 189 | } 190 | 191 | func NewObject[T any](ptr js.Value) *T { 192 | var t T 193 | 194 | obj := pool.Get().(*object) 195 | obj.Map(max(unsafe.Sizeof(t), 1)) 196 | obj.value = ptr 197 | obj.ExtractSelf() 198 | objPtr := unsafe.Pointer(&obj.data[0]) 199 | obj.finalizers = append(obj.finalizers, func() { 200 | delete(jsPtrsByObject, uintptr(objPtr)) 201 | pool.Put(obj) 202 | }) 203 | runtime.SetFinalizer(obj, func(o *object) { 204 | for _, fn := range o.finalizers { 205 | fn() 206 | } 207 | }) 208 | 209 | jsPtrsByObject[uintptr(objPtr)] = obj 210 | ret := (*T)(objPtr) 211 | 212 | return ret 213 | } 214 | 215 | func DeleteJSPointer(ptr uintptr) { 216 | _, ok := jsPtrsByObject[ptr] 217 | if !ok { 218 | return 219 | } 220 | delete(jsPtrsByObject, ptr) 221 | } 222 | 223 | func AttachFinalizer[T any](obj *T, fn func()) { 224 | v, ok := jsPtrsByObject[uintptr(unsafe.Pointer(obj))] 225 | if !ok { 226 | return 227 | } 228 | v.finalizers = append(v.finalizers, fn) 229 | } 230 | 231 | func NewBigInt(n any) js.Value { 232 | return bigInt.Invoke(n) 233 | } 234 | 235 | func NewBoolean(b bool) js.Value { 236 | return boolean.Invoke(b) 237 | } 238 | 239 | func GetBool(v js.Value) bool { 240 | return boolean.Invoke(v).Bool() 241 | } 242 | 243 | func GetInt64(v js.Value) int64 { 244 | str := js.Global().Get("String").Invoke(v).String() 245 | n, _ := strconv.ParseInt(str, 10, 64) 246 | 247 | return n 248 | } 249 | 250 | func GetByteSliceFromJSPtr(ptr js.Value, count int) []byte { 251 | s := make([]byte, count) 252 | arr := heapU8.Call("slice", ptr.Int(), ptr.Int()+count) 253 | js.CopyBytesToGo(s, arr) 254 | 255 | return s 256 | } 257 | 258 | type Numeric interface { 259 | ~int8 | ~int16 | ~int32 | ~int64 | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~float32 | ~float64 260 | } 261 | 262 | func GetNumericSliceFromJSPtr[T Numeric](ptr js.Value, count int) []T { 263 | s := make([]T, count) 264 | bytesCount := count * int(unsafe.Sizeof(T(0))) 265 | arr := heapU8.Call("slice", ptr.Int(), ptr.Int()+bytesCount) 266 | js.CopyBytesToGo(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(s))), bytesCount), arr) 267 | 268 | return s 269 | } 270 | 271 | func GetObjectSliceFromJSPtr[T any](ptr js.Value, count int) []*T { 272 | ptrs := GetNumericSliceFromJSPtr[int32](ptr, count) 273 | ret := make([]*T, count) 274 | for i := range ptrs { 275 | var obj T 276 | CopyJSToObject(&obj, js.ValueOf(ptrs[i])) 277 | ret[i] = &obj 278 | } 279 | 280 | return ret 281 | } 282 | -------------------------------------------------------------------------------- /internal/memory_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package internal 4 | 5 | import "unsafe" 6 | 7 | type Pointer = unsafe.Pointer 8 | -------------------------------------------------------------------------------- /internal/sdl_functions.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | var ( 4 | fnLastErr func() error 5 | fnFree func(uintptr) 6 | ) 7 | 8 | func SetSDLLastErrFunc(fn func() error) { 9 | fnLastErr = fn 10 | } 11 | 12 | func SetSDLFreeFunc(fn func(uintptr)) { 13 | fnFree = fn 14 | } 15 | 16 | // Impl 17 | 18 | func LastErr() error { 19 | return fnLastErr() 20 | } 21 | 22 | func Free(ptr uintptr) { 23 | fnFree(ptr) 24 | } 25 | -------------------------------------------------------------------------------- /internal/slice.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | // ClonePtrSlice returns a newly allocated slice from a uintptr 8 | func ClonePtrSlice[T any](ptr uintptr, count int) []T { 9 | var s []T 10 | 11 | if count > 0 { 12 | s = append(s, unsafe.Slice(*(**T)(unsafe.Pointer(&ptr)), count)...) 13 | } 14 | 15 | return s 16 | } 17 | 18 | // PtrToSlice returns a slice pointing to the provided uintptr data 19 | func PtrToSlice[T any](ptr uintptr, count int) []T { 20 | return unsafe.Slice(*(**T)(unsafe.Pointer(&ptr)), count) 21 | } 22 | -------------------------------------------------------------------------------- /internal/string_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package internal 4 | 5 | import ( 6 | "unsafe" 7 | ) 8 | 9 | // ClonePtrString returns a newly allocated string from a uintptr 10 | func ClonePtrString(ptr uintptr) string { 11 | return "" + PtrToString(ptr) 12 | } 13 | 14 | // PtrToString returns a string pointing to the provided ptr char data 15 | func PtrToString(ptr uintptr) string { 16 | p := *(**uint8)(unsafe.Pointer(&ptr)) 17 | if p == nil { 18 | return "" 19 | } 20 | if *p == 0 { 21 | return "" 22 | } 23 | 24 | // Find NUL terminator. 25 | n := 0 26 | for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ { 27 | ptr = unsafe.Pointer(uintptr(ptr) + 1) 28 | } 29 | return string(unsafe.Slice(p, n)) 30 | } 31 | 32 | // StringToNullablePtr returns a uintptr pointing to the provided string data 33 | func StringToNullablePtr(s string) *byte { 34 | panic("not implemented on js") 35 | } 36 | 37 | // StringToPtr returns a uintptr pointing to the provided string data 38 | func StringToPtr(s string) *byte { 39 | panic("not implemented on js") 40 | } 41 | -------------------------------------------------------------------------------- /internal/string_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package internal 4 | 5 | import ( 6 | "unsafe" 7 | 8 | puregogen "github.com/Zyko0/purego-gen" 9 | ) 10 | 11 | // ClonePtrString returns a newly allocated string from a uintptr 12 | func ClonePtrString(ptr uintptr) string { 13 | return "" + puregogen.BytePtrToString(*(**byte)(unsafe.Pointer(&ptr))) 14 | } 15 | 16 | // PtrToString returns a string pointing to the provided ptr char data 17 | func PtrToString(ptr uintptr) string { 18 | return puregogen.BytePtrToString(*(**byte)(unsafe.Pointer(&ptr))) 19 | } 20 | 21 | // StringToNullablePtr returns a uintptr pointing to the provided string data 22 | func StringToNullablePtr(s string) *byte { 23 | if len(s) == 0 { 24 | return nil 25 | } 26 | return puregogen.BytePtrFromString(s) 27 | } 28 | 29 | // StringToPtr returns a uintptr pointing to the provided string data 30 | func StringToPtr(s string) *byte { 31 | return puregogen.BytePtrFromString(s) 32 | } 33 | -------------------------------------------------------------------------------- /internal/tmp_directory.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "sync" 7 | "syscall" 8 | ) 9 | 10 | type tmpDir struct { 11 | onceCreate sync.Once 12 | onceRemove sync.Once 13 | 14 | Dir string 15 | Err error 16 | } 17 | 18 | var dir tmpDir 19 | 20 | func TmpDir() (string, error) { 21 | dir.onceCreate.Do(func() { 22 | dir.Dir, dir.Err = os.MkdirTemp("", "") 23 | if dir.Err == nil { 24 | // Ensure the temporary directory is removed if program 25 | // exits outside main function 26 | channel := make(chan os.Signal, 1) 27 | signal.Notify(channel, 28 | syscall.SIGTERM, 29 | syscall.SIGINT, 30 | ) 31 | go func() { 32 | <-channel 33 | RemoveTmpDir() 34 | }() 35 | } 36 | }) 37 | 38 | return dir.Dir, dir.Err 39 | } 40 | 41 | func RemoveTmpDir() { 42 | dir.onceRemove.Do(func() { 43 | os.RemoveAll(dir.Dir) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /mixer/functions.go: -------------------------------------------------------------------------------- 1 | package mixer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | internal "github.com/Zyko0/go-sdl3/internal" 7 | "github.com/Zyko0/go-sdl3/sdl" 8 | ) 9 | 10 | // Mix_Version - This function gets the version of the dynamically linked SDL_mixer library. 11 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_Version) 12 | func Version() int32 { 13 | return iVersion() 14 | } 15 | 16 | // Mix_Init - Initialize SDL_mixer. 17 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_Init) 18 | func Init(flags InitFlags) InitFlags { 19 | return InitFlags(iInit(MIX_InitFlags(flags))) 20 | } 21 | 22 | // Mix_Quit - Deinitialize SDL_mixer. 23 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_Quit) 24 | func Quit() { 25 | iQuit() 26 | } 27 | 28 | // Mix_OpenAudio - Open an audio device for playback. 29 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_OpenAudio) 30 | func OpenAudio(devid sdl.AudioDeviceID, spec *sdl.AudioSpec) error { 31 | if !iOpenAudio(devid, spec) { 32 | return internal.LastErr() 33 | } 34 | 35 | return nil 36 | } 37 | 38 | // Mix_PauseAudio - Suspend or resume the whole audio output. 39 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_PauseAudio) 40 | func PauseAudio() { 41 | iPauseAudio(1) 42 | } 43 | 44 | // ResumeAudio calls Mix_PauseAudio with argument 0 to resume 45 | func ResumeAudio() { 46 | iPauseAudio(0) 47 | } 48 | 49 | // Mix_QuerySpec - Find out what the actual audio device parameters are. 50 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_QuerySpec) 51 | func QuerySpec() *AudioParams { 52 | var params AudioParams 53 | 54 | if !iQuerySpec(¶ms.Frequency, ¶ms.Format, ¶ms.Channels) { 55 | return nil 56 | } 57 | 58 | return ¶ms 59 | } 60 | 61 | // Mix_AllocateChannels - Dynamically change the number of channels managed by the mixer. 62 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_AllocateChannels) 63 | func AllocateChannels(numChans int32) int32 { 64 | return iAllocateChannels(numChans) 65 | } 66 | 67 | // Mix_LoadWAV_IO - Load a supported audio format into a chunk. 68 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_LoadWAV_IO) 69 | func LoadWAV_IO(src *sdl.IOStream, closeio bool) (*Chunk, error) { 70 | chunk := iLoadWAV_IO(src, closeio) 71 | if chunk == nil { 72 | return nil, internal.LastErr() 73 | } 74 | 75 | return chunk, nil 76 | } 77 | 78 | // Mix_LoadWAV - Load a supported audio format into a chunk. 79 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_LoadWAV) 80 | func LoadWAV(file string) (*Chunk, error) { 81 | chunk := iLoadWAV(file) 82 | if chunk == nil { 83 | return nil, internal.LastErr() 84 | } 85 | 86 | return chunk, nil 87 | } 88 | 89 | // Mix_LoadMUS_IO - Load a supported audio format into a music object. 90 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_LoadMUS_IO) 91 | func LoadMUS_IO(src *sdl.IOStream, closeio bool) (*Music, error) { 92 | music := iLoadMUS_IO(src, closeio) 93 | if music == nil { 94 | return nil, internal.LastErr() 95 | } 96 | 97 | return music, nil 98 | } 99 | 100 | // Mix_LoadMUS - Load a supported audio format into a music object. 101 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_LoadMUS) 102 | func LoadMUS(file string) (*Music, error) { 103 | music := iLoadMUS(file) 104 | if music == nil { 105 | return nil, internal.LastErr() 106 | } 107 | 108 | return music, nil 109 | } 110 | 111 | // Mix_LoadMUSType_IO - Load an audio format into a music object, assuming a specific format. 112 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_LoadMUSType_IO) 113 | func LoadMUSType_IO(src *sdl.IOStream, typ MusicType, closeio bool) (*Music, error) { 114 | music := iLoadMUSType_IO(src, typ, closeio) 115 | if music == nil { 116 | return nil, internal.LastErr() 117 | } 118 | 119 | return music, nil 120 | } 121 | 122 | // Mix_QuickLoad_WAV - Load a WAV file from memory as quickly as possible. 123 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_QuickLoad_WAV) 124 | func QuickLoadWAV(mem []byte) (*Chunk, error) { 125 | chunk := iQuickLoad_WAV(unsafe.SliceData(mem)) 126 | if chunk == nil { 127 | return nil, internal.LastErr() 128 | } 129 | 130 | return chunk, nil 131 | } 132 | 133 | // Mix_QuickLoad_RAW - Load a raw audio data from memory as quickly as possible. 134 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_QuickLoad_RAW) 135 | func QuickLoadRAW(mem []byte) (*Chunk, error) { 136 | chunk := iQuickLoad_RAW(unsafe.SliceData(mem), uint32(len(mem))) 137 | if chunk == nil { 138 | return nil, internal.LastErr() 139 | } 140 | 141 | return chunk, nil 142 | } 143 | 144 | // Mix_GetNumChunkDecoders - Get a list of chunk decoders that this build of SDL_mixer provides. 145 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetNumChunkDecoders) 146 | func NumChunkDecoders() int32 { 147 | return iGetNumChunkDecoders() 148 | } 149 | 150 | // Mix_GetChunkDecoder - Get a chunk decoder's name. 151 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetChunkDecoder) 152 | func GetChunkDecoder(index int32) string { 153 | return iGetChunkDecoder(index) 154 | } 155 | 156 | // Mix_HasChunkDecoder - Check if a chunk decoder is available by name. 157 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_HasChunkDecoder) 158 | func HasChunkDecoder(name string) bool { 159 | return iHasChunkDecoder(name) 160 | } 161 | 162 | // Mix_GetNumMusicDecoders - Get a list of music decoders that this build of SDL_mixer provides. 163 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetNumMusicDecoders) 164 | func NumMusicDecoders() int32 { 165 | return iGetNumMusicDecoders() 166 | } 167 | 168 | // Mix_GetMusicDecoder - Get a music decoder's name. 169 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicDecoder) 170 | func GetMusicDecoder(index int32) string { 171 | return iGetMusicDecoder(index) 172 | } 173 | 174 | // Mix_HasMusicDecoder - Check if a music decoder is available by name. 175 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_HasMusicDecoder) 176 | func HasMusicDecoder(name string) bool { 177 | return iHasMusicDecoder(name) 178 | } 179 | 180 | // Mix_PlayChannel - Play an audio chunk on a specific channel. 181 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_PlayChannel) 182 | func PlayChannel(channel int32, chunk *Chunk, loops int32) (int32, error) { 183 | selectedChannel := iPlayChannel(channel, chunk, loops) 184 | if selectedChannel == -1 { 185 | return selectedChannel, internal.LastErr() 186 | } 187 | 188 | return selectedChannel, nil 189 | } 190 | 191 | // Mix_PlayChannelTimed - Play an audio chunk on a specific channel for a maximum time. 192 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_PlayChannelTimed) 193 | func PlayChannelTimed(channel int32, chunk *Chunk, loops, ticks int32) (int32, error) { 194 | selectedChannel := iPlayChannelTimed(channel, chunk, loops, ticks) 195 | if selectedChannel == -1 { 196 | return selectedChannel, internal.LastErr() 197 | } 198 | 199 | return selectedChannel, nil 200 | } 201 | 202 | // Mix_PlayingMusic - Check the playing status of the music stream. 203 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_PlayingMusic) 204 | func PlayingMusic() bool { 205 | return iPlayingMusic() 206 | } 207 | 208 | // Mix_PausedMusic - Query whether the music stream is paused. 209 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_PausedMusic) 210 | func PausedMusic() bool { 211 | return iPausedMusic() 212 | } 213 | 214 | // Mix_PauseMusic - Pause the music stream. 215 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_PauseMusic) 216 | func PauseMusic() { 217 | iPauseMusic() 218 | } 219 | 220 | // Mix_ResumeMusic - Resume the music stream. 221 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_ResumeMusic) 222 | func ResumeMusic() { 223 | iResumeMusic() 224 | } 225 | 226 | // Mix_HaltMusic - Halt playing of the music stream. 227 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_HaltMusic) 228 | func HaltMusic() { 229 | iHaltMusic() 230 | } 231 | -------------------------------------------------------------------------------- /mixer/glue.go: -------------------------------------------------------------------------------- 1 | package mixer 2 | 3 | import ( 4 | internal "github.com/Zyko0/go-sdl3/internal" 5 | "github.com/Zyko0/go-sdl3/sdl" 6 | ) 7 | 8 | // Types 9 | 10 | type Pointer = internal.Pointer 11 | 12 | type MIX_InitFlags uint32 13 | 14 | type InitFlags MIX_InitFlags 15 | 16 | // Mix_Chunk - The internal format for an audio chunk 17 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_Chunk) 18 | type Chunk struct { 19 | Allocated int32 20 | abuf Pointer 21 | alen uint32 22 | Volume uint8 23 | } 24 | 25 | type ( 26 | MixCallback func() 27 | MusicFinishedCallback func() 28 | ChannelFinishedCallback func() 29 | EffectFunc_t func() 30 | EffectDone_t func() 31 | EachSoundFontCallback func() 32 | ) 33 | 34 | // Custom 35 | 36 | type AudioParams struct { 37 | Frequency int32 38 | Format sdl.AudioFormat 39 | Channels int32 40 | } 41 | -------------------------------------------------------------------------------- /mixer/glue_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package mixer 4 | 5 | import ( 6 | js "syscall/js" 7 | 8 | internal "github.com/Zyko0/go-sdl3/internal" 9 | ) 10 | 11 | func (c *Chunk) Buffer() []byte { 12 | return internal.GetByteSliceFromJSPtr(js.ValueOf(c.abuf), int(c.alen)) 13 | } 14 | -------------------------------------------------------------------------------- /mixer/glue_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package mixer 4 | 5 | import internal "github.com/Zyko0/go-sdl3/internal" 6 | 7 | func (c *Chunk) Buffer() []byte { 8 | return internal.PtrToSlice[byte](uintptr(c.abuf), int(c.alen)) 9 | } 10 | -------------------------------------------------------------------------------- /mixer/init_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package mixer 4 | 5 | // We can just initialize everything here in js/wasm env 6 | func init() { 7 | initialize() 8 | } 9 | 10 | // Path returns an empty string in js/wasm environment. 11 | func Path() string { 12 | return "" 13 | } 14 | 15 | // LoadLibrary does nothing in js/wasm environment. 16 | func LoadLibrary(path string) error { 17 | return nil 18 | } 19 | 20 | // CloseLibrary does nothing in js/wasm environment. 21 | func CloseLibrary() error { 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /mixer/init_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package mixer 4 | 5 | import ( 6 | "runtime" 7 | 8 | puregogen "github.com/Zyko0/purego-gen" 9 | ) 10 | 11 | // Path returns the library installation path based on the operating 12 | // system 13 | func Path() string { 14 | switch runtime.GOOS { 15 | case "windows": 16 | return "SDL3_mixer.dll" 17 | case "linux", "freebsd": 18 | return "libSDL3_mixer.so.0" 19 | case "darwin": 20 | return "libSDL3_mixer.dylib" 21 | default: 22 | return "" 23 | } 24 | } 25 | 26 | // LoadLibrary loads SDL_mixer library and initializes all functions. 27 | func LoadLibrary(path string) error { 28 | var err error 29 | 30 | runtime.LockOSThread() 31 | 32 | _hnd_mixer, err = puregogen.OpenLibrary(path) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | initialize() 38 | 39 | return nil 40 | } 41 | 42 | // CloseLibrary releases resources associated with the library. 43 | func CloseLibrary() error { 44 | return puregogen.CloseLibrary(_hnd_mixer) 45 | } 46 | -------------------------------------------------------------------------------- /mixer/macros.go: -------------------------------------------------------------------------------- 1 | package mixer 2 | 3 | const ( 4 | INIT_FLAC InitFlags = 0x00000001 5 | INIT_MOD InitFlags = 0x00000002 6 | INIT_MP3 InitFlags = 0x00000008 7 | INIT_OGG InitFlags = 0x00000010 8 | INIT_MID InitFlags = 0x00000020 9 | INIT_OPUS InitFlags = 0x00000040 10 | INIT_WAVPACK InitFlags = 0x00000080 11 | ) 12 | -------------------------------------------------------------------------------- /mixer/methods.go: -------------------------------------------------------------------------------- 1 | package mixer 2 | 3 | import internal "github.com/Zyko0/go-sdl3/internal" 4 | 5 | // MusicFinishedCallback 6 | 7 | // Mix_HookMusicFinished - Set a callback that runs when a music object has stopped playing. 8 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_HookMusicFinished) 9 | func (music_finished MusicFinishedCallback) HookMusicFinished() { 10 | panic("not implemented") 11 | iHookMusicFinished(music_finished) 12 | } 13 | 14 | // ChannelFinishedCallback 15 | 16 | // Mix_ChannelFinished - Set a callback that runs when a channel has finished playing. 17 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_ChannelFinished) 18 | func (channel_finished ChannelFinishedCallback) ChannelFinished() { 19 | panic("not implemented") 20 | iChannelFinished(channel_finished) 21 | } 22 | 23 | // EachSoundFontCallback 24 | 25 | // Mix_EachSoundFont - Iterate SoundFonts paths to use by supported MIDI backends. 26 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_EachSoundFont) 27 | func (function EachSoundFontCallback) EachSoundFont(data uintptr) bool { 28 | panic("not implemented") 29 | return iEachSoundFont(function, data) 30 | } 31 | 32 | // MIX_InitFlags 33 | 34 | // Mix_Init - Initialize SDL_mixer. 35 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_Init) 36 | func (flags MIX_InitFlags) Init() MIX_InitFlags { 37 | panic("not implemented") 38 | return iInit(flags) 39 | } 40 | 41 | // Chunk 42 | 43 | // Mix_FreeChunk - Free an audio chunk. 44 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_FreeChunk) 45 | func (chunk *Chunk) Free() { 46 | iFreeChunk(chunk) 47 | } 48 | 49 | /*func (chunk *Chunk) Volume(volume int32) int32 { // TODO: same name 50 | panic("not implemented") 51 | return iVolumeChunk(chunk, volume) 52 | }*/ 53 | 54 | // Music 55 | 56 | // Mix_FreeMusic - Free a music object. 57 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_FreeMusic) 58 | func (music *Music) Free() { 59 | iFreeMusic(music) 60 | } 61 | 62 | // Mix_GetMusicType - Find out the format of a mixer music. 63 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicType) 64 | func (music *Music) Type() MusicType { 65 | panic("not implemented") 66 | return iGetMusicType(music) 67 | } 68 | 69 | // Mix_GetMusicTitle - Get the title for a music object, or its filename. 70 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicTitle) 71 | func (music *Music) Title() string { 72 | panic("not implemented") 73 | return iGetMusicTitle(music) 74 | } 75 | 76 | // Mix_GetMusicTitleTag - Get the title for a music object. 77 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicTitleTag) 78 | func (music *Music) TitleTag() string { 79 | panic("not implemented") 80 | return iGetMusicTitleTag(music) 81 | } 82 | 83 | // Mix_GetMusicArtistTag - Get the artist name for a music object. 84 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicArtistTag) 85 | func (music *Music) ArtistTag() string { 86 | panic("not implemented") 87 | return iGetMusicArtistTag(music) 88 | } 89 | 90 | // Mix_GetMusicAlbumTag - Get the album name for a music object. 91 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicAlbumTag) 92 | func (music *Music) AlbumTag() string { 93 | panic("not implemented") 94 | return iGetMusicAlbumTag(music) 95 | } 96 | 97 | // Mix_GetMusicCopyrightTag - Get the copyright text for a music object. 98 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicCopyrightTag) 99 | func (music *Music) CopyrightTag() string { 100 | panic("not implemented") 101 | return iGetMusicCopyrightTag(music) 102 | } 103 | 104 | // Mix_PlayMusic - Play a new music object. 105 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_PlayMusic) 106 | func (music *Music) Play(loops int32) error { 107 | if !iPlayMusic(music, loops) { 108 | return internal.LastErr() 109 | } 110 | 111 | return nil 112 | } 113 | 114 | // Mix_FadeInMusic - Play a new music object, fading in the audio. 115 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_FadeInMusic) 116 | func (music *Music) FadeIn(loops int32, ms int32) bool { 117 | panic("not implemented") 118 | return iFadeInMusic(music, loops, ms) 119 | } 120 | 121 | // Mix_FadeInMusicPos - Play a new music object, fading in the audio, from a starting position. 122 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_FadeInMusicPos) 123 | func (music *Music) FadeInPos(loops int32, ms int32, position float64) bool { 124 | panic("not implemented") 125 | return iFadeInMusicPos(music, loops, ms, position) 126 | } 127 | 128 | // Mix_GetMusicVolume - Query the current volume value for a music object. 129 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicVolume) 130 | func (music *Music) Volume() int32 { 131 | panic("not implemented") 132 | return iGetMusicVolume(music) 133 | } 134 | 135 | // Mix_StartTrack - Start a track in music object. 136 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_StartTrack) 137 | func (music *Music) StartTrack(track int32) bool { 138 | panic("not implemented") 139 | return iStartTrack(music, track) 140 | } 141 | 142 | // Mix_GetNumTracks - Get number of tracks in music object. 143 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetNumTracks) 144 | func (music *Music) NumTracks() int32 { 145 | panic("not implemented") 146 | return iGetNumTracks(music) 147 | } 148 | 149 | // Mix_GetMusicPosition - Get the time current position of music stream, in seconds. 150 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicPosition) 151 | func (music *Music) Position() float64 { 152 | panic("not implemented") 153 | return iGetMusicPosition(music) 154 | } 155 | 156 | // Mix_MusicDuration - Get a music object's duration, in seconds. 157 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_MusicDuration) 158 | func (music *Music) Duration() float64 { 159 | panic("not implemented") 160 | return iMusicDuration(music) 161 | } 162 | 163 | // Mix_GetMusicLoopStartTime - Get the loop start time position of music stream, in seconds. 164 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicLoopStartTime) 165 | func (music *Music) LoopStartTime() float64 { 166 | panic("not implemented") 167 | return iGetMusicLoopStartTime(music) 168 | } 169 | 170 | // Mix_GetMusicLoopEndTime - Get the loop end time position of music stream, in seconds. 171 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicLoopEndTime) 172 | func (music *Music) LoopEndTime() float64 { 173 | panic("not implemented") 174 | return iGetMusicLoopEndTime(music) 175 | } 176 | 177 | // Mix_GetMusicLoopLengthTime - Get the loop time length of music stream, in seconds. 178 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_GetMusicLoopLengthTime) 179 | func (music *Music) LoopLengthTime() float64 { 180 | panic("not implemented") 181 | return iGetMusicLoopLengthTime(music) 182 | } 183 | 184 | // MixCallback 185 | 186 | // Mix_SetPostMix - Set a function that is called after all mixing is performed. 187 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_SetPostMix) 188 | func (mix_func MixCallback) SetPostMix(arg uintptr) { 189 | panic("not implemented") 190 | iSetPostMix(mix_func, arg) 191 | } 192 | 193 | // Mix_HookMusic - Add your own music player or additional mixer function. 194 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_HookMusic) 195 | func (mix_func MixCallback) HookMusic(arg uintptr) { 196 | panic("not implemented") 197 | iHookMusic(mix_func, arg) 198 | } 199 | -------------------------------------------------------------------------------- /mixer/mixer_enums.gen.go: -------------------------------------------------------------------------------- 1 | package mixer 2 | 3 | // Code generated by cmd/ffi2go. DO NOT EDIT. 4 | 5 | // Mix_Fading - The different fading types supported 6 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_Fading) 7 | type Fading uint32 8 | 9 | const ( 10 | MIX_NO_FADING Fading = 0 11 | MIX_FADING_OUT Fading = 1 12 | MIX_FADING_IN Fading = 2 13 | ) 14 | 15 | // Mix_MusicType - These are types of music files (not libraries used to load them) 16 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_MusicType) 17 | type MusicType uint32 18 | 19 | const ( 20 | MUS_NONE MusicType = 0 21 | MUS_WAV MusicType = 1 22 | MUS_MOD MusicType = 2 23 | MUS_MID MusicType = 3 24 | MUS_OGG MusicType = 4 25 | MUS_MP3 MusicType = 5 26 | MUS_FLAC MusicType = 6 27 | MUS_OPUS MusicType = 7 28 | MUS_WAVPACK MusicType = 8 29 | MUS_GME MusicType = 9 30 | ) 31 | -------------------------------------------------------------------------------- /mixer/mixer_structs.gen.go: -------------------------------------------------------------------------------- 1 | package mixer 2 | 3 | // Code generated by cmd/ffi2go. DO NOT EDIT. 4 | 5 | // Mix_Music - The internal format for a music chunk interpreted via codecs 6 | // (https://wiki.libsdl.org/SDL3_mixer/Mix_Music) 7 | type Music struct{} 8 | -------------------------------------------------------------------------------- /mixer/mixer_types.gen.go: -------------------------------------------------------------------------------- 1 | package mixer 2 | 3 | // Code generated by cmd/ffi2go. DO NOT EDIT. 4 | -------------------------------------------------------------------------------- /sdl/glue_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package sdl 4 | 5 | import ( 6 | "syscall/js" 7 | 8 | "github.com/Zyko0/go-sdl3/internal" 9 | ) 10 | 11 | func (s *Surface) Pixels() []byte { 12 | return internal.GetByteSliceFromJSPtr(js.ValueOf(s.pixels), int(s.H*s.Pitch)) 13 | } 14 | -------------------------------------------------------------------------------- /sdl/glue_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package sdl 4 | 5 | import "github.com/Zyko0/go-sdl3/internal" 6 | 7 | func (s *Surface) Pixels() []byte { 8 | return internal.PtrToSlice[byte](uintptr(s.pixels), int(s.H*s.Pitch)) 9 | } 10 | -------------------------------------------------------------------------------- /sdl/init_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package sdl 4 | 5 | import ( 6 | "errors" 7 | "syscall/js" 8 | 9 | "github.com/Zyko0/go-sdl3/internal" 10 | ) 11 | 12 | // We can just initialize everything here in js/wasm env 13 | func init() { 14 | initialize() 15 | 16 | // Set free, error functions 17 | internal.SetSDLFreeFunc(func(mem uintptr) { 18 | ifree(mem) 19 | internal.DeleteJSPointer(mem) 20 | }) 21 | internal.SetSDLLastErrFunc(func() error { 22 | if msg := iGetError(); msg != "" { 23 | return errors.New(msg) 24 | } 25 | return nil 26 | }) 27 | } 28 | 29 | // Path returns an empty string in js/wasm environment. 30 | func Path() string { 31 | return "" 32 | } 33 | 34 | // LoadLibrary does nothing in js/wasm environment. 35 | func LoadLibrary(path string) error { 36 | return nil 37 | } 38 | 39 | // CloseLibrary does nothing in js/wasm environment. 40 | func CloseLibrary() error { 41 | return nil 42 | } 43 | 44 | func RunLoop(updateFunc func() error) error { 45 | ch := make(chan error) 46 | fn := js.FuncOf(func(this js.Value, args []js.Value) any { 47 | if err := updateFunc(); err != nil { 48 | js.Global().Call("_emscripten_cancel_main_loop") 49 | ch <- err 50 | } 51 | 52 | return nil 53 | }) 54 | fnAddr := js.Global().Get("Module").Call("addFunction", fn, "v") 55 | js.Global().Call("_emscripten_set_main_loop", fnAddr, -1, 0) 56 | 57 | err := <-ch 58 | 59 | if errors.Is(err, EndLoop) { 60 | return nil 61 | } 62 | return err 63 | } 64 | -------------------------------------------------------------------------------- /sdl/init_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package sdl 4 | 5 | import ( 6 | "errors" 7 | "runtime" 8 | 9 | "github.com/Zyko0/go-sdl3/internal" 10 | puregogen "github.com/Zyko0/purego-gen" 11 | ) 12 | 13 | func init() { 14 | runtime.LockOSThread() 15 | } 16 | 17 | // Path returns the library installation path based on the operating 18 | // system 19 | func Path() string { 20 | switch runtime.GOOS { 21 | case "windows": 22 | return "SDL3.dll" 23 | case "linux", "freebsd": 24 | return "libSDL3.so.0" 25 | case "darwin": 26 | return "libSDL3.dylib" 27 | default: 28 | return "" 29 | } 30 | } 31 | 32 | // LoadLibrary loads SDL library and initializes all functions. 33 | func LoadLibrary(path string) error { 34 | var err error 35 | 36 | _hnd_sdl, err = puregogen.OpenLibrary(path) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | initialize() 42 | initialize_ex() 43 | 44 | // Set free, error functions 45 | internal.SetSDLFreeFunc(ifree) 46 | internal.SetSDLLastErrFunc(func() error { 47 | if msg := iGetError(); msg != "" { 48 | return errors.New(msg) 49 | } 50 | return nil 51 | }) 52 | 53 | return nil 54 | } 55 | 56 | // CloseLibrary releases resources associated with the library. 57 | func CloseLibrary() error { 58 | return puregogen.CloseLibrary(_hnd_sdl) 59 | } 60 | 61 | func RunLoop(updateFunc func() error) error { 62 | for { 63 | if err := updateFunc(); err != nil { 64 | if errors.Is(err, EndLoop) { 65 | return nil 66 | } 67 | return err 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /sdl/sdl_functions_ex.go: -------------------------------------------------------------------------------- 1 | package sdl 2 | 3 | var ( 4 | //puregogen:library path:windows=sdl.dll path:unix=sdl.so alias=sdl 5 | //puregogen:function symbol=SDL_ShowMessageBox 6 | iShowMessageBox func(data *messageBoxData, buttonid *int32) bool 7 | 8 | //puregogen:function symbol=SDL_CreateGPUShader 9 | iCreateGPUShader func(device *GPUDevice, createinfo *gpuShaderCreateInfo) *GPUShader 10 | 11 | //puregogen:function symbol=SDL_CreateGPUComputePipeline 12 | iCreateGPUComputePipeline func(device *GPUDevice, createinfo *gpuComputePipelineCreateInfo) *GPUComputePipeline 13 | ) 14 | -------------------------------------------------------------------------------- /sdl/sdl_functions_ex_impl.go: -------------------------------------------------------------------------------- 1 | //go:build windows || unix 2 | 3 | package sdl 4 | 5 | import ( 6 | puregogen "github.com/Zyko0/purego-gen" 7 | purego "github.com/ebitengine/purego" 8 | "runtime" 9 | "unsafe" 10 | ) 11 | 12 | // File generated by github.com/Zyko0/purego-gen. DO NOT EDIT. 13 | 14 | var ( 15 | // Symbols 16 | // sdl 17 | _addr_SDL_ShowMessageBox uintptr 18 | _addr_SDL_CreateGPUShader uintptr 19 | _addr_SDL_CreateGPUComputePipeline uintptr 20 | ) 21 | 22 | func initialize_ex() { 23 | var err error 24 | 25 | // Symbols sdl 26 | _addr_SDL_ShowMessageBox, err = puregogen.OpenSymbol(_hnd_sdl, "SDL_ShowMessageBox") 27 | if err != nil { 28 | panic("cannot puregogen.OpenSymbol: SDL_ShowMessageBox") 29 | } 30 | _addr_SDL_CreateGPUShader, err = puregogen.OpenSymbol(_hnd_sdl, "SDL_CreateGPUShader") 31 | if err != nil { 32 | panic("cannot puregogen.OpenSymbol: SDL_CreateGPUShader") 33 | } 34 | _addr_SDL_CreateGPUComputePipeline, err = puregogen.OpenSymbol(_hnd_sdl, "SDL_CreateGPUComputePipeline") 35 | if err != nil { 36 | panic("cannot puregogen.OpenSymbol: SDL_CreateGPUComputePipeline") 37 | } 38 | 39 | iShowMessageBox = func(data *messageBoxData, buttonid *int32) bool { 40 | _r0, _, _ := purego.SyscallN(_addr_SDL_ShowMessageBox, uintptr(unsafe.Pointer(data)), uintptr(unsafe.Pointer(buttonid))) 41 | __r0 := _r0 != 0 42 | runtime.KeepAlive(data) 43 | runtime.KeepAlive(buttonid) 44 | return __r0 45 | } 46 | iCreateGPUShader = func(device *GPUDevice, createinfo *gpuShaderCreateInfo) *GPUShader { 47 | _r0, _, _ := purego.SyscallN(_addr_SDL_CreateGPUShader, uintptr(unsafe.Pointer(device)), uintptr(unsafe.Pointer(createinfo))) 48 | __r0 := (*GPUShader)(*(*unsafe.Pointer)(unsafe.Pointer(&_r0))) 49 | runtime.KeepAlive(device) 50 | runtime.KeepAlive(createinfo) 51 | return __r0 52 | } 53 | iCreateGPUComputePipeline = func(device *GPUDevice, createinfo *gpuComputePipelineCreateInfo) *GPUComputePipeline { 54 | _r0, _, _ := purego.SyscallN(_addr_SDL_CreateGPUComputePipeline, uintptr(unsafe.Pointer(device)), uintptr(unsafe.Pointer(createinfo))) 55 | __r0 := (*GPUComputePipeline)(*(*unsafe.Pointer)(unsafe.Pointer(&_r0))) 56 | runtime.KeepAlive(device) 57 | runtime.KeepAlive(createinfo) 58 | return __r0 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /sdl/sdl_types.gen.go: -------------------------------------------------------------------------------- 1 | package sdl 2 | 3 | // Code generated by cmd/ffi2go. DO NOT EDIT. 4 | 5 | // SDL_SpinLock - An atomic spinlock. 6 | // (https://wiki.libsdl.org/SDL3/SDL_SpinLock) 7 | type SpinLock int32 8 | 9 | // SDL_PropertiesID - SDL properties ID 10 | // (https://wiki.libsdl.org/SDL3/SDL_PropertiesID) 11 | type PropertiesID uint32 12 | 13 | // SDL_ThreadID - A unique numeric ID that identifies a thread. 14 | // (https://wiki.libsdl.org/SDL3/SDL_ThreadID) 15 | type ThreadID uint64 16 | 17 | // SDL_TLSID - Thread local storage ID. 18 | // (https://wiki.libsdl.org/SDL3/SDL_TLSID) 19 | type TLSID AtomicInt 20 | 21 | // SDL_AudioDeviceID - SDL Audio Device instance IDs. 22 | // (https://wiki.libsdl.org/SDL3/SDL_AudioDeviceID) 23 | type AudioDeviceID uint32 24 | 25 | // SDL_BlendMode - A set of blend modes used in drawing operations. 26 | // (https://wiki.libsdl.org/SDL3/SDL_BlendMode) 27 | type BlendMode uint32 28 | 29 | // SDL_SurfaceFlags - The flags on an [SDL_Surface](SDL_Surface). 30 | // (https://wiki.libsdl.org/SDL3/SDL_SurfaceFlags) 31 | type SurfaceFlags uint32 32 | 33 | // SDL_CameraID - This is a unique ID for a camera device for the time it is connected to the system, and is never reused for the lifetime of the application. 34 | // (https://wiki.libsdl.org/SDL3/SDL_CameraID) 35 | type CameraID uint32 36 | 37 | // SDL_DisplayID - This is a unique ID for a display for the time it is connected to the system, and is never reused for the lifetime of the application. 38 | // (https://wiki.libsdl.org/SDL3/SDL_DisplayID) 39 | type DisplayID uint32 40 | 41 | // SDL_WindowID - This is a unique ID for a window. 42 | // (https://wiki.libsdl.org/SDL3/SDL_WindowID) 43 | type WindowID uint32 44 | 45 | // SDL_WindowFlags - The flags on a window. 46 | // (https://wiki.libsdl.org/SDL3/SDL_WindowFlags) 47 | type WindowFlags uint64 48 | 49 | // SDL_GLContext - An opaque handle to an OpenGL context. 50 | // (https://wiki.libsdl.org/SDL3/SDL_GLContext) 51 | type GLContext *GLContextState 52 | 53 | // SDL_EGLDisplay - Opaque type for an EGL display. 54 | // (https://wiki.libsdl.org/SDL3/SDL_EGLDisplay) 55 | type EGLDisplay uintptr 56 | 57 | // SDL_EGLConfig - Opaque type for an EGL config. 58 | // (https://wiki.libsdl.org/SDL3/SDL_EGLConfig) 59 | type EGLConfig uintptr 60 | 61 | // SDL_EGLSurface - Opaque type for an EGL surface. 62 | // (https://wiki.libsdl.org/SDL3/SDL_EGLSurface) 63 | type EGLSurface uintptr 64 | 65 | // SDL_SensorID - This is a unique ID for a sensor for the time it is connected to the system, and is never reused for the lifetime of the application. 66 | // (https://wiki.libsdl.org/SDL3/SDL_SensorID) 67 | type SensorID uint32 68 | 69 | // SDL_JoystickID - This is a unique ID for a joystick for the time it is connected to the system, and is never reused for the lifetime of the application. 70 | // (https://wiki.libsdl.org/SDL3/SDL_JoystickID) 71 | type JoystickID uint32 72 | 73 | // SDL_Keycode - The SDL virtual key representation. 74 | // (https://wiki.libsdl.org/SDL3/SDL_Keycode) 75 | type Keycode uint32 76 | 77 | // SDL_Keymod - Valid key modifiers (possibly OR'd together). 78 | // (https://wiki.libsdl.org/SDL3/SDL_Keymod) 79 | type Keymod uint16 80 | 81 | // SDL_KeyboardID - This is a unique ID for a keyboard for the time it is connected to the system, and is never reused for the lifetime of the application. 82 | // (https://wiki.libsdl.org/SDL3/SDL_KeyboardID) 83 | type KeyboardID uint32 84 | 85 | // SDL_MouseID - This is a unique ID for a mouse for the time it is connected to the system, and is never reused for the lifetime of the application. 86 | // (https://wiki.libsdl.org/SDL3/SDL_MouseID) 87 | type MouseID uint32 88 | 89 | // SDL_MouseButtonFlags - A bitmask of pressed mouse buttons, as reported by [SDL_GetMouseState](SDL_GetMouseState), etc. 90 | // (https://wiki.libsdl.org/SDL3/SDL_MouseButtonFlags) 91 | type MouseButtonFlags uint32 92 | 93 | // SDL_TouchID - A unique ID for a touch device. 94 | // (https://wiki.libsdl.org/SDL3/SDL_TouchID) 95 | type TouchID uint64 96 | 97 | // SDL_FingerID - A unique ID for a single finger on a touch device. 98 | // (https://wiki.libsdl.org/SDL3/SDL_FingerID) 99 | type FingerID uint64 100 | 101 | // SDL_PenID - SDL pen instance IDs. 102 | // (https://wiki.libsdl.org/SDL3/SDL_PenID) 103 | type PenID uint32 104 | 105 | // SDL_PenInputFlags - Pen input flags, as reported by various pen events' `pen_state` field. 106 | // (https://wiki.libsdl.org/SDL3/SDL_PenInputFlags) 107 | type PenInputFlags uint32 108 | 109 | // SDL_GlobFlags - Flags for path matching. 110 | // (https://wiki.libsdl.org/SDL3/SDL_GlobFlags) 111 | type GlobFlags uint32 112 | 113 | // SDL_GPUTextureUsageFlags - Specifies how a texture is intended to be used by the client. 114 | // (https://wiki.libsdl.org/SDL3/SDL_GPUTextureUsageFlags) 115 | type GPUTextureUsageFlags uint32 116 | 117 | // SDL_GPUBufferUsageFlags - Specifies how a buffer is intended to be used by the client. 118 | // (https://wiki.libsdl.org/SDL3/SDL_GPUBufferUsageFlags) 119 | type GPUBufferUsageFlags uint32 120 | 121 | // SDL_GPUShaderFormat - Specifies the format of shader code. 122 | // (https://wiki.libsdl.org/SDL3/SDL_GPUShaderFormat) 123 | type GPUShaderFormat uint32 124 | 125 | // SDL_GPUColorComponentFlags - Specifies which color components are written in a graphics pipeline. 126 | // (https://wiki.libsdl.org/SDL3/SDL_GPUColorComponentFlags) 127 | type GPUColorComponentFlags uint8 128 | 129 | // SDL_HapticID - This is a unique ID for a haptic device for the time it is connected to the system, and is never reused for the lifetime of the application. 130 | // (https://wiki.libsdl.org/SDL3/SDL_HapticID) 131 | type HapticID uint32 132 | 133 | // SDL_InitFlags - Initialization flags for [SDL_Init](SDL_Init) and/or [SDL_InitSubSystem](SDL_InitSubSystem) 134 | // (https://wiki.libsdl.org/SDL3/SDL_InitFlags) 135 | type InitFlags uint32 136 | 137 | // SDL_MessageBoxFlags - Message box flags. 138 | // (https://wiki.libsdl.org/SDL3/SDL_MessageBoxFlags) 139 | type MessageBoxFlags uint32 140 | 141 | // SDL_MessageBoxButtonFlags - [SDL_MessageBoxButtonData](SDL_MessageBoxButtonData) flags. 142 | // (https://wiki.libsdl.org/SDL3/SDL_MessageBoxButtonFlags) 143 | type MessageBoxButtonFlags uint32 144 | 145 | // SDL_MetalView - A handle to a CAMetalLayer-backed NSView (macOS) or UIView (iOS/tvOS). 146 | // (https://wiki.libsdl.org/SDL3/SDL_MetalView) 147 | type MetalView uintptr 148 | 149 | // SDL_TimerID - Definition of the timer ID type. 150 | // (https://wiki.libsdl.org/SDL3/SDL_TimerID) 151 | type TimerID uint32 152 | -------------------------------------------------------------------------------- /ttf/functions.go: -------------------------------------------------------------------------------- 1 | package ttf 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "github.com/Zyko0/go-sdl3/internal" 7 | "github.com/Zyko0/go-sdl3/sdl" 8 | ) 9 | 10 | // TTF_Version - This function gets the version of the dynamically linked SDL_ttf library. 11 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_Version) 12 | func Version() int32 { 13 | return iVersion() 14 | } 15 | 16 | // TTF_GetFreeTypeVersion - Query the version of the FreeType library in use. 17 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_GetFreeTypeVersion) 18 | func FreeTypeVersion() LibraryVersion { 19 | var version LibraryVersion 20 | 21 | iGetFreeTypeVersion(&version.Major, &version.Minor, &version.Patch) 22 | 23 | return version 24 | } 25 | 26 | // TTF_GetHarfBuzzVersion - Query the version of the HarfBuzz library in use. 27 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_GetHarfBuzzVersion) 28 | func HarfBuzzVersion() LibraryVersion { 29 | var version LibraryVersion 30 | 31 | iGetHarfBuzzVersion(&version.Major, &version.Minor, &version.Patch) 32 | 33 | return version 34 | } 35 | 36 | // TTF_Init - Initialize SDL_ttf. 37 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_Init) 38 | func Init() error { 39 | if !iInit() { 40 | return internal.LastErr() 41 | } 42 | 43 | return nil 44 | } 45 | 46 | // TTF_OpenFont - Create a font from a file, using a specified point size. 47 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_OpenFont) 48 | func OpenFont(file string, ptSize float32) (*Font, error) { 49 | font := iOpenFont(file, ptSize) 50 | if font == nil { 51 | return nil, internal.LastErr() 52 | } 53 | 54 | return font, nil 55 | } 56 | 57 | // TTF_OpenFontIO - Create a font from an SDL_IOStream, using a specified point size. 58 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_OpenFontIO) 59 | func OpenFontIO(src *sdl.IOStream, closeio bool, ptSize float32) (*Font, error) { 60 | font := iOpenFontIO(src, closeio, ptSize) 61 | if font == nil { 62 | return nil, internal.LastErr() 63 | } 64 | 65 | return font, nil 66 | } 67 | 68 | // TTF_OpenFontWithProperties - Create a font with the specified properties. 69 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_OpenFontWithProperties) 70 | func OpenFontWithProperties(props sdl.PropertiesID) (*Font, error) { 71 | font := iOpenFontWithProperties(props) 72 | if font == nil { 73 | return nil, internal.LastErr() 74 | } 75 | 76 | return font, nil 77 | } 78 | 79 | // TTF_StringToTag - Convert from a 4 character string to a 32-bit tag. 80 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_StringToTag) 81 | func StringToTag(str string) uint32 { 82 | return iStringToTag(str) 83 | } 84 | 85 | // TTF_TagToString - Convert from a 32-bit tag to a 4 character string. 86 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_TagToString) 87 | func TagToString(tag uint32, size uintptr) string { 88 | // https://wiki.libsdl.org/SDL3_ttf/TTF_TagToString 89 | // Size should be at least 4 90 | if size < 4 { 91 | return "" 92 | } 93 | str := make([]byte, size+1) 94 | iTagToString(tag, unsafe.SliceData(str), size) 95 | 96 | return string(str) 97 | } 98 | 99 | // TTF_GetGlyphScript - Get the script used by a 32-bit codepoint. 100 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_GetGlyphScript) 101 | func GetGlyphScript(ch rune) (uint32, error) { 102 | script := iGetGlyphScript(uint32(ch)) 103 | if script == 0 { 104 | return 0, internal.LastErr() 105 | } 106 | 107 | return script, nil 108 | } 109 | 110 | // TTF_CreateSurfaceTextEngine - Create a text engine for drawing text on SDL surfaces. 111 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_CreateSurfaceTextEngine) 112 | func CreateSurfaceTextEngine() (*TextEngine, error) { 113 | engine := iCreateSurfaceTextEngine() 114 | if engine == nil { 115 | return nil, internal.LastErr() 116 | } 117 | 118 | return engine, nil 119 | } 120 | 121 | // TTF_CreateRendererTextEngine - Create a text engine for drawing text on an SDL renderer. 122 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_CreateRendererTextEngine) 123 | func CreateRendererTextEngine(renderer *sdl.Renderer) (*TextEngine, error) { 124 | engine := iCreateRendererTextEngine(renderer) 125 | if engine == nil { 126 | return nil, internal.LastErr() 127 | } 128 | 129 | return engine, nil 130 | } 131 | 132 | // TTF_CreateRendererTextEngineWithProperties - Create a text engine for drawing text on an SDL renderer, with the specified properties. 133 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_CreateRendererTextEngineWithProperties) 134 | func CreateRendererTextEngineWithProperties(props sdl.PropertiesID) (*TextEngine, error) { 135 | engine := iCreateRendererTextEngineWithProperties(props) 136 | if engine == nil { 137 | return nil, internal.LastErr() 138 | } 139 | 140 | return engine, nil 141 | } 142 | 143 | // TTF_Quit - Deinitialize SDL_ttf. 144 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_Quit) 145 | func Quit() { 146 | iQuit() 147 | } 148 | 149 | // TTF_WasInit - Check if SDL_ttf is initialized. 150 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_WasInit) 151 | func WasInit() int32 { 152 | return iWasInit() 153 | } 154 | -------------------------------------------------------------------------------- /ttf/glue.go: -------------------------------------------------------------------------------- 1 | package ttf 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "github.com/Zyko0/go-sdl3/internal" 7 | "github.com/Zyko0/go-sdl3/sdl" 8 | ) 9 | 10 | // Utils 11 | 12 | func colorToUint32(clr sdl.Color) uint32 { 13 | return *(*uint32)(unsafe.Pointer(&clr)) 14 | } 15 | 16 | // Types 17 | 18 | type Pointer = internal.Pointer 19 | 20 | // union type 21 | type DrawOperation struct { 22 | Cmd DrawCommand 23 | data [60]byte 24 | } 25 | 26 | func (d *DrawOperation) CopyOperation() *CopyOperation { 27 | return (*CopyOperation)(unsafe.Pointer(d)) 28 | } 29 | 30 | func (d *DrawOperation) FillOperation() *FillOperation { 31 | return (*FillOperation)(unsafe.Pointer(d)) 32 | } 33 | 34 | // TTF_TextEngine - A text engine used to create text objects. 35 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_TextEngine) 36 | type TextEngine struct { 37 | Version uint32 38 | Userdata Pointer 39 | CreateTextFunc Pointer 40 | DestroyTextFunc Pointer 41 | } 42 | 43 | // TTF_TextData - Internal data for [TTF_Text](TTF_Text) 44 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_TextData) 45 | type TextData struct { 46 | Font *Font 47 | Color sdl.FColor 48 | NeedsLayoutUpdate bool 49 | Layout *TextLayout 50 | X int32 51 | Y int32 52 | W int32 53 | H int32 54 | NumOps int32 55 | Ops *DrawOperation 56 | NumClusters int32 57 | Clusters *SubString 58 | Props sdl.PropertiesID 59 | NeedsEngineUpdate bool 60 | Engine *TextEngine 61 | EngineText Pointer 62 | } 63 | 64 | // Custom 65 | 66 | type GlyphMetrics struct { 67 | MinX int32 68 | MaxX int32 69 | MinY int32 70 | MaxY int32 71 | Advance int32 72 | } 73 | 74 | type LibraryVersion struct { 75 | Minor int32 76 | Major int32 77 | Patch int32 78 | } 79 | -------------------------------------------------------------------------------- /ttf/init_js.go: -------------------------------------------------------------------------------- 1 | //go:build js 2 | 3 | package ttf 4 | 5 | // We can just initialize everything here in js/wasm env 6 | func init() { 7 | initialize() 8 | } 9 | 10 | // Path returns an empty string in js/wasm environment. 11 | func Path() string { 12 | return "" 13 | } 14 | 15 | // LoadLibrary does nothing in js/wasm environment. 16 | func LoadLibrary(path string) error { 17 | return nil 18 | } 19 | 20 | // CloseLibrary does nothing in js/wasm environment. 21 | func CloseLibrary() error { 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /ttf/init_notjs.go: -------------------------------------------------------------------------------- 1 | //go:build !js 2 | 3 | package ttf 4 | 5 | import ( 6 | "runtime" 7 | 8 | puregogen "github.com/Zyko0/purego-gen" 9 | ) 10 | 11 | // Path returns the library installation path based on the operating 12 | // system 13 | func Path() string { 14 | switch runtime.GOOS { 15 | case "windows": 16 | return "SDL3_ttf.dll" 17 | case "linux", "freebsd": 18 | return "libSDL3_ttf.so.0" 19 | case "darwin": 20 | return "libSDL3_ttf.dylib" 21 | default: 22 | return "" 23 | } 24 | } 25 | 26 | // LoadLibrary loads SDL_ttf library and initializes all functions. 27 | func LoadLibrary(path string) error { 28 | var err error 29 | 30 | runtime.LockOSThread() 31 | 32 | _hnd_ttf, err = puregogen.OpenLibrary(path) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | initialize() 38 | initialize_ex() 39 | 40 | return nil 41 | } 42 | 43 | // CloseLibrary releases resources associated with the library. 44 | func CloseLibrary() error { 45 | return puregogen.CloseLibrary(_hnd_ttf) 46 | } 47 | -------------------------------------------------------------------------------- /ttf/macros.go: -------------------------------------------------------------------------------- 1 | package ttf 2 | 3 | const ( 4 | PROP_FONT_CREATE_FILENAME_STRING = "SDL_ttf.font.create.filename" 5 | PROP_FONT_CREATE_IOSTREAM_POINTER = "SDL_ttf.font.create.iostream" 6 | PROP_FONT_CREATE_IOSTREAM_OFFSET_NUMBER = "SDL_ttf.font.create.iostream.offset" 7 | PROP_FONT_CREATE_IOSTREAM_AUTOCLOSE_BOOLEAN = "SDL_ttf.font.create.iostream.autoclose" 8 | PROP_FONT_CREATE_SIZE_FLOAT = "SDL_ttf.font.create.size" 9 | PROP_FONT_CREATE_FACE_NUMBER = "SDL_ttf.font.create.face" 10 | PROP_FONT_CREATE_HORIZONTAL_DPI_NUMBER = "SDL_ttf.font.create.hdpi" 11 | PROP_FONT_CREATE_VERTICAL_DPI_NUMBER = "SDL_ttf.font.create.vdpi" 12 | PROP_FONT_CREATE_EXISTING_FONT = "SDL_ttf.font.create.existing_font" 13 | ) 14 | 15 | const ( 16 | PROP_RENDERER_TEXT_ENGINE_RENDERER = "SDL_ttf.renderer_text_engine.create.renderer" 17 | PROP_RENDERER_TEXT_ENGINE_ATLAS_TEXTURE_SIZE = "SDL_ttf.renderer_text_engine.create.atlas_texture_size" 18 | ) 19 | -------------------------------------------------------------------------------- /ttf/ttf_enums.gen.go: -------------------------------------------------------------------------------- 1 | package ttf 2 | 3 | // Code generated by cmd/ffi2go. DO NOT EDIT. 4 | 5 | // TTF_HintingFlags - Hinting flags for TTF (TrueType Fonts) 6 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_HintingFlags) 7 | type HintingFlags uint32 8 | 9 | const ( 10 | HINTING_NORMAL HintingFlags = 0 11 | HINTING_LIGHT HintingFlags = 1 12 | HINTING_MONO HintingFlags = 2 13 | HINTING_NONE HintingFlags = 3 14 | HINTING_LIGHT_SUBPIXEL HintingFlags = 4 15 | ) 16 | 17 | // TTF_HorizontalAlignment - The horizontal alignment used when rendering wrapped text. 18 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_HorizontalAlignment) 19 | type HorizontalAlignment uint32 20 | 21 | const ( 22 | HORIZONTAL_ALIGN_INVALID HorizontalAlignment = 4294967295 23 | HORIZONTAL_ALIGN_LEFT HorizontalAlignment = 0 24 | HORIZONTAL_ALIGN_CENTER HorizontalAlignment = 1 25 | HORIZONTAL_ALIGN_RIGHT HorizontalAlignment = 2 26 | ) 27 | 28 | // TTF_Direction - Direction flags 29 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_Direction) 30 | type Direction uint32 31 | 32 | const ( 33 | DIRECTION_INVALID Direction = 0 34 | DIRECTION_LTR Direction = 4 35 | DIRECTION_RTL Direction = 5 36 | DIRECTION_TTB Direction = 6 37 | DIRECTION_BTT Direction = 7 38 | ) 39 | 40 | // TTF_ImageType - The type of data in a glyph image 41 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_ImageType) 42 | type ImageType uint32 43 | 44 | const ( 45 | IMAGE_INVALID ImageType = 0 46 | IMAGE_ALPHA ImageType = 1 47 | IMAGE_COLOR ImageType = 2 48 | IMAGE_SDF ImageType = 3 49 | ) 50 | 51 | // TTF_GPUTextEngineWinding - The winding order of the vertices returned by [TTF_GetGPUTextDrawData](TTF_GetGPUTextDrawData) 52 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_GPUTextEngineWinding) 53 | type GPUTextEngineWinding uint32 54 | 55 | const ( 56 | GPU_TEXTENGINE_WINDING_INVALID GPUTextEngineWinding = 4294967295 57 | GPU_TEXTENGINE_WINDING_CLOCKWISE GPUTextEngineWinding = 0 58 | GPU_TEXTENGINE_WINDING_COUNTER_CLOCKWISE GPUTextEngineWinding = 1 59 | ) 60 | 61 | type DrawCommand uint32 62 | 63 | const ( 64 | DRAW_COMMAND_NOOP DrawCommand = 0 65 | DRAW_COMMAND_FILL DrawCommand = 1 66 | DRAW_COMMAND_COPY DrawCommand = 2 67 | ) 68 | -------------------------------------------------------------------------------- /ttf/ttf_functions_ex.go: -------------------------------------------------------------------------------- 1 | package ttf 2 | 3 | import "github.com/Zyko0/go-sdl3/sdl" 4 | 5 | var ( 6 | //puregogen:library path:windows=ttf.dll path:unix=ttf.so alias=ttf 7 | //puregogen:function symbol=TTF_TagToString 8 | iTagToString func(tag uint32, str *byte, size uintptr) 9 | 10 | //puregogen:function symbol=TTF_RenderText_Solid 11 | iRenderText_Solid func(font *Font, str string, length uintptr, fg uint32) *sdl.Surface 12 | 13 | //puregogen:function symbol=TTF_RenderText_Solid_Wrapped 14 | iRenderText_Solid_Wrapped func(font *Font, str string, length uintptr, fg uint32, wrapLength int32) *sdl.Surface 15 | 16 | //puregogen:function symbol=TTF_RenderGlyph_Solid 17 | iRenderGlyph_Solid func(font *Font, ch, fg uint32) *sdl.Surface 18 | 19 | //puregogen:function symbol=TTF_RenderText_Shaded 20 | iRenderText_Shaded func(font *Font, str string, length uintptr, fg, bg uint32) *sdl.Surface 21 | 22 | //puregogen:function symbol=TTF_RenderText_Shaded_Wrapped 23 | iRenderText_Shaded_Wrapped func(font *Font, str string, length uintptr, fg, bg uint32, wrapWidth int32) *sdl.Surface 24 | 25 | //puregogen:function symbol=TTF_RenderGlyph_Shaded 26 | iRenderGlyph_Shaded func(font *Font, ch uint32, fg, bg uint32) *sdl.Surface 27 | 28 | //puregogen:function symbol=TTF_RenderText_Blended 29 | iRenderText_Blended func(font *Font, str string, length uintptr, fg uint32) *sdl.Surface 30 | 31 | //puregogen:function symbol=TTF_RenderText_Blended_Wrapped 32 | iRenderText_Blended_Wrapped func(font *Font, str string, length uintptr, fg uint32, wrapWidth int32) *sdl.Surface 33 | 34 | //puregogen:function symbol=TTF_RenderGlyph_Blended 35 | iRenderGlyph_Blended func(font *Font, ch, fg uint32) *sdl.Surface 36 | 37 | //puregogen:function symbol=TTF_RenderText_LCD 38 | iRenderText_LCD func(font *Font, str string, length uintptr, fg, bg uint32) *sdl.Surface 39 | 40 | //puregogen:function symbol=TTF_RenderText_LCD_Wrapped 41 | iRenderText_LCD_Wrapped func(font *Font, str string, length uintptr, fg, bg uint32, wrapWidth int32) *sdl.Surface 42 | 43 | //puregogen:function symbol=TTF_RenderGlyph_LCD 44 | iRenderGlyph_LCD func(font *Font, ch, fg, bg uint32) *sdl.Surface 45 | ) 46 | -------------------------------------------------------------------------------- /ttf/ttf_structs.gen.go: -------------------------------------------------------------------------------- 1 | package ttf 2 | 3 | import sdl "github.com/Zyko0/go-sdl3/sdl" 4 | 5 | // Code generated by cmd/ffi2go. DO NOT EDIT. 6 | 7 | // TTF_Font - The internal structure containing font information. 8 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_Font) 9 | type Font struct{} 10 | 11 | // TTF_Text - Text created with [TTF_CreateText](TTF_CreateText)() 12 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_Text) 13 | type Text struct { 14 | Text string 15 | NumLines int32 16 | Refcount int32 17 | Internal *TextData 18 | } 19 | 20 | // TTF_GPUAtlasDrawSequence - Draw sequence returned by [TTF_GetGPUTextDrawData](TTF_GetGPUTextDrawData) 21 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_GPUAtlasDrawSequence) 22 | type GPUAtlasDrawSequence struct { 23 | AtlasTexture *sdl.GPUTexture 24 | Xy *sdl.FPoint 25 | Uv *sdl.FPoint 26 | NumVertices int32 27 | Indices *int32 28 | NumIndices int32 29 | ImageType ImageType 30 | Next *GPUAtlasDrawSequence 31 | } 32 | 33 | // TTF_SubString - The representation of a substring within text. 34 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_SubString) 35 | type SubString struct { 36 | Flags SubStringFlags 37 | Offset int32 38 | Length int32 39 | LineIndex int32 40 | ClusterIndex int32 41 | Rect sdl.Rect 42 | } 43 | 44 | type FillOperation struct { 45 | Cmd DrawCommand 46 | Rect sdl.Rect 47 | } 48 | 49 | type CopyOperation struct { 50 | Cmd DrawCommand 51 | TextOffset int32 52 | GlyphFont *Font 53 | GlyphIndex uint32 54 | Src sdl.Rect 55 | Dst sdl.Rect 56 | Reserved Pointer 57 | } 58 | 59 | type TextLayout struct{} 60 | -------------------------------------------------------------------------------- /ttf/ttf_types.gen.go: -------------------------------------------------------------------------------- 1 | package ttf 2 | 3 | // Code generated by cmd/ffi2go. DO NOT EDIT. 4 | 5 | // TTF_FontStyleFlags - Font style flags for [TTF_Font](TTF_Font) 6 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_FontStyleFlags) 7 | type FontStyleFlags uint32 8 | 9 | // TTF_SubStringFlags - Flags for [TTF_SubString](TTF_SubString) 10 | // (https://wiki.libsdl.org/SDL3_ttf/TTF_SubStringFlags) 11 | type SubStringFlags uint32 12 | --------------------------------------------------------------------------------