# Getting started In this tutorial, we will write the mandatory boilerplate code that will allow us to later use the haiku graphics API. ## Module initialization Any haiku application or headless compute program starts by initializing the haiku graphics module. This module allows the library to perform several actions: - It allows to allocate internal data structures inside centralized memory pools. - It allows to log any notification, warning and error messages to the user. - It allows to enable a custom assertion mechanism while debugging. So the first task of any user is to provide the following: - a required custom allocator callbacks/context `hk_user_memory_allocator_t`, - an optional custom assertion callback/context `hk_user_assert_t`, - an optional custom logger callback/context `hk_user_logger_t`. These data structure are given to the function `hkgfx_module_create` to properly setup the graphic backend implementation. Yielding: ```C hkgfx_module_create( &(hk_module_desc) { .allocator = { .context = NULL, .realloc_fn = my_sample_realloc, .alloc_fn = my_sample_alloc, .free_fn = my_sample_free, }, .assertion = { .context = NULL, .assert_fn = my_sample_assert }, .logger = { .context = NULL, .log_fn = my_sample_logger } }); ``` Here is an example of a full definition of every custom mechanism used by the module. Besides the allocator, a user can omit assertion and logging system but it is highly recommended during development. You can also see that haiku uses a typical c99 initialization pattern via designated initializers. The user provides a temporary reference to a descriptor structure with nested parameters. So the temporary data structure only lives during the call to `hkgfx_module_create` and leaves the stack after the call. This is a pattern that is used a lot inside `haiku` public C-API so you'll see it often in our samples/tutorials. Another special note here: if you can omit a field like any `.context` for instance, the designated initialization will set the field to zero or null. ## Module deinitialization In C, when there's a initialization call, there's a deinitialization call to cleanup after you. So at the end of your program, you'll call `hkgfx_module_destroy` function. ```C /* [your code here] */ hkgfx_module_destroy(); return EXIT_SUCCESS; // if at the end the main function ``` ## Default module implementation As the current C99 implementation **does not** provides a default/fallback mechanism, we'll provide a simple implementation using the standard library in this section. You can check this implementation is inside the [online sample repository](https://gitlab.xlim.fr/haiku/haiku-samples). The required C headers used in the following functions are: - stdlib.h (`malloc`, `free`, `realloc`) - stdio.h (`fprintf`, `vfprintf`) - stdarg.h (`va_list`, `va_start`, `va_end`) - stddef.h (`size_t`) - stdbool.h (`bool`) - assert.h (`assert`) ### Simple allocator This dummy allocator will fallback calls to standard `malloc`, `free` and `realloc` functions. ```C void* my_sample_alloc(size_t bytesize, void* ctx) { (void) ctx; void* ptr = malloc(bytesize); if(ptr==NULL) { fprintf(stderr,"Failed to allocate %5zu bytes.\n", bytesize ); exit(EXIT_FAILURE); } return ptr; } ``` ```C void my_sample_free(void* ptr, size_t bytesize, void* ctx) { (void) ctx; (void) bytesize; free(ptr); } ``` ```C void* my_sample_realloc(size_t newsize, void* oldptr, size_t oldsize, void* ctx) { (void) ctx; assert( newsize>0 && "Invalid new size"); assert( newsize>oldsize && "Invalid reallocation size"); void* newptr = realloc(oldptr,newsize); if(newptr==NULL) { fprintf(stderr,"Failed to realloc %5zu bytes from address %p with size %5zu.\n", newsize, oldptr, oldsize ); exit(EXIT_FAILURE); } return newptr; } ``` ### Simple logger This logger will fallback calls to standard `fprintf` formatting functions with variading arguments. ```C void my_sample_logger(void* ctx, int level, const char* message, ...) { (void) ctx; fprintf(stderr, "%s - ", my_logger_level(level)); va_list arguments; va_start(arguments, message); vfprintf(stderr, message, arguments); va_end(arguments); fprintf(stderr, "\033[0m\n"); } ``` `my_logger_level` starts a colored message based on the level returned by haiku: ```C const char* my_logger_level(int level) { switch(level) { case HAIKU_LOG_INFO_LEVEL: return "\033[0;36m[NOTIF]"; case HAIKU_LOG_WARNING_LEVEL: return "\033[0;33m[WARNG]"; case HAIKU_LOG_ERROR_LEVEL: return "\033[0;31m[ERROR]"; default: return "\033[1;0m[NOPE]"; } } ``` ### Simple assertion This assert will print the assertion message and call `abort`. ```C void my_sample_assert(bool expr, const char* message, void* ctx) { (void) ctx; if(!expr) { fprintf(stderr, "%s\n", message); abort(); } } ``` ## Reference The allocator callbacks following the design of this Chris Wellons blog post - [So you want custom allocator support in your C lirabry](https://nullprogram.com/blog/2023/12/17/)