Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
M

minifb

> 编程语言
Open source

MiniFB is a small cross platform library to create a frame buffer that you can draw pixels in

1.3K stars0 likes0 views
WebsiteGitHub

About

MiniFB is a small cross platform library to create a frame buffer that you can draw pixels in

MiniFB

MiniFB (Mini FrameBuffer) is a small cross-platform C library. You give it a buffer of 32-bit pixels, it copies that buffer to a window, and it reports keyboard and mouse events back to you. Your code draws everything: MiniFB never draws for you. It only needs the system libraries of each platform.

Quick Start

The shortest useful program looks like this:

c
int main() {
    struct mfb_window *window = mfb_open_ex("my display", 800, 600, MFB_WF_RESIZABLE);
    if (window == NULL)
        return 0;

    uint32_t *buffer = malloc(800 * 600 * 4);

    mfb_update_state state;
    do {
        // TODO: add some fancy rendering to the buffer of size 800 * 600

        state = mfb_update_ex(window, buffer, 800, 600);

        if (state != MFB_STATE_OK)
            break;

    } while(mfb_wait_sync(window));

    free(buffer);
    buffer = NULL;
    window = NULL;

    return 0;
}

How it works

  1. Create a window with mfb_open() or mfb_open_ex().
  2. Allocate the buffer yourself: one 32-bit value per pixel, width * height pixels. MiniFB never allocates or frees it.
  3. Draw into the buffer, then call mfb_update() or mfb_update_ex() to copy it to the window.
  4. Call mfb_wait_sync() to wait until it is time to draw the next frame.

MFB_STATE_OK means the frame was accepted. MFB_STATE_EXIT means the window was closed and destroyed internally: leave the loop and do not use the handle again. The other values report an error for that frame (invalid window, invalid buffer, backend failure) and leave the window alive.

Note: By default, releasing the ESC key closes the window, so the next update returns MFB_STATE_EXIT. Install your own keyboard callback if you do not want that.

See examples/noise.c for a complete example.

Supported Platforms

Platform Backends Status
Windows GDI, OpenGL Full API
macOS Cocoa, Metal Full API
Linux/Unix X11, Wayland Full API
iOS Metal Reduced API: single window, touch input
Android Native Reduced API: single window, touch input
Web WASM Partial: some calls are accepted but not applied
DOS DJGPP Reduced API: single window, partial keyboard

Build instructions and platform notes: Windows, X11, Wayland, macOS, iOS, Android, Web, MS-DOS. Feature support by platform lists what each backend implements.

MiniFB has been tested on Windows, macOS, Linux, iOS, Android, browsers and DOSBox-x. Results depend on your setup. The library does not convert pixel data: if the system cannot provide a proper 32-bit display, MiniFB does not work around it.

Features

  • Window creation and management
  • Event callbacks (keyboard, mouse, window lifecycle)
  • Window and input state queries, as an alternative to callbacks
  • Per-window custom data
  • Built-in timers and FPS control
  • C and C++ interfaces
  • Cursor control

API Reference

Window Management

…

mfb_set_viewport() returns false if:

  • width == 0 or height == 0
  • viewport bounds exceed the current window drawable size

mfb_open() and mfb_open_ex() return NULL if:

  • width == 0 or height == 0
  • width * 4 would overflow the internal framebuffer stride

If title is NULL or empty, MiniFB uses "minifb" as the effective window/canvas title.

If both MFB_WF_FULLSCREEN and MFB_WF_FULLSCREEN_DESKTOP are provided, MFB_WF_FULLSCREEN takes precedence.

mfb_update_ex() returns MFB_STATE_INVALID_BUFFER if:

  • buffer == NULL
  • width == 0 or height == 0
  • width * 4 would overflow internal stride calculations

mfb_update_ex() behaves differently on two backends:

  • Wayland waits for the compositor frame callback inside mfb_update_ex() (can block). A minimized or hidden window may stop receiving that callback. Each call then waits briefly and returns MFB_STATE_OK without presenting.
  • Android may return MFB_STATE_OK without presenting when ANativeWindow is temporarily unavailable during lifecycle transitions.

What is ready when mfb_open_ex() returns also depends on the backend:

  • Wayland waits for the initial configure handshake before returning from mfb_open_ex().
  • Android may return a window handle before ANativeWindow is ready (rendering starts once the native window becomes available).

mfb_open_ex() flag support by backend:

Backend RESIZABLE BORDERLESS ALWAYS_ON_TOP FULLSCREEN FULLSCREEN_DESKTOP
Windows Yes Yes Yes Yes Yes
X11 Yes Yes* Yes* Yes* Yes*
Wayland Yes Yes No (ignored, warning) Yes Yes (maximized)
macOS Yes Yes Yes Yes Yes (zoom/maximize)
Web No (ignored, warning) No (ignored, warning) No (ignored, warning) Yes** Yes**
DOS No (ignored, warning) No (ignored, warning) No (ignored, warning) No (ignored, warning) No (ignored, warning)
Android No (ignored, warning) No (ignored, warning) No (ignored, warning) No (ignored, warning) No (ignored, warning)
iOS No (ignored, warning) No (ignored, warning) No (ignored, warning) No (ignored, warning) No (ignored, warning)

* Best effort via window-manager hints/properties; behavior depends on compositor/WM support.

** Browser-managed fullscreen; typically requires a user gesture before entering fullscreen.

Event Callbacks

Register callbacks to handle window events:

…

Callback Signature Examples

…

C++ Callback Interface

In C++ you can also point a callback at a class method or a lambda:

cpp
// Using object and pointer to member
mfb_set_active_callback(window, &myObject, &MyClass::onActive);

// Using std::bind
mfb_set_active_callback(std::bind(&MyClass::onActive, &myObject, _1, _2), window);

// Using lambda
mfb_set_active_callback([](struct mfb_window *window, bool is_active) {
    // Handle event
}, window);

Window State Queries

Read window and input state directly, instead of using callbacks:

…

On Android and iOS, touch positions carry the pointer id in their upper bits:

  • mfb_get_mouse_x(), mfb_get_mouse_y() and the x/y given to mfb_mouse_move_func are packed values. Decode them with mfb_decode_touch(), or one at a time with mfb_decode_touch_pos() / mfb_decode_touch_id(). On desktop, Web and DOS the id is always 0.
  • The pointer id also arrives as the button argument of mfb_mouse_button_func (MFB_MOUSE_BTN_0..MFB_MOUSE_BTN_7).
  • On Android, HOVER_MOVE events from external devices use the same packing. When Android reports no valid pointer id, MiniFB uses 15.

mfb_get_mouse_scroll_x() and mfb_get_mouse_scroll_y() only hold the delta of the last event pump: MiniFB sets them to 0.0f before pumping events, then writes the delta if a scroll event arrives during that pump.

Per-Window Data

Attach and retrieve custom data per window:

c
void                mfb_set_user_data(struct mfb_window *window, void *user_data);
void *              mfb_get_user_data(struct mfb_window *window);

Timers

Create and manage timers independently:

c
struct mfb_timer *  mfb_timer_create(void);
void                mfb_timer_destroy(struct mfb_timer *tmr);
void                mfb_timer_reset(struct mfb_timer *tmr);
void                mfb_timer_compensated_reset(struct mfb_timer *tmr);
double              mfb_timer_now(struct mfb_timer *tmr);
double              mfb_timer_delta(struct mfb_timer *tmr);
double              mfb_timer_get_frequency(void);
double              mfb_timer_get_resolution(void);

Frame Rate Control

Control target FPS and frame synchronization:

c
void                mfb_set_target_fps(uint32_t fps);         // Default: 60 fps
unsigned            mfb_get_target_fps(void);

bool                mfb_wait_sync(struct mfb_window *window); // Frame sync point

Note: Where MiniFB renders through OpenGL or Metal, mfb_wait_sync() relies on vertical sync. The other backends pace frames in software.

Logging

MiniFB ships with a simple logger that you can redirect or disable:

c
// Set a custom logger; pass NULL to restore the built-in logger
void mfb_set_logger(mfb_log_func user_logger);

// Control verbosity threshold (inclusive)
void mfb_set_log_level(mfb_log_level level);
  • Levels (low → high): MFB_LOG_TRACE, MFB_LOG_DEBUG, MFB_LOG_INFO, MFB_LOG_WARNING, MFB_LOG_ERROR.
  • Defaults: in _DEBUG builds the threshold is MFB_LOG_DEBUG; in release builds MFB_LOG_INFO.
  • Messages with a level below the threshold are discarded; equal or higher are emitted.
  • Custom loggers receive the message already formatted (level + message).
  • The built-in logger writes TRACE/DEBUG/INFO to stdout and WARNING/ERROR to stderr as [MiniFB (LEVEL)] message.

Setting the level from the environment

MINIFB_LOG_LEVEL sets the threshold without touching the code:

bash
MINIFB_LOG_LEVEL=trace ./my_program

Use a level name, not a number: trace, debug, info, warning or error. Case does not matter. Names keep working if the mfb_log_level enum is ever reordered, which numbers would not.

The variable wins over mfb_set_log_level(). This is on purpose. The point of the variable is to get more output from a program you cannot rebuild, and a hardcoded call in that program would otherwise block you. If you need the program to stay in control, do not set the variable.

An unknown value is reported as an error and then ignored, so the threshold stays where the program left it. Nothing changes without a message.

Since the built-in logger writes low levels to stdout and high levels to stderr, the two streams can interleave out of order when you pipe them into one file. Redirect them separately, or send both to the same place with 2>&1 and accept the ordering.

Cursor Control

c
void                mfb_show_cursor(struct mfb_window *window, bool show);

Note: Cursor hiding is supported on Windows, macOS, X11, and Wayland only.

Monitor Information

c
void                mfb_get_monitor_scale(struct mfb_window *window, float *scale_x, float *scale_y);
void                mfb_get_monitor_dpi(struct mfb_window *window, float *dpi_x, float *dpi_y); // [Deprecated]

mfb_get_monitor_scale():

  • Returns scale multipliers (1.0 = 100%).
  • If window == NULL, outputs still receive a safe fallback (1.0) when their pointers are non-NULL.
  • Some backends provide real scale values (for example Retina/HiDPI); others currently return fixed 1.0.
  • On X11 the scale comes from Xresources, XRandR or a fallback. In most desktops it is read at startup, so changing the global scale while the program runs may have no effect until you restart it.

If your layout is defined in logical units, convert it to drawable coordinates before calling mfb_set_viewport():

c
float sx = 1.0f, sy = 1.0f;
mfb_get_monitor_scale(window, &sx, &sy);
unsigned margin_x_viewport = (unsigned) lroundf(margin_logical_x * sx);
unsigned margin_y_viewport = (unsigned) lroundf(margin_logical_y * sy);

Display Insets

Two functions report the display insets from C:

c
// Physical cutout/notch area only.
bool mfb_get_display_cutout_insets(struct mfb_window *window,
                                   int *left, int *top, int *right, int *bottom);

// Full safe area: cutout + system UI reserved regions.
bool mfb_get_display_safe_insets(struct mfb_window *window,
                                 int *left, int *top, int *right, int *bottom);

Insets are edge margins in pixels, not a rectangle:

  • left, top, right, bottom are distances from each window edge.
  • No reserved area means 0, 0, 0, 0.
  • I

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言