Releases: emoon/minifb
Release list
v0.14.0: One input contract on every backend
This release adds a callback for the cursor entering and leaving the window. Adding it to every backend showed that the backends did not report mouse and keyboard input in the same way, so this release also fixes that.
Mouse and keyboard input now follow one contract on every backend. The same action produces the same callbacks, in the same order, with the same arguments and modifiers. Code tested on one platform now behaves the same on the others. The few differences that remain come from the platform, such as AltGr on native Windows.
Three interactive tests check the contract on any backend: tests/mouse_events.c, tests/keyboard_events.c and tests/window_events.c. Each one ends with a summary that you can compare between backends with diff.
Thanks to @cannedbeef for the color macro work in #142 and #143.
Heads up. Some values your application already receives change in this release:
MFB_RGBnow sets alpha to0xFF. Code that compares pixels with a raw value such as0x00FF0000has to include the alpha byte.mfb_wait_syncnow honours the target frame rate on Web and DOS. Callmfb_set_target_fps(0)to get the old behaviour back.- Android reports the real mouse button in the
buttonargument, not the touch pointer id. - Windows reports AltGr as
MFB_KB_KEY_LEFT_CONTROLfollowed byMFB_KB_KEY_RIGHT_ALT, every time. - On most X11 servers a key is now named by its position, so Dvorak or AZERTY layouts no longer move the letters.
- The interactive tests are built by default. Set
MINIFB_BUILD_TESTS=OFFif you add MiniFB withadd_subdirectory. - macOS now links the Carbon framework. A build that does not use the CMake project has to add
-framework Carbon.
Added
- Cursor enter and leave:
mfb_set_mouse_enter_callbackandmfb_is_mouse_inside. The callback fires only on a real crossing. While a button is held the window keeps the pointer, so dragging out reports no leave until the button is released. The C++ wrapper has the same callback. iOS and DOS never fire it. On Android it needs a mouse, trackpad or hover capable stylus. - Color channel macros:
MFB_GET_A,MFB_GET_R,MFB_GET_GandMFB_GET_Bread one channel from a pixel, with the same platform layout asMFB_ARGB(#142, by @cannedbeef). - Web
MFB_WF_RESIZABLE: the canvas follows its CSS layout box, scaled bydevicePixelRatio. - Web text input: dead keys, input method composition and characters outside the Basic Multilingual Plane now reach
mfb_set_char_input_callback. - X11 text input through xkbcommon: dead keys and Compose sequences come from the system Compose file, as on Wayland. The library is optional.
-DMINIFB_X11_USE_IME=ONputs an X11 input method in front of it. - DOS mouse wheel: the wheel is read from the mouse driver through the CuteMouse API, so the arrow keys keep working.
- Interactive tests for mouse, keyboard and window events, and the testing guides
docs/testing-x11.md,docs/testing-dos.mdanddocs/testing-wayland.md.
Changed
MFB_RGBis nowMFB_ARGB(0xFF, r, g, b)(#142, by @cannedbeef).- Web and DOS pace frames in
mfb_wait_sync, like the other backends. - Android: a mouse click reports the real button. A mouse or hovering stylus reports the new
MFB_POINTER_ID_MOUSEas its pointer id. Touch input works as before. - Windows: AltGr always reports the left Control that Windows presses for it. Before, the same keystroke reported one key on some runs and two on others.
- macOS: the key next to left Shift reports
MFB_KB_KEY_WORLD_2, like every other backend.MFB_KB_MOD_NUM_LOCKis never set, because macOS has no Num Lock. Each Caps Lock toggle reports a press and then a release. - DOS: with Num Lock off, the keypad reports the navigation keys, as DOS programs expect. Define
MINIFB_DOS_KEYPAD_POSITIONALto always report the physical keypad key.
Fixed
Mouse:
- X11 no longer reports every wheel notch twice.
- Windows reports the side buttons as
MFB_MOUSE_BTN_4andMFB_MOUSE_BTN_5, like the other backends. - On Web and Android the wheel axes now have the same sign as on the other backends.
- On macOS one wheel notch is worth the same as on the other backends.
- No scroll callback arrives with both axes at zero.
- A button released outside the window is delivered on Windows. Before, it stayed held.
Keyboard:
- Modifiers come from both the keys that are held and what the platform reports. Releasing one Shift while the other is held keeps Shift set, and AltGr always reports Alt. Mouse callbacks carry the same modifiers.
- Losing the focus releases every key still held, with one callback for each key. Gaining the focus rebuilds the key buffer on Windows, X11 and macOS.
- The physical key is reported before the character it produces.
- The text callback no longer receives control characters. On Windows, Enter, Tab and Backspace used to arrive there.
- Many backend specific key fixes: Shift and the Windows key getting stuck on Windows, Print Screen on Windows, X11 auto-repeat and lock state, left and right modifiers on macOS, ISO keyboards on macOS, AltGr and missing keys on Web, extended keys and the arrow keys on DOS.
Windows and the rest:
- The window title is UTF-8 on every backend. An invalid title is reported once in the log. It no longer produces mojibake on Windows, an empty title on macOS or a protocol error on Wayland.
- Frame pacing no longer stops for good after one long frame.
- X11 and Windows call the resize callback only when the size changes.
- Windows no longer warns about DPI awareness when the application manifest has already set it.
- iOS multi-touch works.
- The DOS mouse position is reported in window units.
- Android big-endian builds use the color layout the Android documentation gives (#142, by @cannedbeef).
Build:
- The Web examples build again when the build directory is outside the source tree.
- GCC builds are warning free again.
- DJGPP executables fit the DOS 8.3 name limit.
The full list of changes is in CHANGELOG.md.
v0.13.0: Client-side window decorations on Wayland
Until now, a window opened on a compositor without xdg-decoration had no frame at all: no title bar, no borders, no close button. GNOME is the main case. MiniFB now draws that frame itself with libdecor.
libdecor is optional and is never linked. MiniFB opens it with dlopen the first time a window needs it, so the same binary runs on machines that do not have it, and falls back to the old undecorated window when it is missing.
Nothing changes on compositors that do implement xdg-decoration, such as KDE. They keep drawing the frame themselves, as before.
Added
- Wayland client-side decorations: when the compositor does not implement
xdg-decoration, MiniFB now draws the window frame with libdecor instead of leaving the window bare. libdecor is opened withdlopenthe first time a window needs it, so it is never a link-time dependency: a binary built on a machine that has libdecor still runs on one that does not. If the library, any symbol it needs, or the compositor support is missing, the window falls back to a plain undecorated toplevel and MiniFB says so in the log. CMake finds the headers withpkg-configand reports which case applies, including the common one where the runtime library is installed but the development package is not.
Changed
- Wayland: the surface now declares its opaque region, and refreshes it whenever the surface changes size. The buffer format has no alpha channel, so the whole surface is opaque, but a compositor that is not told this may still blend it. Declaring it lets the compositor skip that work and discard whatever the window covers. GLFW and SDL do the same.
Known issues
- Wayland on WSLg: maximizing a window that libdecor decorates leaves the drop shadow of the floating window drawn over the maximized one, at its previous size and position. MiniFB removes the shadow correctly and the compositor confirms it, so this is not specific to MiniFB: the same artifact appears with GLFW and was reported against FLTK in microsoft/wslg#914. It does not happen on native Linux compositors. Resizing the window by hand clears it.
Full Changelog: v0.12.0...v0.13.0
v0.12.0: Wayland fixes and diagnostics
Three fixes you may notice. A hidden window no longer uses a whole CPU core. Key repeat runs at the rate the compositor asks for. One mouse wheel notch reports 1.0 on every compositor.
Two new environment variables also make the version-dependent fallback paths testable without changing compositor.
This is the second half of the Wayland rework. The first half is in v0.11.0.
Heads up if you are on Wayland. The wheel change alters values your application already receives. And MINIFB_LOG_LEVEL deliberately overrides mfb_set_log_level().
Added
- Log level from the environment:
MINIFB_LOG_LEVELsets the log threshold by name (trace,debug,info,warning,error) without touching the code. It deliberately wins overmfb_set_log_level(), so you can get more output from a program you cannot rebuild. An unknown value is reported as an error and then ignored, leaving the threshold where the program left it. - Wayland fallback testing:
MINIFB_WAYLAND_FORCE_VERSIONSlowers the version MiniFB binds for one or more protocol globals, andMINIFB_WAYLAND_DISABLE_GLOBALShides globals as if the compositor never advertised them. Both work in every build and are read only while globals are bound, so a single machine can exercise fallback paths that would otherwise need another compositor. - Added
docs/wayland-testing.md: the interfaces each variable accepts, which versions are worth testing and what each one covers, the related variables from libwayland and xkbcommon, and what this approach cannot test.
Changed
- Wayland mouse wheel: the continuous
wl_pointer.axisvalue is now divided by the ratio Weston uses, which SDL and GLFW follow too. One notch reports1.0, like the other backends. This only affects compositors that fall back to the continuous value. Those that sendaxis_value120oraxis_discretealready reported1.0. - Every Wayland listener callback now carries a comment with its protocol interface and the version that introduced it, so the version-dependent paths can be found with grep.
- Wayland:
xdg_toplevel.configurenow logs its state array at DEBUG (activated,suspended,maximized,resizing, ...) instead of discarding it. - Wayland:
wl_keyboard.repeat_infonow logs the advertised rate and delay at DEBUG. It also reports when client-side repeat is off, which happens when the compositor drives it instead. - Wayland: MiniFB now says when a window will have no frame. It logs the negotiated decoration mode at DEBUG. A client-side answer to a server-side request also raises a warning. The warning for a missing decoration manager now states the same fact instead of describing the protocol.
- Wayland: a failed dispatch now reports what libwayland knows about the connection. A protocol error names the interface, the object and the error code. Any other failure reports the system error. Before, every case printed the same generic message.
- Rewrote
README.mdin plainer English and corrected stale details:mfb_updatereturn values, ESC handling, the macOS Metal default, the X11 default on Linux, the Wayland dependencies, and Web monitor scale and cursor support. Added a CMake options table, and replaced the per-platform "Beta" labels with what each backend actually supports.
Fixed
- Wayland:
wl_pointer.axis_discreteno longer marks an axis as valid when the compositor reports a discrete step of0. - Wayland: a protocol error is no longer reported as
EPIPE. The dispatch helpers gave up as soon as the flush failed. They never read the error the compositor had queued before closing the socket. They now continue to the read, as libwayland does in the function they derive from. - Wayland: key repeat now runs at the rate the compositor asks for. MiniFB computed the next deadline from the moment the previous repeat fired. Every interval then rounded up to the next poll. On KWin at 25 cps, the measured rate was 19.18 before the fix and 25.06 after.
- Wayland: a hidden window no longer uses a whole CPU core. The frame throttle's wait budget belonged to the frame callback, and nothing refreshed it once it expired. Every later update then skipped without waiting. An application with no other frame pacing had nothing left to pace it. The budget now belongs to each update call. On KWin, a minimized window with no target FPS dropped from 100% of a core to 3.8%, and the visible frame rate did not change.
Full Changelog: v0.11.0...v0.12.0
v0.11.0: The Wayland rework, part one
Wayland becomes a first-class desktop backend, alongside Windows, macOS and X11. Presentation, event dispatch, frame pacing, scaling, input and resource lifecycle were all reworked. Part two is in v0.12.0.
The CMake build was also restructured, though the unprefixed option names were already deprecated back in v0.9.3 and still work.
Changed
- Wayland backend modernization: promoted Wayland to a first-class desktop backend alongside Windows, macOS, and X11, with reworked SHM presentation, event dispatch, frame pacing, scaling, input, and resource lifecycle handling.
- Updated the bundled Wayland protocol bindings to 1.49 and added viewporter-based fractional and per-surface HiDPI scaling.
- CMake restructure: the build now checks the prefixed option names internally, adds
MINIFB_BUILD_EXAMPLES, and usescmake_dependent_optionwhere one option depends on another. It also fixes the exported package config, the generated version header, and the Emscripten, iOS and macOS build paths. The unprefixed names were already deprecated in 0.9.3 and keep working.
Fixed
- Improved Wayland reliability and responsiveness during initial mapping, resize, minimize, multi-output scale changes, buffer reuse, and compositor-driven configure sequences.
- Completed Wayland keyboard, pointer, and scroll behavior, including compose/dead keys, key repeat, modifier and focus synchronization, stuck-input cleanup, and safe seat/global removal.
- Hardened Wayland protocol negotiation and version-dependent object cleanup across older and newer environments.
- Fixed macOS
flagsChangedevents produced by focus synchronization toggling alphanumeric keys.
Full Changelog: v0.10.1...v0.11.0
v0.10.1: A small follow-up to v0.10.0
mfb_set_title is new. It changes the window title after creation, on Windows, macOS, X11 and Wayland, and does nothing on iOS, Android, Web and DOS.
The rest is keyboard work. Dead-key compose on X11 now emits the standalone accent in the same order as Windows and macOS, and untranslated keys no longer fire callbacks on X11 or Wayland.
Added
- Window title API: added
mfb_set_titleto change the window title after creation. Implemented on Windows, macOS, X11, and Wayland, with no-op stubs on iOS, Android, Web, and DOS.
Changed
- Unified keycode-table initialization across Windows, macOS, X11, and Wayland with one-time setup and explicit reset to
MFB_KB_KEY_UNKNOWN. - Moved the shared
stretch_imagedeclaration intosrc/MiniFB_internal.h.
Fixed
- Fixed X11 dead-key compose cancellation so the standalone accent is emitted before the following character, matching Windows and macOS behavior.
- Fixed X11 and Wayland keyboard handling to avoid updating key state or firing keyboard callbacks for untranslated keys.
- Fixed DOS release completeness by adding the missing
mfb_set_titlebackend stub required by the public API.
Full Changelog: v0.10.0...v0.10.1
v0.10.0: The release that gave the public API its MFB_ prefix
Every public enum constant was renamed. STATE_*, KB_*, MOUSE_* and WF_* are now MFB_*. The old names still compile, as deprecated aliases that warn.
Also new here: the logging API, display inset queries for mobile, and touch decoding. Several APIs were unified so that every backend behaves the same way, so a few details may differ from v0.9.x. The tests/ directory became examples/.
Added
- Logging API:
mfb_set_logger,mfb_set_log_level,mfb_log,mfb_log_level,mfb_log_info, andMFB_LOG*helper macros for runtime log control and source-location-aware diagnostics. Backend messages now route through the shared logger instead of ad-hocfprintf/NSLog. - Display inset APIs:
mfb_get_display_cutout_insetsandmfb_get_display_safe_insetsfor mobile-safe layouts (Android API 28+, iOS, desktop stubs return zeros). - Touch pointer decoding:
mfb_decode_touch,mfb_decode_touch_pos, andmfb_decode_touch_idto decode packed pointer id/position values from mobile mouse getters. - Monitor scale: implemented
mfb_get_monitor_scalefor Web (devicePixelRatio) and Android. - Cursor control: implemented
mfb_show_cursorfor Web. - X11 scale detection: layered fallbacks (XSettings, Xresources, XRandR, physical DPI).
- DOS viewport: basic viewport support for the MS-DOS backend.
- Android
mfb_update_events: event-only pump without rendering, matching other backends. - Android example: new example project using Android Studio Narwhal (native2026).
- New headers:
MiniFB_macros.h(deprecation/pixel/logging macros),MiniFB_types.h(callback and logging typedefs),WindowData_Web.h. - Internal helpers:
calculate_buffer_layout(overflow-safe buffer validation) andmfb_validate_viewport(unified viewport checks), used by all backends.
Changed
- Standardized public enum naming to
MFB_*prefixes across states, keys, modifiers, mouse buttons, and window flags. - Unified
mfb_open_exbehavior across backends: consistent flag handling,NULL/empty title defaults to"minifb", mutually-exclusive fullscreen flags logged. - Unified
mfb_set_viewportbehavior across backends with shared validation and consistent destination recalculation. - Unified
mfb_get_monitor_scalesowindow == NULLis accepted across backends, returning the primary monitor scale where available and1.0fallback otherwise. - Unified mouse wheel reset (
mouse_wheel_x/y = 0) on every update across all backends. - Web backend: auto-creates missing canvas element; pumps events in
mfb_wait_sync. - Moved
accumulated_error_ticksinto the timer struct (was static). - Replaced deprecated Android API
ALooper_pollAllwithALooper_pollOnce. - Callback parameter names unified (
is_active,is_pressed,delta_x,delta_y). - Renamed
tests/toexamples/and updated CMake/example project paths accordingly. - Reorganized Android examples into
native2021/native2026folders. - Moved DOS tools to
tools/dos/, Wayland protocol generator totools/wayland/. - Updated DJGPP GCC toolchain to 12.2.0.
- Normalized line endings with
.gitattributes.
Deprecated
- All non-prefixed enum constants (
STATE_*,KB_*,MOUSE_*,WF_*) in favor ofMFB_*equivalents. Old names remain as deprecated aliases with compiler warnings.
Fixed
- Fixed
MFB_ARGBmacro on Android little-endian (had 3 parameters instead of 4). - Fixed Web
mfb_update_exnot updatingbuffer_width/buffer_height/buffer_stride. - Fixed integer overflow potential in buffer size calculations across all backends.
- Fixed iOS: Metal safety, content scale, touch coordinates, window lookup, active/close event management, and safer cutout/safe-inset handling when no launch screen is configured.
- Fixed Android: API 32-34 display cutout handling; surface transition and rotation edge cases.
- Fixed macOS: improved robustness and replaced
NSLogwithmfb_log. - Fixed Windows: double-click messages now map to regular mouse button press events.
- Fixed Windows: initial window sizing on high-DPI displays so the client area and viewport stay aligned.
- Fixed X11: initial normal-window placement now centers on a real monitor instead of the combined virtual desktop.
- Fixed C++ wrapper: callback stubs are released when windows are destroyed, preventing stale callback reuse after recreating windows.
- Fixed Web: initialization/teardown robustness when
document.bodyis not yet available. - Fixed Wayland
wl_surface_attach: the viewport offset was passed as the buffer offset, so buffers are now attached at0, 0. - Fixed Wayland seat and output handling: pointer and keyboard listeners are added only when the seat really provides the object, and a removed
wl_outputis compared with the current one before it is destroyed. - Fixed MS-DOS keyboard: completed the scancode table (numpad keys, F11/F12), read the initial Caps Lock state from the BIOS, applied Caps Lock to letters only, and stopped an out-of-bounds write for keys mapped to
KB_KEY_UNKNOWN(-1, seen as0xFFFFFFFFwhen indexing the key table). - Fixed MS-DOS mouse: the driver presence check uses the documented
0xFFFFreply, the pointer range follows the real VESA resolution instead of the requested window size, and the middle button fires its callback. - Fixed MS-DOS input under DPMI by locking the ring-buffer code used from the interrupt handler (
_go32_dpmi_lock_code). - Fixed use-after-free during teardown: macOS clears the
MTKViewdelegate before closing the window, the OpenGL path no longer closes the X11 display it does not own, and the DOS backend no longer freeswindow_datawhile the caller is still using it.
Full Changelog: v0.9.3...v0.10.0