├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── License.txt ├── Readme.md ├── assets ├── Hack.ttf ├── LICENSE-Hack.txt ├── blocks.png ├── jurassicDeno.bmp ├── jurassicDeno.png ├── jurassicDenoOriginal.png └── powerup.wav ├── deno.json ├── docs ├── building-sdl-notes.md └── emscripten-notes.md ├── examples ├── doom-fire │ ├── main.ts │ └── sdlConfig.ts ├── getting-started │ ├── deno.json │ └── main.ts ├── hello-world-async │ ├── main.ts │ └── sdlConfig.ts ├── hello-world │ ├── main.ts │ └── sdlConfig.ts ├── play-wav │ ├── main.ts │ └── sdlConfig.ts ├── renderer │ ├── main.ts │ └── sdlConfig.ts ├── same-game │ ├── fonts.ts │ ├── logic │ │ ├── block.ts │ │ ├── blockColors.ts │ │ ├── board.ts │ │ └── random.ts │ ├── main.ts │ ├── rendering │ │ └── board.ts │ └── sdlConfig.ts └── version │ ├── main.ts │ └── sdlConfig.ts ├── mod.SDL.ts ├── mod.SDL_image.ts ├── mod.SDL_ttf.ts ├── mod.ts ├── scripts └── build.ts ├── shared ├── constants.ts ├── http.ts └── utils.ts ├── src ├── SDL │ ├── _callbacks.ts │ ├── _symbols.ts │ ├── audio.ts │ ├── callbacks.ts │ ├── constants.ts │ ├── enums.ts │ ├── events.ts │ ├── functionMacros.ts │ ├── functions.ts │ ├── pixels.ts │ ├── rw.ts │ └── structs.ts ├── SDL_image │ ├── _callbacks.ts │ ├── _symbols.ts │ ├── callbacks.ts │ ├── enums.ts │ ├── functions.ts │ └── structs.ts ├── SDL_ttf │ ├── _callbacks.ts │ ├── _symbols.ts │ ├── callbacks.ts │ ├── enums.ts │ ├── functions.ts │ └── structs.ts ├── _boxes.test.ts ├── _boxes.ts ├── _constants.ts ├── _init.ts ├── _library.ts ├── _platform.ts ├── _structs.ts ├── _types.ts ├── _utils.ts ├── deno │ ├── _callbacks.ts │ ├── _dataView.ts │ ├── _library.ts │ ├── _platform.ts │ ├── _pointers.ts │ ├── _strings.ts │ └── _structs.ts ├── error.ts ├── events.ts ├── structs.ts └── types.ts └── tools ├── codegen-scraper.ts ├── codegen.ts └── codegen ├── SDL.ts ├── SDL ├── callbacks.ts ├── enums.ts ├── events.ts ├── functionImplementations.ts ├── functions.ts ├── structs.ts └── typedefs.ts ├── SDL_image.ts ├── SDL_image ├── callbacks.ts ├── enums.ts ├── functions.ts └── structs.ts ├── SDL_ttf.ts ├── SDL_ttf ├── callbacks.ts ├── enums.ts ├── functions.ts └── structs.ts ├── generators.ts └── types.ts /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: push 4 | 5 | env: 6 | GITHUB_USER: smack0007 7 | GITHUB_REPO: smack0007/sdl2-ts 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Deno Setup 16 | uses: denoland/setup-deno@v2 17 | with: 18 | deno-version: v2.0.2 19 | 20 | - name: Build 21 | run: | 22 | deno task build:ci 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .lib/ 2 | bin/ 3 | ext/SDL/include/ 4 | tmp/ 5 | deno.lock -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "CodeGen", 6 | "type": "node", 7 | "request": "launch", 8 | "cwd": "${workspaceFolder}", 9 | "env": { "DENO_FLAGS": "--inspect-brk" }, 10 | "runtimeExecutable": "deno", 11 | "runtimeArgs": ["task", "codegen"], 12 | "attachSimplePort": 9229 13 | }, 14 | { 15 | "name": "Doom Fire", 16 | "type": "node", 17 | "request": "launch", 18 | "cwd": "${workspaceFolder}", 19 | "env": { "DENO_FLAGS": "--inspect-brk" }, 20 | "runtimeExecutable": "deno", 21 | "runtimeArgs": ["task", "run:doom-fire"], 22 | "attachSimplePort": 9229 23 | }, 24 | { 25 | "name": "Hello World", 26 | "type": "node", 27 | "request": "launch", 28 | "cwd": "${workspaceFolder}", 29 | "env": { "DENO_FLAGS": "--inspect-brk" }, 30 | "runtimeExecutable": "deno", 31 | "runtimeArgs": ["task", "run:hello-world"], 32 | "attachSimplePort": 9229 33 | }, 34 | { 35 | "name": "Hello World Async", 36 | "type": "node", 37 | "request": "launch", 38 | "cwd": "${workspaceFolder}", 39 | "env": { "DENO_FLAGS": "--inspect-brk" }, 40 | "runtimeExecutable": "deno", 41 | "runtimeArgs": ["task", "run:hello-world-async"], 42 | "attachSimplePort": 9229 43 | }, 44 | { 45 | "name": "Play Wav", 46 | "type": "node", 47 | "request": "launch", 48 | "cwd": "${workspaceFolder}", 49 | "env": { "DENO_FLAGS": "--inspect-brk" }, 50 | "runtimeExecutable": "deno", 51 | "runtimeArgs": ["task", "run:play-wav"], 52 | "attachSimplePort": 9229 53 | }, 54 | { 55 | "name": "Renderer", 56 | "type": "node", 57 | "request": "launch", 58 | "cwd": "${workspaceFolder}", 59 | "env": { "DENO_FLAGS": "--inspect-brk" }, 60 | "runtimeExecutable": "deno", 61 | "runtimeArgs": ["task", "run:renderer"], 62 | "attachSimplePort": 9229 63 | }, 64 | { 65 | "name": "Same Game", 66 | "type": "node", 67 | "request": "launch", 68 | "cwd": "${workspaceFolder}", 69 | "env": { "DENO_FLAGS": "--inspect-brk" }, 70 | "runtimeExecutable": "deno", 71 | "runtimeArgs": ["task", "run:same-game"], 72 | "attachSimplePort": 9229 73 | } 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.config": "./deno.json", 3 | "deno.enable": true, 4 | "deno.importMap": "./imports.json", 5 | "deno.lint": true, 6 | "deno.unstable": true, 7 | 8 | "editor.tabSize": 2, 9 | 10 | // Ensure deno is the formatter for all file types. 11 | "editor.defaultFormatter": "denoland.vscode-deno", 12 | "[josn]": { 13 | "editor.defaultFormatter": "denoland.vscode-deno" 14 | }, 15 | "[markdown]": { 16 | "editor.defaultFormatter": "denoland.vscode-deno" 17 | }, 18 | "[typescript]": { 19 | "editor.defaultFormatter": "denoland.vscode-deno" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build", 6 | "group": { 7 | "kind": "build", 8 | "isDefault": true 9 | }, 10 | "type": "deno", 11 | "command": "task", 12 | "args": ["build"], 13 | "options": { 14 | "cwd": "${workspaceFolder}" 15 | }, 16 | "problemMatcher": ["$deno"] 17 | }, 18 | { 19 | "label": "CodeGen", 20 | "group": { 21 | "kind": "none", 22 | "isDefault": true 23 | }, 24 | "type": "deno", 25 | "command": "task", 26 | "args": ["codegen"], 27 | "options": { 28 | "cwd": "${workspaceFolder}" 29 | }, 30 | "problemMatcher": ["$deno"] 31 | }, 32 | { 33 | "label": "CodeGen Scraper", 34 | "group": { 35 | "kind": "none", 36 | "isDefault": true 37 | }, 38 | "type": "deno", 39 | "command": "task", 40 | "args": ["codegen-scraper"], 41 | "options": { 42 | "cwd": "${workspaceFolder}" 43 | }, 44 | "problemMatcher": ["$deno"] 45 | }, 46 | { 47 | "label": "Run Doom Fire", 48 | "group": { 49 | "kind": "none", 50 | "isDefault": true 51 | }, 52 | "type": "deno", 53 | "command": "task", 54 | "args": ["run:doom-fire"], 55 | "options": { 56 | "env": { 57 | "GDK_BACKEND": "wayland" 58 | }, 59 | "cwd": "${workspaceFolder}" 60 | }, 61 | "problemMatcher": ["$deno"] 62 | }, 63 | { 64 | "label": "Run Getting Started", 65 | "group": { 66 | "kind": "none", 67 | "isDefault": true 68 | }, 69 | "type": "deno", 70 | "command": "task", 71 | "args": ["start"], 72 | "options": { 73 | "env": { 74 | "GDK_BACKEND": "wayland" 75 | }, 76 | "cwd": "${workspaceFolder}/examples/getting-started" 77 | }, 78 | "problemMatcher": ["$deno"] 79 | }, 80 | { 81 | "label": "Run Hello World", 82 | "group": { 83 | "kind": "none", 84 | "isDefault": true 85 | }, 86 | "type": "deno", 87 | "command": "task", 88 | "args": ["run:hello-world"], 89 | "options": { 90 | "env": { 91 | "GDK_BACKEND": "wayland" 92 | }, 93 | "cwd": "${workspaceFolder}" 94 | }, 95 | "problemMatcher": ["$deno"] 96 | }, 97 | { 98 | "label": "Run Hello World Async", 99 | "group": { 100 | "kind": "none", 101 | "isDefault": true 102 | }, 103 | "type": "deno", 104 | "command": "task", 105 | "args": ["run:hello-world-async"], 106 | "options": { 107 | "env": { 108 | "GDK_BACKEND": "wayland" 109 | }, 110 | "cwd": "${workspaceFolder}" 111 | }, 112 | "problemMatcher": ["$deno"] 113 | }, 114 | { 115 | "label": "Run Play Wav", 116 | "group": { 117 | "kind": "none", 118 | "isDefault": true 119 | }, 120 | "type": "deno", 121 | "command": "task", 122 | "args": ["run:play-wav"], 123 | "options": { 124 | "env": { 125 | "GDK_BACKEND": "wayland" 126 | }, 127 | "cwd": "${workspaceFolder}" 128 | }, 129 | "problemMatcher": ["$deno"] 130 | }, 131 | { 132 | "label": "Run Renderer", 133 | "group": { 134 | "kind": "none", 135 | "isDefault": true 136 | }, 137 | "type": "deno", 138 | "command": "task", 139 | "args": ["run:renderer"], 140 | "options": { 141 | "env": { 142 | "GDK_BACKEND": "wayland" 143 | }, 144 | "cwd": "${workspaceFolder}" 145 | }, 146 | "problemMatcher": ["$deno"] 147 | }, 148 | { 149 | "label": "Run Same Game", 150 | "group": { 151 | "kind": "none", 152 | "isDefault": true 153 | }, 154 | "type": "deno", 155 | "command": "task", 156 | "args": ["run:same-game"], 157 | "options": { 158 | "env": { 159 | "GDK_BACKEND": "wayland" 160 | }, 161 | "cwd": "${workspaceFolder}" 162 | }, 163 | "problemMatcher": ["$deno"] 164 | }, 165 | { 166 | "label": "Run Version", 167 | "group": { 168 | "kind": "none", 169 | "isDefault": true 170 | }, 171 | "type": "deno", 172 | "command": "task", 173 | "args": ["run:version"], 174 | "options": { 175 | "cwd": "${workspaceFolder}" 176 | }, 177 | "problemMatcher": ["$deno"] 178 | }, 179 | { 180 | "type": "deno", 181 | "command": "task", 182 | "args": ["test"], 183 | "problemMatcher": ["$deno-test"], 184 | "group": "test", 185 | "label": "Run Tests" 186 | } 187 | ] 188 | } 189 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2024 Zachary Snow 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # SDL_ts 2 | 3 | [SDL](https://www.libsdl.org/) bindings for [TypeScript](https://www.typescriptlang.org/). 4 | 5 | Currently working via [deno](https://deno.land/) on Windows, Linux, and mac OS using 6 | [FFI](https://deno.land/manual/runtime/ffi_api) (Foreign Function Interface). 7 | 8 | ## Primary Goals 9 | 10 | - Resembling the C API as much as possible. If someone reads a SDL tutorial (for example 11 | [LazyFoo](https://lazyfoo.net/tutorials/SDL/)) then they should be able to easily translate the tutorial to the 12 | equivalent in this library. 13 | 14 | ## Secondary Goals 15 | 16 | - Provide utiltiy functions which wrap SDL features into idomatic JavaScript (i.e. `Events.asyncIterator`). 17 | 18 | ## Non Goals 19 | 20 | - Creating a higher level API or Game Framework. This library's only purpose is to expose the SDL API to TypeScript. 21 | 22 | ## Versioning 23 | 24 | This library's version number will match the SDL major and minor version against which it is currently tested. The patch 25 | number will be specifici to this library. I.E. 26 | 27 | ## Getting Started 28 | 29 | ### SDL2 Installation 30 | 31 | #### Linux 32 | 33 | ##### Debain 34 | 35 | ```shell 36 | sudo apt install libsdl2-2.0-0 libsdl2-image-2.0-0 libsdl2-ttf-2.0-0 37 | ``` 38 | 39 | ##### Fedora 40 | 41 | ```shell 42 | sudo dnf install SDL2 SDL2_image SDL2_ttf 43 | ``` 44 | 45 | #### Windows 46 | 47 | ##### MSYS2 48 | 49 | ```shell 50 | pacman -S mingw-w64-ucrt-x86_64-SDL2 mingw-w64-ucrt-x86_64-SDL2_image mingw-w64-ucrt-x86_64-SDL2_ttf 51 | ``` 52 | 53 | You'll need to ensure then that the sysroot (in this case ucrt) is in your path. Assuming the default install location 54 | for msys2 then `C:\msys64\ucrt64\bin`. 55 | 56 | #### MacOS 57 | 58 | ##### Homebrew 59 | 60 | ```shell 61 | brew install sdl2 sdl2_image sdl2_ttf 62 | ``` 63 | 64 | ### Creating a Project 65 | 66 | Located in the directory [examples/getting-started](examples/getting-started/) are a minimal set of files needed to get 67 | a project up and running. Copy these files onto your computer and start the project with `deno task start`. You should 68 | see a window with `SDL_ts` as the title. 69 | 70 | ### Loading shared libraries 71 | 72 | Unless `libraryPath` is specified in the `Init` functions, `SDL_ts` will attempt to "find" the SDL shared libraries by 73 | determining the prefix and file extension appropriate for the operating system and looking in a set of different places 74 | also based on the operating system. The places where the shared libraries are searched for can be influenced by the 75 | environment variable [`SDL_TS_LIBRARY_PATH`](#sdl-ts-library-path). 76 | 77 | #### libraryPath 78 | 79 | The `Init` functions (i.e. `SDL_Init`) have in their `options` object a `libraryPath` property. This property should be 80 | a path to the shared library and `SDL_ts` will make no attempt to guess where the library should be loaded from. This 81 | means it's up to the caller of the `Init` function to account for changes in the name of the library between the 82 | different platforms. For example the shared library on Linux is called `libSDL2.so` and Windows it is called `SDL2.dll`. 83 | There is a `lib` prefix on Linux and on Windows the file extension is `.dll` instead of `.so`. 84 | 85 | #### SDL_TS_LIBRARY_PATH 86 | 87 | The environmnet variable `SDL_TS_LIBRARY_PATH` can be used to instruct SDL_ts where the SDL shared libraries should be 88 | loaded from. See [deno.json](deno.json). All example projects will load the SDL shared libraries from the `.lib` 89 | directory if it exists. 90 | 91 | ### Loading only required functions 92 | 93 | Per default `SDL.Init` (or `IMG.Init` or `TTF.Init`) will load all known functions from the SDL assemblies. This can be 94 | problematic when attempting to run your script on an older version of the SDL assemblies than the version against which 95 | this library is developed. The Init functions accept an options parameter in which the functions to load can be 96 | specified: 97 | 98 | ```ts 99 | SDL.Init(SDL.InitFlags.VIDEO, { 100 | functions: [ 101 | SDL.Init, 102 | SDL.PollEvent, 103 | SDL.Quit, 104 | // And so on 105 | ], 106 | }); 107 | ``` 108 | 109 | ## Concepts 110 | 111 | - `AllocatableStruct`: is a `Struct` that can be allocated from JavaScript and will be backed by a `Uint8Array`. The 112 | constructor allows all properties to be set in a few ways. 113 | 114 | - `OpaqueStruct`: is a `Struct` where no properties are known to JavaScript (C code would also know about the fields) 115 | and can only be created by the native side. The `Struct` can only be passed back to the native side and usally to a 116 | function that will return information related to the `Struct`. The best example of this concept is `SDL.Window`. 117 | 118 | - `Pointer`: represents a pointer to something and is explicitly typed as `unknown` by SDL_ts. `Pointer` can only 119 | be returned from functions and will never be created by the JavaScript side. The concept of pointers can't completely 120 | be hidden when interfacing with SDL but SDL_ts attempts to constrain pointers to a type level concept. SDL_ts will 121 | take care of passing pointers to the native side as part of the marshalling process. See `PointerLike`. 122 | 123 | - `PointerLike`: is used only as an input to functions and is used to represent anything to which a pointer can be 124 | created for. This includes `Pointer`, `TypedArray`(s), `Struct`(s) and `StructArray`(s). 125 | 126 | - `Struct`: the JavaScript representation of structs from the native side. `Struct`(s) are special classes because their 127 | data is either a `Pointer` or a `Uint8Array`. If the `Struct` data is a `Pointer` the `Struct` it was allocated 128 | on the native side and is read only (see `OpaqueStruct`). If the `Struct` data is an instance of `Uint8Array` it was 129 | allocated on the JavaScript side and can be written to as well as read from (see `AllocatableStruct`). 130 | 131 | - `StructArray`: Use the `StructArray` class to allocate an array on `Struct`(s). All `Struct`(s) in the the array will 132 | be backed by a shared `Uint8Array` allowing the entire array to be passed to the native side. Normal JavaScript arrays 133 | are not guarenteed to be laid out contiguously in memory. The `Struct` type passed to the constructor of the array 134 | must be an `AllocatableStruct`. All `Struct`(s) passed into the the constructor will have their data copied into the 135 | shared `Uint8Array`. 136 | 137 | ## Credits 138 | 139 | Deno images taken from https://deno.land/artwork. 140 | 141 | Thanks to: 142 | 143 | - [Samip Poudel](https://github.com/SamipPoudel58) for the Jurassic deno image. 144 | - [Andy Baird](https://github.com/ajbdev) for initial MacOS Support. 145 | - [Yiheng Wu](https://github.com/jingkaimori) for struct handling and other bug fixes. 146 | -------------------------------------------------------------------------------- /assets/Hack.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smack0007/SDL_ts/081f4cb1082bc6579022c4077a44f2c4ae75b04d/assets/Hack.ttf -------------------------------------------------------------------------------- /assets/LICENSE-Hack.txt: -------------------------------------------------------------------------------- 1 | The work in the Hack project is Copyright 2018 Source Foundry Authors and licensed under the MIT License 2 | 3 | The work in the DejaVu project was committed to the public domain. 4 | 5 | Bitstream Vera Sans Mono Copyright 2003 Bitstream Inc. and licensed under the Bitstream Vera License with Reserved Font Names "Bitstream" and "Vera" 6 | 7 | MIT License 8 | Copyright (c) 2018 Source Foundry Authors 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | BITSTREAM VERA LICENSE 17 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: 20 | 21 | The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. 22 | 23 | The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". 24 | 25 | This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. 26 | 27 | The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. 28 | 29 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. 30 | 31 | Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. -------------------------------------------------------------------------------- /assets/blocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smack0007/SDL_ts/081f4cb1082bc6579022c4077a44f2c4ae75b04d/assets/blocks.png -------------------------------------------------------------------------------- /assets/jurassicDeno.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smack0007/SDL_ts/081f4cb1082bc6579022c4077a44f2c4ae75b04d/assets/jurassicDeno.bmp -------------------------------------------------------------------------------- /assets/jurassicDeno.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smack0007/SDL_ts/081f4cb1082bc6579022c4077a44f2c4ae75b04d/assets/jurassicDeno.png -------------------------------------------------------------------------------- /assets/jurassicDenoOriginal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smack0007/SDL_ts/081f4cb1082bc6579022c4077a44f2c4ae75b04d/assets/jurassicDenoOriginal.png -------------------------------------------------------------------------------- /assets/powerup.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smack0007/SDL_ts/081f4cb1082bc6579022c4077a44f2c4ae75b04d/assets/powerup.wav -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@smack0007/sdl-ts", 3 | "version": "2.30.2", 4 | "exports": "./mod.ts", 5 | "compilerOptions": { 6 | "lib": ["deno.window", "deno.unstable"] 7 | }, 8 | "fmt": { 9 | "useTabs": false, 10 | "lineWidth": 120, 11 | "indentWidth": 2 12 | }, 13 | "lint": { 14 | "rules": { 15 | "tags": ["recommended"], 16 | "include": ["explicit-function-return-type"], 17 | "exclude": ["no-inferrable-types"] 18 | } 19 | }, 20 | "imports": { 21 | "SDL_ts": "./mod.ts", 22 | "@std/assert": "jsr:@std/assert@1.0.0", 23 | "@std/colors": "jsr:@std/fmt@0.225.6/colors", 24 | "@std/path": "jsr:@std/path@1.0.1" 25 | }, 26 | "tasks": { 27 | "build": "cd ./scripts && deno run -R --allow-run ./build.ts", 28 | "build:ci": "deno task build --ci", 29 | "check": "deno check", 30 | "codegen": "cd ./tools && deno run --unstable-ffi --allow-run -R -W $DENO_FLAGS ./codegen.ts", 31 | "codegen-scraper": "cd ./tools && deno run -R -W --allow-run ./codegen-scraper.ts", 32 | "run:doom-fire": "cd ./examples/doom-fire && SDL_TS_LIBRARY_PATH=$INIT_CWD/.lib deno run --unstable-ffi -E --allow-ffi -R=../.. $DENO_FLAGS ./main.ts", 33 | "run:hello-world": "cd ./examples/hello-world && SDL_TS_LIBRARY_PATH=$INIT_CWD/.lib deno run --unstable-ffi -E --allow-ffi -R=../.. $DENO_FLAGS ./main.ts", 34 | "run:hello-world-async": "cd ./examples/hello-world-async && SDL_TS_LIBRARY_PATH=$INIT_CWD/.lib deno run --unstable-ffi -E --allow-ffi -R=../.. $DENO_FLAGS ./main.ts", 35 | "run:play-wav": "cd ./examples/play-wav && SDL_TS_LIBRARY_PATH=$INIT_CWD/.lib deno run --unstable-ffi -E --allow-ffi -R=../.. $DENO_FLAGS ./main.ts", 36 | "run:renderer": "cd ./examples/renderer && SDL_TS_LIBRARY_PATH=$INIT_CWD/.lib deno run --unstable-ffi -E --allow-ffi -R=../.. $DENO_FLAGS ./main.ts", 37 | "run:same-game": "cd ./examples/same-game && SDL_TS_LIBRARY_PATH=$INIT_CWD/.lib deno run --unstable-ffi -E --allow-ffi -R=../.. $DENO_FLAGS ./main.ts", 38 | "run:version": "cd ./examples/version && SDL_TS_LIBRARY_PATH=$INIT_CWD/.lib deno run --unstable-ffi -E --allow-ffi -R=../.. $DENO_FLAGS ./main.ts", 39 | "test": "deno test --unstable-ffi --allow-ffi" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docs/building-sdl-notes.md: -------------------------------------------------------------------------------- 1 | # Building SDL 2 | 3 | > These are my personal notes and in no way represent the offical documentation for building SDL. 4 | 5 | See https://wiki.libsdl.org/SDL2/Installation#linuxunix 6 | 7 | ## Git clone 8 | 9 | ``` 10 | git@github.com:libsdl-org/SDL.git 11 | git switch --detach release-2.26.2 12 | ``` 13 | 14 | ## Fedora 15 | 16 | ``` 17 | sudo dnf install gcc libXext-devel 18 | ``` 19 | 20 | ## Building 21 | 22 | ``` 23 | mkdir build 24 | cd build 25 | ../configure 26 | make 27 | sudo make install 28 | ``` 29 | 30 | Assemblies are then installed to `/usr/local/lib` 31 | -------------------------------------------------------------------------------- /docs/emscripten-notes.md: -------------------------------------------------------------------------------- 1 | # Start Emscripten in Docker in Git Bash 2 | 3 | MSYS_NO_PATHCONV=1 docker run --rm -it -v "$(pwd):/app" -w /app emscripten/emsdk /bin/bash 4 | -------------------------------------------------------------------------------- /examples/doom-fire/main.ts: -------------------------------------------------------------------------------- 1 | import { IMG, SDL } from "SDL_ts"; 2 | import { IMG_FUNCTIONS, SDL_FUNCTIONS } from "./sdlConfig.ts"; 3 | import { ASSETS_PATH } from "../../shared/constants.ts"; 4 | import { join } from "@std/path"; 5 | 6 | const WINDOW_WIDTH = 1024; 7 | const WINDOW_HEIGHT = 768; 8 | const HALF_WINDOW_HEIGHT = WINDOW_HEIGHT / 2; 9 | 10 | const FIRE_WIDTH = 1024; 11 | const FIRE_HEIGHT = 120; 12 | 13 | const FIRE_COLORS = [ 14 | 0x00000000, 15 | 0xc0070707, 16 | 0xc007071f, 17 | 0xc0070f2f, 18 | 0xc0070f47, 19 | 0xc0071757, 20 | 0xc0071f67, 21 | 0xc0071f77, 22 | 0xc007278f, 23 | 0xc0072f9f, 24 | 0xc0073faf, 25 | 0xc00747bf, 26 | 0xc00747c7, 27 | 0xc0074fdf, 28 | 0xc00757df, 29 | 0xc00757df, 30 | 0xc0075fd7, 31 | 0xc00f67d7, 32 | 0xc00f6fcf, 33 | 0xc00f77cf, 34 | 0xc00f7fcf, 35 | 0xc01787cf, 36 | 0xc01787c7, 37 | 0xc0178fc7, 38 | 0xc01f97c7, 39 | 0xc01f9fbf, 40 | 0xc01f9fbf, 41 | 0xc027a7bf, 42 | 0xc027a7bf, 43 | 0xc02fafbf, 44 | 0xc02fafb7, 45 | 0xc02fb7b7, 46 | 0xc037b7b7, 47 | 0xc06fcfcf, 48 | 0xc09fdfdf, 49 | 0xc0c7efef, 50 | 0xc0ffffff, 51 | ]; 52 | 53 | function main(): void { 54 | SDL.Init(SDL.InitFlags.VIDEO, { functions: SDL_FUNCTIONS }); 55 | IMG.Init(IMG.InitFlags.PNG, { functions: IMG_FUNCTIONS }); 56 | 57 | const window = SDL.CreateWindow( 58 | "Doom Fire", 59 | SDL.WindowPos.CENTERED, 60 | SDL.WindowPos.CENTERED, 61 | WINDOW_WIDTH, 62 | WINDOW_HEIGHT, 63 | SDL.WindowFlags.SHOWN, 64 | ); 65 | 66 | const frontBuffer = SDL.GetWindowSurface(window); 67 | 68 | const denoSurfaceUnoptimized = IMG.Load( 69 | join(ASSETS_PATH, "jurassicDeno.png"), 70 | ); 71 | 72 | const denoSurface = SDL.ConvertSurface( 73 | denoSurfaceUnoptimized, 74 | frontBuffer.format, 75 | 0, 76 | ); 77 | 78 | SDL.FreeSurface(denoSurfaceUnoptimized); 79 | 80 | const firePixels = new Uint32Array(FIRE_WIDTH * FIRE_HEIGHT); 81 | const fireSurface = SDL.CreateRGBSurfaceFrom( 82 | firePixels, 83 | FIRE_WIDTH, 84 | FIRE_HEIGHT, 85 | 32, 86 | 4 * FIRE_WIDTH, 87 | 0x000000ff, 88 | 0x0000ff00, 89 | 0x00ff0000, 90 | 0xff000000, 91 | ); 92 | 93 | const flamesRect = new SDL.Rect( 94 | 0, 95 | HALF_WINDOW_HEIGHT, 96 | frontBuffer.w, 97 | HALF_WINDOW_HEIGHT, 98 | ); 99 | 100 | firePixels.fill(0x00000000); 101 | 102 | for (let x = 0; x < FIRE_WIDTH; x += 1) { 103 | firePixels[(FIRE_HEIGHT - 1) * FIRE_WIDTH + x] = FIRE_COLORS[FIRE_COLORS.length - 1]; 104 | } 105 | 106 | let lastFrame = SDL.GetTicks64(); 107 | 108 | const event = new SDL.Event(); 109 | let done = false; 110 | while (!done) { 111 | while (SDL.PollEvent(event) != 0) { 112 | if (event.type === SDL.EventType.QUIT) { 113 | done = true; 114 | break; 115 | } 116 | } 117 | 118 | if (done) { 119 | break; 120 | } 121 | 122 | const current = SDL.GetTicks64(); 123 | const elapsed = current - lastFrame; 124 | 125 | if (elapsed >= 16) { 126 | update(firePixels); 127 | 128 | SDL.BlitScaled(denoSurface, null, frontBuffer, null); 129 | SDL.BlitScaled(fireSurface, null, frontBuffer, flamesRect); 130 | SDL.UpdateWindowSurface(window); 131 | 132 | lastFrame = current; 133 | SDL.Delay(1); 134 | } 135 | } 136 | 137 | SDL.FreeSurface(denoSurface); 138 | SDL.FreeSurface(fireSurface); 139 | SDL.DestroyWindow(window); 140 | 141 | IMG.Quit(); 142 | SDL.Quit(); 143 | } 144 | 145 | function update(firePixels: Uint32Array): void { 146 | for (let x = 0; x < FIRE_WIDTH; x += 1) { 147 | for (let y = 1; y < FIRE_HEIGHT; y += 1) { 148 | spreadFire(firePixels, y * FIRE_WIDTH + x); 149 | } 150 | } 151 | } 152 | 153 | function spreadFire(firePixels: Uint32Array, from: number): void { 154 | const rand = Math.round(Math.random() * 3) & 3; 155 | const to = from - FIRE_WIDTH - rand + 1; 156 | 157 | let toValue = FIRE_COLORS.indexOf(firePixels[from]) - (rand & 1); 158 | 159 | if (toValue < 0) { 160 | toValue = 0; 161 | } 162 | 163 | firePixels[to] = FIRE_COLORS[toValue]; 164 | } 165 | 166 | try { 167 | main(); 168 | } catch (error) { 169 | console.error(error); 170 | Deno.exit(1); 171 | } 172 | -------------------------------------------------------------------------------- /examples/doom-fire/sdlConfig.ts: -------------------------------------------------------------------------------- 1 | import { IMG, SDL } from "SDL_ts"; 2 | 3 | // This file contains the list of functions that are used in the project. 4 | 5 | export const SDL_FUNCTIONS = [ 6 | SDL.BlitScaled, 7 | SDL.ConvertSurface, 8 | SDL.CreateRGBSurfaceFrom, 9 | SDL.CreateWindow, 10 | SDL.Delay, 11 | SDL.DestroyWindow, 12 | SDL.FillRect, 13 | SDL.FreeSurface, 14 | SDL.GetError, 15 | SDL.GetRevision, 16 | SDL.GetTicks64, 17 | SDL.GetVersion, 18 | SDL.GetWindowSurface, 19 | SDL.Init, 20 | SDL.MapRGB, 21 | SDL.MinimizeWindow, 22 | SDL.PollEvent, 23 | SDL.RestoreWindow, 24 | SDL.Quit, 25 | SDL.UpdateWindowSurface, 26 | ] as const; 27 | 28 | export const IMG_FUNCTIONS = [ 29 | IMG.Init, 30 | IMG.Linked_Version, 31 | IMG.Load, 32 | IMG.Quit, 33 | ] as const; 34 | -------------------------------------------------------------------------------- /examples/getting-started/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { "@smack0007/sdl-ts": "jsr:@smack0007/sdl-ts@2.30.2" }, 3 | "tasks": { 4 | "start": "deno run --unstable-ffi --allow-env --allow-ffi --allow-read=. ./main.ts" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/getting-started/main.ts: -------------------------------------------------------------------------------- 1 | import { SDL } from "@smack0007/sdl-ts"; 2 | 3 | function main(): void { 4 | SDL.Init(SDL.InitFlags.VIDEO); 5 | 6 | const window = SDL.CreateWindow( 7 | "SDL_ts", 8 | SDL.WindowPos.CENTERED, 9 | SDL.WindowPos.CENTERED, 10 | 1024, 11 | 768, 12 | SDL.WindowFlags.SHOWN | SDL.WindowFlags.RESIZABLE, 13 | ); 14 | 15 | const renderer = SDL.CreateRenderer(window, 0, SDL.RendererFlags.ACCELERATED); 16 | 17 | SDL.RenderClear(renderer); 18 | SDL.RenderPresent(renderer); 19 | 20 | const event = new SDL.Event(); 21 | 22 | let done = false; 23 | while (!done) { 24 | while (SDL.PollEvent(event) != 0) { 25 | if (event.type === SDL.EventType.QUIT) { 26 | console.info("Done."); 27 | done = true; 28 | } 29 | } 30 | } 31 | 32 | SDL.DestroyWindow(window); 33 | SDL.Quit(); 34 | } 35 | 36 | try { 37 | main(); 38 | } catch (error) { 39 | console.error(error); 40 | Deno.exit(1); 41 | } 42 | -------------------------------------------------------------------------------- /examples/hello-world-async/main.ts: -------------------------------------------------------------------------------- 1 | import { Events, SDL } from "SDL_ts"; 2 | import { SDL_FUNCTIONS } from "./sdlConfig.ts"; 3 | 4 | SDL.Init(SDL.InitFlags.VIDEO, { functions: SDL_FUNCTIONS }); 5 | 6 | console.info("SDL Initialized."); 7 | 8 | const window = SDL.CreateWindow( 9 | "Hello World!", 10 | SDL.WindowPos.CENTERED, 11 | SDL.WindowPos.CENTERED, 12 | 1024, 13 | 768, 14 | SDL.WindowFlags.SHOWN | SDL.WindowFlags.RESIZABLE 15 | ); 16 | 17 | let surface = SDL.GetWindowSurface(window); 18 | 19 | SDL.FillRect(surface, null, SDL.MapRGB(surface.format, 0x64, 0x95, 0xed)); 20 | SDL.UpdateWindowSurface(window); 21 | 22 | for await (const event of Events.asyncIterator()) { 23 | if (event.type === SDL.EventType.QUIT) { 24 | console.info("Done."); 25 | break; 26 | } else if (event.type === SDL.EventType.WINDOWEVENT) { 27 | if (event.window.event === SDL.WindowEventID.SHOWN) { 28 | console.info(`Window ${event.window.windowID} shown.`); 29 | } else if (event.window.event === SDL.WindowEventID.MINIMIZED) { 30 | console.info(`Window ${event.window.windowID} minimized.`); 31 | } else if (event.window.event === SDL.WindowEventID.RESTORED) { 32 | console.info(`Window ${event.window.windowID} restored.`); 33 | } else if (event.window.event === SDL.WindowEventID.RESIZED) { 34 | console.info( 35 | `Window ${event.window.windowID} resized: ${event.window.data1} ${event.window.data2}` 36 | ); 37 | surface = SDL.GetWindowSurface(window); 38 | 39 | SDL.FillRect(surface, null, SDL.MapRGB(surface.format, 0x64, 0x95, 0xed)); 40 | SDL.UpdateWindowSurface(window); 41 | } 42 | } else if (event.type === SDL.EventType.KEYDOWN) { 43 | console.info( 44 | `KeyDown: ${event.key.keysym.scancode} "${SDL.GetScancodeName( 45 | event.key.keysym.scancode 46 | )}"` 47 | ); 48 | } else if (event.type === SDL.EventType.KEYUP) { 49 | console.info( 50 | `KeyUp: ${event.key.keysym.scancode} "${SDL.GetScancodeName( 51 | event.key.keysym.scancode 52 | )}"` 53 | ); 54 | } else if (event.type === SDL.EventType.MOUSEMOTION) { 55 | console.info( 56 | `MouseMotion: (${event.mousebutton.x}, ${event.mousebutton.y})` 57 | ); 58 | } else if (event.type === SDL.EventType.MOUSEBUTTONDOWN) { 59 | console.info( 60 | `MouseButtonDown: ${event.mousebutton.button} (${event.mousebutton.x}, ${event.mousebutton.y})` 61 | ); 62 | } else if (event.type === SDL.EventType.MOUSEBUTTONUP) { 63 | console.info( 64 | `MouseButtonUp: ${event.mousebutton.button} (${event.mousebutton.x}, ${event.mousebutton.y})` 65 | ); 66 | } else if (event.type === SDL.EventType.MOUSEWHEEL) { 67 | console.info( 68 | `MouseWheel: ${event.mousewheel.direction} (${event.mousebutton.x}, ${event.mousebutton.y})` 69 | ); 70 | } 71 | } 72 | 73 | SDL.DestroyWindow(window); 74 | SDL.Quit(); 75 | console.info("SDL Shutdown."); 76 | -------------------------------------------------------------------------------- /examples/hello-world-async/sdlConfig.ts: -------------------------------------------------------------------------------- 1 | import { SDL } from "SDL_ts"; 2 | 3 | // This file contains the list of functions that are used in the project. 4 | 5 | export const SDL_FUNCTIONS = [ 6 | SDL.CreateWindow, 7 | SDL.DestroyWindow, 8 | SDL.FillRect, 9 | SDL.GetError, 10 | SDL.GetScancodeName, 11 | SDL.GetWindowSurface, 12 | SDL.Init, 13 | SDL.MapRGB, 14 | SDL.Quit, 15 | SDL.UpdateWindowSurface, 16 | SDL.WaitEvent, 17 | ] as const; 18 | -------------------------------------------------------------------------------- /examples/hello-world/main.ts: -------------------------------------------------------------------------------- 1 | import { SDL } from "SDL_ts"; 2 | import { SDL_FUNCTIONS } from "./sdlConfig.ts"; 3 | 4 | SDL.Init(SDL.InitFlags.VIDEO, { functions: SDL_FUNCTIONS }); 5 | 6 | console.info("SDL Initialized."); 7 | 8 | console.info(`${SDL.GetSystemRAM()} MB RAM available.`); 9 | 10 | const window = SDL.CreateWindow( 11 | "Hello World!", 12 | SDL.WindowPos.CENTERED, 13 | SDL.WindowPos.CENTERED, 14 | 1024, 15 | 768, 16 | SDL.WindowFlags.SHOWN | SDL.WindowFlags.RESIZABLE 17 | ); 18 | 19 | let surface: SDL.Surface = null!; 20 | 21 | function redraw(): void { 22 | const getWindowSurfaceResult = SDL.GetWindowSurface(window); 23 | 24 | surface = getWindowSurfaceResult; 25 | SDL.FillRect(surface, null, SDL.MapRGB(surface.format, 0x64, 0x95, 0xed)); 26 | SDL.UpdateWindowSurface(window); 27 | } 28 | 29 | redraw(); 30 | console.info(surface.flags); 31 | console.info("Width", surface.w, "Height", surface.h); 32 | 33 | const event = new SDL.Event(); 34 | let done = false; 35 | while (!done) { 36 | while (SDL.PollEvent(event) != 0) { 37 | if (event.type === SDL.EventType.QUIT) { 38 | console.info("Done."); 39 | done = true; 40 | } else if (event.type === SDL.EventType.WINDOWEVENT) { 41 | if (event.window.event === SDL.WindowEventID.SHOWN) { 42 | console.info(`Window ${event.window.windowID} shown.`); 43 | } else if (event.window.event === SDL.WindowEventID.EXPOSED) { 44 | console.info(`Window ${event.window.windowID} exposed.`); 45 | redraw(); 46 | } else if (event.window.event === SDL.WindowEventID.MINIMIZED) { 47 | console.info(`Window ${event.window.windowID} minimized.`); 48 | } else if (event.window.event === SDL.WindowEventID.RESTORED) { 49 | console.info(`Window ${event.window.windowID} restored.`); 50 | } else if (event.window.event === SDL.WindowEventID.RESIZED) { 51 | console.info( 52 | `Window ${event.window.windowID} resized: ${event.window.data1} ${event.window.data2}` 53 | ); 54 | redraw(); 55 | } else { 56 | console.info("Unknown Window event: ", event.window.event); 57 | } 58 | } else if (event.type === SDL.EventType.KEYDOWN) { 59 | console.info( 60 | `KeyDown: ${event.key.keysym.scancode} "${SDL.GetScancodeName( 61 | event.key.keysym.scancode 62 | )}"` 63 | ); 64 | } else if (event.type === SDL.EventType.KEYUP) { 65 | console.info( 66 | `KeyUp: ${event.key.keysym.scancode} "${SDL.GetScancodeName( 67 | event.key.keysym.scancode 68 | )}"` 69 | ); 70 | } else if (event.type === SDL.EventType.MOUSEMOTION) { 71 | console.info( 72 | `MouseMotion: (${event.mousemotion.x}, ${event.mousemotion.y})` 73 | ); 74 | } else if (event.type === SDL.EventType.MOUSEBUTTONDOWN) { 75 | console.info( 76 | `MouseButtonDown: ${event.mousebutton.button} (${event.mousebutton.x}, ${event.mousebutton.y})` 77 | ); 78 | } else if (event.type === SDL.EventType.MOUSEBUTTONUP) { 79 | console.info( 80 | `MouseButtonUp: ${event.mousebutton.button} (${event.mousebutton.x}, ${event.mousebutton.y})` 81 | ); 82 | } else if (event.type === SDL.EventType.MOUSEWHEEL) { 83 | console.info( 84 | `MouseWheel: ${event.mousewheel.direction} (${event.mousebutton.x}, ${event.mousebutton.y})` 85 | ); 86 | } 87 | } 88 | SDL.Delay(1); 89 | // console.info(SDL.GetTicks64()); 90 | } 91 | 92 | console.info("Destroying SDL Window..."); 93 | SDL.DestroyWindow(window); 94 | console.info("SDL Window destoryed."); 95 | 96 | SDL.Quit(); 97 | console.info("SDL Shutdown."); 98 | -------------------------------------------------------------------------------- /examples/hello-world/sdlConfig.ts: -------------------------------------------------------------------------------- 1 | import { SDL } from "SDL_ts"; 2 | 3 | // This file contains the list of functions that are used in the project. 4 | 5 | export const SDL_FUNCTIONS = [ 6 | SDL.AddEventWatch, 7 | SDL.CreateWindow, 8 | SDL.Delay, 9 | SDL.DelEventWatch, 10 | SDL.DestroyWindow, 11 | SDL.FillRect, 12 | SDL.GetError, 13 | SDL.GetScancodeName, 14 | SDL.GetSystemRAM, 15 | SDL.GetWindowSurface, 16 | SDL.Init, 17 | SDL.MapRGB, 18 | SDL.MinimizeWindow, 19 | SDL.PollEvent, 20 | SDL.RestoreWindow, 21 | SDL.Quit, 22 | SDL.UpdateWindowSurface, 23 | ] as const; 24 | -------------------------------------------------------------------------------- /examples/play-wav/main.ts: -------------------------------------------------------------------------------- 1 | // Adapted from https://gigi.nullneuron.net/gigilabs/playing-a-wav-file-using-sdl2/ 2 | 3 | import { SDL } from "SDL_ts"; 4 | import { SDL_FUNCTIONS } from "./sdlConfig.ts"; 5 | import { ASSETS_PATH } from "../../shared/constants.ts"; 6 | import { join } from "@std/path"; 7 | 8 | function main(): void { 9 | SDL.Init(SDL.InitFlags.AUDIO, { functions: SDL_FUNCTIONS }); 10 | 11 | const [wavSpec, buffer] = SDL.LoadWAV( 12 | join(ASSETS_PATH, "powerup.wav"), 13 | new SDL.AudioSpec() 14 | ); 15 | 16 | const audioDeviceID = SDL.OpenAudioDevice(null, 0, wavSpec, null, 0); 17 | 18 | SDL.QueueAudio(audioDeviceID, buffer, buffer.length); 19 | 20 | SDL.PauseAudioDevice(audioDeviceID, 0); 21 | 22 | SDL.Delay(1000); 23 | 24 | SDL.CloseAudioDevice(audioDeviceID); 25 | SDL.FreeWAV(buffer); 26 | SDL.Quit(); 27 | } 28 | 29 | try { 30 | main(); 31 | } catch (error) { 32 | console.error(error); 33 | Deno.exit(1); 34 | } 35 | -------------------------------------------------------------------------------- /examples/play-wav/sdlConfig.ts: -------------------------------------------------------------------------------- 1 | import { SDL } from "SDL_ts"; 2 | 3 | // This file contains the list of functions that are used in the project. 4 | 5 | export const SDL_FUNCTIONS = [ 6 | SDL.CloseAudioDevice, 7 | SDL.Delay, 8 | SDL.FreeWAV, 9 | SDL.GetError, 10 | SDL.Init, 11 | SDL.LoadWAV_RW, 12 | SDL.OpenAudioDevice, 13 | SDL.PauseAudioDevice, 14 | SDL.QueueAudio, 15 | SDL.Quit, 16 | SDL.RWFromFile, 17 | ] as const; 18 | -------------------------------------------------------------------------------- /examples/renderer/main.ts: -------------------------------------------------------------------------------- 1 | import { SDL, StructArray } from "SDL_ts"; 2 | import { SDL_FUNCTIONS } from "./sdlConfig.ts"; 3 | import { ASSETS_PATH } from "../../shared/constants.ts"; 4 | import { join } from "@std/path"; 5 | 6 | const WINDOW_WIDTH = 1024; 7 | const WINDOW_HEIGHT = 768; 8 | 9 | function main(): void { 10 | SDL.Init(SDL.InitFlags.VIDEO, { functions: SDL_FUNCTIONS }); 11 | 12 | const [window, renderer] = SDL.CreateWindowAndRenderer( 13 | WINDOW_WIDTH, 14 | WINDOW_HEIGHT, 15 | SDL.WindowFlags.SHOWN | SDL.WindowFlags.RESIZABLE, 16 | ); 17 | 18 | SDL.RenderSetLogicalSize(renderer, WINDOW_WIDTH, WINDOW_HEIGHT); 19 | 20 | const rendererInfo = SDL.GetRendererInfo(renderer); 21 | console.info(rendererInfo.name); 22 | console.info(rendererInfo.max_texture_width); 23 | console.info(rendererInfo.max_texture_height); 24 | console.info( 25 | (rendererInfo.flags & SDL.RendererFlags.ACCELERATED) === 26 | SDL.RendererFlags.ACCELERATED, 27 | ); 28 | 29 | SDL.SetRenderDrawColor(renderer, 0, 0, 0, 255); 30 | SDL.RenderClear(renderer); 31 | SDL.RenderPresent(renderer); 32 | SDL.RenderFlush(renderer); 33 | 34 | const denoSurface = SDL.LoadBMP(join(ASSETS_PATH, "jurassicDeno.bmp")); 35 | 36 | const srcRect = new SDL.Rect(0, 0, denoSurface.w, denoSurface.h); 37 | const destRect = new SDL.Rect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); 38 | const textureCenter = new SDL.Point(denoSurface.w / 2, denoSurface.h / 2); 39 | let textureRotation = 0; 40 | const texture = SDL.CreateTextureFromSurface(renderer, denoSurface); 41 | SDL.FreeSurface(denoSurface); 42 | 43 | // deno-fmt-ignore 44 | const points = new StructArray(SDL.Point, [ 45 | new SDL.Point(0, 0), 46 | new SDL.Point(1, 0), 47 | new SDL.Point(1, 1), 48 | new SDL.Point(0, 1) 49 | ]); 50 | 51 | const event = new SDL.Event(); 52 | let done = false; 53 | while (!done) { 54 | while (SDL.PollEvent(event) != 0) { 55 | if (event.type === SDL.EventType.QUIT) { 56 | done = true; 57 | break; 58 | } 59 | } 60 | 61 | if (done) { 62 | break; 63 | } 64 | 65 | // TODO: Move this to some kind of keyboard example. 66 | const state = SDL.GetKeyboardState(); 67 | if (state[SDL.Scancode.ESCAPE]) { 68 | console.info("ESC is down."); 69 | } 70 | 71 | SDL.SetRenderDrawColor(renderer, 0, 0, 0, 255); 72 | SDL.RenderClear(renderer); 73 | 74 | textureRotation += 0.1; 75 | SDL.RenderCopyEx( 76 | renderer, 77 | texture, 78 | srcRect, 79 | destRect, 80 | textureRotation, 81 | textureCenter, 82 | SDL.RendererFlip.NONE, 83 | ); 84 | 85 | SDL.SetRenderDrawColor(renderer, 255, 0, 0, 255); 86 | const rect = new SDL.Rect(100, 100, 200, 400); 87 | SDL.RenderDrawLine(renderer, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); 88 | SDL.RenderFillRect(renderer, rect); 89 | SDL.SetRenderDrawColor(renderer, 0, 0, 255, 255); 90 | SDL.RenderDrawRect(renderer, rect); 91 | 92 | SDL.SetRenderDrawColor(renderer, 0, 0, 255, 255); 93 | SDL.RenderDrawPoints(renderer, points, 4); 94 | 95 | SDL.RenderPresent(renderer); 96 | SDL.RenderFlush(renderer); 97 | 98 | SDL.Delay(1); 99 | } 100 | 101 | SDL.DestroyTexture(texture); 102 | SDL.DestroyRenderer(renderer); 103 | SDL.DestroyWindow(window); 104 | SDL.Quit(); 105 | } 106 | 107 | try { 108 | main(); 109 | } catch (error) { 110 | console.error(error); 111 | Deno.exit(1); 112 | } 113 | -------------------------------------------------------------------------------- /examples/renderer/sdlConfig.ts: -------------------------------------------------------------------------------- 1 | import { SDL } from "SDL_ts"; 2 | 3 | // This file contains the list of functions that are used in the project. 4 | 5 | export const SDL_FUNCTIONS = [ 6 | SDL.CreateTextureFromSurface, 7 | SDL.CreateWindowAndRenderer, 8 | SDL.DestroyRenderer, 9 | SDL.DestroyTexture, 10 | SDL.DestroyWindow, 11 | SDL.Delay, 12 | SDL.FreeSurface, 13 | SDL.GetError, 14 | SDL.GetKeyboardState, 15 | SDL.GetRendererInfo, 16 | SDL.Init, 17 | SDL.LoadBMP_RW, 18 | SDL.PollEvent, 19 | SDL.Quit, 20 | SDL.RenderClear, 21 | SDL.RenderCopyEx, 22 | SDL.RenderDrawLine, 23 | SDL.RenderDrawPoints, 24 | SDL.RenderDrawRect, 25 | SDL.RenderFillRect, 26 | SDL.RenderFlush, 27 | SDL.RenderPresent, 28 | SDL.RenderSetLogicalSize, 29 | SDL.RWFromFile, 30 | SDL.SetRenderDrawColor, 31 | ] as const; 32 | -------------------------------------------------------------------------------- /examples/same-game/fonts.ts: -------------------------------------------------------------------------------- 1 | import { Pointer, SDL, TTF } from "SDL_ts"; 2 | 3 | const FONT_TEXTURE_SIZE = 256; 4 | 5 | type GlyphData = { 6 | character: string; 7 | surface: SDL.Surface; 8 | width: number; 9 | height: number; 10 | }; 11 | 12 | export type FontAtlas = { 13 | texture: Pointer; 14 | glyphs: Record; 15 | }; 16 | 17 | export function createFontAtlas( 18 | renderer: Pointer, 19 | fontPath: string, 20 | fontSize: number, 21 | ): FontAtlas { 22 | const fg = new SDL.Color(255, 255, 255, 255); 23 | const bg = new SDL.Color(0, 0, 0, 0); 24 | 25 | const font = TTF.OpenFont(fontPath, fontSize); 26 | 27 | if (font === null) { 28 | throw new Error( 29 | `Failed to open font in ${createFontAtlas.name}: ${SDL.GetError()}`, 30 | ); 31 | } 32 | 33 | const surface = SDL.CreateRGBSurfaceWithFormat( 34 | 0, 35 | FONT_TEXTURE_SIZE, 36 | FONT_TEXTURE_SIZE, 37 | 32, 38 | SDL.PIXELFORMAT_RGBA8888, 39 | ); 40 | 41 | if (surface === null) { 42 | throw new Error( 43 | `Failed to create font surface in ${createFontAtlas.name}: ${SDL.GetError()}`, 44 | ); 45 | } 46 | 47 | SDL.FillRect( 48 | surface, 49 | null, 50 | SDL.MapRGBA(surface.format, bg.r, bg.g, bg.b, bg.a), 51 | ); 52 | 53 | const glyphs: GlyphData[] = []; 54 | let maxGlyphHeight = 0; 55 | 56 | for (let i = " ".charCodeAt(0); i <= "z".charCodeAt(0); i += 1) { 57 | const character = String.fromCharCode(i); 58 | 59 | const glyphSurface = TTF.RenderUTF8_Blended(font, character, fg); 60 | 61 | if (glyphSurface === null) { 62 | throw new Error( 63 | `Failed to create glyph surface in ${createFontAtlas.name}: ${SDL.GetError()}`, 64 | ); 65 | } 66 | 67 | const [glyphWidth, glyphHeight] = TTF.SizeUTF8( 68 | font, 69 | character, 70 | ); 71 | 72 | glyphs.push({ 73 | character, 74 | surface: glyphSurface, 75 | width: glyphWidth, 76 | height: glyphHeight, 77 | }); 78 | 79 | if (glyphHeight > maxGlyphHeight) { 80 | maxGlyphHeight = glyphHeight; 81 | } 82 | } 83 | 84 | const destination = new SDL.Rect(0, 0, 0, 0); 85 | const glyphDestinations: Record = {}; 86 | 87 | for (const glyph of glyphs) { 88 | destination.w = glyph.width; 89 | destination.h = glyph.height; 90 | 91 | if (destination.x + destination.w >= FONT_TEXTURE_SIZE) { 92 | destination.x = 0; 93 | 94 | destination.y += maxGlyphHeight + 1; 95 | 96 | if (destination.y + destination.h >= FONT_TEXTURE_SIZE) { 97 | throw new Error( 98 | `Ran out of glyph space in font atlas in ${createFontAtlas.name}`, 99 | ); 100 | } 101 | } 102 | 103 | SDL.SetSurfaceBlendMode(glyph.surface, SDL.BlendMode.NONE); 104 | SDL.BlitSurface(glyph.surface, null, surface, destination); 105 | 106 | SDL.FreeSurface(glyph.surface); 107 | 108 | // TODO(idea): dest.clone() 109 | glyphDestinations[glyph.character] = new SDL.Rect(destination); 110 | 111 | destination.x += destination.w; 112 | } 113 | 114 | const texture = SDL.CreateTextureFromSurface(renderer, surface); 115 | 116 | if (texture === null) { 117 | throw new Error( 118 | `Failed to create font texture in ${createFontAtlas.name}: ${SDL.GetError()}`, 119 | ); 120 | } 121 | 122 | SDL.SetTextureBlendMode(texture, SDL.BlendMode.BLEND); 123 | SDL.SetTextureAlphaMod(texture, 255); 124 | 125 | SDL.FreeSurface(surface); 126 | TTF.CloseFont(font); 127 | 128 | return { 129 | texture, 130 | glyphs: glyphDestinations, 131 | }; 132 | } 133 | 134 | export function drawString( 135 | renderer: Pointer, 136 | font: FontAtlas, 137 | destination: SDL.Point, 138 | text: string, 139 | ): void { 140 | const destRect = new SDL.Rect(destination.x, destination.y, 0, 0); 141 | 142 | SDL.SetRenderDrawBlendMode(renderer, SDL.BlendMode.BLEND); 143 | 144 | for (let i = 0; i < text.length; i += 1) { 145 | const glyphRect = font.glyphs[text[i]]; 146 | 147 | destRect.x = destination.x; 148 | destRect.y = destination.y; 149 | destRect.w = glyphRect.w; 150 | destRect.h = glyphRect.h; 151 | 152 | SDL.RenderCopy(renderer, font.texture, font.glyphs[text[i]], destRect); 153 | 154 | destination.x += glyphRect.w; 155 | } 156 | } 157 | 158 | export function measureString( 159 | font: FontAtlas, 160 | text: string, 161 | ): { width: number; height: number } { 162 | let width = 0; 163 | let height = 0; 164 | 165 | let lineWidth = 0; 166 | let lineHeight = 0; 167 | for (let i = 0; i < text.length; i += 1) { 168 | if (text[i] === "\r") { 169 | continue; 170 | } 171 | 172 | if (text[i] !== "\n") { 173 | const glyphRect = font.glyphs[text[i]]; 174 | 175 | lineWidth += glyphRect.w; 176 | if (glyphRect.h > lineHeight) { 177 | lineHeight = glyphRect.h; 178 | } 179 | } else { 180 | if (lineWidth > width) { 181 | width = lineWidth; 182 | } 183 | 184 | if (lineHeight > height) { 185 | height = lineHeight; 186 | } 187 | 188 | lineWidth = 0; 189 | lineHeight = 0; 190 | } 191 | } 192 | 193 | if (lineWidth > width) { 194 | width = lineWidth; 195 | } 196 | 197 | if (lineHeight > height) { 198 | height = lineHeight; 199 | } 200 | 201 | return { width, height }; 202 | } 203 | -------------------------------------------------------------------------------- /examples/same-game/logic/block.ts: -------------------------------------------------------------------------------- 1 | import { BlockColors } from "./blockColors.ts"; 2 | 3 | export class Block { 4 | public static readonly WidthInPixels: number = 32; 5 | public static readonly HeightInPixels: number = 32; 6 | 7 | public static readonly FallRate = Number(Block.HeightInPixels * 0.02); 8 | 9 | private _isActive = true; 10 | private _offsetY = 0; 11 | 12 | public get color(): BlockColors { 13 | return this._color; 14 | } 15 | 16 | public get isActive(): boolean { 17 | return this._isActive; 18 | } 19 | 20 | public get isSelected(): boolean { 21 | return this._isSelected; 22 | } 23 | 24 | public get offsetY(): number { 25 | return this._offsetY; 26 | } 27 | 28 | constructor(private _color: BlockColors, private _isSelected: boolean = false) {} 29 | 30 | public select(): void { 31 | this._isSelected = true; 32 | } 33 | 34 | public unselect(): void { 35 | this._isSelected = false; 36 | } 37 | 38 | public deactivate(): void { 39 | this._isSelected = false; 40 | this._isActive = false; 41 | } 42 | 43 | public fallDown(blockBelow: Block): void { 44 | const tempColor = this._color; 45 | this._color = blockBelow._color; 46 | blockBelow._color = tempColor; 47 | 48 | const tempIsActive = this._isActive; 49 | this._isActive = blockBelow._isActive; 50 | blockBelow._isActive = tempIsActive; 51 | 52 | blockBelow._offsetY = this._offsetY - Block.HeightInPixels; 53 | this._offsetY = 0; 54 | } 55 | 56 | public update(elapsed: bigint): void { 57 | if (this._offsetY < 0) { 58 | this._offsetY += Block.FallRate * Number(elapsed); 59 | } 60 | 61 | if (this._offsetY > 0) { 62 | this._offsetY = 0; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/same-game/logic/blockColors.ts: -------------------------------------------------------------------------------- 1 | import { Enum } from "SDL_ts"; 2 | 3 | export const BlockColors = { 4 | red: 0, 5 | blue: 1, 6 | green: 2, 7 | yellow: 3, 8 | } as const; 9 | 10 | export type BlockColors = Enum; 11 | -------------------------------------------------------------------------------- /examples/same-game/logic/board.ts: -------------------------------------------------------------------------------- 1 | import { Block } from "./block.ts"; 2 | import { BlockColors } from "./blockColors.ts"; 3 | import { Random } from "./random.ts"; 4 | 5 | export class Board { 6 | public static readonly WidthInPixels = 1024; 7 | public static readonly HeightInPixels = 768 - Block.HeightInPixels * 2; 8 | 9 | public static readonly Width = Board.WidthInPixels / Block.WidthInPixels; 10 | public static readonly Height = Board.HeightInPixels / Block.HeightInPixels; 11 | public static readonly BlockCount = Board.Width * Board.Height; 12 | 13 | private _blocks: Block[] = []; 14 | private _score = 0; 15 | 16 | public get selectedBlockCount(): number { 17 | return this._blocks.filter((x) => x.isSelected).length; 18 | } 19 | 20 | public get score(): number { 21 | return this._score; 22 | } 23 | 24 | constructor(private _random: Random) { 25 | for (let i = 0; i < Board.BlockCount; i++) { 26 | const blockColor = this._random.nextInt(0, 3) as BlockColors; 27 | this._blocks[i] = new Block(blockColor); 28 | } 29 | } 30 | 31 | public at(x: number, y: number): Block { 32 | return this._blocks[y * Board.Width + x]; 33 | } 34 | 35 | public onClick(x: number, y: number): void { 36 | this.unselectAllBlocks(); 37 | 38 | const blockX = Math.floor(x / Block.WidthInPixels); 39 | const blockY = Math.floor(y / Block.HeightInPixels); 40 | const block = this.at(blockX, blockY); 41 | 42 | this.selectBlock(block, blockX, blockY); 43 | } 44 | 45 | public onDoubleClick(): void { 46 | const selected = this._blocks.filter((block) => block.isSelected); 47 | if (selected.length <= 1) { 48 | return; 49 | } 50 | 51 | let scoreForSelectedBlocks = 0; 52 | for (const block of selected) { 53 | block.deactivate(); 54 | scoreForSelectedBlocks += 1; 55 | } 56 | 57 | this._score += scoreForSelectedBlocks; 58 | } 59 | 60 | public update(elapsed: bigint): void { 61 | for (let y = Board.Height - 1; y >= 0; y -= 1) { 62 | for (let x = 0; x < Board.Width; x += 1) { 63 | const block = this.at(x, y); 64 | block.update(elapsed); 65 | 66 | if (y < Board.Height - 1) { 67 | const blockBelow = this.at(x, y + 1); 68 | if (!blockBelow.isActive) { 69 | block.fallDown(blockBelow); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | private unselectAllBlocks(): void { 77 | for (const block of this._blocks) { 78 | block.unselect(); 79 | } 80 | } 81 | 82 | private selectBlock(startingBlock: Block, x: number, y: number): void { 83 | const block = this.at(x, y); 84 | 85 | if (!block.isActive || block.isSelected || block.color !== startingBlock.color) { 86 | return; 87 | } 88 | 89 | block.select(); 90 | 91 | if (x > 0) { 92 | this.selectBlock(startingBlock, x - 1, y); 93 | } 94 | 95 | if (x < Board.Width - 1) { 96 | this.selectBlock(startingBlock, x + 1, y); 97 | } 98 | 99 | if (y > 0) { 100 | this.selectBlock(startingBlock, x, y - 1); 101 | } 102 | 103 | if (y < Board.Height - 1) { 104 | this.selectBlock(startingBlock, x, y + 1); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /examples/same-game/logic/random.ts: -------------------------------------------------------------------------------- 1 | export class Random { 2 | private static readonly a = 16807; 3 | private static readonly m = 2147483647; 4 | private static readonly q = 127773; 5 | private static readonly r = 2836; 6 | 7 | constructor(private _seed: number) { 8 | if (this._seed <= 0 || this._seed === Number.MAX_VALUE) { 9 | throw new Error("Seed out of range."); 10 | } 11 | } 12 | 13 | public nextDouble(): number { 14 | const hi = this._seed / Random.q; 15 | const lo = this._seed % Random.q; 16 | 17 | this._seed = Random.a * lo - Random.r * hi; 18 | 19 | if (this._seed <= 0) { 20 | this._seed = this._seed + Random.m; 21 | } 22 | 23 | return (this._seed * 1.0) / Random.m; 24 | } 25 | 26 | public nextInt(min: number, max: number): number { 27 | const range = Math.round(max) - Math.round(min); 28 | return min + Math.round(range * this.nextDouble()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/same-game/main.ts: -------------------------------------------------------------------------------- 1 | import { IMG, Pointer, SDL, SDLError, TTF } from "SDL_ts"; 2 | import { IMG_FUNCTIONS, SDL_FUNCTIONS, TTF_FUNCTIONS } from "./sdlConfig.ts"; 3 | import { join } from "@std/path"; 4 | import { Board } from "./logic/board.ts"; 5 | import { Random } from "./logic/random.ts"; 6 | import { drawBoard } from "./rendering/board.ts"; 7 | import { ASSETS_PATH } from "../../shared/constants.ts"; 8 | import { createFontAtlas, drawString, FontAtlas } from "./fonts.ts"; 9 | 10 | const WINDOW_WIDTH = 1024; 11 | const WINDOW_HEIGHT = 768; 12 | const UPDATE_INTERVAL = 16n; // 1000ms / 60fps 13 | const FONT_SIZE = 24; 14 | 15 | function main(): number { 16 | SDL.Init(SDL.InitFlags.VIDEO, { functions: SDL_FUNCTIONS }); 17 | IMG.Init(IMG.InitFlags.PNG, { functions: IMG_FUNCTIONS }); 18 | TTF.Init({ functions: TTF_FUNCTIONS }); 19 | 20 | const [window, renderer] = SDL.CreateWindowAndRenderer( 21 | WINDOW_WIDTH, 22 | WINDOW_HEIGHT, 23 | SDL.WindowFlags.SHOWN 24 | ); 25 | 26 | SDL.SetWindowTitle(window, "Same Game"); 27 | 28 | const blockTexture = IMG.LoadTexture( 29 | renderer, 30 | join(ASSETS_PATH, "blocks.png") 31 | ); 32 | 33 | if (blockTexture == null) { 34 | throw new SDLError("Failed to create texture for block.png"); 35 | } 36 | 37 | const font = createFontAtlas( 38 | renderer, 39 | join(ASSETS_PATH, "Hack.ttf"), 40 | FONT_SIZE 41 | ); 42 | 43 | const board = new Board(new Random(12345)); 44 | 45 | const event = new SDL.Event(); 46 | 47 | let done = false; 48 | let lastTime = 0n; 49 | 50 | while (!done) { 51 | while (SDL.PollEvent(event) != 0) { 52 | switch (event.type) { 53 | case SDL.EventType.QUIT: 54 | done = true; 55 | break; 56 | 57 | case SDL.EventType.MOUSEBUTTONDOWN: { 58 | const mouseButtonEvent = event.mousebutton; 59 | if (event.mousebutton.clicks >= 2) { 60 | if (board.selectedBlockCount) { 61 | board.onDoubleClick(); 62 | } else { 63 | board.onClick(mouseButtonEvent.x, mouseButtonEvent.y); 64 | } 65 | } else { 66 | board.onClick(mouseButtonEvent.x, mouseButtonEvent.y); 67 | } 68 | break; 69 | } 70 | } 71 | } 72 | 73 | const currentTime = SDL.GetTicks64(); 74 | const elapsedTime = currentTime - lastTime; 75 | 76 | if (elapsedTime >= UPDATE_INTERVAL) { 77 | update(elapsedTime, board); 78 | draw(renderer, board, blockTexture, font); 79 | lastTime = currentTime; 80 | } 81 | } 82 | 83 | SDL.DestroyWindow(window); 84 | SDL.Quit(); 85 | 86 | return 0; 87 | } 88 | 89 | function update(elapsed: bigint, board: Board): void { 90 | board.update(elapsed); 91 | } 92 | 93 | function draw( 94 | renderer: Pointer, 95 | board: Board, 96 | blockTexture: Pointer, 97 | font: FontAtlas 98 | ): void { 99 | SDL.SetRenderDrawColor(renderer, 0, 0, 0, 255); 100 | SDL.RenderClear(renderer); 101 | 102 | drawBoard(renderer, board, blockTexture); 103 | 104 | drawString( 105 | renderer, 106 | font, 107 | new SDL.Point(0, Board.HeightInPixels + 2), 108 | `Score: ${board.score}` 109 | ); 110 | drawString( 111 | renderer, 112 | font, 113 | new SDL.Point(0, Board.HeightInPixels + FONT_SIZE + 2), 114 | `Selected: ${board.selectedBlockCount}` 115 | ); 116 | 117 | SDL.RenderPresent(renderer); 118 | SDL.RenderFlush(renderer); 119 | } 120 | 121 | try { 122 | Deno.exit(main()); 123 | } catch (err) { 124 | console.error(err); 125 | Deno.exit(1); 126 | } 127 | -------------------------------------------------------------------------------- /examples/same-game/rendering/board.ts: -------------------------------------------------------------------------------- 1 | import { Pointer, SDL } from "SDL_ts"; 2 | import { Board } from "../logic/board.ts"; 3 | import { Block } from "../logic/block.ts"; 4 | import { BlockColors } from "../logic/blockColors.ts"; 5 | 6 | export function drawBoard( 7 | renderer: Pointer, 8 | board: Board, 9 | blockTexture: Pointer, 10 | ): void { 11 | SDL.SetTextureBlendMode(blockTexture, SDL.BlendMode.BLEND); 12 | 13 | for (let y = 0; y < Board.Height; y++) { 14 | for (let x = 0; x < Board.Width; x++) { 15 | const block = board.at(x, y); 16 | 17 | if (!block.isActive) { 18 | continue; 19 | } 20 | 21 | const xSrc = 0; 22 | 23 | const xDest = Math.trunc(x * Block.WidthInPixels); 24 | const yDest = Math.trunc(y * Block.HeightInPixels + block.offsetY); 25 | 26 | let r = 0, g = 0, b = 0; 27 | 28 | switch (block.color) { 29 | case BlockColors.red: 30 | r = 255; 31 | break; 32 | 33 | case BlockColors.green: 34 | g = 255; 35 | break; 36 | 37 | case BlockColors.blue: 38 | b = 255; 39 | break; 40 | 41 | case BlockColors.yellow: 42 | r = 255; 43 | g = 255; 44 | break; 45 | } 46 | 47 | SDL.SetTextureColorMod(blockTexture, r, g, b); 48 | SDL.SetTextureAlphaMod(blockTexture, 255); 49 | 50 | SDL.RenderCopy( 51 | renderer, 52 | blockTexture, 53 | new SDL.Rect(xSrc, 0, Block.WidthInPixels, Block.HeightInPixels), 54 | new SDL.Rect(xDest, yDest, Block.WidthInPixels, Block.HeightInPixels), 55 | ); 56 | 57 | if (block.isSelected) { 58 | SDL.SetTextureColorMod(blockTexture, 0, 0, 0); 59 | SDL.SetTextureAlphaMod(blockTexture, 128); 60 | SDL.RenderCopy( 61 | renderer, 62 | blockTexture, 63 | new SDL.Rect(Block.WidthInPixels * 4, 0, Block.WidthInPixels, Block.HeightInPixels), 64 | new SDL.Rect(xDest, yDest, Block.WidthInPixels, Block.HeightInPixels), 65 | ); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/same-game/sdlConfig.ts: -------------------------------------------------------------------------------- 1 | import { IMG, SDL, TTF } from "SDL_ts"; 2 | 3 | // This file contains the list of functions that are used in the project. 4 | 5 | export const SDL_FUNCTIONS = [ 6 | SDL.BlitSurface, 7 | SDL.CreateRGBSurfaceWithFormat, 8 | SDL.CreateTextureFromSurface, 9 | SDL.CreateWindowAndRenderer, 10 | SDL.DestroyWindow, 11 | SDL.FillRect, 12 | SDL.FreeSurface, 13 | SDL.GetError, 14 | SDL.GetTicks64, 15 | SDL.Init, 16 | SDL.MapRGBA, 17 | SDL.PollEvent, 18 | SDL.Quit, 19 | SDL.RenderClear, 20 | SDL.RenderCopy, 21 | SDL.RenderFlush, 22 | SDL.RenderPresent, 23 | SDL.SetRenderDrawBlendMode, 24 | SDL.SetRenderDrawColor, 25 | SDL.SetSurfaceBlendMode, 26 | SDL.SetTextureAlphaMod, 27 | SDL.SetTextureBlendMode, 28 | SDL.SetTextureColorMod, 29 | SDL.SetWindowTitle, 30 | ] as const; 31 | 32 | export const IMG_FUNCTIONS = [ 33 | IMG.Init, 34 | IMG.LoadTexture, 35 | IMG.Quit, 36 | ] as const; 37 | 38 | export const TTF_FUNCTIONS = [ 39 | TTF.CloseFont, 40 | TTF.Init, 41 | TTF.OpenFont, 42 | TTF.Quit, 43 | TTF.RenderUTF8_Blended, 44 | TTF.SizeUTF8, 45 | ] as const; 46 | -------------------------------------------------------------------------------- /examples/version/main.ts: -------------------------------------------------------------------------------- 1 | import { SDL, IMG } from "SDL_ts"; 2 | import { IMG_FUNCTIONS, SDL_FUNCTIONS } from "./sdlConfig.ts"; 3 | 4 | SDL.Init(SDL.InitFlags.VIDEO, { functions: SDL_FUNCTIONS }); 5 | 6 | let version = new SDL.version(); 7 | SDL.GetVersion(version); 8 | console.info(`SDL Version: ${version.major}.${version.minor}.${version.patch}`); 9 | console.info(`SDL Revision: ${SDL.GetRevision()}`); 10 | 11 | IMG.Init(IMG.InitFlags.PNG, { functions: IMG_FUNCTIONS }); 12 | 13 | version = IMG.Linked_Version(); 14 | console.info( 15 | `SDL_image Version: ${version.major}.${version.minor}.${version.patch}` 16 | ); 17 | 18 | SDL.Quit(); 19 | IMG.Quit(); 20 | -------------------------------------------------------------------------------- /examples/version/sdlConfig.ts: -------------------------------------------------------------------------------- 1 | import { IMG, SDL } from "SDL_ts"; 2 | 3 | // This file contains the list of functions that are used in the project. 4 | 5 | export const SDL_FUNCTIONS = [ 6 | SDL.Init, 7 | SDL.GetRevision, 8 | SDL.GetVersion, 9 | SDL.Quit, 10 | ] as const; 11 | 12 | export const IMG_FUNCTIONS = [IMG.Init, IMG.Linked_Version, IMG.Quit]; 13 | -------------------------------------------------------------------------------- /mod.SDL.ts: -------------------------------------------------------------------------------- 1 | export * from "./src/SDL/audio.ts"; 2 | export * from "./src/SDL/callbacks.ts"; 3 | export * from "./src/SDL/constants.ts"; 4 | export * from "./src/SDL/enums.ts"; 5 | export * from "./src/SDL/events.ts"; 6 | export * from "./src/SDL/functionMacros.ts"; 7 | export * from "./src/SDL/functions.ts"; 8 | export * from "./src/SDL/pixels.ts"; 9 | export * from "./src/SDL/rw.ts"; 10 | export * from "./src/SDL/structs.ts"; 11 | -------------------------------------------------------------------------------- /mod.SDL_image.ts: -------------------------------------------------------------------------------- 1 | export * from "./src/SDL_image/enums.ts"; 2 | export * from "./src/SDL_image/functions.ts"; 3 | export * from "./src/SDL_image/structs.ts"; 4 | -------------------------------------------------------------------------------- /mod.SDL_ttf.ts: -------------------------------------------------------------------------------- 1 | export * from "./src/SDL_ttf/enums.ts"; 2 | export * from "./src/SDL_ttf/functions.ts"; 3 | export * from "./src/SDL_ttf/structs.ts"; 4 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | export * as SDL from "./mod.SDL.ts"; 2 | export * as IMG from "./mod.SDL_image.ts"; 3 | export * as TTF from "./mod.SDL_ttf.ts"; 4 | export * from "./src/error.ts"; 5 | export * from "./src/events.ts"; 6 | export * from "./src/structs.ts"; 7 | export * from "./src/types.ts"; 8 | -------------------------------------------------------------------------------- /scripts/build.ts: -------------------------------------------------------------------------------- 1 | import { EXAMPLES_PATH, TOOLS_PATH } from "../shared/constants.ts"; 2 | import { green } from "@std/colors"; 3 | import { join } from "@std/path"; 4 | 5 | async function main(): Promise { 6 | let failure = false; 7 | 8 | if (!(await typeCheck(join(TOOLS_PATH, "codegen.ts")))) { 9 | failure = true; 10 | } 11 | 12 | if (!(await typeCheck(join(TOOLS_PATH, "codegen-scraper.ts")))) { 13 | failure = true; 14 | } 15 | 16 | for await (const entry of Deno.readDir(EXAMPLES_PATH)) { 17 | if (entry.isDirectory) { 18 | if (!(await typeCheck(join(EXAMPLES_PATH, entry.name, "main.ts")))) { 19 | failure = true; 20 | } 21 | } 22 | } 23 | 24 | if (!(await runTests())) { 25 | failure = true; 26 | } 27 | 28 | return failure ? 1 : 0; 29 | } 30 | 31 | async function typeCheck(filePath: string): Promise { 32 | console.info(`${green("Type checking:")} ${filePath}`); 33 | return ( 34 | ( 35 | await Deno.run({ 36 | cmd: ["deno", "task", "-q", "check", filePath], 37 | }).status() 38 | ).code === 0 39 | ); 40 | } 41 | 42 | async function runTests(): Promise { 43 | console.info(`${green("Running tests...")}`); 44 | return ( 45 | ( 46 | await Deno.run({ 47 | cmd: ["deno", "task", "-q", "test"], 48 | }).status() 49 | ).code === 0 50 | ); 51 | } 52 | 53 | Deno.exit(await main()); 54 | -------------------------------------------------------------------------------- /shared/constants.ts: -------------------------------------------------------------------------------- 1 | import { join, SEPARATOR } from "@std/path"; 2 | 3 | const IS_WINDOWS = Deno.build.os === "windows"; 4 | 5 | export const REPO_URL = "https://deno.land/x/sdl_ts"; 6 | 7 | export const ROOT_PATH = new URL(import.meta.url).pathname 8 | .replaceAll("/", IS_WINDOWS ? "\\" : "/") 9 | .substring(IS_WINDOWS ? 1 : 0) 10 | .split(SEPARATOR) 11 | .slice(0, -2) 12 | .join(SEPARATOR); 13 | 14 | export const ASSETS_PATH = join(ROOT_PATH, "assets"); 15 | 16 | export const EXAMPLES_PATH = join(ROOT_PATH, "examples"); 17 | 18 | export const EXT_PATH = join(ROOT_PATH, "ext"); 19 | 20 | export const SRC_PATH = join(ROOT_PATH, "src"); 21 | 22 | export const TOOLS_PATH = join(ROOT_PATH, "tools"); 23 | -------------------------------------------------------------------------------- /shared/http.ts: -------------------------------------------------------------------------------- 1 | export async function downloadFile( 2 | fromUrl: string | URL, 3 | toPath: string | URL, 4 | overwrite = false, 5 | ): Promise { 6 | const response = await fetch(fromUrl); 7 | 8 | const fileOptions: Deno.OpenOptions = overwrite 9 | ? { 10 | create: true, 11 | write: true, 12 | truncate: true, 13 | } 14 | : { 15 | createNew: true, 16 | write: true, 17 | }; 18 | 19 | try { 20 | const file = await Deno.open(toPath, fileOptions); 21 | await response.body?.pipeTo(file.writable); 22 | } catch (error) { 23 | if (!(error instanceof Deno.errors.AlreadyExists && !overwrite)) { 24 | throw error; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /shared/utils.ts: -------------------------------------------------------------------------------- 1 | export function underscoreToCamelCase(value: string): string { 2 | return value 3 | .split("_") 4 | .map((val, index) => { 5 | if (index === 0) { 6 | return val; 7 | } 8 | 9 | return val.substring(0, 1).toUpperCase() + val.substring(1); 10 | }) 11 | .join(""); 12 | } 13 | -------------------------------------------------------------------------------- /src/SDL/_callbacks.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | 5 | import Platform from "../_platform.ts"; 6 | import { PlatformPointer } from "../_types.ts"; 7 | import { Event } from "./events.ts"; 8 | import { int, Uint8 } from "../types.ts"; 9 | 10 | import { AudioCallback, EventFilter } from "./callbacks.ts"; 11 | import { 12 | AudioSpec, 13 | Color, 14 | DisplayMode, 15 | Keysym, 16 | Palette, 17 | PixelFormat, 18 | Point, 19 | Rect, 20 | Renderer, 21 | RendererInfo, 22 | RWops, 23 | Surface, 24 | SysWMinfo, 25 | Texture, 26 | version, 27 | Window, 28 | } from "./structs.ts"; 29 | 30 | export const callbacks = { 31 | SDL_AudioCallback: { 32 | parameters: [ 33 | /* void* userdata */ "pointer", 34 | /* Uint8* stream */ "pointer", 35 | /* int len */ "i32", 36 | ], 37 | result: /* void */ "void", 38 | wrap: (callback: AudioCallback) => { 39 | return (userdata: PlatformPointer, stream: PlatformPointer, len: int): void => { 40 | return callback( 41 | Platform.fromPlatformPointer(userdata)!, 42 | Platform.fromPlatformPointer(stream)!, 43 | len!, 44 | ); 45 | }; 46 | }, 47 | }, 48 | SDL_EventFilter: { 49 | parameters: [ 50 | /* void* userdata */ "pointer", 51 | /* SDL_Event* event */ "pointer", 52 | ], 53 | result: /* int */ "i32", 54 | wrap: (callback: EventFilter) => { 55 | return (userdata: PlatformPointer, event: PlatformPointer): int => { 56 | return callback( 57 | Platform.fromPlatformPointer(userdata)!, 58 | Event.of(Platform.fromPlatformPointer(event))!, 59 | ); 60 | }; 61 | }, 62 | }, 63 | // TODO: Doesn't seem to be supported yet perhaps due to background thread? 64 | // SDL_TimerCallback 65 | } as const; 66 | -------------------------------------------------------------------------------- /src/SDL/audio.ts: -------------------------------------------------------------------------------- 1 | import { Uint16, Uint32 } from "../types.ts"; 2 | 3 | export type AudioFormat = Uint16; 4 | export type AudioDeviceID = Uint32; 5 | -------------------------------------------------------------------------------- /src/SDL/callbacks.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | 5 | import { Callback, int, Pointer, Uint8 } from "../types.ts"; 6 | import { Event } from "./events.ts"; 7 | 8 | export type AudioCallback = 9 | & ( 10 | ( 11 | userdata: Pointer | null, 12 | stream: Pointer, 13 | len: int, 14 | ) => void 15 | ) 16 | & Callback; 17 | 18 | export type EventFilter = 19 | & ( 20 | ( 21 | userdata: Pointer | null, 22 | event: Event, 23 | ) => int 24 | ) 25 | & Callback; 26 | 27 | // TODO: Doesn't seem to be supported yet perhaps due to background thread? 28 | // SDL_TimerCallback 29 | -------------------------------------------------------------------------------- /src/SDL/constants.ts: -------------------------------------------------------------------------------- 1 | export const FALSE = 0; 2 | export const TRUE = 1; 3 | -------------------------------------------------------------------------------- /src/SDL/enums.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | 5 | import { Enum, Flags } from "../types.ts"; 6 | 7 | export const ArrayOrder = { 8 | NONE: 0, 9 | RGB: 1, 10 | RGBA: 2, 11 | ARGB: 3, 12 | BGR: 4, 13 | BGRA: 5, 14 | ABGR: 6, 15 | } as const; 16 | 17 | export type ArrayOrder = Enum; 18 | 19 | export const BitmapOrder = { 20 | NONE: 0, 21 | _4321: 1, 22 | _1234: 2, 23 | } as const; 24 | 25 | export type BitmapOrder = Enum; 26 | 27 | export const BlendFactor = { 28 | ZERO: 1, 29 | ONE: 2, 30 | SRC_COLOR: 3, 31 | ONE_MINUS_SRC_COLOR: 4, 32 | SRC_ALPHA: 5, 33 | ONE_MINUS_SRC_ALPHA: 6, 34 | DST_COLOR: 7, 35 | ONE_MINUS_DST_COLOR: 8, 36 | DST_ALPHA: 9, 37 | ONE_MINUS_DST_ALPHA: 10, 38 | } as const; 39 | 40 | export type BlendFactor = Enum; 41 | 42 | export const BlendMode = { 43 | NONE: 0, 44 | BLEND: 1, 45 | ADD: 2, 46 | MOD: 4, 47 | MUL: 8, 48 | INVALID: 2147483647, 49 | } as const; 50 | 51 | export type BlendMode = Enum; 52 | 53 | export const BlendOperation = { 54 | ADD: 1, 55 | SUBTRACT: 2, 56 | REV_SUBTRACT: 3, 57 | MINIMUM: 4, 58 | MAXIMUM: 5, 59 | } as const; 60 | 61 | export type BlendOperation = Enum; 62 | 63 | export const EventType = { 64 | FIRSTEVENT: 0, 65 | QUIT: 256, 66 | APP_TERMINATING: 257, 67 | APP_LOWMEMORY: 258, 68 | APP_WILLENTERBACKGROUND: 259, 69 | APP_DIDENTERBACKGROUND: 260, 70 | APP_WILLENTERFOREGROUND: 261, 71 | APP_DIDENTERFOREGROUND: 262, 72 | LOCALECHANGED: 263, 73 | DISPLAYEVENT: 336, 74 | WINDOWEVENT: 512, 75 | SYSWMEVENT: 513, 76 | KEYDOWN: 768, 77 | KEYUP: 769, 78 | TEXTEDITING: 770, 79 | TEXTINPUT: 771, 80 | KEYMAPCHANGED: 772, 81 | MOUSEMOTION: 1024, 82 | MOUSEBUTTONDOWN: 1025, 83 | MOUSEBUTTONUP: 1026, 84 | MOUSEWHEEL: 1027, 85 | JOYAXISMOTION: 1536, 86 | JOYBALLMOTION: 1537, 87 | JOYHATMOTION: 1538, 88 | JOYBUTTONDOWN: 1539, 89 | JOYBUTTONUP: 1540, 90 | JOYDEVICEADDED: 1541, 91 | JOYDEVICEREMOVED: 1542, 92 | CONTROLLERAXISMOTION: 1616, 93 | CONTROLLERBUTTONDOWN: 1617, 94 | CONTROLLERBUTTONUP: 1618, 95 | CONTROLLERDEVICEADDED: 1619, 96 | CONTROLLERDEVICEREMOVED: 1620, 97 | CONTROLLERDEVICEREMAPPED: 1621, 98 | CONTROLLERTOUCHPADDOWN: 1622, 99 | CONTROLLERTOUCHPADMOTION: 1623, 100 | CONTROLLERTOUCHPADUP: 1624, 101 | CONTROLLERSENSORUPDATE: 1625, 102 | FINGERDOWN: 1792, 103 | FINGERUP: 1793, 104 | FINGERMOTION: 1794, 105 | DOLLARGESTURE: 2048, 106 | DOLLARRECORD: 2049, 107 | MULTIGESTURE: 2050, 108 | CLIPBOARDUPDATE: 2304, 109 | DROPFILE: 4096, 110 | DROPTEXT: 4097, 111 | DROPBEGIN: 4098, 112 | DROPCOMPLETE: 4099, 113 | AUDIODEVICEADDED: 4352, 114 | AUDIODEVICEREMOVED: 4353, 115 | SENSORUPDATE: 4608, 116 | RENDER_TARGETS_RESET: 8192, 117 | RENDER_DEVICE_RESET: 8193, 118 | POLLSENTINEL: 32512, 119 | USEREVENT: 32768, 120 | LASTEVENT: 65535, 121 | } as const; 122 | 123 | export type EventType = Enum; 124 | 125 | export const FlashOperation = { 126 | FLASH_CANCEL: 0, 127 | FLASH_BRIEFLY: 1, 128 | FLASH_UNTIL_FOCUSED: 2, 129 | } as const; 130 | 131 | export type FlashOperation = Enum; 132 | 133 | export const InitFlags = { 134 | TIMER: 1, 135 | AUDIO: 16, 136 | VIDEO: 32, 137 | JOYSTICK: 512, 138 | HAPTIC: 4096, 139 | GAMECONTROLLER: 8192, 140 | EVENTS: 16384, 141 | SENSOR: 32768, 142 | NOPARACHUTE: 1048576, 143 | EVERYTHING: 62001, 144 | } as const; 145 | 146 | export type InitFlags = Flags; 147 | 148 | export const Keycode = { 149 | SDLK_UNKNOWN: 0, 150 | SDLK_RETURN: 13, 151 | SDLK_ESCAPE: 27, 152 | SDLK_BACKSPACE: 8, 153 | SDLK_TAB: 9, 154 | SDLK_SPACE: 32, 155 | SDLK_EXCLAIM: 33, 156 | SDLK_QUOTEDBL: 34, 157 | SDLK_HASH: 35, 158 | SDLK_PERCENT: 37, 159 | SDLK_DOLLAR: 36, 160 | SDLK_AMPERSAND: 38, 161 | SDLK_QUOTE: 39, 162 | SDLK_LEFTPAREN: 40, 163 | SDLK_RIGHTPAREN: 41, 164 | SDLK_ASTERISK: 42, 165 | SDLK_PLUS: 43, 166 | SDLK_COMMA: 44, 167 | SDLK_MINUS: 45, 168 | SDLK_PERIOD: 46, 169 | SDLK_SLASH: 47, 170 | SDLK_0: 48, 171 | SDLK_1: 49, 172 | SDLK_2: 50, 173 | SDLK_3: 51, 174 | SDLK_4: 52, 175 | SDLK_5: 53, 176 | SDLK_6: 54, 177 | SDLK_7: 55, 178 | SDLK_8: 56, 179 | SDLK_9: 57, 180 | SDLK_COLON: 58, 181 | SDLK_SEMICOLON: 59, 182 | SDLK_LESS: 60, 183 | SDLK_EQUALS: 61, 184 | SDLK_GREATER: 62, 185 | SDLK_QUESTION: 63, 186 | SDLK_AT: 64, 187 | SDLK_LEFTBRACKET: 91, 188 | SDLK_BACKSLASH: 92, 189 | SDLK_RIGHTBRACKET: 93, 190 | SDLK_CARET: 94, 191 | SDLK_UNDERSCORE: 95, 192 | SDLK_BACKQUOTE: 96, 193 | SDLK_a: 97, 194 | SDLK_b: 98, 195 | SDLK_c: 99, 196 | SDLK_d: 100, 197 | SDLK_e: 101, 198 | SDLK_f: 102, 199 | SDLK_g: 103, 200 | SDLK_h: 104, 201 | SDLK_i: 105, 202 | SDLK_j: 106, 203 | SDLK_k: 107, 204 | SDLK_l: 108, 205 | SDLK_m: 109, 206 | SDLK_n: 110, 207 | SDLK_o: 111, 208 | SDLK_p: 112, 209 | SDLK_q: 113, 210 | SDLK_r: 114, 211 | SDLK_s: 115, 212 | SDLK_t: 116, 213 | SDLK_u: 117, 214 | SDLK_v: 118, 215 | SDLK_w: 119, 216 | SDLK_x: 120, 217 | SDLK_y: 121, 218 | SDLK_z: 122, 219 | SDLK_CAPSLOCK: 1073741881, 220 | SDLK_F1: 1073741882, 221 | SDLK_F2: 1073741883, 222 | SDLK_F3: 1073741884, 223 | SDLK_F4: 1073741885, 224 | SDLK_F5: 1073741886, 225 | SDLK_F6: 1073741887, 226 | SDLK_F7: 1073741888, 227 | SDLK_F8: 1073741889, 228 | SDLK_F9: 1073741890, 229 | SDLK_F10: 1073741891, 230 | SDLK_F11: 1073741892, 231 | SDLK_F12: 1073741893, 232 | SDLK_PRINTSCREEN: 1073741894, 233 | SDLK_SCROLLLOCK: 1073741895, 234 | SDLK_PAUSE: 1073741896, 235 | SDLK_INSERT: 1073741897, 236 | SDLK_HOME: 1073741898, 237 | SDLK_PAGEUP: 1073741899, 238 | SDLK_DELETE: 127, 239 | SDLK_END: 1073741901, 240 | SDLK_PAGEDOWN: 1073741902, 241 | SDLK_RIGHT: 1073741903, 242 | SDLK_LEFT: 1073741904, 243 | SDLK_DOWN: 1073741905, 244 | SDLK_UP: 1073741906, 245 | SDLK_NUMLOCKCLEAR: 1073741907, 246 | SDLK_KP_DIVIDE: 1073741908, 247 | SDLK_KP_MULTIPLY: 1073741909, 248 | SDLK_KP_MINUS: 1073741910, 249 | SDLK_KP_PLUS: 1073741911, 250 | SDLK_KP_ENTER: 1073741912, 251 | SDLK_KP_1: 1073741913, 252 | SDLK_KP_2: 1073741914, 253 | SDLK_KP_3: 1073741915, 254 | SDLK_KP_4: 1073741916, 255 | SDLK_KP_5: 1073741917, 256 | SDLK_KP_6: 1073741918, 257 | SDLK_KP_7: 1073741919, 258 | SDLK_KP_8: 1073741920, 259 | SDLK_KP_9: 1073741921, 260 | SDLK_KP_0: 1073741922, 261 | SDLK_KP_PERIOD: 1073741923, 262 | SDLK_APPLICATION: 1073741925, 263 | SDLK_POWER: 1073741926, 264 | SDLK_KP_EQUALS: 1073741927, 265 | SDLK_F13: 1073741928, 266 | SDLK_F14: 1073741929, 267 | SDLK_F15: 1073741930, 268 | SDLK_F16: 1073741931, 269 | SDLK_F17: 1073741932, 270 | SDLK_F18: 1073741933, 271 | SDLK_F19: 1073741934, 272 | SDLK_F20: 1073741935, 273 | SDLK_F21: 1073741936, 274 | SDLK_F22: 1073741937, 275 | SDLK_F23: 1073741938, 276 | SDLK_F24: 1073741939, 277 | SDLK_EXECUTE: 1073741940, 278 | SDLK_HELP: 1073741941, 279 | SDLK_MENU: 1073741942, 280 | SDLK_SELECT: 1073741943, 281 | SDLK_STOP: 1073741944, 282 | SDLK_AGAIN: 1073741945, 283 | SDLK_UNDO: 1073741946, 284 | SDLK_CUT: 1073741947, 285 | SDLK_COPY: 1073741948, 286 | SDLK_PASTE: 1073741949, 287 | SDLK_FIND: 1073741950, 288 | SDLK_MUTE: 1073741951, 289 | SDLK_VOLUMEUP: 1073741952, 290 | SDLK_VOLUMEDOWN: 1073741953, 291 | SDLK_KP_COMMA: 1073741957, 292 | SDLK_KP_EQUALSAS400: 1073741958, 293 | SDLK_ALTERASE: 1073741977, 294 | SDLK_SYSREQ: 1073741978, 295 | SDLK_CANCEL: 1073741979, 296 | SDLK_CLEAR: 1073741980, 297 | SDLK_PRIOR: 1073741981, 298 | SDLK_RETURN2: 1073741982, 299 | SDLK_SEPARATOR: 1073741983, 300 | SDLK_OUT: 1073741984, 301 | SDLK_OPER: 1073741985, 302 | SDLK_CLEARAGAIN: 1073741986, 303 | SDLK_CRSEL: 1073741987, 304 | SDLK_EXSEL: 1073741988, 305 | SDLK_KP_00: 1073742000, 306 | SDLK_KP_000: 1073742001, 307 | SDLK_THOUSANDSSEPARATOR: 1073742002, 308 | SDLK_DECIMALSEPARATOR: 1073742003, 309 | SDLK_CURRENCYUNIT: 1073742004, 310 | SDLK_CURRENCYSUBUNIT: 1073742005, 311 | SDLK_KP_LEFTPAREN: 1073742006, 312 | SDLK_KP_RIGHTPAREN: 1073742007, 313 | SDLK_KP_LEFTBRACE: 1073742008, 314 | SDLK_KP_RIGHTBRACE: 1073742009, 315 | SDLK_KP_TAB: 1073742010, 316 | SDLK_KP_BACKSPACE: 1073742011, 317 | SDLK_KP_A: 1073742012, 318 | SDLK_KP_B: 1073742013, 319 | SDLK_KP_C: 1073742014, 320 | SDLK_KP_D: 1073742015, 321 | SDLK_KP_E: 1073742016, 322 | SDLK_KP_F: 1073742017, 323 | SDLK_KP_XOR: 1073742018, 324 | SDLK_KP_POWER: 1073742019, 325 | SDLK_KP_PERCENT: 1073742020, 326 | SDLK_KP_LESS: 1073742021, 327 | SDLK_KP_GREATER: 1073742022, 328 | SDLK_KP_AMPERSAND: 1073742023, 329 | SDLK_KP_DBLAMPERSAND: 1073742024, 330 | SDLK_KP_VERTICALBAR: 1073742025, 331 | SDLK_KP_DBLVERTICALBAR: 1073742026, 332 | SDLK_KP_COLON: 1073742027, 333 | SDLK_KP_HASH: 1073742028, 334 | SDLK_KP_SPACE: 1073742029, 335 | SDLK_KP_AT: 1073742030, 336 | SDLK_KP_EXCLAM: 1073742031, 337 | SDLK_KP_MEMSTORE: 1073742032, 338 | SDLK_KP_MEMRECALL: 1073742033, 339 | SDLK_KP_MEMCLEAR: 1073742034, 340 | SDLK_KP_MEMADD: 1073742035, 341 | SDLK_KP_MEMSUBTRACT: 1073742036, 342 | SDLK_KP_MEMMULTIPLY: 1073742037, 343 | SDLK_KP_MEMDIVIDE: 1073742038, 344 | SDLK_KP_PLUSMINUS: 1073742039, 345 | SDLK_KP_CLEAR: 1073742040, 346 | SDLK_KP_CLEARENTRY: 1073742041, 347 | SDLK_KP_BINARY: 1073742042, 348 | SDLK_KP_OCTAL: 1073742043, 349 | SDLK_KP_DECIMAL: 1073742044, 350 | SDLK_KP_HEXADECIMAL: 1073742045, 351 | SDLK_LCTRL: 1073742048, 352 | SDLK_LSHIFT: 1073742049, 353 | SDLK_LALT: 1073742050, 354 | SDLK_LGUI: 1073742051, 355 | SDLK_RCTRL: 1073742052, 356 | SDLK_RSHIFT: 1073742053, 357 | SDLK_RALT: 1073742054, 358 | SDLK_RGUI: 1073742055, 359 | SDLK_MODE: 1073742081, 360 | SDLK_AUDIONEXT: 1073742082, 361 | SDLK_AUDIOPREV: 1073742083, 362 | SDLK_AUDIOSTOP: 1073742084, 363 | SDLK_AUDIOPLAY: 1073742085, 364 | SDLK_AUDIOMUTE: 1073742086, 365 | SDLK_MEDIASELECT: 1073742087, 366 | SDLK_WWW: 1073742088, 367 | SDLK_MAIL: 1073742089, 368 | SDLK_CALCULATOR: 1073742090, 369 | SDLK_COMPUTER: 1073742091, 370 | SDLK_AC_SEARCH: 1073742092, 371 | SDLK_AC_HOME: 1073742093, 372 | SDLK_AC_BACK: 1073742094, 373 | SDLK_AC_FORWARD: 1073742095, 374 | SDLK_AC_STOP: 1073742096, 375 | SDLK_AC_REFRESH: 1073742097, 376 | SDLK_AC_BOOKMARKS: 1073742098, 377 | SDLK_BRIGHTNESSDOWN: 1073742099, 378 | SDLK_BRIGHTNESSUP: 1073742100, 379 | SDLK_DISPLAYSWITCH: 1073742101, 380 | SDLK_KBDILLUMTOGGLE: 1073742102, 381 | SDLK_KBDILLUMDOWN: 1073742103, 382 | SDLK_KBDILLUMUP: 1073742104, 383 | SDLK_EJECT: 1073742105, 384 | SDLK_SLEEP: 1073742106, 385 | SDLK_APP1: 1073742107, 386 | SDLK_APP2: 1073742108, 387 | SDLK_AUDIOREWIND: 1073742109, 388 | SDLK_AUDIOFASTFORWARD: 1073742110, 389 | SDLK_SOFTLEFT: 1073742111, 390 | SDLK_SOFTRIGHT: 1073742112, 391 | SDLK_CALL: 1073742113, 392 | SDLK_ENDCALL: 1073742114, 393 | } as const; 394 | 395 | export type Keycode = Enum; 396 | 397 | export const PackedLayout = { 398 | NONE: 0, 399 | _332: 1, 400 | _4444: 2, 401 | _1555: 3, 402 | _5551: 4, 403 | _565: 5, 404 | _8888: 6, 405 | _2101010: 7, 406 | _1010102: 8, 407 | } as const; 408 | 409 | export type PackedLayout = Enum; 410 | 411 | export const PackedOrder = { 412 | NONE: 0, 413 | XRGB: 1, 414 | RGBX: 2, 415 | ARGB: 3, 416 | RGBA: 4, 417 | XBGR: 5, 418 | BGRX: 6, 419 | ABGR: 7, 420 | BGRA: 8, 421 | } as const; 422 | 423 | export type PackedOrder = Enum; 424 | 425 | export const PixelType = { 426 | UNKNOWN: 0, 427 | INDEX1: 1, 428 | INDEX4: 2, 429 | INDEX8: 3, 430 | PACKED8: 4, 431 | PACKED16: 5, 432 | PACKED32: 6, 433 | ARRAYU8: 7, 434 | ARRAYU16: 8, 435 | ARRAYU32: 9, 436 | ARRAYF16: 10, 437 | ARRAYF32: 11, 438 | } as const; 439 | 440 | export type PixelType = Enum; 441 | 442 | export const RendererFlags = { 443 | SOFTWARE: 1, 444 | ACCELERATED: 2, 445 | PRESENTVSYNC: 4, 446 | TARGETTEXTURE: 8, 447 | } as const; 448 | 449 | export type RendererFlags = Flags; 450 | 451 | export const RendererFlip = { 452 | NONE: 0, 453 | HORIZONTAL: 1, 454 | VERTICAL: 2, 455 | } as const; 456 | 457 | export type RendererFlip = Enum; 458 | 459 | export const SYSWM_TYPE = { 460 | SYSWM_UNKNOWN: 0, 461 | SYSWM_WINDOWS: 1, 462 | SYSWM_X11: 2, 463 | SYSWM_DIRECTFB: 3, 464 | SYSWM_COCOA: 4, 465 | SYSWM_UIKIT: 5, 466 | SYSWM_WAYLAND: 6, 467 | SYSWM_MIR: 7, 468 | SYSWM_WINRT: 8, 469 | SYSWM_ANDROID: 9, 470 | SYSWM_VIVANTE: 10, 471 | SYSWM_OS2: 11, 472 | SYSWM_HAIKU: 12, 473 | SYSWM_KMSDRM: 13, 474 | SYSWM_RISCOS: 14, 475 | } as const; 476 | 477 | export type SYSWM_TYPE = Enum; 478 | 479 | export const ScaleMode = { 480 | Nearest: 0, 481 | Linear: 1, 482 | Best: 2, 483 | } as const; 484 | 485 | export type ScaleMode = Enum; 486 | 487 | export const Scancode = { 488 | UNKNOWN: 0, 489 | A: 4, 490 | B: 5, 491 | C: 6, 492 | D: 7, 493 | E: 8, 494 | F: 9, 495 | G: 10, 496 | H: 11, 497 | I: 12, 498 | J: 13, 499 | K: 14, 500 | L: 15, 501 | M: 16, 502 | N: 17, 503 | O: 18, 504 | P: 19, 505 | Q: 20, 506 | R: 21, 507 | S: 22, 508 | T: 23, 509 | U: 24, 510 | V: 25, 511 | W: 26, 512 | X: 27, 513 | Y: 28, 514 | Z: 29, 515 | _1: 30, 516 | _2: 31, 517 | _3: 32, 518 | _4: 33, 519 | _5: 34, 520 | _6: 35, 521 | _7: 36, 522 | _8: 37, 523 | _9: 38, 524 | _0: 39, 525 | RETURN: 40, 526 | ESCAPE: 41, 527 | BACKSPACE: 42, 528 | TAB: 43, 529 | SPACE: 44, 530 | MINUS: 45, 531 | EQUALS: 46, 532 | LEFTBRACKET: 47, 533 | RIGHTBRACKET: 48, 534 | BACKSLASH: 49, 535 | NONUSHASH: 50, 536 | SEMICOLON: 51, 537 | APOSTROPHE: 52, 538 | GRAVE: 53, 539 | COMMA: 54, 540 | PERIOD: 55, 541 | SLASH: 56, 542 | CAPSLOCK: 57, 543 | F1: 58, 544 | F2: 59, 545 | F3: 60, 546 | F4: 61, 547 | F5: 62, 548 | F6: 63, 549 | F7: 64, 550 | F8: 65, 551 | F9: 66, 552 | F10: 67, 553 | F11: 68, 554 | F12: 69, 555 | PRINTSCREEN: 70, 556 | SCROLLLOCK: 71, 557 | PAUSE: 72, 558 | INSERT: 73, 559 | HOME: 74, 560 | PAGEUP: 75, 561 | DELETE: 76, 562 | END: 77, 563 | PAGEDOWN: 78, 564 | RIGHT: 79, 565 | LEFT: 80, 566 | DOWN: 81, 567 | UP: 82, 568 | NUMLOCKCLEAR: 83, 569 | KP_DIVIDE: 84, 570 | KP_MULTIPLY: 85, 571 | KP_MINUS: 86, 572 | KP_PLUS: 87, 573 | KP_ENTER: 88, 574 | KP_1: 89, 575 | KP_2: 90, 576 | KP_3: 91, 577 | KP_4: 92, 578 | KP_5: 93, 579 | KP_6: 94, 580 | KP_7: 95, 581 | KP_8: 96, 582 | KP_9: 97, 583 | KP_0: 98, 584 | KP_PERIOD: 99, 585 | NONUSBACKSLASH: 100, 586 | APPLICATION: 101, 587 | POWER: 102, 588 | KP_EQUALS: 103, 589 | F13: 104, 590 | F14: 105, 591 | F15: 106, 592 | F16: 107, 593 | F17: 108, 594 | F18: 109, 595 | F19: 110, 596 | F20: 111, 597 | F21: 112, 598 | F22: 113, 599 | F23: 114, 600 | F24: 115, 601 | EXECUTE: 116, 602 | HELP: 117, 603 | MENU: 118, 604 | SELECT: 119, 605 | STOP: 120, 606 | AGAIN: 121, 607 | UNDO: 122, 608 | CUT: 123, 609 | COPY: 124, 610 | PASTE: 125, 611 | FIND: 126, 612 | MUTE: 127, 613 | VOLUMEUP: 128, 614 | VOLUMEDOWN: 129, 615 | KP_COMMA: 133, 616 | KP_EQUALSAS400: 134, 617 | INTERNATIONAL1: 135, 618 | INTERNATIONAL2: 136, 619 | INTERNATIONAL3: 137, 620 | INTERNATIONAL4: 138, 621 | INTERNATIONAL5: 139, 622 | INTERNATIONAL6: 140, 623 | INTERNATIONAL7: 141, 624 | INTERNATIONAL8: 142, 625 | INTERNATIONAL9: 143, 626 | LANG1: 144, 627 | LANG2: 145, 628 | LANG3: 146, 629 | LANG4: 147, 630 | LANG5: 148, 631 | LANG6: 149, 632 | LANG7: 150, 633 | LANG8: 151, 634 | LANG9: 152, 635 | ALTERASE: 153, 636 | SYSREQ: 154, 637 | CANCEL: 155, 638 | CLEAR: 156, 639 | PRIOR: 157, 640 | RETURN2: 158, 641 | SEPARATOR: 159, 642 | OUT: 160, 643 | OPER: 161, 644 | CLEARAGAIN: 162, 645 | CRSEL: 163, 646 | EXSEL: 164, 647 | KP_00: 176, 648 | KP_000: 177, 649 | THOUSANDSSEPARATOR: 178, 650 | DECIMALSEPARATOR: 179, 651 | CURRENCYUNIT: 180, 652 | CURRENCYSUBUNIT: 181, 653 | KP_LEFTPAREN: 182, 654 | KP_RIGHTPAREN: 183, 655 | KP_LEFTBRACE: 184, 656 | KP_RIGHTBRACE: 185, 657 | KP_TAB: 186, 658 | KP_BACKSPACE: 187, 659 | KP_A: 188, 660 | KP_B: 189, 661 | KP_C: 190, 662 | KP_D: 191, 663 | KP_E: 192, 664 | KP_F: 193, 665 | KP_XOR: 194, 666 | KP_POWER: 195, 667 | KP_PERCENT: 196, 668 | KP_LESS: 197, 669 | KP_GREATER: 198, 670 | KP_AMPERSAND: 199, 671 | KP_DBLAMPERSAND: 200, 672 | KP_VERTICALBAR: 201, 673 | KP_DBLVERTICALBAR: 202, 674 | KP_COLON: 203, 675 | KP_HASH: 204, 676 | KP_SPACE: 205, 677 | KP_AT: 206, 678 | KP_EXCLAM: 207, 679 | KP_MEMSTORE: 208, 680 | KP_MEMRECALL: 209, 681 | KP_MEMCLEAR: 210, 682 | KP_MEMADD: 211, 683 | KP_MEMSUBTRACT: 212, 684 | KP_MEMMULTIPLY: 213, 685 | KP_MEMDIVIDE: 214, 686 | KP_PLUSMINUS: 215, 687 | KP_CLEAR: 216, 688 | KP_CLEARENTRY: 217, 689 | KP_BINARY: 218, 690 | KP_OCTAL: 219, 691 | KP_DECIMAL: 220, 692 | KP_HEXADECIMAL: 221, 693 | LCTRL: 224, 694 | LSHIFT: 225, 695 | LALT: 226, 696 | LGUI: 227, 697 | RCTRL: 228, 698 | RSHIFT: 229, 699 | RALT: 230, 700 | RGUI: 231, 701 | MODE: 257, 702 | AUDIONEXT: 258, 703 | AUDIOPREV: 259, 704 | AUDIOSTOP: 260, 705 | AUDIOPLAY: 261, 706 | AUDIOMUTE: 262, 707 | MEDIASELECT: 263, 708 | WWW: 264, 709 | MAIL: 265, 710 | CALCULATOR: 266, 711 | COMPUTER: 267, 712 | AC_SEARCH: 268, 713 | AC_HOME: 269, 714 | AC_BACK: 270, 715 | AC_FORWARD: 271, 716 | AC_STOP: 272, 717 | AC_REFRESH: 273, 718 | AC_BOOKMARKS: 274, 719 | BRIGHTNESSDOWN: 275, 720 | BRIGHTNESSUP: 276, 721 | DISPLAYSWITCH: 277, 722 | KBDILLUMTOGGLE: 278, 723 | KBDILLUMDOWN: 279, 724 | KBDILLUMUP: 280, 725 | EJECT: 281, 726 | SLEEP: 282, 727 | APP1: 283, 728 | APP2: 284, 729 | AUDIOREWIND: 285, 730 | AUDIOFASTFORWARD: 286, 731 | NUM_SCANCODES: 512, 732 | } as const; 733 | 734 | export type Scancode = Enum; 735 | 736 | export const TextureAccess = { 737 | STATIC: 0, 738 | STREAMING: 1, 739 | TARGET: 2, 740 | } as const; 741 | 742 | export type TextureAccess = Enum; 743 | 744 | export const TextureModulate = { 745 | NONE: 0, 746 | COLOR: 1, 747 | ALPHA: 2, 748 | } as const; 749 | 750 | export type TextureModulate = Enum; 751 | 752 | export const WindowEventID = { 753 | NONE: 0, 754 | SHOWN: 1, 755 | HIDDEN: 2, 756 | EXPOSED: 3, 757 | MOVED: 4, 758 | RESIZED: 5, 759 | SIZE_CHANGED: 6, 760 | MINIMIZED: 7, 761 | MAXIMIZED: 8, 762 | RESTORED: 9, 763 | ENTER: 10, 764 | LEAVE: 11, 765 | FOCUS_GAINED: 12, 766 | FOCUS_LOST: 13, 767 | CLOSE: 14, 768 | TAKE_FOCUS: 15, 769 | HIT_TEST: 16, 770 | ICCPROF_CHANGED: 17, 771 | DISPLAY_CHANGED: 18, 772 | } as const; 773 | 774 | export type WindowEventID = Enum; 775 | 776 | export const WindowFlags = { 777 | FULLSCREEN: 1, 778 | OPENGL: 2, 779 | SHOWN: 4, 780 | HIDDEN: 8, 781 | BORDERLESS: 16, 782 | RESIZABLE: 32, 783 | MINIMIZED: 64, 784 | MAXIMIZED: 128, 785 | MOUSE_GRABBED: 256, 786 | INPUT_FOCUS: 512, 787 | MOUSE_FOCUS: 1024, 788 | FULLSCREEN_DESKTOP: 4097, 789 | FOREIGN: 2048, 790 | ALLOW_HIGHDPI: 8192, 791 | MOUSE_CAPTURE: 16384, 792 | ALWAYS_ON_TOP: 32768, 793 | SKIP_TASKBAR: 65536, 794 | UTILITY: 131072, 795 | TOOLTIP: 262144, 796 | POPUP_MENU: 524288, 797 | KEYBOARD_GRABBED: 1048576, 798 | VULKAN: 268435456, 799 | METAL: 536870912, 800 | INPUT_GRABBED: 256, 801 | } as const; 802 | 803 | export type WindowFlags = Flags; 804 | 805 | export const WindowPos = { 806 | UNDEFINED: 536805376, 807 | CENTERED: 805240832, 808 | } as const; 809 | 810 | export type WindowPos = Enum; 811 | -------------------------------------------------------------------------------- /src/SDL/events.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | import Platform from "../_platform.ts"; 4 | import { PlatformDataView } from "../_types.ts"; 5 | import { EventType, WindowEventID } from "./enums.ts"; 6 | import { Keysym } from "./structs.ts"; 7 | import { AllocatableStruct, float, int, Pointer, Sint32, Uint32, Uint8 } from "../types.ts"; 8 | 9 | export class CommonEvent { 10 | constructor( 11 | public readonly _data: Uint8Array | Pointer, 12 | private _view: PlatformDataView, 13 | ) { 14 | } 15 | 16 | public get type(): EventType { 17 | return this._view.getU32(0) as EventType; 18 | } 19 | 20 | public get timestamp(): Uint32 { 21 | return this._view.getU32(4); 22 | } 23 | } 24 | 25 | export class DisplayEvent { 26 | constructor( 27 | public readonly _data: Uint8Array | Pointer, 28 | private _view: PlatformDataView, 29 | ) { 30 | } 31 | 32 | public get type(): EventType { 33 | return this._view.getU32(0) as EventType; 34 | } 35 | 36 | public get timestamp(): Uint32 { 37 | return this._view.getU32(4); 38 | } 39 | 40 | public get display(): Uint32 { 41 | return this._view.getU32(8); 42 | } 43 | 44 | public get event(): Uint8 { 45 | return this._view.getU8(12); 46 | } 47 | 48 | // padding1 49 | 50 | // padding2 51 | 52 | // padding3 53 | 54 | public get data1(): Sint32 { 55 | return this._view.getI32(16); 56 | } 57 | } 58 | 59 | export class KeyboardEvent { 60 | private _keysym: Keysym; 61 | 62 | constructor( 63 | public readonly _data: Uint8Array | Pointer, 64 | private _view: PlatformDataView, 65 | ) { 66 | this._keysym = Keysym.of(this._data, 16) as Keysym; 67 | } 68 | 69 | public get type(): EventType { 70 | return this._view.getU32(0) as EventType; 71 | } 72 | 73 | public get timestamp(): Uint32 { 74 | return this._view.getU32(4); 75 | } 76 | 77 | public get windowID(): Uint32 { 78 | return this._view.getU32(8); 79 | } 80 | 81 | public get state(): Uint8 { 82 | return this._view.getU8(12); 83 | } 84 | 85 | public get repeat(): Uint8 { 86 | return this._view.getU8(13); 87 | } 88 | 89 | // padding2 90 | 91 | // padding3 92 | 93 | public get keysym(): Keysym { 94 | return this._keysym; 95 | } 96 | } 97 | 98 | export class MouseButtonEvent { 99 | constructor( 100 | public readonly _data: Uint8Array | Pointer, 101 | private _view: PlatformDataView, 102 | ) { 103 | } 104 | 105 | public get type(): EventType { 106 | return this._view.getU32(0) as EventType; 107 | } 108 | 109 | public get timestamp(): Uint32 { 110 | return this._view.getU32(4); 111 | } 112 | 113 | public get windowID(): Uint32 { 114 | return this._view.getU32(8); 115 | } 116 | 117 | public get which(): Uint32 { 118 | return this._view.getU32(12); 119 | } 120 | 121 | public get button(): Uint8 { 122 | return this._view.getU8(16); 123 | } 124 | 125 | public get state(): Uint8 { 126 | return this._view.getU8(17); 127 | } 128 | 129 | public get clicks(): Uint8 { 130 | return this._view.getU8(18); 131 | } 132 | 133 | // padding1 134 | 135 | public get x(): Sint32 { 136 | return this._view.getI32(20); 137 | } 138 | 139 | public get y(): Sint32 { 140 | return this._view.getI32(24); 141 | } 142 | } 143 | 144 | export class MouseMotionEvent { 145 | constructor( 146 | public readonly _data: Uint8Array | Pointer, 147 | private _view: PlatformDataView, 148 | ) { 149 | } 150 | 151 | public get type(): EventType { 152 | return this._view.getU32(0) as EventType; 153 | } 154 | 155 | public get timestamp(): Uint32 { 156 | return this._view.getU32(4); 157 | } 158 | 159 | public get windowID(): Uint32 { 160 | return this._view.getU32(8); 161 | } 162 | 163 | public get which(): Uint32 { 164 | return this._view.getU32(12); 165 | } 166 | 167 | public get state(): Uint32 { 168 | return this._view.getU32(16); 169 | } 170 | 171 | public get x(): Sint32 { 172 | return this._view.getI32(20); 173 | } 174 | 175 | public get y(): Sint32 { 176 | return this._view.getI32(24); 177 | } 178 | 179 | public get xrel(): Sint32 { 180 | return this._view.getI32(28); 181 | } 182 | 183 | public get yrel(): Sint32 { 184 | return this._view.getI32(32); 185 | } 186 | } 187 | 188 | export class MouseWheelEvent { 189 | constructor( 190 | public readonly _data: Uint8Array | Pointer, 191 | private _view: PlatformDataView, 192 | ) { 193 | } 194 | 195 | public get type(): EventType { 196 | return this._view.getU32(0) as EventType; 197 | } 198 | 199 | public get timestamp(): Uint32 { 200 | return this._view.getU32(4); 201 | } 202 | 203 | public get windowID(): Uint32 { 204 | return this._view.getU32(8); 205 | } 206 | 207 | public get which(): Uint32 { 208 | return this._view.getU32(12); 209 | } 210 | 211 | public get x(): Sint32 { 212 | return this._view.getI32(16); 213 | } 214 | 215 | public get y(): Sint32 { 216 | return this._view.getI32(20); 217 | } 218 | 219 | public get direction(): Uint32 { 220 | return this._view.getU32(24); 221 | } 222 | 223 | public get preciseX(): float { 224 | return this._view.getF32(28); 225 | } 226 | 227 | public get preciseY(): float { 228 | return this._view.getF32(32); 229 | } 230 | } 231 | 232 | export class WindowEvent { 233 | constructor( 234 | public readonly _data: Uint8Array | Pointer, 235 | private _view: PlatformDataView, 236 | ) { 237 | } 238 | 239 | public get type(): EventType { 240 | return this._view.getU32(0) as EventType; 241 | } 242 | 243 | public get timestamp(): Uint32 { 244 | return this._view.getU32(4); 245 | } 246 | 247 | public get windowID(): Uint32 { 248 | return this._view.getU32(8); 249 | } 250 | 251 | public get event(): WindowEventID { 252 | return this._view.getU8(12) as WindowEventID; 253 | } 254 | 255 | // padding1 256 | 257 | // padding2 258 | 259 | // padding3 260 | 261 | public get data1(): Sint32 { 262 | return this._view.getI32(16); 263 | } 264 | 265 | public get data2(): Sint32 { 266 | return this._view.getI32(20); 267 | } 268 | } 269 | 270 | export class Event implements AllocatableStruct { 271 | public static SIZE_IN_BYTES = 64; 272 | 273 | public readonly _data: Uint8Array | Pointer; 274 | public readonly _view: PlatformDataView; 275 | 276 | public readonly common: CommonEvent; 277 | public readonly display: DisplayEvent; 278 | public readonly key: KeyboardEvent; 279 | public readonly mousebutton: MouseButtonEvent; 280 | public readonly mousemotion: MouseMotionEvent; 281 | public readonly mousewheel: MouseWheelEvent; 282 | public readonly window: WindowEvent; 283 | 284 | constructor( 285 | data?: Uint8Array | Pointer, 286 | byteOffset: number = 0, 287 | ) { 288 | this._data = data ?? new Uint8Array(Event.SIZE_IN_BYTES); 289 | this._view = new Platform.DataView(this._data, byteOffset); 290 | 291 | this.common = new CommonEvent(this._data, this._view); 292 | this.display = new DisplayEvent(this._data, this._view); 293 | this.key = new KeyboardEvent(this._data, this._view); 294 | this.mousebutton = new MouseButtonEvent(this._data, this._view); 295 | this.mousemotion = new MouseMotionEvent(this._data, this._view); 296 | this.mousewheel = new MouseWheelEvent(this._data, this._view); 297 | this.window = new WindowEvent(this._data, this._view); 298 | } 299 | 300 | public static of( 301 | data: Uint8Array | Pointer | null, 302 | byteOffset: number = 0, 303 | ): Event | null { 304 | return data !== null ? new Event(data, byteOffset) : null; 305 | } 306 | 307 | public get _byteOffset(): number { 308 | return this._view.byteOffset; 309 | } 310 | 311 | public get type(): EventType { 312 | return this._view.getU32(0) as EventType; 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /src/SDL/functionMacros.ts: -------------------------------------------------------------------------------- 1 | // This file contains function macros that aren't just a simple 2 | // 1 to 1 mapping. 3 | 4 | import { LoadBMP_RW, RWFromFile } from "./functions.ts"; 5 | import { AudioSpec, Surface } from "./structs.ts"; 6 | import { type PointerLike } from "../types.ts"; 7 | import { LoadWAV_RW } from "../../mod.SDL.ts"; 8 | 9 | export function LoadBMP(file: string): Surface { 10 | return LoadBMP_RW(RWFromFile(file, "rb"), 1); 11 | } 12 | 13 | export function LoadWAV( 14 | file: string, 15 | spec: PointerLike, 16 | ): [AudioSpec, Uint8Array] { 17 | return LoadWAV_RW(RWFromFile(file, "rb"), 1, spec); 18 | } 19 | -------------------------------------------------------------------------------- /src/SDL/pixels.ts: -------------------------------------------------------------------------------- 1 | // deno-fmt-ignore-file 2 | import { Uint32 } from "../types.ts"; 3 | import { 4 | ArrayOrder, 5 | BitmapOrder, 6 | PackedLayout, 7 | PackedOrder, 8 | PixelType 9 | } from "./enums.ts"; 10 | 11 | function SDL_DEFINE_PIXELFORMAT( 12 | type: number, 13 | order: number, 14 | layout: number, 15 | bits: number, 16 | bytes: number, 17 | ): number { 18 | return ((1 << 28) | ((type) << 24) | ((order) << 20) | 19 | ((layout) << 16) | ((bits) << 8) | ((bytes) << 0)); 20 | } 21 | 22 | export const PIXELFORMAT_UNKNOWN: Uint32 = 0; 23 | export const PIXELFORMAT_INDEX1LSB: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.INDEX1, BitmapOrder._4321, 0, 1, 0); 24 | export const PIXELFORMAT_INDEX1MSB: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.INDEX1, BitmapOrder._1234, 0, 1, 0); 25 | export const PIXELFORMAT_INDEX4LSB: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.INDEX4, BitmapOrder._4321, 0, 4, 0); 26 | export const PIXELFORMAT_INDEX4MSB: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.INDEX4, BitmapOrder._1234, 0, 4, 0); 27 | export const PIXELFORMAT_INDEX8: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.INDEX8, 0, 0, 8, 1); 28 | export const PIXELFORMAT_RGB332: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED8, PackedOrder.XRGB, PackedLayout._332, 8, 1); 29 | export const PIXELFORMAT_XRGB4444: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.XRGB, PackedLayout._4444, 12, 2); 30 | export const PIXELFORMAT_RGB444: Uint32 = PIXELFORMAT_XRGB4444; 31 | export const PIXELFORMAT_XBGR4444: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.XBGR, PackedLayout._4444, 12, 2); 32 | export const PIXELFORMAT_BGR444: Uint32 = PIXELFORMAT_XBGR4444; 33 | export const PIXELFORMAT_XRGB1555: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.XRGB, PackedLayout._1555, 15, 2); 34 | export const PIXELFORMAT_RGB555: Uint32 = PIXELFORMAT_XRGB1555; 35 | export const PIXELFORMAT_XBGR1555: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.XBGR, PackedLayout._1555, 15, 2); 36 | export const PIXELFORMAT_BGR555: Uint32 = PIXELFORMAT_XBGR1555; 37 | export const PIXELFORMAT_ARGB4444: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.ARGB, PackedLayout._4444, 16, 2); 38 | export const PIXELFORMAT_RGBA4444: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.RGBA, PackedLayout._4444, 16, 2); 39 | export const PIXELFORMAT_ABGR4444: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.ABGR, PackedLayout._4444, 16, 2); 40 | export const PIXELFORMAT_BGRA4444: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.BGRA, PackedLayout._4444, 16, 2); 41 | export const PIXELFORMAT_ARGB1555: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.ARGB, PackedLayout._1555, 16, 2); 42 | export const PIXELFORMAT_RGBA5551: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.RGBA, PackedLayout._5551, 16, 2); 43 | export const PIXELFORMAT_ABGR1555: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.ABGR, PackedLayout._1555, 16, 2); 44 | export const PIXELFORMAT_BGRA5551: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.BGRA, PackedLayout._5551, 16, 2); 45 | export const PIXELFORMAT_RGB565: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.XRGB, PackedLayout._565, 16, 2); 46 | export const PIXELFORMAT_BGR565: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED16, PackedOrder.XBGR, PackedLayout._565, 16, 2); 47 | export const PIXELFORMAT_RGB24: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.ARRAYU8, ArrayOrder.RGB, 0, 24, 3); 48 | export const PIXELFORMAT_BGR24: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.ARRAYU8, ArrayOrder.BGR, 0, 24, 3); 49 | export const PIXELFORMAT_XRGB8888: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED32, PackedOrder.XRGB, PackedLayout._8888, 24, 4); 50 | export const PIXELFORMAT_RGB888: Uint32 = PIXELFORMAT_XRGB8888; 51 | export const PIXELFORMAT_RGBX8888: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED32, PackedOrder.RGBX, PackedLayout._8888, 24, 4); 52 | export const PIXELFORMAT_XBGR8888: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED32, PackedOrder.XBGR, PackedLayout._8888, 24, 4); 53 | export const PIXELFORMAT_BGR888: Uint32 = PIXELFORMAT_XBGR8888; 54 | export const PIXELFORMAT_BGRX8888: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED32, PackedOrder.BGRX, PackedLayout._8888, 24, 4); 55 | export const PIXELFORMAT_ARGB8888: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED32, PackedOrder.ARGB, PackedLayout._8888, 32, 4); 56 | export const PIXELFORMAT_RGBA8888: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED32, PackedOrder.RGBA, PackedLayout._8888, 32, 4); 57 | export const PIXELFORMAT_ABGR8888: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED32, PackedOrder.ABGR, PackedLayout._8888, 32, 4); 58 | export const PIXELFORMAT_BGRA8888: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED32, PackedOrder.BGRA, PackedLayout._8888, 32, 4); 59 | export const PIXELFORMAT_ARGB2101010: Uint32 = SDL_DEFINE_PIXELFORMAT(PixelType.PACKED32, PackedOrder.ARGB, PackedLayout._2101010, 32, 4); 60 | /* 61 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN 62 | SDL_PIXELFORMAT_RGBA32 = SDL_PIXELFORMAT_RGBA8888, 63 | SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_ARGB8888, 64 | SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_BGRA8888, 65 | SDL_PIXELFORMAT_ABGR32 = SDL_PIXELFORMAT_ABGR8888, 66 | #else 67 | SDL_PIXELFORMAT_RGBA32 = SDL_PIXELFORMAT_ABGR8888, 68 | SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_BGRA8888, 69 | SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_ARGB8888, 70 | SDL_PIXELFORMAT_ABGR32 = SDL_PIXELFORMAT_RGBA8888, 71 | #endif 72 | 73 | SDL_PIXELFORMAT_YV12 = 74 | SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2'), 75 | SDL_PIXELFORMAT_IYUV = 76 | SDL_DEFINE_PIXELFOURCC('I', 'Y', 'U', 'V'), 77 | SDL_PIXELFORMAT_YUY2 = 78 | SDL_DEFINE_PIXELFOURCC('Y', 'U', 'Y', '2'), 79 | SDL_PIXELFORMAT_UYVY = 80 | SDL_DEFINE_PIXELFOURCC('U', 'Y', 'V', 'Y'), 81 | SDL_PIXELFORMAT_YVYU = 82 | SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U'), 83 | SDL_PIXELFORMAT_NV12 = 84 | SDL_DEFINE_PIXELFOURCC('N', 'V', '1', '2'), 85 | SDL_PIXELFORMAT_NV21 = 86 | SDL_DEFINE_PIXELFOURCC('N', 'V', '2', '1'), 87 | SDL_PIXELFORMAT_EXTERNAL_OES = 88 | SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' ') 89 | */ 90 | -------------------------------------------------------------------------------- /src/SDL/rw.ts: -------------------------------------------------------------------------------- 1 | export type RWMode = 2 | | "a" 3 | | "a+" 4 | | "r" 5 | | "r+" 6 | | "w" 7 | | "w+" 8 | | "ab" 9 | | "ab+" 10 | | "rb" 11 | | "rb+" 12 | | "wb" 13 | | "wb+"; -------------------------------------------------------------------------------- /src/SDL_image/_callbacks.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | 5 | import Platform from "../_platform.ts"; 6 | import { PlatformPointer } from "../_types.ts"; 7 | import { int, Uint8 } from "../types.ts"; 8 | 9 | export const callbacks = {} as const; 10 | -------------------------------------------------------------------------------- /src/SDL_image/_symbols.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | export const symbols = { 4 | IMG_Init: { 5 | parameters: [ 6 | /* int flags */ "i32", 7 | ], 8 | result: /* int */ "i32", 9 | }, 10 | IMG_Linked_Version: { 11 | parameters: [], 12 | result: /* SDL_version* */ "pointer", 13 | }, 14 | IMG_Load: { 15 | parameters: [ 16 | /* char* file */ "pointer", 17 | ], 18 | result: /* SDL_Surface* */ "pointer", 19 | }, 20 | IMG_LoadTexture: { 21 | parameters: [ 22 | /* SDL_Renderer* renderer */ "pointer", 23 | /* char* file */ "pointer", 24 | ], 25 | result: /* SDL_Texture* */ "pointer", 26 | }, 27 | IMG_Quit: { 28 | parameters: [], 29 | result: /* void */ "void", 30 | }, 31 | } as const; 32 | -------------------------------------------------------------------------------- /src/SDL_image/callbacks.ts: -------------------------------------------------------------------------------- 1 | // Placeholder file until the single struct is generated. 2 | -------------------------------------------------------------------------------- /src/SDL_image/enums.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | 5 | import { Enum, Flags } from "../types.ts"; 6 | 7 | export const InitFlags = { 8 | JPG: 1, 9 | PNG: 2, 10 | TIF: 4, 11 | WEBP: 8, 12 | JXL: 16, 13 | AVIF: 32, 14 | } as const; 15 | 16 | export type InitFlags = Flags; 17 | -------------------------------------------------------------------------------- /src/SDL_image/functions.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | 5 | import Platform from "../_platform.ts"; 6 | import { callbacks } from "./_callbacks.ts"; 7 | import { getSymbolsFromFunctions } from "../_init.ts"; 8 | import { DynamicLibrary } from "../_library.ts"; 9 | import { symbols } from "./_symbols.ts"; 10 | import { PlatformPointer } from "../_types.ts"; 11 | import { Box } from "../_boxes.ts"; 12 | import { SDLError } from "../error.ts"; 13 | import { double, float, InitOptions, int, Pointer, PointerLike, Uint16, Uint32, Uint64, Uint8 } from "../types.ts"; 14 | 15 | import {} from "./callbacks.ts"; 16 | import { InitFlags } from "./enums.ts"; 17 | import {} from "./structs.ts"; 18 | 19 | import { GetError } from "../SDL/functions.ts"; 20 | import { Renderer, Surface, Texture, version } from "../SDL/structs.ts"; 21 | 22 | let _library: DynamicLibrary = null!; 23 | 24 | export function Init(flags: InitFlags, options?: InitOptions): void; 25 | export function Init(flags: number, options?: InitOptions): void; 26 | export function Init(flags: InitFlags | number, options?: InitOptions): void { 27 | const symbolsToLoad = options?.functions ? getSymbolsFromFunctions(symbols, options.functions) : symbols; 28 | _library = Platform.loadLibrary("SDL2_image", symbolsToLoad, options?.libraryPath); 29 | const _result = _library.symbols.IMG_Init(flags) as number; 30 | if (_result < 0) { 31 | throw new SDLError(GetError()); 32 | } 33 | } 34 | Init.symbolName = "IMG_Init"; 35 | 36 | export function Linked_Version(): version { 37 | const _result = version.of( 38 | Platform.fromPlatformPointer(_library.symbols.IMG_Linked_Version() as PlatformPointer), 39 | ); 40 | if (_result === null) { 41 | throw new SDLError(GetError()); 42 | } 43 | return _result; 44 | } 45 | Linked_Version.symbolName = "IMG_Linked_Version"; 46 | 47 | export function Load( 48 | file: string, 49 | ): Pointer { 50 | const _result = Platform.fromPlatformPointer(_library.symbols.IMG_Load( 51 | Platform.toPlatformString(file), 52 | ) as PlatformPointer>); 53 | if (_result === null) { 54 | throw new SDLError(GetError()); 55 | } 56 | return _result; 57 | } 58 | Load.symbolName = "IMG_Load"; 59 | 60 | export function LoadTexture( 61 | renderer: Pointer, 62 | file: string, 63 | ): Pointer { 64 | const _result = Platform.fromPlatformPointer(_library.symbols.IMG_LoadTexture( 65 | Platform.toPlatformPointer(renderer), 66 | Platform.toPlatformString(file), 67 | ) as PlatformPointer>); 68 | if (_result === null) { 69 | throw new SDLError(GetError()); 70 | } 71 | return _result; 72 | } 73 | LoadTexture.symbolName = "IMG_LoadTexture"; 74 | 75 | export function Quit(): void { 76 | _library.symbols.IMG_Quit(); 77 | _library.close(); 78 | } 79 | Quit.symbolName = "IMG_Quit"; 80 | -------------------------------------------------------------------------------- /src/SDL_image/structs.ts: -------------------------------------------------------------------------------- 1 | // Placeholder file until the single struct is generated. 2 | -------------------------------------------------------------------------------- /src/SDL_ttf/_callbacks.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | 5 | import Platform from "../_platform.ts"; 6 | import { PlatformPointer } from "../_types.ts"; 7 | import { int, Uint8 } from "../types.ts"; 8 | 9 | export const callbacks = {} as const; 10 | -------------------------------------------------------------------------------- /src/SDL_ttf/_symbols.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | export const symbols = { 4 | TTF_Init: { 5 | parameters: [], 6 | result: /* int */ "i32", 7 | }, 8 | TTF_CloseFont: { 9 | parameters: [ 10 | /* TTF_Font* font */ "pointer", 11 | ], 12 | result: /* void */ "void", 13 | }, 14 | TTF_Linked_Version: { 15 | parameters: [], 16 | result: /* SDL_version* */ "pointer", 17 | }, 18 | TTF_OpenFont: { 19 | parameters: [ 20 | /* char* file */ "pointer", 21 | /* int ptsize */ "i32", 22 | ], 23 | result: /* TTF_Font* */ "pointer", 24 | }, 25 | TTF_Quit: { 26 | parameters: [], 27 | result: /* void */ "void", 28 | }, 29 | TTF_RenderText_Blended: { 30 | parameters: [ 31 | /* TTF_Font* font */ "pointer", 32 | /* char* text */ "pointer", 33 | /* SDL_Color fg */ { "struct": ["u8", "u8", "u8", "u8"] }, 34 | ], 35 | result: /* SDL_Surface* */ "pointer", 36 | }, 37 | TTF_RenderText_LCD: { 38 | parameters: [ 39 | /* TTF_Font* font */ "pointer", 40 | /* char* text */ "pointer", 41 | /* SDL_Color fg */ { "struct": ["u8", "u8", "u8", "u8"] }, 42 | /* SDL_Color bg */ { "struct": ["u8", "u8", "u8", "u8"] }, 43 | ], 44 | result: /* SDL_Surface* */ "pointer", 45 | }, 46 | TTF_RenderText_Solid: { 47 | parameters: [ 48 | /* TTF_Font* font */ "pointer", 49 | /* char* text */ "pointer", 50 | /* SDL_Color fg */ { "struct": ["u8", "u8", "u8", "u8"] }, 51 | ], 52 | result: /* SDL_Surface* */ "pointer", 53 | }, 54 | TTF_RenderText_Shaded: { 55 | parameters: [ 56 | /* TTF_Font* font */ "pointer", 57 | /* char* text */ "pointer", 58 | /* SDL_Color fg */ { "struct": ["u8", "u8", "u8", "u8"] }, 59 | /* SDL_Color bg */ { "struct": ["u8", "u8", "u8", "u8"] }, 60 | ], 61 | result: /* SDL_Surface* */ "pointer", 62 | }, 63 | TTF_RenderUTF8_Blended: { 64 | parameters: [ 65 | /* TTF_Font* font */ "pointer", 66 | /* char* text */ "pointer", 67 | /* SDL_Color fg */ { "struct": ["u8", "u8", "u8", "u8"] }, 68 | ], 69 | result: /* SDL_Surface* */ "pointer", 70 | }, 71 | TTF_RenderUTF8_LCD: { 72 | parameters: [ 73 | /* TTF_Font* font */ "pointer", 74 | /* char* text */ "pointer", 75 | /* SDL_Color fg */ { "struct": ["u8", "u8", "u8", "u8"] }, 76 | /* SDL_Color bg */ { "struct": ["u8", "u8", "u8", "u8"] }, 77 | ], 78 | result: /* SDL_Surface* */ "pointer", 79 | }, 80 | TTF_RenderUTF8_Solid: { 81 | parameters: [ 82 | /* TTF_Font* font */ "pointer", 83 | /* char* text */ "pointer", 84 | /* SDL_Color fg */ { "struct": ["u8", "u8", "u8", "u8"] }, 85 | ], 86 | result: /* SDL_Surface* */ "pointer", 87 | }, 88 | TTF_RenderUTF8_Shaded: { 89 | parameters: [ 90 | /* TTF_Font* font */ "pointer", 91 | /* char* text */ "pointer", 92 | /* SDL_Color fg */ { "struct": ["u8", "u8", "u8", "u8"] }, 93 | /* SDL_Color bg */ { "struct": ["u8", "u8", "u8", "u8"] }, 94 | ], 95 | result: /* SDL_Surface* */ "pointer", 96 | }, 97 | TTF_SizeText: { 98 | parameters: [ 99 | /* TTF_Font* font */ "pointer", 100 | /* char* text */ "pointer", 101 | /* int* w */ "pointer", 102 | /* int* h */ "pointer", 103 | ], 104 | result: /* int */ "i32", 105 | }, 106 | TTF_SizeUTF8: { 107 | parameters: [ 108 | /* TTF_Font* font */ "pointer", 109 | /* char* text */ "pointer", 110 | /* int* w */ "pointer", 111 | /* int* h */ "pointer", 112 | ], 113 | result: /* int */ "i32", 114 | }, 115 | TTF_SizeUNICODE: { 116 | parameters: [ 117 | /* TTF_Font* font */ "pointer", 118 | /* char* text */ "pointer", 119 | /* int* w */ "pointer", 120 | /* int* h */ "pointer", 121 | ], 122 | result: /* int */ "i32", 123 | }, 124 | } as const; 125 | -------------------------------------------------------------------------------- /src/SDL_ttf/callbacks.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | -------------------------------------------------------------------------------- /src/SDL_ttf/enums.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | 5 | import { Enum, Flags } from "../types.ts"; 6 | -------------------------------------------------------------------------------- /src/SDL_ttf/functions.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | 5 | import Platform from "../_platform.ts"; 6 | import { callbacks } from "./_callbacks.ts"; 7 | import { getSymbolsFromFunctions } from "../_init.ts"; 8 | import { DynamicLibrary } from "../_library.ts"; 9 | import { symbols } from "./_symbols.ts"; 10 | import { PlatformPointer } from "../_types.ts"; 11 | import { Box } from "../_boxes.ts"; 12 | import { SDLError } from "../error.ts"; 13 | import { double, float, InitOptions, int, Pointer, PointerLike, Uint16, Uint32, Uint64, Uint8 } from "../types.ts"; 14 | 15 | import {} from "./callbacks.ts"; 16 | import {} from "./enums.ts"; 17 | import { Font } from "./structs.ts"; 18 | 19 | import { GetError } from "../SDL/functions.ts"; 20 | import { Color, Surface, version } from "../SDL/structs.ts"; 21 | 22 | let _library: DynamicLibrary = null!; 23 | 24 | export function Init(options?: InitOptions): void { 25 | const symbolsToLoad = options?.functions ? getSymbolsFromFunctions(symbols, options.functions) : symbols; 26 | _library = Platform.loadLibrary("SDL2_ttf", symbolsToLoad, options?.libraryPath); 27 | const _result = _library.symbols.TTF_Init() as number; 28 | if (_result < 0) { 29 | throw new SDLError(GetError()); 30 | } 31 | } 32 | Init.symbolName = "TTF_Init"; 33 | 34 | export function CloseFont( 35 | font: Pointer, 36 | ): void { 37 | _library.symbols.TTF_CloseFont( 38 | Platform.toPlatformPointer(font), 39 | ); 40 | } 41 | CloseFont.symbolName = "TTF_CloseFont"; 42 | 43 | export function Linked_Version(): version { 44 | const _result = version.of( 45 | Platform.fromPlatformPointer(_library.symbols.TTF_Linked_Version() as PlatformPointer), 46 | ); 47 | if (_result === null) { 48 | throw new SDLError(GetError()); 49 | } 50 | return _result; 51 | } 52 | Linked_Version.symbolName = "TTF_Linked_Version"; 53 | 54 | export function OpenFont( 55 | file: string, 56 | ptsize: int, 57 | ): Pointer { 58 | const _result = Platform.fromPlatformPointer(_library.symbols.TTF_OpenFont( 59 | Platform.toPlatformString(file), 60 | ptsize, 61 | ) as PlatformPointer>); 62 | if (_result === null) { 63 | throw new SDLError(GetError()); 64 | } 65 | return _result; 66 | } 67 | OpenFont.symbolName = "TTF_OpenFont"; 68 | 69 | export function Quit(): void { 70 | _library.symbols.TTF_Quit(); 71 | _library.close(); 72 | } 73 | Quit.symbolName = "TTF_Quit"; 74 | 75 | export function RenderText_Blended( 76 | font: Pointer, 77 | text: string, 78 | fg: Color, 79 | ): Surface { 80 | const _result = Surface.of(Platform.fromPlatformPointer(_library.symbols.TTF_RenderText_Blended( 81 | Platform.toPlatformPointer(font), 82 | Platform.toPlatformString(text), 83 | Platform.toPlatformStruct(fg, Color), 84 | ) as PlatformPointer)); 85 | if (_result === null) { 86 | throw new SDLError(GetError()); 87 | } 88 | return _result; 89 | } 90 | RenderText_Blended.symbolName = "TTF_RenderText_Blended"; 91 | 92 | export function RenderText_LCD( 93 | font: Pointer, 94 | text: string, 95 | fg: Color, 96 | bg: Color, 97 | ): Surface { 98 | const _result = Surface.of(Platform.fromPlatformPointer(_library.symbols.TTF_RenderText_LCD( 99 | Platform.toPlatformPointer(font), 100 | Platform.toPlatformString(text), 101 | Platform.toPlatformStruct(fg, Color), 102 | Platform.toPlatformStruct(bg, Color), 103 | ) as PlatformPointer)); 104 | if (_result === null) { 105 | throw new SDLError(GetError()); 106 | } 107 | return _result; 108 | } 109 | RenderText_LCD.symbolName = "TTF_RenderText_LCD"; 110 | 111 | export function RenderText_Solid( 112 | font: Pointer, 113 | text: string, 114 | fg: Color, 115 | ): Surface { 116 | const _result = Surface.of(Platform.fromPlatformPointer(_library.symbols.TTF_RenderText_Solid( 117 | Platform.toPlatformPointer(font), 118 | Platform.toPlatformString(text), 119 | Platform.toPlatformStruct(fg, Color), 120 | ) as PlatformPointer)); 121 | if (_result === null) { 122 | throw new SDLError(GetError()); 123 | } 124 | return _result; 125 | } 126 | RenderText_Solid.symbolName = "TTF_RenderText_Solid"; 127 | 128 | export function RenderText_Shaded( 129 | font: Pointer, 130 | text: string, 131 | fg: Color, 132 | bg: Color, 133 | ): Surface { 134 | const _result = Surface.of(Platform.fromPlatformPointer(_library.symbols.TTF_RenderText_Shaded( 135 | Platform.toPlatformPointer(font), 136 | Platform.toPlatformString(text), 137 | Platform.toPlatformStruct(fg, Color), 138 | Platform.toPlatformStruct(bg, Color), 139 | ) as PlatformPointer)); 140 | if (_result === null) { 141 | throw new SDLError(GetError()); 142 | } 143 | return _result; 144 | } 145 | RenderText_Shaded.symbolName = "TTF_RenderText_Shaded"; 146 | 147 | export function RenderUTF8_Blended( 148 | font: Pointer, 149 | text: string, 150 | fg: Color, 151 | ): Surface { 152 | const _result = Surface.of(Platform.fromPlatformPointer(_library.symbols.TTF_RenderUTF8_Blended( 153 | Platform.toPlatformPointer(font), 154 | Platform.toPlatformString(text), 155 | Platform.toPlatformStruct(fg, Color), 156 | ) as PlatformPointer)); 157 | if (_result === null) { 158 | throw new SDLError(GetError()); 159 | } 160 | return _result; 161 | } 162 | RenderUTF8_Blended.symbolName = "TTF_RenderUTF8_Blended"; 163 | 164 | export function RenderUTF8_LCD( 165 | font: Pointer, 166 | text: string, 167 | fg: Color, 168 | bg: Color, 169 | ): Surface { 170 | const _result = Surface.of(Platform.fromPlatformPointer(_library.symbols.TTF_RenderUTF8_LCD( 171 | Platform.toPlatformPointer(font), 172 | Platform.toPlatformString(text), 173 | Platform.toPlatformStruct(fg, Color), 174 | Platform.toPlatformStruct(bg, Color), 175 | ) as PlatformPointer)); 176 | if (_result === null) { 177 | throw new SDLError(GetError()); 178 | } 179 | return _result; 180 | } 181 | RenderUTF8_LCD.symbolName = "TTF_RenderUTF8_LCD"; 182 | 183 | export function RenderUTF8_Solid( 184 | font: Pointer, 185 | text: string, 186 | fg: Color, 187 | ): Surface { 188 | const _result = Surface.of(Platform.fromPlatformPointer(_library.symbols.TTF_RenderUTF8_Solid( 189 | Platform.toPlatformPointer(font), 190 | Platform.toPlatformString(text), 191 | Platform.toPlatformStruct(fg, Color), 192 | ) as PlatformPointer)); 193 | if (_result === null) { 194 | throw new SDLError(GetError()); 195 | } 196 | return _result; 197 | } 198 | RenderUTF8_Solid.symbolName = "TTF_RenderUTF8_Solid"; 199 | 200 | export function RenderUTF8_Shaded( 201 | font: Pointer, 202 | text: string, 203 | fg: Color, 204 | bg: Color, 205 | ): Surface { 206 | const _result = Surface.of(Platform.fromPlatformPointer(_library.symbols.TTF_RenderUTF8_Shaded( 207 | Platform.toPlatformPointer(font), 208 | Platform.toPlatformString(text), 209 | Platform.toPlatformStruct(fg, Color), 210 | Platform.toPlatformStruct(bg, Color), 211 | ) as PlatformPointer)); 212 | if (_result === null) { 213 | throw new SDLError(GetError()); 214 | } 215 | return _result; 216 | } 217 | RenderUTF8_Shaded.symbolName = "TTF_RenderUTF8_Shaded"; 218 | 219 | export function SizeText( 220 | font: Pointer, 221 | text: string, 222 | w: PointerLike, 223 | h: PointerLike, 224 | ): int { 225 | const _result = _library.symbols.TTF_SizeText( 226 | Platform.toPlatformPointer(font), 227 | Platform.toPlatformString(text), 228 | Platform.toPlatformPointer(w), 229 | Platform.toPlatformPointer(h), 230 | ) as int; 231 | if (_result < 0) { 232 | throw new SDLError(GetError()); 233 | } 234 | return _result; 235 | } 236 | SizeText.symbolName = "TTF_SizeText"; 237 | 238 | export function SizeUTF8( 239 | font: Pointer, 240 | text: string, 241 | ): [int, int] { 242 | const w = new Box(int); 243 | const h = new Box(int); 244 | const _result = _library.symbols.TTF_SizeUTF8( 245 | Platform.toPlatformPointer(font), 246 | Platform.toPlatformString(text), 247 | Platform.toPlatformPointer(w), 248 | Platform.toPlatformPointer(h), 249 | ) as int; 250 | if (_result < 0) { 251 | throw new SDLError(GetError()); 252 | } 253 | return [w.value, h.value]; 254 | } 255 | SizeUTF8.symbolName = "TTF_SizeUTF8"; 256 | 257 | export function SizeUNICODE( 258 | font: Pointer, 259 | text: string, 260 | w: PointerLike, 261 | h: PointerLike, 262 | ): int { 263 | const _result = _library.symbols.TTF_SizeUNICODE( 264 | Platform.toPlatformPointer(font), 265 | Platform.toPlatformString(text), 266 | Platform.toPlatformPointer(w), 267 | Platform.toPlatformPointer(h), 268 | ) as int; 269 | if (_result < 0) { 270 | throw new SDLError(GetError()); 271 | } 272 | return _result; 273 | } 274 | SizeUNICODE.symbolName = "TTF_SizeUNICODE"; 275 | -------------------------------------------------------------------------------- /src/SDL_ttf/structs.ts: -------------------------------------------------------------------------------- 1 | // This file is auto generated. To update the file make changes to the code generator. 2 | 3 | // deno-lint-ignore-file no-unused-vars 4 | 5 | import Platform from "../_platform.ts"; 6 | import { callbacks } from "./_callbacks.ts"; 7 | import { PlatformDataView } from "../_types.ts"; 8 | import { isPointer, isTypedArray } from "../_utils.ts"; 9 | import { AllocatableStruct, double, float, int, Pointer, Struct, Uint16, Uint32, Uint8 } from "../types.ts"; 10 | 11 | import {} from "./callbacks.ts"; 12 | 13 | import {} from "./enums.ts"; 14 | 15 | declare const _: unique symbol; 16 | export type Font = { [_]: "Font" }; 17 | -------------------------------------------------------------------------------- /src/_boxes.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { Box, BoxValueFactory } from "./_boxes.ts"; 3 | import { double, float, int, Pointer, Sint32, Uint16, Uint32, Uint8 } from "./types.ts"; 4 | import { PlatformDataView, PlatformPointer } from "./_types.ts"; 5 | 6 | (, keyof PlatformDataView, number]>> [ 7 | [int, "setI32", -42], 8 | [Sint32, "setI32", -42], 9 | [Uint8, "setU8", 42], 10 | [Uint16, "setU16", 256], 11 | [Uint32, "setU32", 32768], 12 | ]).forEach((testData) => { 13 | Deno.test(`${testData[0].name} can be boxed`, () => { 14 | const box = new Box(testData[0]); 15 | const setter = box._view[testData[1]] as ( 16 | arg1: number, 17 | arg2: number, 18 | ) => void; 19 | setter.apply(box._view, [0, testData[2]]); 20 | 21 | const result = box.value; 22 | assertEquals(result, testData[2]); 23 | assertEquals(typeof result, "number"); 24 | }); 25 | }); 26 | 27 | (, keyof PlatformDataView, number]>> [ 28 | [float, "setF32", 12.34], 29 | [double, "setF64", 12.34], 30 | ]).forEach((testData) => { 31 | Deno.test(`${testData[0].name} can be boxed`, () => { 32 | const box = new Box(testData[0]); 33 | const setter = box._view[testData[1]] as ( 34 | arg1: number, 35 | arg2: number, 36 | ) => void; 37 | setter.apply(box._view, [0, testData[2]]); 38 | 39 | const result = box.value; 40 | assertEquals(Math.round(result), Math.round(testData[2])); 41 | assertEquals(typeof result, "number"); 42 | }); 43 | }); 44 | 45 | Deno.test(`Pointer can be boxed`, () => { 46 | const pointerValue = Pointer( 47 | Deno.UnsafePointer.create(12345n) as unknown as PlatformPointer, 48 | ); 49 | 50 | const box = new Box>(Pointer); 51 | box._view.setPointer(0, pointerValue); 52 | 53 | const value = box.value; 54 | assertEquals(value, pointerValue); 55 | }); 56 | -------------------------------------------------------------------------------- /src/_boxes.ts: -------------------------------------------------------------------------------- 1 | import Platform from "./_platform.ts"; 2 | import { 3 | Constructor, 4 | double, 5 | Factory, 6 | float, 7 | int, 8 | Pointer, 9 | Sint32, 10 | TypedNumber, 11 | Uint16, 12 | Uint32, 13 | Uint64, 14 | Uint8, 15 | } from "./types.ts"; 16 | import { PlatformDataView } from "./_types.ts"; 17 | import { sizeof } from "./_utils.ts"; 18 | 19 | export type BoxValue = TypedNumber | Pointer; 20 | export type BoxValueConstructor = Constructor; 21 | export type BoxValueFactory = Factory; 22 | 23 | type BoxValueTransformer = ( 24 | data: Uint8Array, 25 | view: PlatformDataView, 26 | offset: number, 27 | ) => T; 28 | 29 | export function getTransformer( 30 | factoryOrConstructor: BoxValueFactory | BoxValueConstructor, 31 | ): BoxValueTransformer { 32 | switch (factoryOrConstructor) { 33 | case double: 34 | return ((_, view, offset) => view.getF64(offset)) as BoxValueTransformer; 35 | 36 | case float: 37 | return ((_, view, offset) => view.getF32(offset)) as BoxValueTransformer; 38 | 39 | case int: 40 | case Sint32: 41 | return ((_, view, offset) => view.getI32(offset)) as BoxValueTransformer; 42 | 43 | case Uint8: 44 | return ((_, view, offset) => view.getU8(offset)) as BoxValueTransformer; 45 | 46 | case Uint16: 47 | return ((_, view, offset) => view.getU16(offset)) as BoxValueTransformer; 48 | 49 | case Uint32: 50 | return ((_, view, offset) => view.getU32(offset)) as BoxValueTransformer; 51 | 52 | case Uint64: 53 | return ((_, view, offset) => view.getU64(offset)) as BoxValueTransformer; 54 | 55 | case Pointer as unknown as BoxValueFactory: 56 | return ((_, view, offset) => view.getPointer(offset)) as BoxValueTransformer; 57 | } 58 | 59 | throw new Error( 60 | `${factoryOrConstructor?.name} is not boxable. getTransformer not implemented.`, 61 | ); 62 | } 63 | 64 | export class Box { 65 | private readonly _transformer: BoxValueTransformer; 66 | public readonly _data: Uint8Array; 67 | public readonly _view: PlatformDataView; 68 | 69 | public constructor( 70 | factoryOrConstructor: BoxValueFactory | BoxValueConstructor, 71 | ) { 72 | const dataLength = sizeof(factoryOrConstructor); 73 | this._transformer = getTransformer(factoryOrConstructor); 74 | 75 | this._data = new Uint8Array(dataLength); 76 | this._view = new Platform.DataView(this._data); 77 | } 78 | 79 | public static isBox(value: unknown): value is Box { 80 | return value instanceof Box; 81 | } 82 | 83 | public get value(): T { 84 | return this._transformer(this._data, this._view, 0); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/_constants.ts: -------------------------------------------------------------------------------- 1 | export const ENV_LIBRARY_PATH = "SDL_TS_LIBRARY_PATH"; 2 | -------------------------------------------------------------------------------- /src/_init.ts: -------------------------------------------------------------------------------- 1 | import { DynamicLibraryInterface } from "./_library.ts"; 2 | import { FunctionWithSymbolName } from "./types.ts"; 3 | 4 | export function getSymbolsFromFunctions( 5 | symbols: T, 6 | functions: ReadonlyArray, 7 | ): T { 8 | const result: DynamicLibraryInterface = {}; 9 | 10 | for (const func of functions) { 11 | result[func.symbolName] = symbols[func.symbolName]; 12 | } 13 | 14 | return result as T; 15 | } 16 | -------------------------------------------------------------------------------- /src/_library.ts: -------------------------------------------------------------------------------- 1 | import { Callback } from "./types.ts"; 2 | 3 | export type DynamicLibraryType = 4 | | "bool" 5 | | "i8" 6 | | "u8" 7 | | "i16" 8 | | "u16" 9 | | "i32" 10 | | "u32" 11 | | "i64" 12 | | "u64" 13 | | "f32" 14 | | "f64" 15 | | "pointer" 16 | | "function" 17 | | { struct: Readonly> }; 18 | 19 | export type DynamicLibraryFunctionDefinition = { 20 | readonly parameters: readonly DynamicLibraryType[]; 21 | readonly result: DynamicLibraryType | "void"; 22 | }; 23 | 24 | export type DynamicCallbackDefinition = DynamicLibraryFunctionDefinition & { 25 | wrap: (callback: T) => unknown; 26 | }; 27 | 28 | export type DynamicLibraryInterface = Record; 29 | 30 | export type DynamicLibrary = { 31 | symbols: Record unknown>; 32 | 33 | close(): void; 34 | }; 35 | -------------------------------------------------------------------------------- /src/_platform.ts: -------------------------------------------------------------------------------- 1 | import DenoPlatform from "./deno/_platform.ts"; 2 | import { SDLError } from "./error.ts"; 3 | import { Platform } from "./_types.ts"; 4 | 5 | let platform: Platform | null = null; 6 | 7 | if ("Deno" in globalThis) { 8 | platform = DenoPlatform; 9 | } 10 | 11 | if (!platform) { 12 | throw new SDLError("Unsupported platform."); 13 | } 14 | 15 | export default platform as Platform; 16 | -------------------------------------------------------------------------------- /src/_structs.ts: -------------------------------------------------------------------------------- 1 | import type { Pointer, Struct } from "./types.ts"; 2 | import type { PlatformDataView } from "./_types.ts"; 3 | 4 | export const STRUCT_NO_ALLOCATE = Symbol("STRUCT_NO_ALLOCATE"); 5 | 6 | export type StructCommand = typeof STRUCT_NO_ALLOCATE; 7 | 8 | export interface StructInternal { 9 | _data: Uint8Array | Pointer; 10 | _view: PlatformDataView; 11 | } 12 | 13 | export function isStruct(value: unknown): value is T & StructInternal { 14 | return typeof value === "object" && 15 | ("_data" in (value as Struct)) && 16 | ("_view" in (value as Struct)); 17 | } 18 | -------------------------------------------------------------------------------- /src/_types.ts: -------------------------------------------------------------------------------- 1 | import { type Pointer } from "./types.ts"; 2 | import { Callback, PointerLike, Struct, StructConstructor } from "./types.ts"; 3 | import { DynamicCallbackDefinition, DynamicLibrary, DynamicLibraryInterface } from "./_library.ts"; 4 | import { Box, BoxValue } from "./_boxes.ts"; 5 | 6 | declare const _: unique symbol; 7 | 8 | export type PlatformCallback = { [_]: "PlatformCallback" }; 9 | export type PlatformPointer = { [_]: "PlatformPointer" }; 10 | export type PlatformString = { [_]: "PlatformString" }; 11 | 12 | export interface PlatformDataViewConstructor { 13 | new (data: Uint8Array | Pointer, offset?: number): PlatformDataView; 14 | } 15 | 16 | export interface PlatformDataView { 17 | readonly data: Uint8Array | Pointer; 18 | readonly byteOffset: number; 19 | 20 | getArray(byteLength: number, byteOffset: number): Uint8Array; 21 | getArrayBuffer(byteLength: number, byteOffset: number): ArrayBuffer; 22 | getCallback( 23 | byteOffset: number, 24 | definition: DynamicCallbackDefinition, 25 | ): T; 26 | getF32(byteOffset: number): number; 27 | getF64(byteOffset: number): number; 28 | getI8(byteOffset: number): number; 29 | getI16(byteOffset: number): number; 30 | getI32(byteOffset: number): number; 31 | getI64(byteOffset: number): bigint; 32 | getPointer(byteOffset: number): Pointer; 33 | getU8(byteOffset: number): number; 34 | getU16(byteOffset: number): number; 35 | getU32(byteOffset: number): number; 36 | getU64(byteOffset: number): bigint; 37 | setCallback( 38 | byteOffset: number, 39 | value: T, 40 | definition: DynamicCallbackDefinition, 41 | ): void; 42 | setF32(byteOffset: number, value: number): void; 43 | setF64(byteOffset: number, value: number): void; 44 | setI8(byteOffset: number, value: number): void; 45 | setI16(byteOffset: number, value: number): void; 46 | setI32(byteOffset: number, value: number): void; 47 | setI64(byteOffset: number, value: bigint): void; 48 | setPointer(byteOffset: number, value: Pointer): void; 49 | setU8(byteOffset: number, value: number): void; 50 | setU16(byteOffset: number, value: number): void; 51 | setU32(byteOffset: number, value: number): void; 52 | setU64(byteOffset: number, value: bigint): void; 53 | } 54 | 55 | export interface Platform { 56 | POINTER_SIZE_IN_BYTES: number; 57 | 58 | DataView: PlatformDataViewConstructor; 59 | 60 | fromPlatformCallback( 61 | value: PlatformCallback, 62 | definition: DynamicCallbackDefinition, 63 | ): T; 64 | 65 | fromPlatformPointer(value: PlatformPointer | null): Pointer | null; 66 | 67 | fromPlatformString(value: Uint8Array | PlatformPointer): string; 68 | 69 | fromPlatformStruct( 70 | data: PlatformPointer, 71 | structConstructor: StructConstructor, 72 | ): T | null; 73 | 74 | isPlatformPointer(value: unknown): value is PlatformPointer; 75 | 76 | loadLibrary( 77 | libraryName: string, 78 | symbols: T, 79 | libraryPath?: string, 80 | ): DynamicLibrary; 81 | 82 | toPlatformCallback( 83 | value: T, 84 | definition: DynamicCallbackDefinition, 85 | ): PlatformCallback; 86 | 87 | toPlatformPointer(value: PointerLike | (T extends BoxValue ? Box : never) | null): PlatformPointer | null; 88 | 89 | toPlatformString(value: string | null): PlatformString; 90 | 91 | toPlatformStruct( 92 | struct: T, 93 | stuctConstructor: StructConstructor, 94 | ): Uint8Array; 95 | } 96 | -------------------------------------------------------------------------------- /src/_utils.ts: -------------------------------------------------------------------------------- 1 | // This file includes private utility types which should not be 2 | // exposed as part of the API. 3 | 4 | import Platform from "./_platform.ts"; 5 | import { SDLError } from "./error.ts"; 6 | import { 7 | AllocatableStruct, 8 | AllocatableStructConstructor, 9 | Constructor, 10 | double, 11 | Factory, 12 | float, 13 | int, 14 | type OrFactory, 15 | Pointer, 16 | Sint32, 17 | type TypedArray, 18 | Uint16, 19 | Uint32, 20 | Uint64, 21 | Uint8, 22 | } from "./types.ts"; 23 | 24 | // 25 | // Constants 26 | // 27 | 28 | export const ENDIANNESS = (function (): "BE" | "LE" { 29 | const buffer = new ArrayBuffer(2); 30 | new globalThis.DataView(buffer).setInt16(0, 256, true); 31 | return new Int16Array(buffer)[0] === 256 ? "LE" : "BE"; 32 | })(); 33 | 34 | // 35 | // Functions 36 | // 37 | 38 | // deno-lint-ignore ban-types 39 | export function hasSizeInBytesProperty(value: {}): value is { SIZE_IN_BYTES: number } { 40 | return "SIZE_IN_BYTES" in value; 41 | } 42 | 43 | export function isPointer(value: unknown): value is Pointer { 44 | return Platform.isPlatformPointer(value); 45 | } 46 | 47 | export function isTypedArray(value: unknown): value is TypedArray { 48 | return ( 49 | value instanceof Uint8Array || 50 | value instanceof Uint8ClampedArray || 51 | value instanceof Int8Array || 52 | value instanceof Uint16Array || 53 | value instanceof Int16Array || 54 | value instanceof Uint32Array || 55 | value instanceof Int32Array || 56 | value instanceof BigUint64Array || 57 | value instanceof BigInt64Array || 58 | value instanceof Float32Array || 59 | value instanceof Float64Array 60 | ); 61 | } 62 | 63 | export function sizeof( 64 | factoryOrConstructor: Constructor | Factory, 65 | ): number { 66 | if ( 67 | "SIZE_IN_BYTES" in 68 | (factoryOrConstructor as unknown as AllocatableStructConstructor) 69 | ) { 70 | return ( 71 | factoryOrConstructor as unknown as AllocatableStructConstructor 72 | ).SIZE_IN_BYTES; 73 | } 74 | 75 | switch (factoryOrConstructor) { 76 | case Uint8: 77 | return 1; 78 | 79 | case Uint16: 80 | return 2; 81 | 82 | case float: 83 | case int: 84 | case Sint32: 85 | case Uint32: 86 | return 4; 87 | 88 | case double: 89 | case Uint64: 90 | return 8; 91 | 92 | case Pointer: 93 | return Platform.POINTER_SIZE_IN_BYTES; 94 | } 95 | 96 | throw new Error( 97 | `${factoryOrConstructor?.name} is not boxable. sizeof not implemented.`, 98 | ); 99 | } 100 | 101 | export function throwError(message: OrFactory, cause?: Error): never { 102 | throw new SDLError(typeof message === "function" ? message() : message, cause); 103 | } 104 | -------------------------------------------------------------------------------- /src/deno/_callbacks.ts: -------------------------------------------------------------------------------- 1 | import { DynamicCallbackDefinition } from "../_library.ts"; 2 | import { PlatformCallback } from "../_types.ts"; 3 | import { SDLError } from "../error.ts"; 4 | import { Callback } from "../types.ts"; 5 | 6 | interface CallbackInternal extends Callback { 7 | denoCallback?: Deno.UnsafeCallback; 8 | } 9 | 10 | export function denoFromPlatformCallback( 11 | _callback: PlatformCallback, 12 | _definition: DynamicCallbackDefinition 13 | ): T { 14 | throw new SDLError(`${denoFromPlatformCallback.name} not implemented.`); 15 | } 16 | 17 | export function denoToPlatformCallback( 18 | callback: T, 19 | definition: DynamicCallbackDefinition 20 | ): PlatformCallback { 21 | const callbackInternal = callback as CallbackInternal; 22 | if (callbackInternal.denoCallback) { 23 | return callbackInternal.denoCallback.pointer as unknown as PlatformCallback; 24 | } 25 | 26 | callbackInternal.denoCallback = new Deno.UnsafeCallback( 27 | // deno-lint-ignore no-explicit-any 28 | definition as any, 29 | // deno-lint-ignore no-explicit-any 30 | definition.wrap(callback) as any 31 | ); 32 | 33 | return callbackInternal.denoCallback.pointer as unknown as PlatformCallback; 34 | } 35 | -------------------------------------------------------------------------------- /src/deno/_dataView.ts: -------------------------------------------------------------------------------- 1 | import { Callback, type Pointer } from "../types.ts"; 2 | import { PlatformCallback, PlatformPointer } from "../_types.ts"; 3 | import { ENDIANNESS } from "../_utils.ts"; 4 | import { denoFromPlatformCallback } from "./_callbacks.ts"; 5 | import { denoFromPlatformPointer, denoToPlatformPointer } from "./_pointers.ts"; 6 | import { DynamicCallbackDefinition } from "../_library.ts"; 7 | import { denoToPlatformCallback } from "./_callbacks.ts"; 8 | 9 | export class DenoPlatformDataView { 10 | private static DATA_MUST_BE_ARRAY_BUFFER_ERROR = "data must be an instance of ArrayBuffer in order to set values."; 11 | 12 | public static LITTLE_ENDIAN = ENDIANNESS === "LE"; 13 | 14 | private _view: globalThis.DataView | Deno.UnsafePointerView; 15 | 16 | constructor( 17 | public readonly data: Uint8Array | Pointer, 18 | public readonly byteOffset: number = 0, 19 | ) { 20 | if (this.data instanceof Uint8Array) { 21 | this._view = new globalThis.DataView( 22 | this.data.buffer, 23 | this.data.byteOffset, 24 | this.data.byteLength, 25 | ); 26 | } else { 27 | this._view = new Deno.UnsafePointerView( 28 | // deno-lint-ignore no-explicit-any 29 | denoToPlatformPointer(this.data) as any, 30 | ); 31 | } 32 | } 33 | 34 | private static ensureViewIsDataView( 35 | view: globalThis.DataView | Deno.UnsafePointerView, 36 | ): asserts view is globalThis.DataView { 37 | if (!(view instanceof globalThis.DataView)) { 38 | throw new Error(DenoPlatformDataView.DATA_MUST_BE_ARRAY_BUFFER_ERROR); 39 | } 40 | } 41 | 42 | public getArray(byteLength: number, byteOffset: number): Uint8Array { 43 | if (this._view instanceof globalThis.DataView) { 44 | throw new Error("Not implemented."); 45 | } else { 46 | return new Uint8Array(this._view.getArrayBuffer(byteLength, byteOffset)); 47 | } 48 | } 49 | 50 | public getArrayBuffer(byteLength: number, byteOffset: number): ArrayBuffer { 51 | if (this._view instanceof globalThis.DataView) { 52 | throw new Error("Not implemented."); 53 | } else { 54 | return this._view.getArrayBuffer(byteLength, byteOffset); 55 | } 56 | } 57 | 58 | public getCallback( 59 | byteOffset: number, 60 | definition: DynamicCallbackDefinition, 61 | ): T { 62 | return denoFromPlatformCallback( 63 | this._view.getBigUint64( 64 | this.byteOffset + byteOffset, 65 | DenoPlatformDataView.LITTLE_ENDIAN, 66 | ) as unknown as PlatformCallback, 67 | definition, 68 | ); 69 | } 70 | 71 | public getF32(byteOffset: number): number { 72 | return this._view.getFloat32( 73 | this.byteOffset + byteOffset, 74 | DenoPlatformDataView.LITTLE_ENDIAN, 75 | ); 76 | } 77 | 78 | public getF64(byteOffset: number): number { 79 | return this._view.getFloat64( 80 | this.byteOffset + byteOffset, 81 | DenoPlatformDataView.LITTLE_ENDIAN, 82 | ); 83 | } 84 | 85 | public getI8(byteOffset: number): number { 86 | return this._view.getInt8(this.byteOffset + byteOffset); 87 | } 88 | 89 | public getI16(byteOffset: number): number { 90 | return this._view.getInt16( 91 | this.byteOffset + byteOffset, 92 | DenoPlatformDataView.LITTLE_ENDIAN, 93 | ); 94 | } 95 | 96 | public getI32(byteOffset: number): number { 97 | return this._view.getInt32( 98 | this.byteOffset + byteOffset, 99 | DenoPlatformDataView.LITTLE_ENDIAN, 100 | ); 101 | } 102 | 103 | public getI64(byteOffset: number): bigint { 104 | return this._view.getBigInt64( 105 | this.byteOffset + byteOffset, 106 | DenoPlatformDataView.LITTLE_ENDIAN, 107 | ); 108 | } 109 | 110 | public getPointer(byteOffset: number): Pointer { 111 | // TODO: We should test here if we're on 32 or 64 bit. 112 | return denoFromPlatformPointer( 113 | Deno.UnsafePointer.create( 114 | this._view.getBigUint64( 115 | this.byteOffset + byteOffset, 116 | DenoPlatformDataView.LITTLE_ENDIAN, 117 | ), 118 | ) as unknown as PlatformPointer, 119 | ) as Pointer; 120 | } 121 | 122 | public getU8(byteOffset: number): number { 123 | return this._view.getUint8(this.byteOffset + byteOffset); 124 | } 125 | 126 | public getU16(byteOffset: number): number { 127 | return this._view.getUint16( 128 | this.byteOffset + byteOffset, 129 | DenoPlatformDataView.LITTLE_ENDIAN, 130 | ); 131 | } 132 | 133 | public getU32(byteOffset: number): number { 134 | return this._view.getUint32( 135 | this.byteOffset + byteOffset, 136 | DenoPlatformDataView.LITTLE_ENDIAN, 137 | ); 138 | } 139 | 140 | public getU64(byteOffset: number): bigint { 141 | return this._view.getBigUint64( 142 | this.byteOffset + byteOffset, 143 | DenoPlatformDataView.LITTLE_ENDIAN, 144 | ); 145 | } 146 | 147 | public setCallback( 148 | byteOffset: number, 149 | value: T, 150 | definition: DynamicCallbackDefinition, 151 | ): void { 152 | DenoPlatformDataView.ensureViewIsDataView(this._view); 153 | this._view.setBigInt64( 154 | byteOffset, 155 | denoToPlatformCallback(value, definition) as unknown as bigint, 156 | DenoPlatformDataView.LITTLE_ENDIAN, 157 | ); 158 | } 159 | 160 | public setF32(byteOffset: number, value: number): void { 161 | DenoPlatformDataView.ensureViewIsDataView(this._view); 162 | this._view.setFloat32( 163 | this.byteOffset + byteOffset, 164 | value, 165 | DenoPlatformDataView.LITTLE_ENDIAN, 166 | ); 167 | } 168 | 169 | public setF64(byteOffset: number, value: number): void { 170 | DenoPlatformDataView.ensureViewIsDataView(this._view); 171 | this._view.setFloat64( 172 | this.byteOffset + byteOffset, 173 | value, 174 | DenoPlatformDataView.LITTLE_ENDIAN, 175 | ); 176 | } 177 | 178 | public setI8(byteOffset: number, value: number): void { 179 | DenoPlatformDataView.ensureViewIsDataView(this._view); 180 | this._view.setInt8(this.byteOffset + byteOffset, value); 181 | } 182 | 183 | public setI16(byteOffset: number, value: number): void { 184 | DenoPlatformDataView.ensureViewIsDataView(this._view); 185 | this._view.setInt16( 186 | this.byteOffset + byteOffset, 187 | value, 188 | DenoPlatformDataView.LITTLE_ENDIAN, 189 | ); 190 | } 191 | 192 | public setI32(byteOffset: number, value: number): void { 193 | DenoPlatformDataView.ensureViewIsDataView(this._view); 194 | this._view.setInt32( 195 | this.byteOffset + byteOffset, 196 | value, 197 | DenoPlatformDataView.LITTLE_ENDIAN, 198 | ); 199 | } 200 | 201 | public setI64(byteOffset: number, value: bigint): void { 202 | DenoPlatformDataView.ensureViewIsDataView(this._view); 203 | this._view.setBigInt64( 204 | this.byteOffset + byteOffset, 205 | value, 206 | DenoPlatformDataView.LITTLE_ENDIAN, 207 | ); 208 | } 209 | 210 | public setPointer(byteOffset: number, value: Pointer): void { 211 | DenoPlatformDataView.ensureViewIsDataView(this._view); 212 | return this._view.setBigUint64( 213 | this.byteOffset + byteOffset, 214 | BigInt( 215 | Deno.UnsafePointer.value( 216 | denoToPlatformPointer( 217 | value, 218 | ) as unknown as NonNullable, 219 | ), 220 | ), 221 | DenoPlatformDataView.LITTLE_ENDIAN, 222 | ); 223 | } 224 | 225 | public setU8(byteOffset: number, value: number): void { 226 | DenoPlatformDataView.ensureViewIsDataView(this._view); 227 | this._view.setUint8(this.byteOffset + byteOffset, value); 228 | } 229 | 230 | public setU16(byteOffset: number, value: number): void { 231 | DenoPlatformDataView.ensureViewIsDataView(this._view); 232 | this._view.setUint16( 233 | this.byteOffset + byteOffset, 234 | value, 235 | DenoPlatformDataView.LITTLE_ENDIAN, 236 | ); 237 | } 238 | 239 | public setU32(byteOffset: number, value: number): void { 240 | DenoPlatformDataView.ensureViewIsDataView(this._view); 241 | this._view.setUint32( 242 | this.byteOffset + byteOffset, 243 | value, 244 | DenoPlatformDataView.LITTLE_ENDIAN, 245 | ); 246 | } 247 | 248 | public setU64(byteOffset: number, value: bigint): void { 249 | DenoPlatformDataView.ensureViewIsDataView(this._view); 250 | this._view.setBigUint64( 251 | this.byteOffset + byteOffset, 252 | value, 253 | DenoPlatformDataView.LITTLE_ENDIAN, 254 | ); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/deno/_library.ts: -------------------------------------------------------------------------------- 1 | import { join, SEPARATOR } from "@std/path"; 2 | import { SDLError } from "../error.ts"; 3 | import { DynamicLibrary, DynamicLibraryInterface } from "../_library.ts"; 4 | import { ENV_LIBRARY_PATH } from "../_constants.ts"; 5 | 6 | const IS_WINDOWS = Deno.build.os === "windows"; 7 | const IS_MAC = Deno.build.os === "darwin"; 8 | 9 | const WINDOWS_LIBRARY_PATHS: string[] = []; 10 | 11 | const UNIX_LIBRARY_PATHS: string[] = [ 12 | "/usr/local/lib", 13 | "/usr/lib64", 14 | "/home/linuxbrew/.linuxbrew/lib", 15 | ]; 16 | 17 | const MACOS_LIBRARY_PATHS: string[] = [ 18 | "/usr/local/lib", 19 | "/System/Volumes/Data/opt/homebrew/lib", 20 | ]; 21 | 22 | function getLibrarySuffix(): string { 23 | switch (Deno.build.os) { 24 | case "windows": 25 | return ".dll"; 26 | 27 | case "darwin": 28 | return ".dylib"; 29 | } 30 | 31 | return ".so"; 32 | } 33 | 34 | function getLibraryPaths(libraryName: string): string[] { 35 | const libraryPrefix = !IS_WINDOWS ? "lib" : ""; 36 | const librarySuffix = getLibrarySuffix(); 37 | const fullLibraryName = libraryPrefix + libraryName + librarySuffix; 38 | 39 | const libraryPaths: string[] = []; 40 | 41 | // Try and load it from the same directory. 42 | // NOTE: Using `join` here doesn't seem to work as it was just returning the file without the ./ 43 | libraryPaths.push(`.${SEPARATOR}${fullLibraryName}`); 44 | 45 | // Then look and see if SDL_TS_LIBRARY_PATH was specified. 46 | const envLibraryPath = Deno.env.get(ENV_LIBRARY_PATH); 47 | if (envLibraryPath) { 48 | libraryPaths.push(join(envLibraryPath, fullLibraryName)); 49 | } 50 | 51 | // Next try and load it globally 52 | libraryPaths.push(fullLibraryName); 53 | 54 | if (!IS_WINDOWS && !IS_MAC) { 55 | // On Debain libSDL2_image and libSDL2_ttf only have symbolic links with this format so 56 | // search for those globally as well. 57 | libraryPaths.push( 58 | libraryPrefix + libraryName + "-2.0" + librarySuffix + ".0", 59 | ); 60 | } 61 | 62 | if (!IS_WINDOWS) { 63 | const ldLibraryPath = Deno.env.get("LD_LIBRARY_PATH"); 64 | 65 | if (ldLibraryPath) { 66 | libraryPaths.push( 67 | ...ldLibraryPath 68 | .split(":") 69 | .map((libraryPath) => join(libraryPath, fullLibraryName)), 70 | ); 71 | } 72 | } 73 | 74 | let searchPaths: string[] = []; 75 | 76 | switch (Deno.build.os) { 77 | case "windows": 78 | searchPaths = WINDOWS_LIBRARY_PATHS; 79 | break; 80 | 81 | case "darwin": 82 | searchPaths = MACOS_LIBRARY_PATHS; 83 | break; 84 | 85 | case "linux": 86 | searchPaths = UNIX_LIBRARY_PATHS; 87 | break; 88 | } 89 | 90 | libraryPaths.push( 91 | ...searchPaths.map((libraryPath) => join(libraryPath, fullLibraryName)), 92 | ); 93 | 94 | return libraryPaths; 95 | } 96 | 97 | export function denoLoadLibrary( 98 | libraryName: string, 99 | symbols: T, 100 | libraryPath?: string, 101 | ): DynamicLibrary { 102 | const libraryPaths = libraryPath ? [libraryPath] : getLibraryPaths(libraryName); 103 | const errors: Record = {}; 104 | 105 | for (const libraryPath of libraryPaths) { 106 | try { 107 | const result = Deno.dlopen( 108 | libraryPath, 109 | // Cast the symbols as any in order to prevent a type checking bug. 110 | // deno-lint-ignore no-explicit-any 111 | symbols as any, 112 | ) as DynamicLibrary; 113 | console.debug(`SDL_ts: Loaded ${libraryName} from "${libraryPath}"`); 114 | return result; 115 | } catch (error) { 116 | if (error instanceof Error) { 117 | errors[libraryPath] = error; 118 | } else { 119 | throw error; 120 | } 121 | } 122 | } 123 | 124 | throw new SDLError( 125 | `Failed to load library "${libraryName}" from "${ 126 | libraryPaths.join( 127 | ", ", 128 | ) 129 | }"\n` + 130 | Object.entries(errors) 131 | .map(([libraryPath, error]) => `\t=> ${libraryPath}: ${error.message}`) 132 | .join("\n"), 133 | new AggregateError(Object.values(errors)), 134 | ); 135 | } 136 | -------------------------------------------------------------------------------- /src/deno/_platform.ts: -------------------------------------------------------------------------------- 1 | import { 2 | denoFromPlatformCallback, 3 | denoToPlatformCallback, 4 | } from "./_callbacks.ts"; 5 | import { DenoPlatformDataView } from "./_dataView.ts"; 6 | import { denoLoadLibrary } from "./_library.ts"; 7 | import { denoFromPlatformPointer, denoIsPlatformPointer, denoToPlatformPointer } from "./_pointers.ts"; 8 | import { denoFromPlatformString, denoToPlatformString } from "./_strings.ts"; 9 | import { denoFromPlatformStruct, denoToPlatformStruct } from "./_structs.ts"; 10 | 11 | export default class { 12 | // TODO: Is there any way to detect this correctly? 13 | public static POINTER_SIZE_IN_BYTES = 8; 14 | 15 | public static DataView = DenoPlatformDataView; 16 | 17 | public static fromPlatformCallback = denoFromPlatformCallback; 18 | public static fromPlatformPointer = denoFromPlatformPointer; 19 | public static fromPlatformString = denoFromPlatformString; 20 | public static fromPlatformStruct = denoFromPlatformStruct; 21 | public static isPlatformPointer = denoIsPlatformPointer; 22 | public static loadLibrary = denoLoadLibrary; 23 | public static toPlatformCallback = denoToPlatformCallback; 24 | public static toPlatformPointer = denoToPlatformPointer; 25 | public static toPlatformString = denoToPlatformString; 26 | public static toPlatformStruct = denoToPlatformStruct; 27 | } 28 | -------------------------------------------------------------------------------- /src/deno/_pointers.ts: -------------------------------------------------------------------------------- 1 | import { Box } from "../_boxes.ts"; 2 | import { isStruct } from "../_structs.ts"; 3 | import { PlatformPointer } from "../_types.ts"; 4 | import { isTypedArray } from "../_utils.ts"; 5 | import { Pointer, PointerLike, TypedArray } from "../types.ts"; 6 | 7 | type DenoPointer = ReturnType>; 8 | 9 | class DenoPointerWrapper { 10 | constructor(public readonly _data: DenoPointer) {} 11 | } 12 | 13 | export function denoIsPlatformPointer(value: unknown): value is PlatformPointer & DenoPointerWrapper { 14 | return value instanceof DenoPointerWrapper; 15 | } 16 | 17 | export function denoToPlatformPointer(value: PointerLike | null): PlatformPointer | null { 18 | if (value === undefined || value === null) { 19 | return null; 20 | } 21 | 22 | // If it's an existing pointer unwrap it. 23 | if (denoIsPlatformPointer(value)) { 24 | return value._data as unknown as PlatformPointer; 25 | } 26 | 27 | if (isTypedArray(value)) { 28 | return Deno.UnsafePointer.of(value) as unknown as PlatformPointer; 29 | } else if (Box.isBox(value)) { 30 | return Deno.UnsafePointer.of(value._data) as unknown as PlatformPointer; 31 | } else if (isStruct(value)) { 32 | if (denoIsPlatformPointer(value._data)) { 33 | // Unwrap the struct's pointer. 34 | return value._data._data as unknown as PlatformPointer; 35 | } else { 36 | // If it's not a pointer it has to be a TypedArray. TypeScript can't infer that here though. 37 | return Deno.UnsafePointer.of(value._data as TypedArray) as unknown as PlatformPointer; 38 | } 39 | } 40 | 41 | throw new Error(`Unable to convert "${typeof value}" into a pointer.`); 42 | } 43 | 44 | export function denoFromPlatformPointer(value: PlatformPointer): Pointer | null { 45 | if (value === null) { 46 | return null; 47 | } 48 | 49 | // Return DenoPointerWrapper here so that denoIsPlatformPointer and denoToPlatformPointer can 50 | // identify the pointer later and unwrap it. 51 | return new DenoPointerWrapper(value as unknown as DenoPointer) as unknown as Pointer; 52 | } 53 | -------------------------------------------------------------------------------- /src/deno/_strings.ts: -------------------------------------------------------------------------------- 1 | import { PlatformPointer, PlatformString } from "../_types.ts"; 2 | 3 | export function denoFromPlatformString( 4 | value: Uint8Array | PlatformPointer 5 | ): string { 6 | if (value instanceof Uint8Array) { 7 | return new TextDecoder().decode(value); 8 | } 9 | 10 | return new Deno.UnsafePointerView( 11 | value as unknown as NonNullable 12 | ).getCString(); 13 | } 14 | 15 | export function denoToPlatformString(value: string | null): PlatformString { 16 | if (value === null) { 17 | return null as unknown as PlatformString; 18 | } 19 | 20 | return Deno.UnsafePointer.of( 21 | new TextEncoder().encode(value + "\0") 22 | ) as unknown as PlatformString; 23 | } 24 | -------------------------------------------------------------------------------- /src/deno/_structs.ts: -------------------------------------------------------------------------------- 1 | import { Pointer, Struct, StructConstructor } from "../types.ts"; 2 | import { DenoPlatformDataView } from "./_dataView.ts"; 3 | import { PlatformPointer } from "../_types.ts"; 4 | import { denoFromPlatformPointer } from "./_pointers.ts"; 5 | import { hasSizeInBytesProperty, throwError } from "../_utils.ts"; 6 | 7 | export function denoToPlatformStruct( 8 | struct: T, 9 | structConstructor: StructConstructor, 10 | ): Uint8Array { 11 | if (struct._data instanceof Uint8Array) { 12 | if (struct._byteOffset === 0) { 13 | return struct._data; 14 | } else { 15 | return new Uint8Array(struct._data, struct._byteOffset); 16 | } 17 | } else if (hasSizeInBytesProperty(structConstructor)) { 18 | const view = new DenoPlatformDataView(struct._data as Pointer); 19 | return view.getArray(structConstructor.SIZE_IN_BYTES, struct._byteOffset); 20 | } 21 | 22 | throwError(`Unable to convert struct to platform struct in ${denoToPlatformStruct.name}.`); 23 | } 24 | 25 | export function denoFromPlatformStruct( 26 | data: PlatformPointer, 27 | structConstructor: StructConstructor, 28 | ): T | null { 29 | return structConstructor.of(denoFromPlatformPointer(data)); 30 | } 31 | -------------------------------------------------------------------------------- /src/error.ts: -------------------------------------------------------------------------------- 1 | export class SDLError extends Error { 2 | constructor(message: string, cause?: Error) { 3 | super(message, { cause }); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/events.ts: -------------------------------------------------------------------------------- 1 | import { Event } from "./SDL/events.ts"; 2 | import { GetError, WaitEvent } from "./SDL/functions.ts"; 3 | export class Events { 4 | /** 5 | * Get a lazy stream of @see SDL.Event using SDL_WaitEvent. 6 | * @returns A lazy stream of @see SDL.Event 7 | * @example 8 | * ```ts 9 | * for await (const event of Events.asyncIterator()) { 10 | * console.log(event.type); 11 | * } 12 | * ``` 13 | */ 14 | public static asyncIterator(_event?: Event): AsyncIterable { 15 | const event = _event ?? new Event(); 16 | return { 17 | [Symbol.asyncIterator](): AsyncIterator { 18 | return { 19 | next(): Promise> { 20 | return new Promise>((resolve, reject) => { 21 | const result = WaitEvent(event); 22 | if (result === 0) { 23 | reject(GetError()); 24 | } else { 25 | resolve({ 26 | done: false, 27 | value: event, 28 | }); 29 | } 30 | }); 31 | } 32 | }; 33 | }, 34 | }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/structs.ts: -------------------------------------------------------------------------------- 1 | import Platform from "./_platform.ts"; 2 | import { PlatformDataView } from "./_types.ts"; 3 | import { sizeof } from "./_utils.ts"; 4 | import { AllocatableStruct, AllocatableStructConstructor } from "./types.ts"; 5 | 6 | export class StructArray { 7 | public readonly sizeOfElementInBytes: number; 8 | public readonly _data: Uint8Array; 9 | public readonly _view: PlatformDataView; 10 | 11 | public constructor(constructor: AllocatableStructConstructor, data: T[]); 12 | public constructor(constructor: AllocatableStructConstructor, length: number); 13 | public constructor( 14 | constructor: AllocatableStructConstructor, 15 | dataOrLength: T[] | number, 16 | ) { 17 | this.sizeOfElementInBytes = sizeof(constructor); 18 | 19 | const isArray = Array.isArray(dataOrLength); 20 | const length = isArray ? dataOrLength.length : dataOrLength; 21 | 22 | if (length < 0) { 23 | throw new Error("length must be >= 0."); 24 | } 25 | 26 | this._data = new Uint8Array(this.sizeOfElementInBytes * length); 27 | this._view = new Platform.DataView(this._data); 28 | 29 | if (isArray) { 30 | for (let i = 0; i < dataOrLength.length; i += 1) { 31 | if (!(dataOrLength[i]._data instanceof Uint8Array)) { 32 | throw new Error(`Struct at index ${i} is not backed by a Uint8Array.`); 33 | } 34 | 35 | this._data.set(dataOrLength[i]._data as Uint8Array, i * this.sizeOfElementInBytes); 36 | } 37 | } 38 | } 39 | } 40 | 41 | export function isStructArray(value: unknown): value is StructArray { 42 | return value instanceof StructArray; 43 | } 44 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | // deno-lint-ignore-file no-empty-interface 2 | // This file is for types exposed as part of the API. 3 | 4 | import Platform from "./_platform.ts"; 5 | import { PlatformPointer } from "./_types.ts"; 6 | import { throwError } from "./_utils.ts"; 7 | import { StructArray } from "./structs.ts"; 8 | 9 | declare const _: unique symbol; 10 | 11 | // 12 | // Simple types 13 | // 14 | 15 | export type double = number; 16 | export type float = number; 17 | export type int = number; 18 | export type Sint32 = number; 19 | export type Uint8 = number; 20 | export type Uint16 = number; 21 | export type Uint32 = number; 22 | export type Uint64 = bigint; 23 | 24 | export type Pointer = { [_]: "Pointer" }; 25 | 26 | export const double = (value = 0): double => value; 27 | export const float = (value = 0): float => value; 28 | export const int = (value = 0): int => value; 29 | export const Sint32 = (value = 0): Sint32 => value; 30 | export const Uint8 = (value = 0): Uint8 => value; 31 | export const Uint16 = (value = 0): Uint16 => value; 32 | export const Uint32 = (value = 0): Uint32 => value; 33 | export const Uint64 = (value = 0): Uint64 => BigInt(value); 34 | 35 | export const Pointer = (value: unknown): Pointer => 36 | Platform.fromPlatformPointer(value as unknown as PlatformPointer) ?? throwError("Cannot convert null to pointer."); 37 | 38 | export type TypedNumber = 39 | | double 40 | | float 41 | | int 42 | | Uint8 43 | | Uint16 44 | | Uint32 45 | | Uint64; 46 | 47 | export type TypedArray = 48 | | Int8Array 49 | | Uint8Array 50 | | Uint8ClampedArray 51 | | Int16Array 52 | | Uint16Array 53 | | Int32Array 54 | | Uint32Array 55 | | Float32Array 56 | | Float64Array 57 | | BigInt64Array 58 | | BigUint64Array; 59 | 60 | // deno-lint-ignore ban-types 61 | export type Callback = Function; 62 | 63 | // deno-lint-ignore ban-types 64 | export type FunctionWithSymbolName = Function & { symbolName: string }; 65 | 66 | type PointerStructArray = T extends AllocatableStruct ? StructArray : never; 67 | export type PointerLike = Pointer | TypedArray | Struct | PointerStructArray; 68 | 69 | // 70 | // Complex types 71 | // 72 | 73 | export type AllocatableStructConstructor = StructConstructor & { 74 | SIZE_IN_BYTES: number; 75 | }; 76 | 77 | export interface AllocatableStruct extends Struct { 78 | } 79 | 80 | export interface InitOptions { 81 | libraryPath?: string; 82 | functions?: ReadonlyArray; 83 | } 84 | 85 | export interface StructConstructor extends Constructor { 86 | of(data: Uint8Array | Pointer | null, byteOffset?: number): T | null; 87 | } 88 | 89 | export interface Struct { 90 | readonly _data: Uint8Array | Pointer; 91 | readonly _byteOffset: number; 92 | } 93 | 94 | // 95 | // Type Helpers 96 | // 97 | 98 | // deno-lint-ignore no-explicit-any 99 | export type Constructor = new (...args: any[]) => T; 100 | 101 | export type Enum> = T[keyof T]; 102 | 103 | // deno-lint-ignore no-explicit-any 104 | export type Factory = (...args: any[]) => T; 105 | 106 | export type Flags, Name extends string> = 107 | | { 108 | [K in keyof T]: { [_]: Name } & T[K]; 109 | }[keyof T] 110 | | number; 111 | 112 | export type Mutable = T extends object ? { -readonly [P in keyof T]: Mutable } 113 | : T; 114 | 115 | export type OrFactory = T | Factory; 116 | 117 | export type Predicate = (value: T) => boolean; 118 | -------------------------------------------------------------------------------- /tools/codegen-scraper.ts: -------------------------------------------------------------------------------- 1 | // This is a blunt tool that will produce output from the SDL headers 2 | // that can then be fed directly into the codegen. 3 | 4 | // clang -Wno-everything $(sdl2-config --cflags --libs) ./tmp/out/codegen-scraper.c -o ./tmp/out/codegen-scraper 5 | // ./tmp/out/codegen-scraper > ./tmp/out/codegen-scraper.txt 6 | 7 | import { 8 | CodeGenEnums, 9 | CodeGenFunctions, 10 | CodeGenStructs, 11 | } from "./codegen/types.ts"; 12 | 13 | const TMP_PATH = "../tmp"; 14 | // TODO: This should be read from sdl2-config 15 | const SDL_PATH = `/usr/include/SDL2`; 16 | const OUTPUT_PATH = `${TMP_PATH}/out`; 17 | 18 | const MACROS_TO_COMPUTE = ["SDL_SCANCODE_TO_KEYCODE"]; 19 | 20 | let buffer = ""; 21 | 22 | Deno.exit(await main()); 23 | 24 | function write(value: string): void { 25 | buffer += value + "\n"; 26 | } 27 | 28 | function writePrintF(value: string, ...args: string[]): void { 29 | value = value 30 | .replaceAll("\n", "\\n") 31 | .replaceAll("\t", "\\t") 32 | .replaceAll('"', '\\"'); 33 | 34 | if (args.length <= 0) { 35 | value = value.replaceAll("%", "%%"); 36 | } 37 | 38 | let argsString = args.join(", "); 39 | 40 | if (argsString.length > 0) { 41 | argsString = ", " + argsString; 42 | } 43 | 44 | write(`printf("${value}\\n"${argsString});`); 45 | } 46 | 47 | async function main(): Promise { 48 | writeStartCode(); 49 | 50 | for await (const entry of Deno.readDir(`${SDL_PATH}`)) { 51 | if (entry.name.startsWith("SDL") && entry.name.endsWith(".h")) { 52 | writePrintF(`// ${entry.name}`); 53 | await scrapeFile(`${SDL_PATH}/${entry.name}`); 54 | writePrintF(""); 55 | } 56 | } 57 | 58 | writeEndCode(); 59 | 60 | try { 61 | await Deno.mkdir(OUTPUT_PATH); 62 | } catch { 63 | // Ignore 64 | } 65 | 66 | const cOutputPath = `${OUTPUT_PATH}/codegen-scraper.c`; 67 | await Deno.writeTextFile(cOutputPath, buffer); 68 | 69 | const sdlConfigCommand = new Deno.Command("sdl2-config", { 70 | args: ["--cflags", "--libs"], 71 | }); 72 | const { stdout: sdlFlags } = await sdlConfigCommand.output(); 73 | console.info(`sdlFlags: ${new TextDecoder().decode(sdlFlags)}`); 74 | 75 | const exeOutputPath = `${OUTPUT_PATH}/codegen-scraper`; 76 | const compileCommand = new Deno.Command("clang", { 77 | args: [ 78 | ...new TextDecoder().decode(sdlFlags).split("\n"), 79 | cOutputPath, 80 | "-o", 81 | exeOutputPath, 82 | ], 83 | }); 84 | const { code: compileCode, stderr: compileError } = 85 | await compileCommand.output(); 86 | 87 | if (compileCode !== 0) { 88 | console.error("Failed to compile with clang:"); 89 | console.error(new TextDecoder().decode(compileError)); 90 | return 1; 91 | } 92 | 93 | // await Deno.copyFile( 94 | // `${SDL_PATH}/lib/x64/SDL2.dll`, 95 | // `${OUTPUT_PATH}/SDL2.dll` 96 | // ); 97 | 98 | const exeCommand = new Deno.Command(exeOutputPath); 99 | 100 | const { 101 | code: exeCode, 102 | stdout: exeStdout, 103 | stderr: exeStderr, 104 | } = await exeCommand.output(); 105 | 106 | if (exeCode !== 0) { 107 | console.info(exeStdout); 108 | console.error(exeStderr); 109 | return 1; 110 | } 111 | 112 | await Deno.writeFile(`${OUTPUT_PATH}/codegen-scraper.ts`, exeStdout); 113 | const { enums, functions, structs } = await import( 114 | `${OUTPUT_PATH}/codegen-scraper.ts` 115 | ); 116 | 117 | await updateCodeGenInput( 118 | enums as CodeGenEnums, 119 | functions as CodeGenFunctions, 120 | structs as CodeGenStructs 121 | ); 122 | 123 | return 0; 124 | } 125 | 126 | function writeStartCode(): void { 127 | write("#include "); 128 | write("#include "); 129 | write("#include "); 130 | write("#include "); 131 | write("#include "); 132 | write(""); 133 | write("int main(int argc, char* args[]) {"); 134 | write("SDL_Init(SDL_INIT_VIDEO);"); 135 | writePrintF( 136 | 'import { CodeGenEnums, CodeGenFunctions, CodeGenStructs } from "../../tools/codegen/types.ts";' 137 | ); 138 | writePrintF("export const sizeOfInt = %llu;", "sizeof(int)"); 139 | writePrintF("export const enums: CodeGenEnums = {};"); 140 | writePrintF("export const functions: CodeGenFunctions = {};"); 141 | writePrintF("export const structs: CodeGenStructs = {};"); 142 | 143 | write(`printf("structs[\\"SDL_SysWMinfo\\"] = {\\n"); 144 | printf("\\tsize: %llu,\\n", sizeof(SDL_SysWMinfo)); 145 | printf("\\tmembers: {\\n"); 146 | printf("\\t\\tversion: {\\n"); 147 | printf("\\t\\t\\ttype: \\"SDL_version\\",\\n"); 148 | printf("\\t\\t\\toffset: %llu,\\n", offsetof(SDL_SysWMinfo, version)); 149 | printf("\\t\\t},\\n"); 150 | printf("\\t\\tsubsystem: {\\n"); 151 | printf("\\t\\t\\ttype: \\"SDL_SYSWM_TYPE\\",\\n"); 152 | printf("\\t\\t\\toffset: %llu,\\n", offsetof(SDL_SysWMinfo, subsystem)); 153 | printf("\\t\\t},\\n"); 154 | printf("\\t\\tinfo: {\\n"); 155 | printf("\\t\\t\\ttype: \\"Uint8[]\\",\\n"); 156 | printf("\\t\\t\\toffset: %llu,\\n", offsetof(SDL_SysWMinfo, info)); 157 | printf("\\t\\t},\\n"); 158 | printf("\\t}\\n"); 159 | printf("};\\n");`); 160 | } 161 | 162 | function writeEndCode(): void { 163 | write("SDL_Quit();"); 164 | write("return 0;"); 165 | write("}"); 166 | write(""); 167 | } 168 | 169 | function writeLineNumber(num: number): void { 170 | writePrintF(`// Line ${num}`); 171 | } 172 | 173 | async function scrapeFile(filePath: string): Promise { 174 | let captureMode: "define" | "enum" | "function" | "struct" | null = null; 175 | 176 | const lines = (await Deno.readTextFile(filePath)).split("\n"); 177 | let capture = ""; 178 | 179 | const flush = () => { 180 | captureMode = null; 181 | capture = ""; 182 | }; 183 | 184 | for (let i = 0; i < lines.length; i++) { 185 | const line = lines[i].trim(); 186 | 187 | if (line.startsWith("#define SDL_")) { 188 | flush(); 189 | captureMode = "define"; 190 | writeLineNumber(i); 191 | } else if (line.startsWith("typedef enum")) { 192 | flush(); 193 | captureMode = "enum"; 194 | writeLineNumber(i); 195 | } else if (line.startsWith("extern DECLSPEC ")) { 196 | flush(); 197 | captureMode = "function"; 198 | writeLineNumber(i); 199 | } else if (line.startsWith("typedef struct") && !line.endsWith(";")) { 200 | flush(); 201 | captureMode = "struct"; 202 | writeLineNumber(i); 203 | } 204 | 205 | if (captureMode !== null) { 206 | capture += line; 207 | } 208 | 209 | let shouldFlush = false; 210 | 211 | if (captureMode === "define" && !line.endsWith("\\")) { 212 | // outputDefine(capture); 213 | shouldFlush = true; 214 | } else if ( 215 | captureMode === "enum" && 216 | line.startsWith("} ") && 217 | line.endsWith(";") 218 | ) { 219 | outputEnum(capture); 220 | shouldFlush = true; 221 | } else if (captureMode === "function" && line.endsWith(";")) { 222 | outputFunction(capture); 223 | shouldFlush = true; 224 | } else if ( 225 | captureMode === "struct" && 226 | line.startsWith("} ") && 227 | line.endsWith(";") 228 | ) { 229 | outputStruct(capture); 230 | shouldFlush = true; 231 | } 232 | 233 | if (shouldFlush) { 234 | flush(); 235 | } 236 | } 237 | } 238 | 239 | function outputDefine(capture: string): void { 240 | capture = capture.replaceAll("#define", "").replaceAll("\\", ""); 241 | 242 | const parts = capture 243 | .split(" ") 244 | .map((x) => x.trim()) 245 | .filter((x) => x !== ""); 246 | 247 | if (parts.length < 2) { 248 | return; 249 | } 250 | 251 | writePrintF(`/* define */ ${parts[0]}: ${parts.slice(1).join(" ")};`); 252 | } 253 | 254 | function outputEnum(capture: string): void { 255 | capture = capture 256 | .replace(/\/\*([.\s\S]*?)\*\//g, "") 257 | .replaceAll("typedef enum", "") 258 | .replaceAll("{", "") 259 | .replaceAll("}", "") 260 | .replaceAll(";", "") 261 | .replaceAll(" | ", "|") 262 | .replaceAll("' '", "_") // These 2 come from SDL_keycode 263 | .replaceAll("','", "__"); 264 | 265 | const parts = capture 266 | .split(/(\,|\s)/) 267 | .map((x) => x.trim()) 268 | .filter((x) => x !== "") 269 | .filter((x) => x !== ","); 270 | 271 | const enumName = parts[parts.length - 1]; 272 | 273 | if (enumName.startsWith("SDL_WinRT")) { 274 | return; 275 | } 276 | 277 | if ( 278 | ["SDL_KeyCode", "SDL_PixelFormatEnum", "SDL_WindowFlags"].includes(enumName) 279 | ) { 280 | return; 281 | } 282 | 283 | writePrintF(`enums["${enumName}"] = {`); 284 | writePrintF(`\tvalues: {`); 285 | 286 | for (let i = 0; i < parts.length - 1; i++) { 287 | const key = parts[i]; 288 | let value: string | null = null; 289 | 290 | if (parts[i + 1] === "=") { 291 | value = parts[i + 2]; 292 | i += 2; 293 | } else if (parts[i + 1].startsWith("=")) { 294 | value = parts[i + 1].substring(1); 295 | i += 1; 296 | } 297 | 298 | if (value === "_") { 299 | value = "' '"; 300 | } else if (value === "__") { 301 | value = "','"; 302 | } 303 | 304 | if (value !== null) { 305 | value = value.replaceAll("\\", "\\\\"); 306 | 307 | const computeMacroValue = MACROS_TO_COMPUTE.some((x) => 308 | value!.startsWith(x) 309 | ); 310 | 311 | if (computeMacroValue) { 312 | writePrintF(`\t\t${key}: "%d",`, value); 313 | } else { 314 | writePrintF(`\t\t${key}: "${value}",`); 315 | } 316 | } else { 317 | writePrintF(`\t\t${key}: "%d",`, key); 318 | } 319 | } 320 | 321 | writePrintF(`\t}`); 322 | writePrintF("};"); 323 | } 324 | 325 | function outputFunction(capture: string): void { 326 | capture = capture 327 | .replaceAll("extern DECLSPEC", "") 328 | .replaceAll("SDLCALL", "") 329 | .replaceAll(";", "") 330 | .replaceAll("const", "") 331 | .replaceAll("(", " ") 332 | .replaceAll(")", " "); 333 | 334 | let parts = capture 335 | .split(/(\,|\s)/) 336 | .map((x) => x.trim()) 337 | .filter((x) => x !== "") 338 | .filter((x) => x !== ","); 339 | 340 | for (let i = 0; i < parts.length; i++) { 341 | if (parts[i] === "*") { 342 | parts[i - 1] += "*"; 343 | parts[i] = ""; 344 | } 345 | } 346 | 347 | parts = parts.filter((x) => x !== ""); 348 | 349 | let returnType = parts[0]; 350 | let functionName = parts[1]; 351 | 352 | if (functionName.startsWith("*")) { 353 | returnType += "*"; 354 | functionName = functionName.substring(1); 355 | } 356 | 357 | // These functions are problematic 358 | if ( 359 | [ 360 | "int", // SDL_HapticQuery 361 | "long", // SDL_strtoul 362 | 363 | "SDL_asprintf", 364 | "SDL_bsearch", 365 | "SDL_CreateShapedWindow", 366 | "SDL_hid_open_path", 367 | "SDL_LockMutex", 368 | "SDL_Log", 369 | "SDL_LogCritical", 370 | "SDL_LogDebug", 371 | "SDL_LogError", 372 | "SDL_LogInfo", 373 | "SDL_LogMessage", 374 | "SDL_LogMessageV", 375 | "SDL_LogVerbose", 376 | "SDL_LogWarn", 377 | "SDL_memcpy", 378 | "SDL_memmove", 379 | "SDL_memset", 380 | "SDL_qsort", 381 | "SDL_ReportAssertion", 382 | "SDL_SetError", 383 | "SDL_snprintf", 384 | "SDL_sscanf", 385 | "SDL_strlcat", 386 | "SDL_strlcpy", 387 | "SDL_UIKitRunApp", 388 | "SDL_vsnprintf", 389 | "SDL_uitoa", 390 | "SDL_UnlockMutex", 391 | "SDL_utf8strlcpy", 392 | "SDL_wcslcat", 393 | "SDL_wcslcpy", 394 | ].includes(functionName) 395 | ) { 396 | return; 397 | } 398 | 399 | writePrintF(`functions["${functionName}"] = {`); 400 | writePrintF("\tparameters: {"); 401 | 402 | for (let i = 2; i < parts.length; i += 2) { 403 | let paramType = parts[i]; 404 | let paramName = parts[i + 1]; 405 | 406 | if (paramName === undefined) { 407 | continue; 408 | } 409 | 410 | if (paramName.startsWith("**")) { 411 | paramType += "**"; 412 | paramName = paramName.substring("**".length); 413 | } else if (paramName.startsWith("*")) { 414 | paramType += "*"; 415 | paramName = paramName.substring("*".length); 416 | } 417 | 418 | writePrintF(`\t\t${paramName}: {`); 419 | writePrintF(`\t\t\ttype: "${paramType}",`); 420 | writePrintF(`\t\t},`); 421 | } 422 | 423 | writePrintF("\t},"); 424 | writePrintF("\tresult: {"); 425 | writePrintF(`\t\ttype: "${returnType}",`); 426 | writePrintF(`\t},`); 427 | writePrintF("};"); 428 | } 429 | 430 | function outputStruct(capture: string): void { 431 | capture = capture 432 | .replace(/\/\*([.\s\S]*?)\*\//g, "") 433 | .replaceAll("typedef struct", "") 434 | .replaceAll("{", " ") 435 | .replaceAll("}", " ") 436 | .replaceAll(";", " ") 437 | .replaceAll(", ", ",") // Move multiple members declarations together 438 | .replaceAll("unsigned int", "uint") 439 | .replaceAll("unsigned short", "ushort") 440 | .replaceAll("char * ", "char* "); 441 | 442 | const parts = capture 443 | .split(/(\;|\s)/) 444 | .map((x) => x.trim()) 445 | .filter((x) => x !== "") 446 | .filter((x) => x !== ";"); 447 | 448 | // TODO: Figure out what to do with unions. 449 | if (parts.indexOf("union") !== -1) { 450 | return; 451 | } 452 | 453 | // write(`/* struct ${JSON.stringify(parts, undefined, 2)} */`); 454 | 455 | let structName = parts[0]; 456 | let membersStart = 1; 457 | 458 | // When struct name / typedef don't match. 459 | if (parts[0] !== parts[parts.length - 1]) { 460 | structName = parts[parts.length - 1]; 461 | membersStart = 0; 462 | } 463 | 464 | // Skip these structs for now. 465 | if ( 466 | structName.startsWith("SDLTest_") || 467 | [ 468 | "SDL_AudioCVT", 469 | "SDL_ControllerSensorEvent", 470 | "SDL_GUID", 471 | "SDL_HapticCondition", 472 | "SDL_HapticDirection", 473 | "SDL_MessageBoxColorScheme", 474 | "SDL_PixelFormat", 475 | "SDL_RendererInfo", 476 | "SDL_SensorEvent", 477 | "SDL_TextEditingEvent", 478 | "SDL_TextInputEvent", 479 | "SDL_VirtualJoystickDesc", 480 | ].includes(structName) 481 | ) { 482 | return; 483 | } 484 | 485 | writePrintF(`structs["${structName}"] = {`); 486 | writePrintF("\tsize: %llu,", `sizeof(${structName})`); 487 | writePrintF("\tmembers: {"); 488 | 489 | for (let i = membersStart; i < parts.length - 1; i += 2) { 490 | if (parts[i] === "const") { 491 | i += 1; 492 | } 493 | 494 | if (parts[i] === "struct") { 495 | i += 1; 496 | } 497 | 498 | let type = parts[i]; 499 | let name = parts[i + 1]; 500 | 501 | if (name === undefined) { 502 | continue; 503 | } 504 | 505 | if (name.startsWith("**")) { 506 | type += "**"; 507 | name = name.substring(2); 508 | } else if (name.startsWith("*")) { 509 | type += "*"; 510 | name = name.substring(1); 511 | } 512 | 513 | // If multiple members are specified with a comma. 514 | const names = name.split(","); 515 | 516 | for (const name of names) { 517 | writePrintF(`\t\t${name}: {`); 518 | writePrintF(`\t\t\ttype: "${type}",`); 519 | writePrintF(`\t\t\toffset: %llu,`, `offsetof(${structName}, ${name})`); 520 | writePrintF("\t\t},"); 521 | } 522 | } 523 | 524 | writePrintF("\t}"); 525 | writePrintF("};"); 526 | } 527 | 528 | function stringify(obj: unknown): string { 529 | const json = JSON.stringify(obj, undefined, 2); 530 | const unquoted = json.replace(/"([^"]+)":/g, "$1:"); 531 | return unquoted; 532 | } 533 | 534 | function sortObjectKeys>(input: T): T { 535 | return Object.keys(input) 536 | .sort() 537 | .reduce((obj, key) => { 538 | obj[key] = input[key]; 539 | return obj; 540 | }, {} as Record) as T; 541 | } 542 | 543 | async function formatFile(path: string): Promise { 544 | await new Deno.Command(Deno.execPath(), { args: ["fmt", path] }).output(); 545 | } 546 | 547 | async function updateCodeGenInput( 548 | scrapedEnums: CodeGenEnums, 549 | scrapedFunctions: CodeGenFunctions, 550 | scrapedStructs: CodeGenStructs 551 | ): Promise { 552 | await updateCodeGenEnums(scrapedEnums); 553 | await updateCodeGenFunctions(scrapedFunctions); 554 | await updateCodeGenStructs(scrapedStructs); 555 | } 556 | 557 | async function updateCodeGenEnums(scrapedEnums: CodeGenEnums): Promise { 558 | const { enums } = await import("./codegen/SDL/enums.ts"); 559 | 560 | for (const [enumName, _enum] of Object.entries(scrapedEnums)) { 561 | if ( 562 | ["SDL_FlashOperation", "SDL_SYSWM_TYPE"].includes(enumName) && 563 | enums[enumName] === undefined 564 | ) { 565 | enums[enumName] = _enum; 566 | } 567 | } 568 | 569 | const output = `import { CodeGenEnums } from "../types.ts"; 570 | 571 | export const enums: CodeGenEnums = ${stringify( 572 | sortObjectKeys(enums) 573 | )} as const;`; 574 | 575 | await Deno.writeTextFile("./codegen/SDL/enums.ts", output); 576 | await formatFile("./codegen/SDL/enums.ts"); 577 | } 578 | 579 | async function updateCodeGenFunctions( 580 | scrapedFunctions: CodeGenFunctions 581 | ): Promise { 582 | const { functions } = await import("./codegen/SDL/functions.ts"); 583 | 584 | for (const [funcName, func] of Object.entries(scrapedFunctions)) { 585 | if (funcName.startsWith("SDL_GL") || funcName.startsWith("SDL_Vulkan")) { 586 | continue; 587 | } 588 | 589 | if (funcName.includes("Window") && functions[funcName] === undefined) { 590 | functions[funcName] = func; 591 | } 592 | } 593 | 594 | const output = `import { CodeGenFunctions } from "../types.ts"; 595 | 596 | export const functions: CodeGenFunctions = ${stringify( 597 | sortObjectKeys(functions) 598 | )} as const;`; 599 | 600 | await Deno.writeTextFile("./codegen/SDL/functions.ts", output); 601 | await formatFile("./codegen/SDL/functions.ts"); 602 | } 603 | 604 | async function updateCodeGenStructs( 605 | scrapedStructs: CodeGenStructs 606 | ): Promise { 607 | const { structs } = await import("./codegen/SDL/structs.ts"); 608 | 609 | for (const [structName, struct] of Object.entries(scrapedStructs)) { 610 | console.info(structName); 611 | if ( 612 | ["SDL_DisplayMode", "SDL_SysWMinfo"].includes(structName) && 613 | structs[structName] === undefined 614 | ) { 615 | structs[structName] = struct; 616 | } 617 | } 618 | 619 | const output = `import { CodeGenOpaqueStructs, CodeGenStructs } from "../types.ts"; 620 | 621 | export const opaqueStructs: CodeGenOpaqueStructs = [ 622 | // TODO: Figure out how to implement SDL_RWops in deno. 623 | // "SDL_BlitMap", 624 | "SDL_Renderer", 625 | "SDL_RWops", 626 | "SDL_Texture", 627 | "SDL_Window", 628 | ]; 629 | 630 | export const structs: CodeGenStructs = ${stringify( 631 | sortObjectKeys(structs) 632 | )} as const;`; 633 | 634 | await Deno.writeTextFile("./codegen/SDL/structs.ts", output); 635 | await formatFile("./codegen/SDL/structs.ts"); 636 | } 637 | -------------------------------------------------------------------------------- /tools/codegen.ts: -------------------------------------------------------------------------------- 1 | import { codegenSDL } from "./codegen/SDL.ts"; 2 | import { codegenSDL_image } from "./codegen/SDL_image.ts"; 3 | import { codegenSDL_ttf } from "./codegen/SDL_ttf.ts"; 4 | 5 | await main(); 6 | 7 | async function main(): Promise { 8 | await codegenSDL(); 9 | await codegenSDL_image(); 10 | await codegenSDL_ttf(); 11 | } 12 | -------------------------------------------------------------------------------- /tools/codegen/SDL.ts: -------------------------------------------------------------------------------- 1 | import { join } from "@std/path"; 2 | import { SRC_PATH } from "../../shared/constants.ts"; 3 | import { 4 | writeCallbacks, 5 | writeCallbacksSymbols, 6 | writeEnums, 7 | writeEvents, 8 | writeFunctions, 9 | writeStructs, 10 | writeSymbols, 11 | } from "./generators.ts"; 12 | import { callbacks } from "./SDL/callbacks.ts"; 13 | import { enums } from "./SDL/enums.ts"; 14 | import { events } from "./SDL/events.ts"; 15 | import { functions } from "./SDL/functions.ts"; 16 | import { opaqueStructs, structs } from "./SDL/structs.ts"; 17 | import { typedefs } from "./SDL/typedefs.ts"; 18 | import { CodeGenContext } from "./types.ts"; 19 | 20 | const SDL_SRC_PATH = join(SRC_PATH, "SDL"); 21 | 22 | export async function codegenSDL(): Promise { 23 | const context: CodeGenContext = { 24 | libraryName: "SDL2", 25 | events, 26 | functions, 27 | callbacks, 28 | enums, 29 | structs, 30 | opaqueStructs, 31 | typedefs, 32 | }; 33 | 34 | await writeEnums(context, `${SDL_SRC_PATH}/enums.ts`, []); 35 | await writeEvents( 36 | `${SDL_SRC_PATH}/events.ts`, 37 | context, 38 | ); 39 | await writeStructs( 40 | context, 41 | `${SDL_SRC_PATH}/structs.ts`, 42 | [ 43 | `import { AudioFormat } from "./audio.ts"`, 44 | ], 45 | ); 46 | await writeSymbols( 47 | context, 48 | `${SDL_SRC_PATH}/_symbols.ts`, 49 | ); 50 | await writeCallbacksSymbols( 51 | context, 52 | `${SDL_SRC_PATH}/_callbacks.ts`, 53 | [], 54 | ); 55 | await writeCallbacks( 56 | context, 57 | `${SDL_SRC_PATH}/callbacks.ts`, 58 | [], 59 | ); 60 | await writeFunctions( 61 | context, 62 | `${SDL_SRC_PATH}/functions.ts`, 63 | [ 64 | `import { AudioDeviceID } from "./audio.ts"`, 65 | `import { Event } from "./events.ts";`, 66 | `import { RWMode } from "./rw.ts";`, 67 | ], 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /tools/codegen/SDL/callbacks.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenCallbacks } from "../types.ts"; 2 | 3 | export const callbacks: CodeGenCallbacks = { 4 | SDL_AudioCallback: { 5 | parameters: { 6 | userdata: { 7 | type: "void*", 8 | isNullable: true, 9 | }, 10 | stream: { 11 | type: "Uint8*", 12 | }, 13 | len: { 14 | type: "int", 15 | }, 16 | }, 17 | result: { 18 | type: "void", 19 | }, 20 | }, 21 | SDL_EventFilter: { 22 | parameters: { 23 | userdata: { 24 | type: "void*", 25 | isNullable: true, 26 | }, 27 | event: { 28 | type: "SDL_Event*", 29 | }, 30 | }, 31 | result: { 32 | type: "int", 33 | }, 34 | }, 35 | SDL_TimerCallback: { 36 | todo: "Doesn't seem to be supported yet perhaps due to background thread?", 37 | parameters: { 38 | interval: { 39 | type: "Uint32", 40 | }, 41 | param: { 42 | type: "void*", 43 | }, 44 | }, 45 | result: { 46 | type: "Uint32", 47 | }, 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /tools/codegen/SDL/events.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenEvents } from "../types.ts"; 2 | 3 | export const events: CodeGenEvents = { 4 | SDL_CommonEvent: { 5 | size: 8, 6 | members: { 7 | type: { 8 | type: "Uint32", 9 | overrideType: "EventType", 10 | offset: 0, 11 | }, 12 | timestamp: { 13 | type: "Uint32", 14 | offset: 4, 15 | }, 16 | }, 17 | }, 18 | 19 | SDL_DisplayEvent: { 20 | size: 20, 21 | members: { 22 | type: { 23 | type: "Uint32", 24 | overrideType: "EventType", 25 | offset: 0, 26 | }, 27 | timestamp: { 28 | type: "Uint32", 29 | 30 | offset: 4, 31 | }, 32 | display: { 33 | type: "Uint32", 34 | 35 | offset: 8, 36 | }, 37 | event: { 38 | type: "Uint8", 39 | offset: 12, 40 | }, 41 | padding1: { 42 | type: "Uint8", 43 | offset: 13, 44 | }, 45 | padding2: { 46 | type: "Uint8", 47 | offset: 14, 48 | }, 49 | padding3: { 50 | type: "Uint8", 51 | offset: 15, 52 | }, 53 | data1: { 54 | type: "Sint32", 55 | offset: 16, 56 | }, 57 | }, 58 | }, 59 | 60 | SDL_KeyboardEvent: { 61 | unionName: "key", 62 | size: 32, 63 | members: { 64 | type: { 65 | type: "Uint32", 66 | overrideType: "EventType", 67 | offset: 0, 68 | }, 69 | timestamp: { 70 | type: "Uint32", 71 | 72 | offset: 4, 73 | }, 74 | windowID: { 75 | type: "Uint32", 76 | 77 | offset: 8, 78 | }, 79 | state: { 80 | type: "Uint8", 81 | offset: 12, 82 | }, 83 | repeat: { 84 | type: "Uint8", 85 | offset: 13, 86 | }, 87 | padding2: { 88 | type: "Uint8", 89 | offset: 14, 90 | }, 91 | padding3: { 92 | type: "Uint8", 93 | offset: 15, 94 | }, 95 | keysym: { 96 | type: "SDL_Keysym", 97 | offset: 16, 98 | }, 99 | }, 100 | }, 101 | 102 | SDL_MouseButtonEvent: { 103 | size: 28, 104 | members: { 105 | type: { 106 | type: "Uint32", 107 | overrideType: "EventType", 108 | offset: 0, 109 | }, 110 | timestamp: { 111 | type: "Uint32", 112 | offset: 4, 113 | }, 114 | windowID: { 115 | type: "Uint32", 116 | offset: 8, 117 | }, 118 | which: { 119 | type: "Uint32", 120 | offset: 12, 121 | }, 122 | button: { 123 | type: "Uint8", 124 | offset: 16, 125 | }, 126 | state: { 127 | type: "Uint8", 128 | offset: 17, 129 | }, 130 | clicks: { 131 | type: "Uint8", 132 | offset: 18, 133 | }, 134 | padding1: { 135 | type: "Uint8", 136 | offset: 19, 137 | }, 138 | x: { 139 | type: "Sint32", 140 | offset: 20, 141 | }, 142 | y: { 143 | type: "Sint32", 144 | offset: 24, 145 | }, 146 | }, 147 | }, 148 | 149 | SDL_MouseMotionEvent: { 150 | size: 36, 151 | members: { 152 | type: { 153 | type: "Uint32", 154 | overrideType: "EventType", 155 | offset: 0, 156 | }, 157 | timestamp: { 158 | type: "Uint32", 159 | offset: 4, 160 | }, 161 | windowID: { 162 | type: "Uint32", 163 | offset: 8, 164 | }, 165 | which: { 166 | type: "Uint32", 167 | offset: 12, 168 | }, 169 | state: { 170 | type: "Uint32", 171 | offset: 16, 172 | }, 173 | x: { 174 | type: "Sint32", 175 | offset: 20, 176 | }, 177 | y: { 178 | type: "Sint32", 179 | offset: 24, 180 | }, 181 | xrel: { 182 | type: "Sint32", 183 | offset: 28, 184 | }, 185 | yrel: { 186 | type: "Sint32", 187 | offset: 32, 188 | }, 189 | }, 190 | }, 191 | 192 | SDL_MouseWheelEvent: { 193 | size: 36, 194 | members: { 195 | type: { 196 | type: "Uint32", 197 | overrideType: "EventType", 198 | offset: 0, 199 | }, 200 | timestamp: { 201 | type: "Uint32", 202 | offset: 4, 203 | }, 204 | windowID: { 205 | type: "Uint32", 206 | offset: 8, 207 | }, 208 | which: { 209 | type: "Uint32", 210 | offset: 12, 211 | }, 212 | x: { 213 | type: "Sint32", 214 | offset: 16, 215 | }, 216 | y: { 217 | type: "Sint32", 218 | offset: 20, 219 | }, 220 | direction: { 221 | type: "Uint32", 222 | offset: 24, 223 | }, 224 | preciseX: { 225 | type: "float", 226 | offset: 28, 227 | }, 228 | preciseY: { 229 | type: "float", 230 | offset: 32, 231 | }, 232 | }, 233 | }, 234 | 235 | SDL_WindowEvent: { 236 | size: 24, 237 | members: { 238 | type: { 239 | type: "Uint32", 240 | overrideType: "EventType", 241 | offset: 0, 242 | }, 243 | timestamp: { 244 | type: "Uint32", 245 | offset: 4, 246 | }, 247 | windowID: { 248 | type: "Uint32", 249 | offset: 8, 250 | }, 251 | event: { 252 | type: "Uint8", 253 | overrideType: "WindowEventID", 254 | offset: 12, 255 | }, 256 | padding1: { 257 | type: "Uint8", 258 | offset: 13, 259 | }, 260 | padding2: { 261 | type: "Uint8", 262 | offset: 14, 263 | }, 264 | padding3: { 265 | type: "Uint8", 266 | offset: 15, 267 | }, 268 | data1: { 269 | type: "Sint32", 270 | offset: 16, 271 | }, 272 | data2: { 273 | type: "Sint32", 274 | offset: 20, 275 | }, 276 | }, 277 | }, 278 | } as const; 279 | -------------------------------------------------------------------------------- /tools/codegen/SDL/functionImplementations.ts: -------------------------------------------------------------------------------- 1 | export const GET_KEYBOARD_STATE = `export function GetKeyboardState(): Uint8Array { 2 | const numkeys = new Box(int); 3 | const _result = Platform.fromPlatformPointer( 4 | _library.symbols.SDL_GetKeyboardState( 5 | Platform.toPlatformPointer(numkeys) 6 | ) as PlatformPointer 7 | )!; 8 | const dataView = new Platform.DataView(_result); 9 | return new Uint8Array(dataView.getArrayBuffer(numkeys.value, 0)); 10 | }`; 11 | 12 | export const LOADWAV_RW = `export function LoadWAV_RW( 13 | src: PointerLike, 14 | freesrc: int, 15 | spec: PointerLike, 16 | ): [AudioSpec, Uint8Array] { 17 | const audio_buf = new Box>(Pointer); 18 | const audio_len = new Box(Uint32); 19 | const _result = AudioSpec.of(Platform.fromPlatformPointer(_library.symbols.SDL_LoadWAV_RW( 20 | Platform.toPlatformPointer(src), 21 | freesrc, 22 | Platform.toPlatformPointer(spec), 23 | Platform.toPlatformPointer(audio_buf), 24 | Platform.toPlatformPointer(audio_len), 25 | ) as PlatformPointer)); 26 | if (_result === null) { 27 | throw new SDLError(GetError()); 28 | } 29 | const dataView = new Platform.DataView(audio_buf.value); 30 | return [_result, new Uint8Array(dataView.getArrayBuffer(audio_len.value, 0))]; 31 | }`; 32 | -------------------------------------------------------------------------------- /tools/codegen/SDL/structs.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenOpaqueStructs, CodeGenStructs } from "../types.ts"; 2 | 3 | export const opaqueStructs: CodeGenOpaqueStructs = [ 4 | // TODO: Figure out how to implement SDL_RWops in deno. 5 | // "SDL_BlitMap", 6 | "SDL_Renderer", 7 | "SDL_RWops", 8 | "SDL_Texture", 9 | "SDL_Window", 10 | ]; 11 | 12 | export const structs: CodeGenStructs = { 13 | SDL_AudioSpec: { 14 | allocatable: true, 15 | mutable: true, 16 | size: 32, 17 | members: { 18 | freq: { 19 | type: "int", 20 | offset: 0, 21 | }, 22 | format: { 23 | type: "SDL_AudioFormat", 24 | offset: 4, 25 | }, 26 | channels: { 27 | type: "Uint8", 28 | offset: 6, 29 | }, 30 | silence: { 31 | type: "Uint8", 32 | offset: 7, 33 | }, 34 | samples: { 35 | type: "Uint16", 36 | offset: 8, 37 | }, 38 | padding: { 39 | type: "Uint16", 40 | offset: 10, 41 | }, 42 | size: { 43 | type: "Uint32", 44 | offset: 12, 45 | }, 46 | callback: { 47 | todo: "Doesn't seem to work due to background thread.", 48 | type: "SDL_AudioCallback", 49 | offset: 16, 50 | }, 51 | userdata: { 52 | type: "void*", 53 | offset: 24, 54 | }, 55 | }, 56 | }, 57 | SDL_Color: { 58 | allocatable: true, 59 | mutable: true, 60 | size: 4, 61 | members: { 62 | r: { 63 | type: "Uint8", 64 | offset: 0, 65 | }, 66 | g: { 67 | type: "Uint8", 68 | offset: 1, 69 | }, 70 | b: { 71 | type: "Uint8", 72 | offset: 2, 73 | }, 74 | a: { 75 | type: "Uint8", 76 | offset: 3, 77 | }, 78 | }, 79 | }, 80 | SDL_DisplayMode: { 81 | size: 24, 82 | members: { 83 | format: { 84 | type: "Uint32", 85 | offset: 0, 86 | }, 87 | w: { 88 | type: "int", 89 | offset: 4, 90 | }, 91 | h: { 92 | type: "int", 93 | offset: 8, 94 | }, 95 | refresh_rate: { 96 | type: "int", 97 | offset: 12, 98 | }, 99 | driverdata: { 100 | type: "void*", 101 | offset: 16, 102 | }, 103 | }, 104 | }, 105 | SDL_Keysym: { 106 | size: 16, 107 | members: { 108 | scancode: { 109 | type: "SDL_Scancode", 110 | offset: 0, 111 | }, 112 | sym: { 113 | type: "SDL_Keycode", 114 | offset: 4, 115 | }, 116 | mod: { 117 | type: "Uint16", 118 | offset: 8, 119 | }, 120 | unused: { 121 | type: "Uint32", 122 | offset: 12, 123 | }, 124 | }, 125 | }, 126 | SDL_Palette: { 127 | size: 24, 128 | members: { 129 | ncolors: { 130 | type: "int", 131 | offset: 0, 132 | }, 133 | colors: { 134 | type: "SDL_Color*", 135 | offset: 8, 136 | }, 137 | version: { 138 | internal: true, 139 | type: "Uint32", 140 | offset: 16, 141 | }, 142 | refcount: { 143 | internal: true, 144 | type: "int", 145 | offset: 20, 146 | }, 147 | }, 148 | }, 149 | SDL_PixelFormat: { 150 | size: 56, 151 | members: { 152 | format: { 153 | type: "Uint32", 154 | offset: 0, 155 | }, 156 | palette: { 157 | type: "SDL_Palette*", 158 | offset: 8, 159 | }, 160 | BitsPerPixel: { 161 | type: "Uint8", 162 | offset: 16, 163 | }, 164 | BytesPerPixel: { 165 | type: "Uint8", 166 | offset: 17, 167 | }, 168 | Rmask: { 169 | type: "Uint32", 170 | offset: 20, 171 | }, 172 | Gmask: { 173 | type: "Uint32", 174 | offset: 24, 175 | }, 176 | Bmask: { 177 | type: "Uint32", 178 | offset: 28, 179 | }, 180 | Amask: { 181 | type: "Uint32", 182 | offset: 32, 183 | }, 184 | Rloss: { 185 | internal: true, 186 | type: "Uint8", 187 | offset: 36, 188 | }, 189 | Gloss: { 190 | internal: true, 191 | type: "Uint8", 192 | offset: 37, 193 | }, 194 | Bloss: { 195 | internal: true, 196 | type: "Uint8", 197 | offset: 38, 198 | }, 199 | Aloss: { 200 | internal: true, 201 | type: "Uint8", 202 | offset: 39, 203 | }, 204 | Rshift: { 205 | internal: true, 206 | type: "Uint8", 207 | offset: 40, 208 | }, 209 | Gshift: { 210 | internal: true, 211 | type: "Uint8", 212 | offset: 41, 213 | }, 214 | Bshift: { 215 | internal: true, 216 | type: "Uint8", 217 | offset: 42, 218 | }, 219 | Ashift: { 220 | internal: true, 221 | type: "Uint8", 222 | offset: 43, 223 | }, 224 | refcount: { 225 | internal: true, 226 | type: "int", 227 | offset: 44, 228 | }, 229 | next: { 230 | internal: true, 231 | type: "SDL_PixelFormat*", 232 | offset: 48, 233 | }, 234 | }, 235 | }, 236 | SDL_Point: { 237 | allocatable: true, 238 | mutable: true, 239 | size: 8, 240 | members: { 241 | x: { 242 | type: "int", 243 | offset: 0, 244 | }, 245 | y: { 246 | type: "int", 247 | offset: 4, 248 | }, 249 | }, 250 | }, 251 | SDL_Rect: { 252 | allocatable: true, 253 | mutable: true, 254 | size: 16, 255 | members: { 256 | x: { 257 | type: "int", 258 | offset: 0, 259 | }, 260 | y: { 261 | type: "int", 262 | offset: 4, 263 | }, 264 | w: { 265 | type: "int", 266 | offset: 8, 267 | }, 268 | h: { 269 | type: "int", 270 | offset: 12, 271 | }, 272 | }, 273 | }, 274 | SDL_RendererInfo: { 275 | allocatable: true, 276 | mutable: false, 277 | size: 88, 278 | members: { 279 | name: { 280 | type: "char*", 281 | offset: 0, 282 | }, 283 | flags: { 284 | type: "Uint32", 285 | offset: 8, 286 | }, 287 | num_texture_formats: { 288 | type: "Uint32", 289 | offset: 12, 290 | }, 291 | texture_formats: { 292 | todo: "Add support for arrays in structs.", 293 | type: "Uint32", 294 | offset: 16, 295 | }, 296 | max_texture_width: { 297 | type: "int", 298 | offset: 80, 299 | }, 300 | max_texture_height: { 301 | type: "int", 302 | offset: 84, 303 | }, 304 | }, 305 | }, 306 | SDL_Surface: { 307 | size: 96, 308 | members: { 309 | flags: { 310 | type: "Uint32", 311 | offset: 0, 312 | }, 313 | format: { 314 | type: "SDL_PixelFormat*", 315 | offset: 8, 316 | }, 317 | w: { 318 | type: "int", 319 | offset: 16, 320 | }, 321 | h: { 322 | type: "int", 323 | offset: 20, 324 | }, 325 | pitch: { 326 | type: "int", 327 | offset: 24, 328 | }, 329 | pixels: { 330 | type: "void*", 331 | offset: 32, 332 | }, 333 | userdata: { 334 | type: "void*", 335 | offset: 40, 336 | }, 337 | locked: { 338 | internal: true, 339 | type: "int", 340 | offset: 48, 341 | }, 342 | list_data: { 343 | internal: true, 344 | type: "void*", 345 | offset: 56, 346 | }, 347 | clip_rect: { 348 | type: "SDL_Rect", 349 | offset: 64, 350 | }, 351 | map: { 352 | internal: true, 353 | type: "SDL_BlitMap*", 354 | offset: 80, 355 | }, 356 | refcount: { 357 | type: "int", 358 | offset: 88, 359 | }, 360 | }, 361 | }, 362 | SDL_SysWMinfo: { 363 | size: 72, 364 | members: { 365 | version: { 366 | type: "SDL_version", 367 | offset: 0, 368 | }, 369 | subsystem: { 370 | type: "SDL_SYSWM_TYPE", 371 | offset: 4, 372 | }, 373 | info: { 374 | todo: "Figure out how to map unions.", 375 | type: "Uint8[]", 376 | offset: 8, 377 | }, 378 | }, 379 | }, 380 | SDL_version: { 381 | allocatable: true, 382 | size: 3, 383 | members: { 384 | major: { 385 | type: "Uint8", 386 | offset: 0, 387 | }, 388 | minor: { 389 | type: "Uint8", 390 | offset: 1, 391 | }, 392 | patch: { 393 | type: "Uint8", 394 | offset: 2, 395 | }, 396 | }, 397 | }, 398 | } as const; 399 | -------------------------------------------------------------------------------- /tools/codegen/SDL/typedefs.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenTypedefs } from "../types.ts"; 2 | 3 | export const typedefs: CodeGenTypedefs = { 4 | "SDL_AudioDeviceID": "Uint32", 5 | "SDL_AudioFormat": "Uint16", 6 | "SDL_TimerID": "int", 7 | } as const; 8 | -------------------------------------------------------------------------------- /tools/codegen/SDL_image.ts: -------------------------------------------------------------------------------- 1 | import { join } from "@std/path"; 2 | import { SRC_PATH } from "../../shared/constants.ts"; 3 | import { writeEnums, writeFunctions, writeSymbols } from "./generators.ts"; 4 | import { callbacks } from "./SDL_image/callbacks.ts"; 5 | import { enums } from "./SDL_image/enums.ts"; 6 | import { functions } from "./SDL_image/functions.ts"; 7 | import { opaqueStructs, structs } from "./SDL_image/structs.ts"; 8 | import { structs as SDL_structs } from "./SDL/structs.ts"; 9 | import { CodeGenContext } from "./types.ts"; 10 | 11 | const SDL_IMAGE_SRC_PATH = join(SRC_PATH, "SDL_image"); 12 | 13 | export async function codegenSDL_image(): Promise { 14 | const allOpaqueStructs = ["SDL_Renderer", "SDL_Surface", "SDL_Texture", ...opaqueStructs]; 15 | 16 | const allStructs = { 17 | SDL_version: { 18 | ...SDL_structs["SDL_version"], 19 | doNotImport: true, 20 | }, 21 | ...structs, 22 | }; 23 | 24 | const context: CodeGenContext = { 25 | libraryName: "SDL2_image", 26 | events: {}, 27 | functions, 28 | callbacks, 29 | enums, 30 | structs: allStructs, 31 | opaqueStructs: allOpaqueStructs, 32 | typedefs: {}, 33 | }; 34 | 35 | await writeEnums(context, `${SDL_IMAGE_SRC_PATH}/enums.ts`, []); 36 | 37 | await writeSymbols(context, `${SDL_IMAGE_SRC_PATH}/_symbols.ts`); 38 | 39 | await writeFunctions( 40 | context, 41 | `${SDL_IMAGE_SRC_PATH}/functions.ts`, 42 | [ 43 | `import { GetError } from "../SDL/functions.ts";`, 44 | `import { Renderer, Surface, Texture, version } from "../SDL/structs.ts";`, 45 | ], 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /tools/codegen/SDL_image/callbacks.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenCallbacks } from "../types.ts"; 2 | 3 | export const callbacks: CodeGenCallbacks = {}; 4 | -------------------------------------------------------------------------------- /tools/codegen/SDL_image/enums.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenEnums } from "../types.ts"; 2 | 3 | export const enums: CodeGenEnums = { 4 | IMG_InitFlags: { 5 | prefixToStrip: "IMG_INIT", 6 | values: { 7 | IMG_INIT_JPG: "0x00000001", 8 | IMG_INIT_PNG: "0x00000002", 9 | IMG_INIT_TIF: "0x00000004", 10 | IMG_INIT_WEBP: "0x00000008", 11 | IMG_INIT_JXL: "0x00000010", 12 | IMG_INIT_AVIF: "0x00000020", 13 | }, 14 | }, 15 | } as const; 16 | -------------------------------------------------------------------------------- /tools/codegen/SDL_image/functions.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenFunctions } from "../types.ts"; 2 | 3 | export const functions: CodeGenFunctions = { 4 | IMG_Init: { 5 | parameters: { 6 | flags: { 7 | type: "int", 8 | overrideType: "InitFlags", 9 | }, 10 | }, 11 | result: { 12 | type: "int", 13 | }, 14 | checkForError: true, 15 | }, 16 | 17 | IMG_Linked_Version: { 18 | parameters: {}, 19 | result: { 20 | type: "SDL_version*", 21 | }, 22 | checkForError: true, 23 | }, 24 | 25 | IMG_Load: { 26 | parameters: { 27 | file: { 28 | type: "char*", 29 | }, 30 | }, 31 | result: { 32 | type: "SDL_Surface*", 33 | }, 34 | checkForError: true, 35 | }, 36 | 37 | IMG_LoadTexture: { 38 | parameters: { 39 | renderer: { 40 | type: "SDL_Renderer*", 41 | }, 42 | file: { 43 | type: "char*", 44 | }, 45 | }, 46 | result: { 47 | type: "SDL_Texture*", 48 | }, 49 | checkForError: true, 50 | }, 51 | 52 | IMG_Quit: { 53 | parameters: {}, 54 | result: { 55 | type: "void", 56 | }, 57 | }, 58 | } as const; 59 | -------------------------------------------------------------------------------- /tools/codegen/SDL_image/structs.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenOpaqueStructs, CodeGenStructs } from "../types.ts"; 2 | 3 | export const opaqueStructs: CodeGenOpaqueStructs = []; 4 | 5 | export const structs: CodeGenStructs = {} as const; 6 | -------------------------------------------------------------------------------- /tools/codegen/SDL_ttf.ts: -------------------------------------------------------------------------------- 1 | import { join } from "@std/path"; 2 | import { SRC_PATH } from "../../shared/constants.ts"; 3 | import { writeCallbacks, writeEnums, writeFunctions, writeStructs, writeSymbols } from "./generators.ts"; 4 | import { callbacks } from "./SDL_ttf/callbacks.ts"; 5 | import { enums } from "./SDL_ttf/enums.ts"; 6 | import { functions } from "./SDL_ttf/functions.ts"; 7 | import { opaqueStructs, structs } from "./SDL_ttf/structs.ts"; 8 | import { structs as SDL_structs } from "./SDL/structs.ts"; 9 | import { CodeGenContext } from "./types.ts"; 10 | 11 | const SDL_TTF_SRC_PATH = join(SRC_PATH, "SDL_ttf"); 12 | 13 | export async function codegenSDL_ttf(): Promise { 14 | const context: CodeGenContext = { 15 | libraryName: "SDL2_ttf", 16 | events: {}, 17 | functions, 18 | callbacks, 19 | enums, 20 | structs, 21 | opaqueStructs, 22 | typedefs: {}, 23 | }; 24 | 25 | await writeStructs( 26 | context, 27 | `${SDL_TTF_SRC_PATH}/structs.ts`, 28 | [], 29 | ); 30 | 31 | await writeFunctions( 32 | { 33 | ...context, 34 | structs: { 35 | SDL_Color: { 36 | ...SDL_structs["SDL_Color"], 37 | doNotImport: true, 38 | }, 39 | SDL_Surface: { 40 | ...SDL_structs["SDL_Surface"], 41 | doNotImport: true, 42 | }, 43 | SDL_version: { 44 | ...SDL_structs["SDL_version"], 45 | doNotImport: true, 46 | }, 47 | ...context.structs, 48 | }, 49 | }, 50 | `${SDL_TTF_SRC_PATH}/functions.ts`, 51 | [ 52 | `import { GetError } from "../SDL/functions.ts";`, 53 | `import { Color, Surface, version } from "../SDL/structs.ts";`, 54 | ], 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /tools/codegen/SDL_ttf/callbacks.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenCallbacks } from "../types.ts"; 2 | 3 | export const callbacks: CodeGenCallbacks = {}; 4 | -------------------------------------------------------------------------------- /tools/codegen/SDL_ttf/enums.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenEnums } from "../types.ts"; 2 | 3 | export const enums: CodeGenEnums = {} as const; 4 | -------------------------------------------------------------------------------- /tools/codegen/SDL_ttf/functions.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenFunctions } from "../types.ts"; 2 | 3 | export const functions: CodeGenFunctions = { 4 | TTF_Init: { 5 | parameters: {}, 6 | result: { 7 | type: "int", 8 | }, 9 | checkForError: true, 10 | }, 11 | 12 | TTF_CloseFont: { 13 | parameters: { 14 | font: { 15 | type: "TTF_Font*", 16 | }, 17 | }, 18 | result: { 19 | type: "void", 20 | }, 21 | }, 22 | 23 | TTF_Linked_Version: { 24 | parameters: {}, 25 | result: { 26 | type: "SDL_version*", 27 | }, 28 | checkForError: true, 29 | }, 30 | 31 | TTF_OpenFont: { 32 | parameters: { 33 | file: { 34 | type: "char*", 35 | }, 36 | ptsize: { 37 | type: "int", 38 | }, 39 | }, 40 | result: { 41 | type: "TTF_Font*", 42 | }, 43 | checkForError: true, 44 | }, 45 | 46 | TTF_Quit: { 47 | parameters: {}, 48 | result: { 49 | type: "void", 50 | }, 51 | }, 52 | 53 | TTF_RenderText_Blended: { 54 | parameters: { 55 | font: { 56 | type: "TTF_Font*", 57 | }, 58 | text: { 59 | type: "char*", 60 | }, 61 | fg: { 62 | type: "SDL_Color", 63 | }, 64 | }, 65 | result: { 66 | type: "SDL_Surface*", 67 | }, 68 | checkForError: true, 69 | }, 70 | 71 | TTF_RenderText_LCD: { 72 | parameters: { 73 | font: { 74 | type: "TTF_Font*", 75 | }, 76 | text: { 77 | type: "char*", 78 | }, 79 | fg: { 80 | type: "SDL_Color", 81 | }, 82 | bg: { 83 | type: "SDL_Color", 84 | }, 85 | }, 86 | result: { 87 | type: "SDL_Surface*", 88 | }, 89 | checkForError: true, 90 | }, 91 | 92 | TTF_RenderText_Solid: { 93 | parameters: { 94 | font: { 95 | type: "TTF_Font*", 96 | }, 97 | text: { 98 | type: "char*", 99 | }, 100 | fg: { 101 | type: "SDL_Color", 102 | }, 103 | }, 104 | result: { 105 | type: "SDL_Surface*", 106 | }, 107 | checkForError: true, 108 | }, 109 | 110 | TTF_RenderText_Shaded: { 111 | parameters: { 112 | font: { 113 | type: "TTF_Font*", 114 | }, 115 | text: { 116 | type: "char*", 117 | }, 118 | fg: { 119 | type: "SDL_Color", 120 | }, 121 | bg: { 122 | type: "SDL_Color", 123 | }, 124 | }, 125 | result: { 126 | type: "SDL_Surface*", 127 | }, 128 | checkForError: true, 129 | }, 130 | 131 | TTF_RenderUTF8_Blended: { 132 | parameters: { 133 | font: { 134 | type: "TTF_Font*", 135 | }, 136 | text: { 137 | type: "char*", 138 | }, 139 | fg: { 140 | type: "SDL_Color", 141 | }, 142 | }, 143 | result: { 144 | type: "SDL_Surface*", 145 | }, 146 | checkForError: true, 147 | }, 148 | 149 | TTF_RenderUTF8_LCD: { 150 | parameters: { 151 | font: { 152 | type: "TTF_Font*", 153 | }, 154 | text: { 155 | type: "char*", 156 | }, 157 | fg: { 158 | type: "SDL_Color", 159 | }, 160 | bg: { 161 | type: "SDL_Color", 162 | }, 163 | }, 164 | result: { 165 | type: "SDL_Surface*", 166 | }, 167 | checkForError: true, 168 | }, 169 | 170 | TTF_RenderUTF8_Solid: { 171 | parameters: { 172 | font: { 173 | type: "TTF_Font*", 174 | }, 175 | text: { 176 | type: "char*", 177 | }, 178 | fg: { 179 | type: "SDL_Color", 180 | }, 181 | }, 182 | result: { 183 | type: "SDL_Surface*", 184 | }, 185 | checkForError: true, 186 | }, 187 | 188 | TTF_RenderUTF8_Shaded: { 189 | parameters: { 190 | font: { 191 | type: "TTF_Font*", 192 | }, 193 | text: { 194 | type: "char*", 195 | }, 196 | fg: { 197 | type: "SDL_Color", 198 | }, 199 | bg: { 200 | type: "SDL_Color", 201 | }, 202 | }, 203 | result: { 204 | type: "SDL_Surface*", 205 | }, 206 | checkForError: true, 207 | }, 208 | 209 | TTF_SizeText: { 210 | parameters: { 211 | font: { 212 | type: "TTF_Font*", 213 | }, 214 | text: { 215 | type: "char*", 216 | }, 217 | w: { 218 | type: "int*", 219 | }, 220 | h: { 221 | type: "int*", 222 | }, 223 | }, 224 | result: { 225 | type: "int", 226 | }, 227 | checkForError: true, 228 | }, 229 | 230 | TTF_SizeUTF8: { 231 | parameters: { 232 | font: { 233 | type: "TTF_Font*", 234 | }, 235 | text: { 236 | type: "char*", 237 | }, 238 | w: { 239 | type: "int*", 240 | isOutput: true, 241 | }, 242 | h: { 243 | type: "int*", 244 | isOutput: true, 245 | }, 246 | }, 247 | result: { 248 | type: "int", 249 | }, 250 | checkForError: true, 251 | }, 252 | 253 | TTF_SizeUNICODE: { 254 | parameters: { 255 | font: { 256 | type: "TTF_Font*", 257 | }, 258 | text: { 259 | type: "char*", 260 | }, 261 | w: { 262 | type: "int*", 263 | }, 264 | h: { 265 | type: "int*", 266 | }, 267 | }, 268 | result: { 269 | type: "int", 270 | }, 271 | checkForError: true, 272 | }, 273 | } as const; 274 | -------------------------------------------------------------------------------- /tools/codegen/SDL_ttf/structs.ts: -------------------------------------------------------------------------------- 1 | import { CodeGenOpaqueStructs, CodeGenStructs } from "../types.ts"; 2 | 3 | export const opaqueStructs: CodeGenOpaqueStructs = ["TTF_Font"]; 4 | 5 | export const structs: CodeGenStructs = {} as const; 6 | -------------------------------------------------------------------------------- /tools/codegen/types.ts: -------------------------------------------------------------------------------- 1 | export type CodeGenContext = { 2 | libraryName: string; 3 | callbacks: CodeGenCallbacks; 4 | enums: CodeGenEnums; 5 | events: CodeGenEvents; 6 | functions: CodeGenFunctions; 7 | opaqueStructs: CodeGenOpaqueStructs; 8 | structs: CodeGenStructs; 9 | typedefs: CodeGenTypedefs; 10 | }; 11 | 12 | export type CodeGenTypedefs = Record; 13 | 14 | export type CodeGenCallbacks = Record; 15 | 16 | export interface CodeGenCallback { 17 | // If set the callback is not yet supported and shouldn't be output. 18 | todo?: string; 19 | 20 | parameters: Record; 21 | 22 | result: CodeGenFunctionResult; 23 | } 24 | 25 | export type CodeGenEnums = Record; 26 | 27 | export interface CodeGenEnum { 28 | // Use to specify a name to use as the prefix to strip if it's not the same 29 | // as the enum name to uppercase. 30 | prefixToStrip?: string; 31 | 32 | values: Record; 33 | } 34 | 35 | export type CodeGenEvents = Record; 36 | 37 | export interface CodeGenEventType extends CodeGenStruct { 38 | // The name the struct has in the event union in cases 39 | // where the name can simply not be infered (i.e. KeyboardEvent). 40 | unionName?: string; 41 | } 42 | 43 | export type CodeGenFunctions = Record; 44 | 45 | export interface CodeGenFunctionParam { 46 | // SDL type. 47 | type: string; 48 | 49 | // Can the parameter be null. 50 | isNullable?: boolean; 51 | 52 | // If set this type will be used as the script type. 53 | overrideType?: string; 54 | 55 | isOutput?: boolean; 56 | } 57 | 58 | export interface CodeGenFunctionResult { 59 | // SDL type 60 | type: string; 61 | 62 | // Can the result be null. 63 | nullable?: boolean; 64 | 65 | // If set this type will be used as the script type. 66 | overrideType?: string; 67 | 68 | // If any parameter is marked as an output parameter than the return value 69 | // will generally be discarded. This flag specifies that the return value 70 | // should not be discarded when there are output parameters. 71 | isOutput?: boolean; 72 | } 73 | 74 | export interface CodeGenFunction { 75 | // If set the function is not yet supported and shouldn't be output. 76 | todo?: string; 77 | 78 | // Some functions are (i.e. SDL_BlitSurface) are just 79 | // macros that proxy to another name. 80 | symbolName?: string; 81 | 82 | parameters: Record; 83 | 84 | result: CodeGenFunctionResult; 85 | 86 | overloads?: ReadonlyArray<{ 87 | parameters?: Record>; 88 | result?: Partial; 89 | }>; 90 | 91 | // If true the CodeGen will generate code to check for an error 92 | // and throw an exception if needed. This can probably be determined 93 | // from the return type in the future but while developing the feature 94 | // just set this manually. 95 | checkForError?: boolean; 96 | 97 | // If set the function is implemented by hand only the symbol definition will be generated. 98 | implementation?: string; 99 | } 100 | 101 | export type CodeGenStructs = Record; 102 | 103 | export type CodeGenOpaqueStructs = string[]; 104 | 105 | export interface CodeGenStruct { 106 | // Indicates whether the struct can be allocated in 107 | // in script. If false it will only be allocated by SDL. 108 | allocatable?: boolean; 109 | 110 | // Indicates whether the struct can be written to in 111 | // in script. If false it will only be written to by SDL. 112 | mutable?: boolean; 113 | 114 | // Size of the struct in bytes. 115 | size: number; 116 | 117 | // Struct members. 118 | members: Record; 119 | 120 | // Used when including structs from different libraries. 121 | doNotImport?: boolean; 122 | } 123 | 124 | export interface CodeGenStructMember { 125 | // If true the member is internal to SDL and shouldn't be output. 126 | internal?: boolean; 127 | 128 | // If set the member is not yet supported and shouldn't be output. 129 | todo?: string; 130 | 131 | // SDL type. 132 | type: string; 133 | 134 | // A type override to use. 135 | overrideType?: string; 136 | 137 | // Offset of the member in bytes. 138 | offset: number; 139 | } 140 | --------------------------------------------------------------------------------