MiniFB (Mini FrameBuffer) is a small cross platform library that makes it easy to render (32-bit) pixels in a window. An example is the best way to show how it works:
structmfb_window*window=mfb_open_ex("my display", 800, 600, WF_RESIZABLE);
if (!window)
return0;
for (;;)
{
intstate;
// TODO: add some fancy rendering to the buffer of size 800 * 600state=mfb_update(buffer);
if (state<0)
break;
}Furthermore, you can add callbacks to the windows:
voidactive(structmfb_window*window, boolisActive) {
...
}
voidresize(structmfb_window*window, intwidth, intheight) {
...
// Optionally you can also change the viewport sizemfb_set_viewport(window, x, y, width, height);
}
voidkeyboard(structmfb_window*window, mfb_keykey, mfb_key_modmod, boolisPressed) {
...
// Remember to close the window in some wayif(key==KB_KEY_ESCAPE) {
mfb_close(window);
}
fprintf(stdout, "%s > keyboard: key: %s (pressed: %d) [key_mod: %x]\n", window_title, mfb_get_key_name(key), isPressed, mod);
}
voidchar_input(structmfb_window*window, unsigned intcharCode) {
...
}
voidmouse_btn(structmfb_window*window, mfb_mouse_buttonbutton, mfb_key_modmod, boolisPressed) {
...
}
// Use wisely this event. It can be sent too oftenvoidmouse_move(structmfb_window*window, intx, inty) {
...
}
// Mouse wheelvoidmouse_scroll(structmfb_window*window, mfb_key_modmod, floatdeltaX, floatdeltaY) {
...
}
structmfb_window*window=mfb_open_ex("my display", 800, 600, WF_RESIZABLE);
if (!window)
return0;
mfb_set_active_callback(window, active);
mfb_set_resize_callback(window, resize);
mfb_set_keyboard_callback(window, keyboard);
mfb_set_char_input_callback(window, char_input);
mfb_set_mouse_button_callback(window, mouse_btn);
mfb_set_mouse_move_callback(window, mouse_move);
mfb_set_mouse_scroll_callback(window, mouse_scroll);Additionally you can set data per window and recover it:
mfb_set_user_data(window, (void*) myData);
...
myData= (someCast*) mfb_get_user_data(window);Finally, you can get information about the events in the window directly:
boolmfb_is_window_active(structmfb_window*window);
unsignedmfb_get_window_width(structmfb_window*window);
unsignedmfb_get_window_height(structmfb_window*window);
intmfb_get_mouse_x(structmfb_window*window); // Last mouse pos Xintmfb_get_mouse_y(structmfb_window*window); // Last mouse pos Yfloatmfb_get_mouse_scroll_x(structmfb_window*window); // Mouse wheel X as a sum. When you call this function it resets.floatmfb_get_mouse_scroll_y(structmfb_window*window); // Mouse wheel Y as a sum. When you call this function it resets.constuint8_t*mfb_get_mouse_button_buffer(structmfb_window*window); // One byte for every button. Press (1), Release 0. (up to 8 buttons)constuint8_t*mfb_get_key_buffer(structmfb_window*window); // One byte for every key. Press (1), Release 0.First the code creates window with the mfb_open call that is used to display the data, next it's the applications responsibility to allocate a buffer (which has to be at least the size of the window and in 32-bit) Next when calling mfb_update function the buffer will be copied over to the window and displayed. Currently the mfb_update will return -1 if ESC key is pressed but later on it will support to return a key code for a pressed button. See https://github.com/emoon/minifb/blob/master/tests/noise.c for a complete example
MiniFB has been tested on Windows, Mac OS X and Linux but may of course have trouble depending on your setup. Currently the code will not do any converting of data if not a proper 32-bit display can be created.
Extra: Timers and target FPS
You can create timers for your own purposes.
structmfb_timer*mfb_timer_create();
voidmfb_timer_destroy(structmfb_timer*tmr);
voidmfb_timer_reset(structmfb_timer*tmr);
doublemfb_timer_now(structmfb_timer*tmr);
doublemfb_timer_delta(structmfb_timer*tmr);
doublemfb_timer_get_frequency();
doublemfb_timer_get_resolution();Furthermore you can set a target fps for the application. The default is 60 frames per second.
voidmfb_set_target_fps(uint32_tfps);This avoid the problem of update too fast the window collapsing the redrawing in fast processors.
To use this you need to call the function:
boolmfb_wait_sync(structmfb_window*window);Example:
do {
inti;
mfb_update_statestate;
// TODO: add some awesome rendering to the bufferstate=mfb_update(window, g_buffer);
if (state!=STATE_OK) {
window=0x0;
break;
}
} while(mfb_wait_sync(window));Note that if you have several windows running on the same thread it makes no sense to wait them all...
MiniFB uses tundra https://github.com/deplinenoise/tundra as build system and is required to build the code as is but not many changes should be needed if you want to use it directly in your own code.
You can also use CMake as build system.
Cocoa and clang is assumed to be installed on the system (downloading latest XCode + installing the command line tools should do the trick) then to build run: tundra2 macosx-clang-debug and you should be able to run the noise example (t2-output/macosx-clang-debug-default/noise)
MacOS X Mojave does not support Cocoa framework as expected. For that reason now you can switch to Metal API. To enable it just compile defining the preprocessor macro USE_METAL_API.
If you use CMake just enable the flag:
mkdir build
cd build
cmake .. -DUSE_METAL_API=ON
or if you don't want to use Metal API:
mkdir build
cd build
cmake .. -DUSE_METAL_API=OFF
It works with and without an UIWindow created. If you want to create the UIWindow through an Story Board, remember to set the UIViewController as iOSViewController and the UIView as iOSView.
Issues:
- It seems that you have to manually set 'tvOS Deployment Target' to less than 13.
- It seems that you have to manually set 'Launch Screen File' in project > executable > general to be able to get the real device height.
- You need to manually set the 'Signing Team' and 'Bundle Identifier'.
- No multitouch is available yet.
- As this version uses Metal API it cannot be run in the emulator.
Functions:
Some of the MiniFB functions don't make sense on mobile. The available functions for iOS are:
structmfb_window*mfb_open(constchar*title, unsignedwidth, unsignedheight);
structmfb_window*mfb_open_ex(constchar*title, unsignedwidth, unsignedheight, unsignedflags); // flags ignoredmfb_update_statemfb_update(structmfb_window*window, void*buffer);
voidmfb_close(structmfb_window*window);
voidmfb_set_user_data(structmfb_window*window, void*user_data);
void*mfb_get_user_data(structmfb_window*window);
boolmfb_set_viewport(structmfb_window*window, unsignedoffset_x, unsignedoffset_y, unsignedwidth, unsignedheight);
voidmfb_set_mouse_button_callback(structmfb_window*window, mfb_mouse_button_funccallback);
voidmfb_set_mouse_move_callback(structmfb_window*window, mfb_mouse_move_funccallback);
voidmfb_set_resize_callback(structmfb_window*window, mfb_resize_funccallback);
unsignedmfb_get_window_width(structmfb_window*window);
unsignedmfb_get_window_height(structmfb_window*window);
intmfb_get_mouse_x(structmfb_window*window); // Last mouse pos Xintmfb_get_mouse_y(structmfb_window*window); // Last mouse pos YTimers are also available.
structmfb_timer*mfb_timer_create(void);
voidmfb_timer_destroy(structmfb_timer*tmr);
voidmfb_timer_reset(structmfb_timer*tmr);
doublemfb_timer_now(structmfb_timer*tmr);
doublemfb_timer_delta(structmfb_timer*tmr);
doublemfb_timer_get_frequency(void);
doublemfb_timer_get_resolution(void);For now, no multitouch is available.
Example:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if(g_window == 0x0) {
g_width = [UIScreen mainScreen].bounds.size.width;
g_height = [UIScreen mainScreen].bounds.size.height;
g_window = mfb_open("noise", g_width, g_height);
if(g_window != 0x0) {
g_buffer = malloc(g_width * g_height * 4);
}
}
returnYES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application { mDisplayLink = [CADisplayLinkdisplayLinkWithTarget:selfselector:@selector(OnUpdateFrame)];
[mDisplayLink addToRunLoop:[NSRunLoopcurrentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)applicationWillTerminate:(UIApplication *)application {
[mDisplayLink invalidate];
mfb_close(g_window);
}
- (void) OnUpdateFrame {
if(g_buffer != 0x0) {
// Do your wonderful rendering stuff
}
mfb_update_state state = mfb_update(g_window, g_buffer);
if (state != STATE_OK) {
free(g_buffer);
g_buffer = 0x0;
g_width = 0;
g_height = 0;
}
}CMake
mkdir build
cd build
cmake -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 ..
Visual Studio (ver 2012 express has been tested) tools needed (using the vcvars32.bat (for 32-bit) will set up the enviroment) to build run: tundra2 win32-msvc-debug and you should be able to run noise in t2-output/win32-msvc-debug-default/noise.exe
If you use CMake the Visual Studio project will be generated (2015, 2017 and 2019 have been tested).
gcc and x11-dev libs needs to be installed. To build the code run tundra2 x11-gcc-debug and you should be able to run t2-output/x11-gcc-debug-default/noise
If you use CMake just disable the flag:
mkdir build
cd build
cmake .. -DUSE_WAYLAND_API=OFF
Depends on gcc and wayland-client and wayland-cursor. Built using the wayland-gcc variants.
If you use CMake just enable the flag:
mkdir build
cd build
cmake .. -DUSE_WAYLAND_API=ON