This commit is contained in:
Dario48 2026-06-13 15:08:28 +02:00
commit b38a3b7bda
9 changed files with 801 additions and 0 deletions

3
.envrc Normal file
View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
use flake

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
zig-out/
zig-pkg/
result
.direnv/
.zig-cache/

69
build.zig Normal file
View file

@ -0,0 +1,69 @@
const std = @import("std");
const cimgui = @import("cimgui_zig");
const Renderer = cimgui.Renderer;
const Platform = cimgui.Platform;
fn addIncludePathsToTranslateC(translate_c: *std.Build.Step.TranslateC, lib: *std.Build.Step.Compile) void {
for (lib.root_module.include_dirs.items) |*included| {
switch (included.*) {
.path => translate_c.addIncludePath(included.path),
.config_header_step => translate_c.addConfigHeader(included.config_header_step),
.path_system => translate_c.addSystemIncludePath(included.path_system),
.other_step => addIncludePathsToTranslateC(translate_c, included.other_step),
else => unreachable,
}
}
}
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const translate_c = b.addTranslateC(.{
.root_source_file = b.path("src/c.h"),
.target = target,
.optimize = optimize,
});
translate_c.linkSystemLibrary("gl", .{ .needed = true, .preferred_link_mode = .static });
const cimgui_dep = b.dependency("cimgui_zig", .{
.target = target,
.optimize = optimize,
.platforms = &[_]Platform{.GLFW},
.renderers = &[_]Renderer{.OpenGL3},
// .docking = true, // Default value: false
// .no_renderer = true, // Default value: false. Comment `renderers` field if you use this one
// .no_platform = true, // Default value: false. Comment `platforms` field if you use this one
});
const cimgui_lib = cimgui_dep.artifact("cimgui");
addIncludePathsToTranslateC(translate_c, cimgui_lib);
const c_module = translate_c.createModule();
c_module.linkLibrary(cimgui_lib);
const exe = b.addExecutable(.{
.name = "emu_ui",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "imgui", .module = c_module },
},
}),
});
b.installArtifact(exe);
const run_step = b.step("run", "Run the app");
const run_cmd = b.addRunArtifact(exe);
run_step.dependOn(&run_cmd.step);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
}

48
build.zig.zon Normal file
View file

@ -0,0 +1,48 @@
.{
// This is the default name used by packages depending on this one. For
// example, when a user runs `zig fetch --save <url>`, this field is used
// as the key in the `dependencies` table. Although the user can choose a
// different name, most users will stick with this provided value.
//
// It is redundant to include "zig" in this name because it is already
// within the Zig package namespace.
.name = .emu_ui,
// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.0.0",
// Together with name, this represents a globally unique package
// identifier. This field is generated by the Zig toolchain when the
// package is first created, and then *never changes*. This allows
// unambiguous detection of one package being an updated version of
// another.
//
// When forking a Zig project, this id should be regenerated (delete the
// field and run `zig build`) if the upstream project is still maintained.
// Otherwise, the fork is *hostile*, attempting to take control over the
// original project's identity. Thus it is recommended to leave the comment
// on the following line intact, so that it shows up in code reviews that
// modify the field.
.fingerprint = 0xd57ee1da94d7a196, // Changing this has security and trust implications.
// Tracks the earliest Zig version that the package considers to be a
// supported use case.
.minimum_zig_version = "0.16.0",
// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
.cimgui_zig = .{
.url = "git+https://github.com/tiawl/cimgui.zig.git#6236ef65bcb28dce7c85551ff666c6a05958ce70",
.hash = "cimgui_zig-1.0.0-2Xubko0ywgAapZ7NuD-boXc-ilbB5pSk77zwZmQ3va2o",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
// For example...
//"LICENSE",
//"README.md",
},
}

152
build.zig.zon2json-lock Normal file
View file

@ -0,0 +1,152 @@
{
"cimgui_zig-1.0.0-2Xubko0ywgAapZ7NuD-boXc-ilbB5pSk77zwZmQ3va2o": {
"name": "cimgui_zig",
"url": "git+https://github.com/tiawl/cimgui.zig.git#6236ef65bcb28dce7c85551ff666c6a05958ce70",
"hash": "sha256-2se9y0czFALivKakfCGfLEQFXRpGvSzwzdDQmXzEcG0="
},
"sdl-0.5.0+3.4.8-SDL--vADpgEwm_HQV4hf2-_vY_zH6G6yfuMVonp0DjOg": {
"name": "sdl",
"url": "git+https://github.com/tiawl-forks/SDL#053b0a1dd231a15dcab4572b902474a58919c48d",
"hash": "sha256-YZQ3NqAtn5dFYDsOXp+40lBZHYFHBev8vH9UdeVHv/o="
},
"sdl_linux_deps-0.0.0-SDL_ltg8hgAOayMwFN6BhHW3Rs5UPtf6n8m-2qcQHuGS": {
"name": "sdl_linux_deps",
"url": "git+https://github.com/castholm/SDL_linux_deps.git#fd349940b9dbaa0f221703b05df826d745d7ce2a",
"hash": "sha256-buTJQNS3UgjJALqC7RUdXJHYPr27oKRkxKENXQvjL5w="
},
"N-V-__8AAGksdwBW9WS5PeUEDj4h55ps1ItuvJNZWfw86hyN": {
"name": "imgui-master",
"url": "git+https://github.com/ocornut/imgui?ref=v1.92.8#8936b58fe26e8c3da834b8f60b06511d537b4c63",
"hash": "sha256-R8jfwG2pI4g+RIZHZkFBdg7e8Xd9n/bKsz8ZlVbthkM="
},
"N-V-__8AAKmrgQA-iEFHovAwDtF8olu0VkgmKg_3T15wRPzJ": {
"name": "imgui-docking",
"url": "git+https://github.com/ocornut/imgui?ref=v1.92.8-docking#b61e56346a92cfcaf1f43a545ca37b0b32239654",
"hash": "sha256-0IN6+FMiM81j1xuUr0vkkkBlYKDiJ90iJL5iHwf1NhQ="
},
"N-V-__8AAPErCQARgFL-uXKTjXbwnJv4IPOIC8kr-yJrFXEr": {
"name": "dcimgui",
"url": "git+https://github.com/dearimgui/dear_bindings#c9ff64913915df41c0f4beef485b98a1c685eda5",
"hash": "sha256-X7Exy7fqTLI3IVX79xfsxGBdHAopT4ugM099/9i9ZOY="
},
"toolbox-2.0.2-kRAu4MuuAADahV3pylUEIihubqTEIbEmII7gNYBayjSu": {
"name": "toolbox",
"url": "git+https://github.com/tiawl/toolbox?ref=2.1.3#659715fb99caa23cf07654c04cb930684125df6b",
"hash": "sha256-N3n+j1XES44cti7IH83VLDdNIlZQV3rw+u934HAPAfE="
},
"vulkan_zig-1.0.0-p2wg3pHGgQE_QxYcDPhmDC-lgYQU-Lip0v4zhxJCl1Q8": {
"name": "vulkan_zig",
"url": "git+https://github.com/tiawl/vulkan.zig?ref=v1.4.353#3110a3d4fa68935a1ae1cb6b03008aaadb6e089b",
"hash": "sha256-LFIQ3X0jjn67car0vqqQJHsGwGzAtjJxmZUBqYlk1CI="
},
"N-V-__8AAA_gewLAqvkaEZqi4gTYHHr2t1zlcXTnm1S3GNin": {
"name": "Vulkan-Headers",
"url": "git+https://github.com/KhronosGroup/Vulkan-Headers?ref=v1.4.353#45834b722e29ca6968f3811ae930eae7bb73e9e8",
"hash": "sha256-uiVHqwN52HyHcHi5nX84AZ5Qlx5+2uGXtBdwZUeWMQQ="
},
"glfw_zig-1.0.0-NrvYo4bRGQAP317l7e7dT4IZdJGieD-xtbGixDqW7IK5": {
"name": "glfw_zig",
"url": "git+https://github.com/tiawl/glfw.zig?ref=wayland.zig-wayland-protocols-1.49#113d4eb9a4d9de35d7c225559bf830922c721b6f",
"hash": "sha256-MAkFu1Mn0BkPzYs6A/HFo1KuETPVpEoUrCn/IUQzoik="
},
"N-V-__8AAKE_TAAJO1RHyQ6MPZFPLLCAyHNu7iYXh7n1KEfH": {
"name": "glfw",
"url": "git+https://github.com/glfw/glfw?ref=3.4#7b6aead9fb88b3623e3b3725ebb42670cbe4c579",
"hash": "sha256-6BjiSh8ZZCcdUVmzAc8q5yYjXEpQj1hBd61qQ6/J4Sg="
},
"wayland_zig-1.0.0-JmY-j4xYCgCbGpwvz1hOveHYDJq1SmrTog1FvtmP8Ltu": {
"name": "wayland_zig",
"url": "git+https://github.com/tiawl/wayland.zig?ref=wayland-protocols-1.49#7ccc39a568482d6df73a40193728111182ca7997",
"hash": "sha256-6+enMHEN4PB45jrAbYQy0QD5D02xTkieQl66UJVODhs="
},
"N-V-__8AAHr0JACeIVK5CkrixKeVluYqJ4OPDz1ss5bvTWd9": {
"name": "wayland",
"url": "git+https://gitlab.freedesktop.org/wayland/wayland?ref=1.24.91#e6aeed9816f9fa202ed49318b7b25f867676999a",
"hash": "sha256-/mx8YCE13aeVZ/ZAzJKoZmmxDgsmG0FE5FOa0q/ZnkI="
},
"N-V-__8AAOTNEADZkChhsSciYizE4kIbZsozjDZXVeG5R_uY": {
"name": "wayland-protocols",
"url": "git+https://gitlab.freedesktop.org/wayland/wayland-protocols?ref=1.49#ee78491a237eaff9389a0ccf8680521d074407d3",
"hash": "sha256-lYhs7Aw29p+1J5ITXV8HmTpyDlSflpnsi0rQ11C7vMk="
},
"X11_zig-1.0.0-UuAzbDYLwABQ45p0hs4ZfOfKTTx_swCXOx8tqVzHxdzA": {
"name": "X11_zig",
"url": "git+https://github.com/tiawl/X11.zig?ref=toolbox-2.1.3#26bb7dff7ddc5ec417ac35338af51caf689e9853",
"hash": "sha256-aiccXQW/P7G5/IzukTOf8qLdM84NtZHi6K6R7COGxYg="
},
"N-V-__8AAMXRAADYeHz48Y1IuZUzOOqRvOO7f0wrdeNkWcBF": {
"name": "XScrnSaver",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libxscrnsaver?ref=libXScrnSaver-1.2.5#60c957ba8a727d01b376da0142aef7e18aa86fe1",
"hash": "sha256-7ghogh+nawGje8CCOjeOBlkpUkmZQXiBPWftTS4ldzU="
},
"N-V-__8AANorBADbt-RlITZJofNvQWTiaQlv7r7lWYtxAqGx": {
"name": "Xrender",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libxrender?ref=libXrender-0.9.12#46e12ff9e8e4d3f0313a2f097df93dbfdc14f92f",
"hash": "sha256-ivh/6iTEYDVHpki4YtRA2vZAIFcr+C/bwxp3llBK6lQ="
},
"N-V-__8AAGlRAgBdkf1F-Y410qIz6cV4C62ZAUk_SGeUVriw": {
"name": "Xrandr",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libxrandr?ref=libXrandr-1.5.5#540c3ceef2be3e513b18002a858c7a9591dd749a",
"hash": "sha256-KuQrKRdrW/Zzi60vqLMjKV0FdJN670n5hqZILcVMrLM="
},
"N-V-__8AAG86ZgA2-SHGTO4n81-Sj8LNvSASCKZ91wOWZ7Sg": {
"name": "xorgproto",
"url": "git+https://gitlab.freedesktop.org/xorg/proto/xorgproto?ref=xorgproto-2025.1#c18d2bc22813793bba7f0e4e603c0104d7724802",
"hash": "sha256-f7dgvCBCLWE3RLovvSH5Ny8dD1WtgDlE5e2b/mEaZag="
},
"N-V-__8AADGbAADcSgAMV_BzmUHtufxO65p2fWn8YYOXU16O": {
"name": "Xinerama",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libxinerama?ref=libXinerama-1.1.6#cc4ac9cf36fdf3815381b791e47a90b8aebc7e78",
"hash": "sha256-qt89LRNFIv632KB3KwspUhBC9WlrqiQ1Rn/auQY54go="
},
"N-V-__8AAKtlAgAAqwksheZkCSajEEPpi2XivNet-_3hZyz2": {
"name": "Xcursor",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libxcursor?ref=libXcursor-1.2.3#8ae6cf61b18ecddf26f72a07bbfbd1ea9f022c36",
"hash": "sha256-JT+lZa1ErnzPE7D/Dte/JeNPrLhO2Y6MaG97wrn9/0w="
},
"N-V-__8AAHTdBwAEWsgYzKEVP-WmGT0wYKIJjBtqH1No6y73": {
"name": "Xext",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libxext?ref=libXext-1.3.7#2a694ba264ccdb205909909abfe8b136f9156ebe",
"hash": "sha256-FkXKbwvOO8Q6OGzlvrQ4VKvuxUlLa2abz0ub15hCfk8="
},
"N-V-__8AAPmRAwD2MFbqdgQz922YH3E6X_64ouktIekZrgb6": {
"name": "Xtrans",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libxtrans?ref=xtrans-1.6.0#cf05ba4a10c90da2c63805a5375e983b174e28b0",
"hash": "sha256-1CFWuqUGU3lHszzb9aEyLxzsfl820N09UTVeFa6blls="
},
"N-V-__8AAFIgAQCJSug37AGy4zypGtiDtUR9wtQa8U74Nm0a": {
"name": "Xfixes",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libxfixes?ref=libXfixes-6.0.2#70d5b0e37f8a759f3dbc218f22954347ceed094a",
"hash": "sha256-JbvkJ7xJXPWPFTedLqCzKGswS2NuhLy4ieh07P8Ijbo="
},
"N-V-__8AAPR0DQAuD3_ZopKbezyogf0SLNe6GiYF3RlaaNrh": {
"name": "Xi",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libxi?ref=libXi-1.8.3#f00f7e9140b4707775331950dd67851e55058c40",
"hash": "sha256-qPT8uEKxyZPgzgxkaajujZbne2pvUILDVVz0lXI7aCk="
},
"N-V-__8AAGTlAABgkQ5gmw2C1xhypMB4PArGI_hNIFTXs3mQ": {
"name": "Xau",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libXau?ref=libXau-1.0.12#a9c65683e68b3a4349afee5d7673b393fb924d2e",
"hash": "sha256-re1JMPNz86U3WrhRsWCPJY/FZCUJolCclqHmeTqUUAg="
},
"N-V-__8AANL5xACqderCBKpnIFKe282dZAikvyc--kNSKWgd": {
"name": "X11",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libx11?ref=libX11-1.8.13#6c75545a1deb51f5903992c52af6bc35cc9bc103",
"hash": "sha256-FeTdnV/GR7P2SJRLbSylmqQlUVUq9rEsIpkLMU/eDDk="
},
"N-V-__8AAPZxCQD7JyQLx0_D9J9dnkSoP7iziBVxreTgUqlj": {
"name": "xcb",
"url": "git+https://gitlab.freedesktop.org/xorg/lib/libxcb?ref=libxcb-1.17.0#622152ee42a310876f10602601206954b8d0613e",
"hash": "sha256-fkJMVvijt2GKae6RDUDDTqezEcZsiWvklh4jQdBcjOw="
},
"N-V-__8AAFnMDACdXT5YIakLgEMFZ-K7ZICgB18p8xKobHjk": {
"name": "xcbproto",
"url": "git+https://gitlab.freedesktop.org/xorg/proto/xcbproto?ref=xcb-proto-1.17.0#77d7fc04da729ddc5ed4aacf30253726fac24dca",
"hash": "sha256-U8BgRvUro1ew+58MnZcVgksMY/hmtETASGsuLdn9KSs="
},
"N-V-__8AAPSQawCcuTcqIxPOXcScgtciyBHFP_S54FcaFo5L": {
"name": "xkbcommon",
"url": "git+https://github.com/xkbcommon/libxkbcommon?ref=xkbcommon-1.13.0#3049d310694fd70f1269bf48a1200aa4259e79b1",
"hash": "sha256-zBYsGRbDrQItCtNJd9L+7njQ+BvtXQ0/GnOOMSNwn1k="
}
}

160
flake.lock generated Normal file
View file

@ -0,0 +1,160 @@
{
"nodes": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1778716662,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1780930886,
"narHash": "sha256-rppURzHviaQN131F+nLiLdGfcb0uCd9gGP0E5+iw9MI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8c3cede7ddc26bd659d2d383b5610efbd2c7a16e",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1777168982,
"narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs",
"zig2nix": "zig2nix",
"zls": "zls"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"zig-flake": {
"inputs": {
"nixpkgs": [
"zls",
"nixpkgs"
]
},
"locked": {
"lastModified": 1780942553,
"narHash": "sha256-UcgOn/UltTXewaD1jafUP5R3ck3YgTLJegac7w1Qqyw=",
"owner": "silversquirl",
"repo": "zig-flake",
"rev": "ad1b13d48d4f976e5faf68fc31639e34e5071ca4",
"type": "github"
},
"original": {
"owner": "silversquirl",
"repo": "zig-flake",
"type": "github"
}
},
"zig2nix": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1781323399,
"narHash": "sha256-GcyULaAQOGzHO3bhlW8Q7/fP6oi7Q3GITEyzXvota4s=",
"owner": "Cloudef",
"repo": "zig2nix",
"rev": "63ee49216f3ba5f6289c5f60dc404a375b92a1ef",
"type": "github"
},
"original": {
"owner": "Cloudef",
"repo": "zig2nix",
"type": "github"
}
},
"zls": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"zig-flake": "zig-flake"
},
"locked": {
"lastModified": 1776368637,
"narHash": "sha256-k0xWObsw9K12BKfK+UB5TieWDFEFfBQhN1X1NO35fWk=",
"owner": "zigtools",
"repo": "zls",
"rev": "494486203c3a48927f2383aa3d5ce5fca112186d",
"type": "github"
},
"original": {
"owner": "zigtools",
"ref": "0.16.0",
"repo": "zls",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

79
flake.nix Normal file
View file

@ -0,0 +1,79 @@
{
description = "A Zig program using dear_imgui_zig";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
zig2nix = {
url = "github:Cloudef/zig2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
zls = {
url = "github:zigtools/zls/0.16.0";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
inputs@{
flake-parts,
nixpkgs,
zig2nix,
zls,
...
}:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
perSystem =
{
system,
pkgs,
config,
...
}:
let
env = zig2nix.outputs.zig-env.${system} { };
zlsPkg = zls.packages.${system}.zls;
in
{
packages.emu_ui = env.package {
src = ./.;
buildInputs = with pkgs; [
libGL
wayland
wayland-protocols
];
nativeBuildInputs = with pkgs; [
pkg-config
];
zigWrapperLibs = with pkgs; [
wayland
libGL
];
};
packages.default = config.packages.emu_ui;
devShells.default = pkgs.mkShellNoCC {
packages = [ zlsPkg ];
inputsFrom = [ config.packages.default ];
shellHook = ''
echo "zig $(zig version)"
echo "zls $(zls --version)"
'';
};
};
};
}

6
src/c.h Normal file
View file

@ -0,0 +1,6 @@
#define GLFW_INCLUDE_NONE 1
#include "GL/gl.h"
#include "GLFW/glfw3.h"
#include "backends/dcimgui_impl_glfw.h"
#include "backends/dcimgui_impl_opengl3.h"
#include "dcimgui.h"

279
src/main.zig Normal file
View file

@ -0,0 +1,279 @@
const std = @import("std");
const c = @import("imgui");
const CError = error{cError};
fn wrap(@"fn": anytype, fnArgs: anytype) CError!void {
const ret = @call(.auto, @"fn", fnArgs);
if (ret != 0) {
std.log.err("function " ++ @typeName(@TypeOf(@"fn")) ++ " returned code {}", .{ret});
return error.cError;
}
}
fn setFlags(flags: []const c_int, io: *c.struct_ImGuiIO_t) void {
for (flags) |flag| {
io.ConfigFlags |= flag;
}
}
export fn glfw_error_callback(@"error": c_int, description: [*c]const u8) void {
std.log.err("GLFW error {}: {s}", .{ @"error", description });
}
pub const std_options: std.Options = .{};
fn formatArgv(alloc: std.mem.Allocator, format: []const u8, path: []const u8) ![][]u8 {
var outer = std.ArrayList([]u8).empty;
errdefer {
for (outer.items) |arr| {
alloc.free(arr);
}
outer.deinit(alloc);
}
var inner = std.ArrayList(u8).empty;
errdefer inner.deinit(alloc);
var quoted: enum {
@"\"",
@"'",
no,
pub fn toBool(self: @This()) bool {
return switch (self) {
.@"\"", .@"'" => true,
else => false,
};
}
pub inline fn fromU8(char: u8) @This() {
return switch (char) {
'"' => .@"\"",
'\'' => .@"'",
else => unreachable,
};
}
} = .no;
var escaped: bool = false;
for (format) |char| {
switch (char) {
inline '\'', '"' => |q| {
if (escaped)
try inner.append(alloc, q)
else {
const tmp: @TypeOf(quoted) = .fromU8(q);
if (quoted.toBool()) {
if (tmp == quoted)
quoted = .no
else
try inner.append(alloc, q);
} else quoted = tmp;
}
},
'\\' => {
if (escaped)
try inner.append(alloc, '\\')
else
escaped = true;
},
's' => try if (escaped) inner.appendSlice(alloc, path) else inner.append(alloc, 's'),
'\t'...'\r', ' ' => |w| {
if (escaped or quoted.toBool()) {
try inner.append(alloc, w);
} else {
const owned = try inner.toOwnedSlice(alloc);
errdefer alloc.free(owned);
try outer.append(alloc, owned);
}
},
else => |g| try inner.append(alloc, g),
}
if (escaped) escaped = false;
}
return outer.toOwnedSlice(alloc);
}
const opts = struct {
vsync: bool = true,
fullscreen: bool = false,
light: bool = false,
classic: bool = false,
width: f32 = 1280,
height: f32 = 800,
zonPath: [:0]const u8 = ".",
};
var args: opts = .{};
const map = blk: {
var boolFields: []const struct { []const u8, *bool } = &.{};
var floatFields: []const struct { []const u8, *f32 } = &.{};
for (@typeInfo(opts).@"struct".fields) |field| {
switch (field.type) {
bool => {
boolFields = .{.{ "--" ++ field.name, &@field(args, field.name) }} ++ boolFields;
boolFields = .{.{ "-" ++ .{field.name[0]}, &@field(args, field.name) }} ++ boolFields;
},
f32 => {
floatFields = .{.{ "--" ++ field.name, &@field(args, field.name) }} ++ floatFields;
floatFields = .{.{ "-" ++ .{field.name[0]}, &@field(args, field.name) }} ++ floatFields;
},
else => {},
}
}
break :blk .{ std.StaticStringMap(*bool).initComptime(boolFields), std.StaticStringMap(*f32).initComptime(floatFields) };
};
const binOptMap = map[0];
const floatOptMap = map[1];
var argv: ?[][]u8 = null;
pub fn main(init: std.process.Init) !void {
{
var iter = try init.minimal.args.iterateAllocator(init.gpa);
defer iter.deinit();
_ = iter.next().?;
while (iter.next()) |i| {
if (binOptMap.get(i)) |o|
o.* = true
else {
if (std.mem.cutScalar(u8, i, '=')) |v| {
const opt, const value = v;
if (floatOptMap.get(opt)) |o|
o.* = try std.fmt.parseFloat(f32, value);
}
}
}
const file = std.Io.Dir.cwd().openFile(init.io, args.zonPath, .{ .allow_directory = false }) catch |err| switch (err) {
error.IsDir => blk: {
const dir = try std.Io.Dir.cwd().openDir(init.io, args.zonPath, .{});
defer dir.close(init.io);
break :blk try dir.openFile(init.io, "rom_list.zon", .{});
},
else => |e| return e,
};
defer file.close(init.io);
var buf: [1024]u8 = undefined;
var reader = file.reader(init.io, &buf);
const content = try reader.interface.allocRemainingAlignedSentinel(init.gpa, .unlimited, .of(u8), 0);
var diag: std.zon.parse.Diagnostics = .{};
const zon = std.zon.parse.fromSliceAlloc(struct {
emus: []struct {
name: []const u8,
exec_format: []const u8,
},
roms: []struct {
name: [:0]const u8,
emu: []const u8,
path: []const u8,
},
}, init.gpa, content, &diag, .{}) catch |err| {
std.log.err("{f}", .{diag});
return err;
};
defer std.zon.parse.free(init.gpa, zon);
_ = c.glfwSetErrorCallback(glfw_error_callback);
try wrap(c.glfwInit, .{});
defer c.glfwTerminate();
const glsl_version = "#version 300 es";
c.glfwWindowHint(c.GLFW_VERSION_MAJOR, 3);
c.glfwWindowHint(c.GLFW_VERSION_MINOR, 3);
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_OPENGL_ES_API);
const main_scale = c.cImGui_ImplGlfw_GetContentScaleForMonitor(c.glfwGetPrimaryMonitor());
const window = c.glfwCreateWindow(@trunc(args.width * main_scale), @trunc(args.height * main_scale), "choose rom to execute", c.glfwGetPrimaryMonitor(), null) orelse return error.GLFWWindowInitErr;
defer c.glfwDestroyWindow(window);
c.glfwMakeContextCurrent(window);
c.glfwSwapInterval(@intFromBool(args.vsync)); // enable vsync
if (!c.CIMGUI_CHECKVERSION())
return error.imguiVersionError;
const ctx = c.ImGui_CreateContext(null);
defer c.ImGui_DestroyContext(ctx);
const imguiIO = c.ImGui_GetIO();
setFlags(&.{ c.ImGuiConfigFlags_NavEnableKeyboard, c.ImGuiConfigFlags_NavEnableGamepad }, imguiIO);
const style = c.ImGui_GetStyle();
if (args.classic)
c.ImGui_StyleColorsClassic(style)
else if (args.light)
c.ImGui_StyleColorsLight(style)
else
c.ImGui_StyleColorsDark(style);
c.ImGuiStyle_ScaleAllSizes(style, main_scale);
style.*.FontScaleDpi = main_scale;
if (!c.cImGui_ImplGlfw_InitForOpenGL(window, true)) return error.GLFWInit;
defer c.cImGui_ImplGlfw_Shutdown();
if (!c.cImGui_ImplOpenGL3_InitEx(glsl_version)) return error.OpenGlInit;
defer c.cImGui_ImplOpenGL3_Shutdown();
const clear_color = c.ImVec4{ .x = 0.45, .y = 0.55, .w = 0.60, .z = 1 };
while (c.glfwWindowShouldClose(window) == 0) {
c.glfwPollEvents();
if (c.glfwGetWindowAttrib(window, c.GLFW_ICONIFIED) != 0) {
c.cImGui_ImplGlfw_Sleep(10);
}
c.cImGui_ImplOpenGL3_NewFrame();
c.cImGui_ImplGlfw_NewFrame();
c.ImGui_NewFrame();
{
if (!c.ImGui_Begin("console", null, 0)) return error.BeginError;
defer c.ImGui_End();
var err: anyerror = error.NoError;
errdefer |encounteredErr| {
err = encounteredErr;
c.ImGui_OpenPopup("errPopup", 0);
}
{
if (c.ImGui_BeginPopup("errPopUp", 0)) {
defer c.ImGui_EndPopup();
c.ImGui_Text("encontered error '%s'", @errorName(err).ptr);
}
}
for (zon.roms) |rom| {
if (c.ImGui_Button(rom.name.ptr)) {
for (zon.emus) |emu|
if (std.mem.eql(u8, emu.name, rom.emu)) {
c.glfwSetWindowShouldClose(window, 1);
argv = try formatArgv(init.arena.allocator(), emu.exec_format, rom.name);
};
return error.InvalidEmu;
}
}
}
c.ImGui_Render();
var w: c_int = undefined;
var h: c_int = undefined;
c.glfwGetFramebufferSize(window, &w, &h);
c.glViewport(0, 0, w, h);
c.glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
c.glClear(c.GL_COLOR_BUFFER_BIT);
c.cImGui_ImplOpenGL3_RenderDrawData(c.ImGui_GetDrawData());
c.glfwSwapBuffers(window);
}
}
if (argv) |a| {
return std.process.replace(init.io, .{ .argv = a });
}
}