diff options
| author | Amir Rajan <[email protected]> | 2020-10-13 00:45:16 -0500 |
|---|---|---|
| committer | Amir Rajan <[email protected]> | 2020-10-13 00:48:54 -0500 |
| commit | 05cbef7fb8224332795e5685be499d81d20e7d93 (patch) | |
| tree | 13ec5f1755c2f45618741f2f016ed8729dbedd41 /samples/12_c_extensions/01_basics/README.md | |
| parent | abad948c1154d88d79b9f891e3b7315540e0b0a3 (diff) | |
| download | dragonruby-game-toolkit-contrib-05cbef7fb8224332795e5685be499d81d20e7d93.tar.gz dragonruby-game-toolkit-contrib-05cbef7fb8224332795e5685be499d81d20e7d93.zip | |
Synced with 1.26.
Diffstat (limited to 'samples/12_c_extensions/01_basics/README.md')
| -rw-r--r-- | samples/12_c_extensions/01_basics/README.md | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/samples/12_c_extensions/01_basics/README.md b/samples/12_c_extensions/01_basics/README.md new file mode 100644 index 0000000..6993173 --- /dev/null +++ b/samples/12_c_extensions/01_basics/README.md @@ -0,0 +1,227 @@ +# C Extensions introduction + +This sample app serves as an introduction on how to write C extensions for the +DragonRuby Game Toolkit. You'll need a Pro License +which can be purchased at http://dragonruby.org. The sample app is provided in +the Standard license for those that are curious as to what implementing C Extensions +looks like. + +## Requirements + +In order to use C extensions you need a C compiler. We strongly recommend you +using [Clang](https://clang.llvm.org). + +### Windows + +To get Clang for Windows you can use the [official build](https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe). + +During installation, make sure to choose "Add LLVM to the system PATH for all +users", as on the screenshot  + +On Windows, you also need to install MinGW-w64 toolchain from [here](http://mingw-w64.org/doku.php/download/mingw-builds) +Set the installation directory to "C:\mingw-w64":  and on the next step choose the right version and the +other setting as on the screenshot: . + + +### Linux + +On Debian-based systems you can install Clang via `apt`. It is recommended you +install `clang-10`, but any other 'reasonably' fresh version should work just +fine: + +``` +> apt-get install clang-10 +> clang-10 --version +Apple clang version 10.0.0-4ubuntu1-18.04.2 +Target: x86_64-pc-linux-gnu +Thread model: posix +InstalledDir: /usr/bin +``` + +Raspberry Pi instructions are mostly the same as x86-64 Linux. Out of the box, +Clang can cross-compile for the Pi on any platform, but you'll need to set up +a proper sysroot, which can be complicated, and beyond the scope of this +document. The bold--and patient--can always compile on the Pi device directly, +too. + + +### macOS + +To get Clang on macOS it is recommended to install [Xcode](https://developer.apple.com/xcode/). +Once you've done that you should be able to use Clang from a terminal: + +``` +> clang --version +Apple clang version 11.0.3 (clang-1103.0.32.29) +Target: x86_64-apple-darwin19.5.0 +Thread model: posix +InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin +``` + +### Android + +For Android, you can [install the Android NDK](https://developer.android.com/ndk/) +on Windows, Linux, or macOS. This lets you compile C code for the platform. You +won't be using Java here. + +DragonRuby ships with support for Android on arm32, arm64, x86, and x86-64. This might +be overkill, but it allows for maximum hardware compatibility. You should plan to +compile for all four architectures too, but of course this is optional. + + +## Hello World + +Let's craft the simplest possible C extension: a simple function that +calculates square of an integer. Put the following code into a file named +`ext.c` under `mygame` directory: + +``` +int square(int x) { + return x * x; +} +``` + +You need some glue code to connect this function to the GameToolKit. Good news +are that you don't need to craft this code yourself: it can be generated for +you by `dragonruby-bind` (or `dragonruby-bind.exe` if you are on Windows). + +Run the following command from Linux/macOS terminal: + +``` +> ./dragonruby-bind --output=mygame/ext-bind.c mygame/ext.c +``` + +Or from Windows Powershell: + +``` +> .\dragobruby-bind.exe --output=mygame\ext-bind.c mygame\ext.c +``` + +With the `ext-bind.c` in place you can compile your C extension into a dynamic +or shared library. Here is how you do it: + +On Windows: + +``` +clang -shared \ + --sysroot=C:\mingw-w64\mingw64^ + --target=x86_64-w64-mingw32 -fuse-ld=lld^ + -isystem include -I . -fPIC^ + -o mygame\native\windows-amd64\ext.dll mygame\ext-bind.c +``` + +On Linux: + +``` +> clang -shared \ + -isystem include -I . -fPIC \ + -o mygame/native/linux-amd64/ext.so mygame/ext-bind.c +``` + +On Raspberry Pi, on the device: + +``` +> clang -shared \ + -isystem include -I . -fPIC \ + -o mygame/native/linux-raspberrypi/ext.so mygame/ext-bind.c +``` + +On Raspberry Pi, with a cross-compiler: + +``` +> clang -shared --sysroot=/where/i/installed/a/sysroot \ + --target=arm-linux-gnueabihf -fuse-ld=lld \ + -isystem include -I . -fPIC \ + -o mygame/native/linux-raspberrypi/ext.so mygame/ext-bind.c +``` + +On macOS: + +``` +> clang -shared \ + -isystem include -I . -fPIC \ + -o mygame/native/macos/ext.dylib mygame/ext-bind.c +``` + +On Android: + +(This is what it looks like on Linux, change the appropriate parts for Windows, etc) + +``` +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=armv7-none-linux-androideabi26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a \ + -mthumb -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-arm32/ext.so mygame/ext-bind.c + +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=aarch64-none-linux-android26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \ + -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-arm64/ext.so mygame/ext-bind.c + +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=i686-none-linux-android26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \ + -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-x86/ext.so mygame/ext-bind.c + +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=x86_64-none-linux-android26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \ + -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-amd64/ext.so mygame/ext-bind.c +``` + + + +Now, include the following snippet into the very beginning of `mygame/app/main.rb`: + +``` +$gtk.ffi_misc.gtk_dlopen("ext") +include FFI::CExt +puts square(11) +``` + +Now, simply run `dragonruby` (or `dragonruby.exe`) and you should see `121` on +the console. + +Let's do a breakdown of each line! + +1. `$gtk.ffi_misc.gtk_dlopen("ext")` - DragonRuby exposes a special function + called `gtk_dlopen`, you can use it to load a dynamic that holds + the C extension code. It looks for the shared library in + "mygame/native/$PLATFORM/ext.$PLATFORM_DLL_EXTENSION" +2. `include FFI::CExt` - by default, DragonRuby puts all the code available in + the C extension under `FFI::CExt` module. This line serves as a shortcut so + that you don't need to write `FFI::CExt::square` any time you want to call + a function. +3. `puts square(11)` - this line simply prints the value returned from C code. + +Now, you can call the `square` function at any place in the code. Let's see +what's the square value of every pixel on the screen. Here is the full program: + +``` +$gtk.ffi_misc.gtk_dlopen("ext") +include FFI::CExt + +def tick args + args.outputs.labels << [640, 500, "mouse.x = #{args.mouse.x.to_i}", 5, 1] + args.outputs.labels << [640, 460, "square(mouse.x) = #{square(args.mouse.x.to_i)}", 5, 1] + args.outputs.labels << [640, 420, "mouse.y = #{args.mouse.y.to_i}", 5, 1] + args.outputs.labels << [640, 380, "square(mouse.y) = #{square(args.mouse.y.to_i)}", 5, 1] +end +``` + +When you run the game now, you will see something like this: + + |
