
A lightweight, cross-platform C library providing a unified API for window management, input/event handling, and system utilities. Designed for applications that need direct control over windowing and input without heavy framework dependencies.
- Cross-platform: Native support for Windows (Win32/WGL via MinGW), Linux (Wayland or X11), macOS (AppKit/Cocoa), QNX, and WebGL (browser)
- Window Management: Create and manage windows with OpenGL/EGL context support
- Event System: Unified event handling for mouse, keyboard, and window events
- Input Processing: Comprehensive keyboard and mouse input with modifier support
- Networking: Portable TCP/UDP sockets, DNS resolution, TLS (Schannel/Secure Transport/OpenSSL), and non-blocking I/O
- File Dialogs: Native file open/save dialogs
- System Integration: Theme detection, directory paths, timing utilities
- Minimal Dependencies: Simple C API with no bloat
Here's a minimal example showing how to create a window and process input events:
#include"platform.h"#include<stdio.h>intmain(void) {
// Initialize the platform libraryaxInit();
// Create a window (800x600 pixels)if (!axCreateWindow("My Application", 800, 600, 0)) {
fprintf(stderr, "Failed to create window\n");
return1;
}
// Main event loopstructAXmessagemsg;
intrunning=1;
while (running&&axGetMessage(&msg)) {
switch (msg.message) {
casekEventWindowClosed:
running=0;
break;
casekEventKeyDown:
printf("Key pressed: %d (modifiers: 0x%x)\n",
msg.keyCode, msg.modflags);
// Exit on ESC keyif (msg.keyCode==AX_KEY_ESCAPE) {
running=0;
}
break;
casekEventLeftButtonDown:
printf("Mouse clicked at: %d, %d\n", msg.x, msg.y);
break;
casekEventMouseMoved:
// Mouse position in msg.x, msg.ybreak;
casekEventWindowResized:
printf("Window resized\n");
break;
}
// Render your frame hereaxBeginPaint();
// ... OpenGL rendering calls ...axEndPaint();
}
// CleanupaxShutdown();
return0;
}For non-blocking event loops with axPeekMessage, see
docs/event-loop.md.
On Windows (MinGW/MSYS2):
gcc -o myapp main.c -L. -lplatform -lopengl32 -lgdi32 -luser32On Linux (X11):
gcc -o myapp main.c -L. -lplatform -lX11 -lEGL -lGLOn Linux (Wayland):
gcc -o myapp main.c -L. -lplatform -lwayland-client -lwayland-egl -lEGL -lGLOn macOS:
clang -o myapp main.c -L. -lplatform -framework AppKit -framework OpenGLOn WebGL (browser via Emscripten):
emcc -o myapp.js main.c -L. -lplatform -sUSE_WEBGL2=1 -sEXPORTED_RUNTIME_METHODS=ccall,cwrapMake sure libplatform.so (Linux) or libplatform.dylib (macOS) is in your library path or current directory.
voidaxInit(void); // Initialize the platform libraryvoidaxShutdown(void); // Clean up and shutdownbool_taxCreateWindow(constchar*title, uint32_twidth, uint32_theight, uint32_tflags);
bool_taxCreateSurface(uint32_twidth, uint32_theight);
bool_taxSetSize(uint32_twidth, uint32_theight, bool_tcentered);
uint32_taxGetSize(structAXsize*size);
floataxGetScaling(void); // Get display scaling factorintaxPeekMessage(structAXmessage*msg); // Poll for next event (non-blocking)intaxGetMessage(structAXmessage*msg); // Get next event (blocking where supported)intaxWaitMessage(longTime_ttimeout_ms); // Wait for events with timeoutvoidaxPostMessageW(void*hobj, uint32_tevent, uint32_twparam, void*lparam);The library supports comprehensive event handling through the AX_Message structure:
- Mouse Events:
kEventLeftButtonDown,kEventLeftButtonUp,kEventMouseMoved,kEventScrollWheel - Keyboard Events:
kEventKeyDown,kEventKeyUp,kEventChar - Window Events:
kEventWindowClosed,kEventWindowResized,kEventWindowPaint - Drag & Drop:
kEventDragDrop,kEventDragEnter
voidaxMakeCurrentContext(void); // Make OpenGL context currentvoidaxBeginPaint(void); // Begin rendering framevoidaxEndPaint(void); // End rendering and swap buffersvoidaxBindFramebuffer(void); // Bind default framebufferbool_taxGetOpenFileName(AXopenfilenameconst*params);
bool_taxGetSaveFileName(AXopenfilenameconst*params);
bool_taxGetFolderName(AXopenfilenameconst*params);bool_taxNetInit(void); // Initialise networking subsystemvoidaxNetShutdown(void); // Release networking resourcesintaxNetSocket(intaf, inttype); // Create a TCP or UDP socketvoidaxNetClose(intsock); // Close a socketbool_taxNetConnect(intsock, constchar*host, uint16_tport); // Resolve host and connectintaxNetSend(intsock, constvoid*buf, intlen); // Send dataintaxNetRecv(intsock, void*buf, intlen); // Receive databool_taxNetWouldBlock(void); // Test for EAGAIN/EWOULDBLOCKbool_taxNetResolve(constchar*host, char*out, intoutlen); // DNS lookup to IP stringintaxNetPoll(intsock, intevents, inttimeout_ms); // Wait for I/O readinessbool_taxNetBind(intsock, constchar*host, uint16_tport); // Bind to a local addressbool_taxNetListen(intsock, intbacklog); // Mark socket as passiveintaxNetAccept(intsock); // Accept an incoming connection// TLS (Schannel on Windows, Secure Transport on macOS, OpenSSL on Linux)AXtlsctx*axTlsConnect(intsock, constchar*hostname); // TLS handshakevoidaxTlsClose(AXtlsctx*ctx); // Send close_notify and freeintaxTlsSend(AXtlsctx*ctx, constvoid*buf, intlen); // Send through TLSintaxTlsRecv(AXtlsctx*ctx, void*buf, intlen); // Receive through TLSSee docs/networking.md for full examples.
Standalone applications using GEM_STANDALONE_MAIN can be started with
-rc to expose a localhost TCP control socket on port 17777:
build/bin/helloworld -rc
printf'click 120 80\n'| nc 127.0.0.1 17777
printf'screenshot /tmp/helloworld.jpg\n'| nc 127.0.0.1 17777Use -rc PORT to select another port. Commands are newline-delimited and
receive ok or err replies. The supported commands are click x y,
rclick x y, move x y, scroll x y dx dy, key code, keydown code,
keyup code, screenshot path, stop, and quit. Coordinates and key codes
use the same logical coordinate and platform key-code conventions as native
events. The server binds to IPv4 localhost traffic only through the platform
socket API and runs its accept loop on a background platform thread.
longTime_taxGetMilliseconds(void); // Get current time in millisecondsvoidaxSleep(longTime_tmsec); // Sleep for specified millisecondsbool_taxIsDarkTheme(void); // Check if dark theme is activeconstchar*axGetPlatform(void); // Get platform nameconstchar*axSettingsDirectory(void); // Get settings directory pathconstchar*axShareDirectory(void); // Get share directory pathconstchar*axLibDirectory(void); // Get library directory pathbool_taxSetLogFile(constchar*path); // Open or close the process log fileconstchar*axGetLogFile(void); // Query current log file pathvoidaxLog(constchar*fmt, ...); // Append one formatted log linevoidaxLogFlush(void); // Flush buffered log outputExample:
charlog_path[1024];
snprintf(log_path, sizeof(log_path), "%s/myapp.log", axSettingsDirectory());
if (axSetLogFile(log_path)) {
axLog("app started on %s", axGetPlatform());
}On WebGL builds, file logging is unavailable and axSetLogFile("...") returns FALSE.
The library defines comprehensive key codes including:
- Standard ASCII characters
- Function keys (
AX_KEY_F1-AX_KEY_F12) - Arrow keys (
AX_KEY_UPARROW,AX_KEY_DOWNARROW, etc.) - Special keys (
AX_KEY_ENTER,AX_KEY_ESCAPE,AX_KEY_SPACE, etc.) - Mouse buttons (
AX_KEY_MOUSE1,AX_KEY_MOUSE2,AX_KEY_MOUSE3) - Modifier flags (
AX_MOD_SHIFT,AX_MOD_CTRL,AX_MOD_ALT,AX_MOD_CMD)
See platform.h for the complete list of key codes and event definitions.
The recommended way to use platform in your own project is to add it as a git submodule and build it as part of your Makefile — in a single compiler pass, with no intermediate .o files. This keeps rebuilds fast and the integration simple.
git submodule add https://github.com/corepunch/platform libs/platform
git submodule update --init --recursivePLATFORM_DIR = libs/platform
PLATFORM_OUTDIR = build/lib
# Build platform into your output lib directoryplatform:
$(MAKE) -C $(PLATFORM_DIR) OUTDIR=$(abspath$(PLATFORM_OUTDIR))# Link your app against itmyapp: platform
$(CC)$(CFLAGS) -I$(PLATFORM_DIR) main.c \
-L$(PLATFORM_OUTDIR) -lplatform \
-Wl,-rpath,$(abspath$(PLATFORM_OUTDIR))\
-o myappInternally, platform's Makefile compiles all sources in a single pass — no .o files are generated. All source files are collected with find, converted to #include directives, and piped directly to the compiler:
$(FIND_SOURCES) | sed 's|.*|#include "&"|' | $(CC) $(CFLAGS) -x $(LANG) - $(LDFLAGS) -o $@This means the entire library recompiles in one shot, which is extremely fast and requires no dependency tracking.
clean:
$(MAKE) -C $(PLATFORM_DIR) clean
rm -f myappFor a real-world example using this pattern, see corepunch/orca.
Simple Makefile that auto-detects your platform and builds the appropriate dynamic library using wildcards for source files.
Windows (MinGW/MSYS2):
- MinGW-w64 toolchain (GCC for Windows):
# Install via MSYS2 (https://www.msys2.org/) pacman -S mingw-w64-x86_64-gcc make - No extra libraries required — uses Win32, WGL, and Winsock2 (all included with Windows)
WebGL (Emscripten):
- Emscripten SDK (
emcccompiler):# Install via emsdk (https://emscripten.org/docs/getting_started/downloads.html) git clone https://github.com/emscripten-core/emsdk.git cd emsdk && ./emsdk install latest && ./emsdk activate latest source ./emsdk_env.sh
Linux:
- GCC compiler
- Wayland development libraries (preferred, for Wayland support):
sudo apt-get install libwayland-dev libwayland-egl1-mesa libxkbcommon-dev libegl-dev libgl-dev
- Or X11 development libraries (fallback, if Wayland is unavailable):
sudo apt-get install libx11-dev libegl-dev libgl-dev
- Build order: Wayland (preferred) → X11 (fallback) → unix-only (networking only)
macOS:
- Clang compiler (included with Xcode Command Line Tools)
- AppKit framework (included with macOS)
make # Build the dynamic library
emmake make # Build for WebGL (requires Emscripten environment sourced)
make clean # Clean build artifacts
make install # Install to /usr/local/lib (requires sudo; Linux/macOS only)- Windows:
libplatform.dll(+libplatform.dll.aimport library) - Linux:
libplatform.so - macOS:
libplatform.dylib - WebGL:
libplatform.wasm(Emscripten side module)
The library exports platform-specific functions for window management, event handling, networking, and system utilities.
- Windows: Full support via Win32 API and WGL — window creation, OpenGL rendering, native file dialogs, dark-theme detection, Winsock2 networking; built with MinGW
- Linux (Wayland): Full support with Wayland, EGL, and OpenGL (preferred when available)
- Linux (X11): Full support with Xlib, EGL, and OpenGL (fallback when Wayland is unavailable)
- macOS: Full support with AppKit and Cocoa frameworks
- QNX: Source files available (qnx/)
- WebGL (browser): Full support via Emscripten — mouse, keyboard, scroll, and resize events; WebGL 1/2 rendering context
The Linux backend is selected automatically at build time:
- If Wayland libraries are detected via
pkg-config, the Wayland backend is used - If Wayland is unavailable but X11 libraries are detected, the X11 backend is used
- If neither is available, a unix-only build is produced (networking only, no windowing)
Include platform.h in your project to access platform types and APIs.