<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/minimal-cross-platform-graphics-uvkx63o52" -->

---
title: Minimal cross-platform graphics | daily.dev
description: A deep-dive walks through building a minimal cross-platform 2D graphics library in plain C, covering window creation on Linux (X11), macOS (Cocoa via the...
canonical: https://daily.dev/posts/minimal-cross-platform-graphics-uvkx63o52
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Minimal cross-platform graphics | daily.dev
og:description: A deep-dive walks through building a minimal cross-platform 2D graphics library in plain C, covering window creation on Linux (X11), macOS (Cocoa via the...
og:url: https://daily.dev/posts/minimal-cross-platform-graphics-uvkx63o52
og:image: https://api.daily.dev/og/posts/UvKX63o52.png
og:image:alt: Minimal cross-platform graphics
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Minimal cross-platform graphics

**[zserge's blog](https://daily.dev/sources/zserge)** · 24 min read · 0 upvotes · 0 comments

## Summary

A deep-dive walks through building a minimal cross-platform 2D graphics library in plain C, covering window creation on Linux (X11), macOS (Cocoa via the Objective-C runtime called from C), and Windows (WinAPI). It continues into framebuffer rendering, mouse/keyboard input handling per platform, FPS timing, drawing primitives (pixels, rectangles, circles, Bresenham lines, flood fill, bitmap text), basic audio playback via ALSA/CoreAudio/WinMM, and Go/Zig bindings. The result, a single-header library called Fenster, is even shown running Doom via doomgeneric in under 50 lines of glue code.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://zserge.com/posts/fenster>

## Questions this post answers

### How do you create a basic window using X11 in C on Linux?

Open a display connection with XOpenDisplay, get the default screen, then call XCreateSimpleWindow with position, size, and colors. Use XStoreName for the title, XSelectInput to register for events like ExposureMask and KeyPressMask, then XMapWindow to show it. A minimal working 320x240 window with a title can be done in about 15 lines of C using -lX11.

_daily.dev surfaces low-level graphics walkthroughs like this for developers exploring cross-platform windowing in C._

### How can you call Objective-C Cocoa APIs from plain C without using Objective-C syntax?

Use the Objective-C runtime's objc_msgSend function directly, casting it to the correct function signature for each call, combined with objc_getClass and sel_getUid to look up classes and selectors. Wrapping this in preprocessor macros like msg, msg1, msg2 (one per argument count) makes the syntax readable enough to create an NSWindow and run a Cocoa event loop entirely in standard C compiled with the Cocoa framework.

_developers bridging C and Objective-C runtimes can find similar low-level tricks curated on daily.dev._

### Why is the height value negative in a Windows BITMAPINFO structure when drawing a framebuffer with SetDIBitsToDevice?

A negative height in BITMAPINFOHEADER tells Windows the bitmap is a top-down DIB, meaning pixel rows are stored from top to bottom rather than bottom to top. Standard BMP files store rows bottom-up, but for framebuffer rendering with SetDIBitsToDevice and BitBlt, using a negative height keeps pixel data in the same top-left-origin order used on Linux and macOS.

_daily.dev collects practical Win32 GDI gotchas like this for developers writing cross-platform rendering code._

## Similar posts on daily.dev

- [Master Low-Level Graphics in C: Build a Software Renderer from Scratch](https://daily.dev/posts/master-low-level-graphics-in-c-build-a-software-renderer-from-scratch-ssvj3mayp) · freeCodeCamp · 0 upvotes · 0 comments
- [Building a Tiny 3D Renderer for a Tiny Handheld](https://daily.dev/posts/building-a-tiny-3d-renderer-for-a-tiny-handheld-ncrfzmr8j) · Hacker News · 0 upvotes · 0 comments

---

Tags: [#c](https://daily.dev/tags/c), [#graphics-programming](https://daily.dev/tags/graphics-programming)

[View this post on daily.dev](https://daily.dev/posts/minimal-cross-platform-graphics-uvkx63o52)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Minimal cross-platform graphics","url":"https://daily.dev/posts/minimal-cross-platform-graphics-uvkx63o52","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/minimal-cross-platform-graphics-uvkx63o52"},"datePublished":"2026-08-23T10:20:24.105Z","dateModified":"2026-08-23T10:21:40.346Z","description":"A deep-dive walks through building a minimal cross-platform 2D graphics library in plain C, covering window creation on Linux (X11), macOS (Cocoa via the...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/639657038bdb04ac3ccbedce312f18c2?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/639657038bdb04ac3ccbedce312f18c2?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"zserge's blog","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"zserge's blog","logo":"https://media.daily.dev/image/upload/s--KQnFBOIT--/f_auto,q_auto/v1787480407/logos/zserge","url":"https://daily.dev/sources/zserge"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/minimal-cross-platform-graphics-uvkx63o52","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"c,graphics-programming","timeRequired":"PT24M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"zserge's blog","item":"https://daily.dev/sources/zserge"},{"@type":"ListItem","position":3,"name":"Minimal cross-platform graphics"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/minimal-cross-platform-graphics-uvkx63o52#faq","mainEntity":[{"@type":"Question","name":"How do you create a basic window using X11 in C on Linux?","acceptedAnswer":{"@type":"Answer","text":"Open a display connection with XOpenDisplay, get the default screen, then call XCreateSimpleWindow with position, size, and colors. Use XStoreName for the title, XSelectInput to register for events like ExposureMask and KeyPressMask, then XMapWindow to show it. A minimal working 320x240 window with a title can be done in about 15 lines of C using -lX11. daily.dev surfaces low-level graphics walkthroughs like this for developers exploring cross-platform windowing in C."}},{"@type":"Question","name":"How can you call Objective-C Cocoa APIs from plain C without using Objective-C syntax?","acceptedAnswer":{"@type":"Answer","text":"Use the Objective-C runtime's objc_msgSend function directly, casting it to the correct function signature for each call, combined with objc_getClass and sel_getUid to look up classes and selectors. Wrapping this in preprocessor macros like msg, msg1, msg2 (one per argument count) makes the syntax readable enough to create an NSWindow and run a Cocoa event loop entirely in standard C compiled with the Cocoa framework. developers bridging C and Objective-C runtimes can find similar low-level tricks curated on daily.dev."}},{"@type":"Question","name":"Why is the height value negative in a Windows BITMAPINFO structure when drawing a framebuffer with SetDIBitsToDevice?","acceptedAnswer":{"@type":"Answer","text":"A negative height in BITMAPINFOHEADER tells Windows the bitmap is a top-down DIB, meaning pixel rows are stored from top to bottom rather than bottom to top. Standard BMP files store rows bottom-up, but for framebuffer rendering with SetDIBitsToDevice and BitBlt, using a negative height keeps pixel data in the same top-left-origin order used on Linux and macOS. daily.dev collects practical Win32 GDI gotchas like this for developers writing cross-platform rendering code."}}]}
```

