A Zig library for cross-platform window handling and CPU-based rendering, without any dependencies.
It is an experimental project with constant changes.
- Abstraction between X11, Windows and macOS (soon?)
- Window management, keyboard and mouse input events
- Text rendering with Unicode support (embedded Terminus and Unifont fonts)
- Image loading (PBM format) and scaling
- Generic event loop with comptime union generation and thread-per-source polling
- Fullscreen toggle and window icon support
- Pure Zig — only uses C when linking to Win32 required libraries
- Produces small and performant statically-linked binaries
- No dependencies, all in one
- It is not a GUI library or game engine
- It does not use GPU (no OpenGL nor Vulkan)
- It does not support mobile (no Android nor iOS)
Each module is independent and usable by itself.
- anywindow — Window handling abstraction and image display
- x11: native X11 protocol implementation (no Xlib)
- windows: Win32 API
- macos: (planned)
- image: positioned image with DPI scaling and nearest-neighbor upscaling
- text — Font loading, glyph rendering, Unicode support
- fonts: embedded Unifont and Terminus (multiple sizes)
- bdf: BDF font format parser (with gzip support)
- image — Image loading
- pbm: PBM format (P1 ASCII and P4 binary)
- loop — Generic event loop
- comptime union generation from multiple event sources
- thread-per-source polling with thread-safe queue
Notably missing:
- Wayland support, works using XWayland
- macOS support
zig build # build the library and demo
zig build run # run the demo
zig build test # run tests
zig build docs # generate documentationzig fetch --save git+https://github.com/diogok/make-it-renderThen in your build.zig, import the module:
const make_it_render = b.dependency("make_it_render", .{ .target = target, .optimize = optimize });
exe_mod.addImport("make_it_render", make_it_render.module("make_it_render"));const std = @import("std");
const make_it_render = @import("make_it_render");
const anywin = make_it_render.anywindow;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(gpa.deinit() != .leak);
const allocator = gpa.allocator();
var wm = try anywin.WindowManager.init(allocator);
defer wm.deinit();
var window = try wm.createWindow(.{ .title = "hello, world." });
defer window.deinit();
// create an image and set its pixels
var img = try anywin.Image.init(allocator, &window, .{ .width = 200, .height = 100, .x = 10, .y = 10 });
defer img.deinit();
// render text to RGBA pixels and display it
const fonts = try make_it_render.text.loadFonts(allocator, .{});
defer make_it_render.text.freeFonts(allocator, fonts);
var text = try make_it_render.text.render(allocator, fonts, "hello, world.");
defer text.deinit();
const rgba = try text.toRgba(allocator, &[_]u8{ 255, 150, 0, 255 });
defer allocator.free(rgba);
try img.setPixels(rgba);
try window.show();
// event loop with thread-per-source polling
var window_source: anywin.EventSource = .{ .wm = &wm };
var ev_loop = make_it_render.loop.eventLoop(.{ .window = &window_source });
defer ev_loop.deinit();
try ev_loop.start();
while (ev_loop.receive()) |wrapped| {
switch (wrapped) {
.window => |event| switch (event) {
.close => {
window.close();
ev_loop.stop();
},
.draw => {
try window.beginDraw();
try window.clear(.{});
try img.draw();
try window.endDraw();
},
else => {},
},
}
}
}See src/demo.zig for a more complete example with text rendering, image loading, mouse tracking, fullscreen toggle and icon.
MIT