diff --git a/examples/raylib/README.md b/examples/raylib/README.md new file mode 100644 index 0000000..f61d924 --- /dev/null +++ b/examples/raylib/README.md @@ -0,0 +1,59 @@ +# Raylib Example +**Note**: +Due to [an upstream bug](https://github.com/ziglang/zig/issues/20476), you will probably receive a warning (or multiple warnings if building for multiple targets) like this: +``` +error: warning(link): unexpected LLD stderr: +ld.lld: warning: /.zig-cache/o/4227869d730f094811a7cdaaab535797/libraylib.a: archive member '/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/35/libGLESv2.so' is neither ET_REL nor LLVM bitcode +``` +You can ignore this error for now. + +### Build, install to test one target against a local emulator and run + +```sh +zig build -Dtarget=x86_64-linux-android +adb install ./zig-out/bin/raylib.apk +adb shell am start -S -W -n com.zig.raylib/android.app.NativeActivity +``` + +### Build and install for all supported Android targets + +```sh +zig build -Dandroid=true +adb install ./zig-out/bin/raylib.apk +``` + +### Build and run natively on your operating system + +```sh +zig build run +``` + +### Uninstall your application + +If installing your application fails with something like: +``` +adb: failed to install ./zig-out/bin/raylib.apk: Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.zig.raylib signatures do not match newer version; ignoring!] +``` + +```sh +adb uninstall "com.zig.raylib" +``` + +### View logs of application + +Powershell (app doesn't need to be running) +```sh +adb logcat | Select-String com.zig.raylib: +``` + +Bash (app doesn't need running to be running) +```sh +adb logcat com.zig.raylib:D *:S +``` + +Bash (app must be running, logs everything by the process including modules) +```sh +adb logcat --pid=`adb shell pidof -s com.zig.raylib` +``` + + diff --git a/examples/raylib/android/AndroidManifest.xml b/examples/raylib/android/AndroidManifest.xml new file mode 100644 index 0000000..514547b --- /dev/null +++ b/examples/raylib/android/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + diff --git a/examples/raylib/android/res/mipmap/ic_launcher.png b/examples/raylib/android/res/mipmap/ic_launcher.png new file mode 100644 index 0000000..7ea4bd8 Binary files /dev/null and b/examples/raylib/android/res/mipmap/ic_launcher.png differ diff --git a/examples/raylib/android/res/values/strings.xml b/examples/raylib/android/res/values/strings.xml new file mode 100644 index 0000000..99203e9 --- /dev/null +++ b/examples/raylib/android/res/values/strings.xml @@ -0,0 +1,10 @@ + + + + Zig Raylib + + com.zig.raylib + diff --git a/examples/raylib/build.zig b/examples/raylib/build.zig new file mode 100644 index 0000000..4b054b8 --- /dev/null +++ b/examples/raylib/build.zig @@ -0,0 +1,99 @@ +const android = @import("android"); +const std = @import("std"); + +//This is targeting android version 10 / API level 29. +//Change the value here and in android/AndroidManifest.xml to target your desired API level +const android_version: android.APILevel = .android10; +const android_api = std.fmt.comptimePrint("{}", .{@intFromEnum(android_version)}); +const exe_name = "raylib"; + +pub fn build(b: *std.Build) void { + const root_target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + const android_targets = android.standardTargets(b, root_target); + + var root_target_single = [_]std.Build.ResolvedTarget{root_target}; + const targets: []std.Build.ResolvedTarget = if (android_targets.len == 0) + root_target_single[0..] + else + android_targets; + + const android_apk: ?*android.APK = blk: { + if (android_targets.len == 0) { + break :blk null; + } + const android_tools = android.Tools.create(b, .{ + .api_level = android_version, + .build_tools_version = "35.0.1", + .ndk_version = "29.0.13113456", + }); + const apk = android.APK.create(b, android_tools); + + const key_store_file = android_tools.createKeyStore(android.CreateKey.example()); + apk.setKeyStore(key_store_file); + apk.setAndroidManifest(b.path("android/AndroidManifest.xml")); + apk.addResourceDirectory(b.path("android/res")); + + break :blk apk; + }; + + for (targets) |target| { + const lib_mod = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + const lib = b.addLibrary(.{ + .linkage = .dynamic, + .name = exe_name, + .root_module = lib_mod, + }); + lib.linkLibC(); + b.installArtifact(lib); + + const android_ndk_path = if(android_apk) |apk| (b.fmt("{s}/ndk/{s}", .{ apk.tools.android_sdk_path, apk.tools.ndk_version })) else ""; + const raylib_dep = if (target.result.abi.isAndroid()) ( + b.dependency("raylib_zig", .{ + .target = target, + .optimize = optimize, + .android_api_version = @as([]const u8, android_api), + .android_ndk = @as([]const u8, android_ndk_path), + })) else ( + b.dependency("raylib_zig", .{ + .target = target, + .optimize = optimize, + .shared = true + })); + const raylib_artifact = raylib_dep.artifact("raylib"); + lib.linkLibrary(raylib_artifact); + const raylib_mod = raylib_dep.module("raylib"); + lib.root_module.addImport("raylib", raylib_mod); + + if (target.result.abi.isAndroid()) { + const apk: *android.APK = android_apk orelse @panic("Android APK should be initialized"); + const android_dep = b.dependency("android", .{ + .optimize = optimize, + .target = target, + }); + lib.root_module.linkSystemLibrary("android", .{ .preferred_link_mode = .dynamic }); + lib.root_module.addImport("android", android_dep.module("android")); + + const native_app_glue_dir: std.Build.LazyPath = .{ .cwd_relative = b.fmt("{s}/sources/android/native_app_glue", .{android_ndk_path}) }; + lib.root_module.addCSourceFile(.{ .file = native_app_glue_dir.path(b, "android_native_app_glue.c") }); + lib.root_module.addIncludePath(native_app_glue_dir); + + lib.root_module.linkSystemLibrary("log", .{ .preferred_link_mode = .dynamic }); + apk.addArtifact(lib); + } else { + const exe = b.addExecutable(.{ .name = exe_name, .optimize = optimize, .root_module = lib_mod }); + b.installArtifact(exe); + + const run_exe = b.addRunArtifact(exe); + const run_step = b.step("run", "Run the application"); + run_step.dependOn(&run_exe.step); + } + } + if (android_apk) |apk| { + apk.installApk(); + } +} diff --git a/examples/raylib/build.zig.zon b/examples/raylib/build.zig.zon new file mode 100644 index 0000000..7766afd --- /dev/null +++ b/examples/raylib/build.zig.zon @@ -0,0 +1,18 @@ +.{ + .name = .raylib, + .version = "0.0.0", + .minimum_zig_version = "0.14.0", + .fingerprint = 0x13035e5cf42e313f, + .dependencies = .{ + .raylib_zig = .{ + .url = "git+https://github.com/lumenkeyes/raylib-zig#b00d4c2b973665e3a88c2565b6cd63c56d0173c2", + .hash = "raylib_zig-5.6.0-dev-KE8REPwqBQC35iWa2sFblBUNWkTlEi1gjit9dtyOLu_b" + }, + .android = .{ .path = "../.." }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/examples/raylib/src/main.zig b/examples/raylib/src/main.zig new file mode 100644 index 0000000..98c0dc8 --- /dev/null +++ b/examples/raylib/src/main.zig @@ -0,0 +1,46 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const android = @import("android"); +const rl = @import("raylib"); + +pub fn main() void { + const screenWidth = 800; + const screenHeight = 450; + rl.initWindow(screenWidth, screenHeight, "raylib-zig [core] example - basic window"); + defer rl.closeWindow(); + rl.setTargetFPS(60); + while (!rl.windowShouldClose()) { + rl.beginDrawing(); + defer rl.endDrawing(); + + rl.clearBackground(.white); + + rl.drawText("Congrats! You created your first window!", 190, 200, 20, .light_gray); + } +} + +/// custom panic handler for Android +pub const panic = if (builtin.abi.isAndroid()) + android.panic +else + std.debug.FullPanic(std.debug.defaultPanic); + +/// custom standard options for Android +pub const std_options: std.Options = if (builtin.abi.isAndroid()) + .{ + .logFn = android.logFn, + } +else + .{}; + +comptime { + if (builtin.abi.isAndroid()) { + // Setup exported C-function as defined in AndroidManifest.xml + // ie. + @export(&androidMain, .{ .name = "main" }); + } +} + +fn androidMain() callconv(.c) c_int { + return std.start.callMain(); +}