Terminal Doom - Zig making things easier

You can now play DOOM in modern terminals like Ghostty, Kitty and WezTerm:

While the doom engine is still C (that might eventually change!), there’s plenty of Zig in the project.

In particular, it’s a showcase for the awesome libvaxis TUI library, the Zig build system, and the performance of Ghostty (a terminal written in Zig).

Thanks to @rockorager for making additions to how images are transmitted over the Kitty graphics protocol (and a bunch of other helpful input). Mitchell made Ghostty even faster by SIMDifying the base64 decoding of images.

The original build system had some pain points, but after moving that to zig build people are reporting that it just builds. Even on Windows… (though no Windows terminal can play the game yet.) It also helped to remove the dependency on SDL by integrating miniaudio.

17 Likes

I agree with your sentiment on libvaxis! It’s one of my favorite experiences working with a TUI library, ever.

I went ahead and gave it a shot, but couldn’t get it to work (I do use Kitty and Zig 0.13.0). Here is the log output:

debug(vaxis): enabling mouse mode: cell coordinates
debug(vaxis): resizing screen: width=234 height=46
thread 1052684 panic: attempt to unwrap error: NoGraphicsCapability
/home/james/.cache/zig/p/1220523a3a301cbfbd83a374e2a69073fa14b211f2f6aaa4fc9497c936feaa67f738/src/Vaxis.zig:781:36: 0x118a30e in transmitImage (terminal-doom)
    if (!self.caps.kitty_graphics) return error.NoGraphicsCapability;
                                   ^
/home/james/Projects/zig/terminal-doom/src/main.zig:76:120: 0x11a1edb in DG_DrawFrame (terminal-doom)
    const img = state.loop.vaxis.transmitImage(std.heap.c_allocator, state.loop.tty.anyWriter(), &pixels, .rgba) catch unreachable;
                                                                                                                       ^
src/doomgeneric/i_video.c:314:2: 0x117c2c8 in I_FinishUpdate (/home/james/Projects/zig/terminal-doom/src/doomgeneric/i_video.c)
 DG_DrawFrame();
 ^
src/doomgeneric/d_main.c:304:2: 0x1139426 in D_Display (/home/james/Projects/zig/terminal-doom/src/doomgeneric/d_main.c)
 I_FinishUpdate ();              // page flip or blit buffer
 ^
src/doomgeneric/d_main.c:417:9: 0x1139795 in doomgeneric_Tick (/home/james/Projects/zig/terminal-doom/src/doomgeneric/d_main.c)
        D_Display ();
        ^
/home/james/Projects/zig/terminal-doom/src/main.zig:258:25: 0x1184eb8 in main (terminal-doom)
        doomgeneric_Tick();
                        ^
/nix/store/ys5qy0gp97dnp7vny393f36q62f7w39c-zig-0.13.0/lib/zig/std/start.zig:524:37: 0x118587e in main (terminal-doom)
            const result = root.main() catch |err| {
                                    ^
???:?:?: 0x7f1e85d6c10d in ??? (libc.so.6)
Unwind information for `libc.so.6:0x7f1e85d6c10d` was not available, trace may be incomplete

What’s your Kitty version / OS? We have it running on Kitty on both macOS and Linux, but somehow the graphics capability isn’t detected on your end.

1 Like

Here you are :smiley:

❯ kitty --version
kitty 0.35.2 created by Kovid Goyal

❯ uname -a
Linux laptop 6.6.37 #1-NixOS SMP PREEMPT_DYNAMIC Fri Jul  5 07:34:07 UTC 2024 x86_64 GNU/Linux

Yeah I run 0.35.2 as well (on the mac at least) so the OS / desktop environment / display backend is the next thing to look at.

1 Like

I have that same version of Kitty on Fedora and it is working for me…

Do you get any output from this command: printf "\x1b_Gi=1,a=q\x1b\\"?

@cryptocode here are the obvious env vars I could think of:

DISPLAY                 :0
WAYLAND_DISPLAY         wayland-1
XDG_BACKEND             wayland
XDG_CURRENT_DESKTOP     Hyprland

@rockorager I do not get any visible output from that command.

Very odd - does the icat kitten work for you?? kitten icat <path to some image>?

Oddly enough that works flawlessly.

Do you happen to be in tmux?

Yes I do actually, I didn’t even think about that. I know I’ve had similar trouble with it in the past, I’ll try it soon outside of tmux and see how it goes

2 Likes

That was it! It’s a shame tmux doesn’t play nicely with so many things. Great work guys :+1:

1 Like

It’s still possible that tmux might work, perhaps you need to change the terminal definition from xterm-256color to something else.

I just tried export TERM=screen-256color from an existing tmux session, as well as a new one, with the same results. Not sure if I need to change my actual .tmux.conf or not but let me know if there’s anything else I can try!

I don’t think the Kitty graphics protocol will work in tmux without going through some serious hoops: FAQ · tmux/tmux Wiki · GitHub

For now it’s probably best to run Terminal Doom outside multiplexers (though Zellij may gain Kitty support: [Feature Request] - Implement kittys terminal graphics protocol · Issue #2814 · zellij-org/zellij · GitHub)

Thanks for looking into it! That seems like a ton of trouble :sweat_smile:

1 Like

Sorry for the terseness in my original comment – I was falling asleep. I checked and I have the following in my tmux.conf (including that weird comma), maybe this could work for you:

set -s default-terminal "xterm-kitty"
set-option -sa terminal-features ',xterm-kitty:RGB'
1 Like

All good, I totally get that! I added that to my tmux.conf but didn’t notice any changes from what I could tell.

I need to make some changes to libvaxis to support kitty images in tmux. There is a passthrough mode, but libvaxis needs to print the proper placeholder characters where the image should be - I haven’t implemented that yet.