diff options
author | Bad Diode <bd@badd10de.dev> | 2021-01-25 22:01:00 +0100 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-01-25 22:01:00 +0100 |
commit | e24186fc1917c5e8b91924af0c3c7c55816ff5d6 (patch) | |
tree | 6b20f24824736e822ba614811f312a7c7c2fd43b | |
download | mic-e24186fc1917c5e8b91924af0c3c7c55816ff5d6.tar.gz mic-e24186fc1917c5e8b91924af0c3c7c55816ff5d6.zip |
Introducing MIC
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | LICENSE | 51 | ||||
-rw-r--r-- | Makefile | 61 | ||||
-rw-r--r-- | README.md | 118 | ||||
-rw-r--r-- | src/app.c | 44 | ||||
-rw-r--r-- | src/app.h | 37 | ||||
-rw-r--r-- | src/main.c | 25 | ||||
-rw-r--r-- | src/platform.h | 25 | ||||
-rw-r--r-- | src/platform_posix.c | 171 | ||||
-rw-r--r-- | src/shorthand.h | 35 |
10 files changed, 568 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore | |||
@@ -0,0 +1 @@ | |||
build | |||
@@ -0,0 +1,51 @@ | |||
1 | This software is dual licensed under Unlicense/MIT. Choose whichever you prefer. | ||
2 | |||
3 | ---------------------------------------------------------------------- | ||
4 | UNLICENSE | ||
5 | ---------------------------------------------------------------------- | ||
6 | |||
7 | This is free and unencumbered software released into the public domain. | ||
8 | |||
9 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this | ||
10 | software, either in source code form or as a compiled binary, for any purpose, | ||
11 | commercial or non-commercial, and by any means. | ||
12 | |||
13 | In jurisdictions that recognize copyright laws, the author or authors of this | ||
14 | software dedicate any and all copyright interest in the software to the public | ||
15 | domain. We make this dedication for the benefit of the public at large and to | ||
16 | the detriment of our heirs and successors. We intend this dedication to be an | ||
17 | overt act of relinquishment in perpetuity of all present and future rights to | ||
18 | this software under copyright law. | ||
19 | |||
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
22 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE | ||
23 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | ||
24 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
25 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
26 | |||
27 | For more information, please refer to <http://unlicense.org/> | ||
28 | |||
29 | |||
30 | ---------------------------------------------------------------------- | ||
31 | MIT License | ||
32 | ---------------------------------------------------------------------- | ||
33 | |||
34 | Copyright (c) 2020 Bad Diode | ||
35 | |||
36 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
37 | this software and associated documentation files (the "Software"), to deal in | ||
38 | the Software without restriction, including without limitation the rights to | ||
39 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
40 | the Software, and to permit persons to whom the Software is furnished to do so, | ||
41 | subject to the following conditions: | ||
42 | |||
43 | The above copyright notice and this permission notice shall be included in all | ||
44 | copies or substantial portions of the Software. | ||
45 | |||
46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
47 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
48 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
49 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
50 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
51 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b31e5c8 --- /dev/null +++ b/Makefile | |||
@@ -0,0 +1,61 @@ | |||
1 | .POSIX: | ||
2 | .SUFFIXES: | ||
3 | |||
4 | # Source code location and files to watch for changes. | ||
5 | SRC_DIR := src | ||
6 | SRC_MAIN := $(SRC_DIR)/main.c | ||
7 | SRC_APP := $(SRC_DIR)/app.c | ||
8 | WATCH_SRC := $(wildcard $(SRC_DIR)/*.c) | ||
9 | WATCH_SRC += $(wildcard $(SRC_DIR)/*.h) | ||
10 | |||
11 | # Output library names and executables. | ||
12 | BIN_NAME := app | ||
13 | LIB_NAME := libmic.so | ||
14 | BUILD_DIR := build | ||
15 | BIN := $(BUILD_DIR)/$(BIN_NAME) | ||
16 | LIB := $(BUILD_DIR)/$(LIB_NAME) | ||
17 | LIB_DIR := $(BUILD_DIR) | ||
18 | |||
19 | # Compiler and linker configuration. | ||
20 | CC := gcc | ||
21 | CFLAGS := -Wall -Wextra -pedantic -std=c99 | ||
22 | LDFLAGS := | ||
23 | LDLIBS := | ||
24 | RELEASE_CFLAGS := -DNDEBUG -O2 | ||
25 | DEBUG_CFLAGS := -DDEBUG -g | ||
26 | |||
27 | .PHONY: dynamic static clean run | ||
28 | |||
29 | # Setup debug/release builds. | ||
30 | # make clean && make <target> DEBUG=0 | ||
31 | # make clean && make <target> DEBUG=1 | ||
32 | DEBUG ?= 0 | ||
33 | ifeq ($(DEBUG), 1) | ||
34 | CFLAGS += $(DEBUG_CFLAGS) | ||
35 | else | ||
36 | CFLAGS += $(RELEASE_CFLAGS) | ||
37 | endif | ||
38 | |||
39 | dynamic: CFLAGS += -fPIC | ||
40 | dynamic: CFLAGS += -DLIB_NAME=\"$(LIB_NAME)\" | ||
41 | dynamic: CFLAGS += -DLIB_DIR=\"$(LIB_DIR)\" | ||
42 | dynamic: LDFLAGS += -ldl | ||
43 | dynamic: $(BUILD_DIR) $(LIB) $(BIN) | ||
44 | |||
45 | static: SRC_MAIN += $(SRC_APP) | ||
46 | static: $(BUILD_DIR) $(BIN) | ||
47 | |||
48 | $(BIN): $(SRC_MAIN) $(WATCH_SRC) | ||
49 | $(CC) $(CFLAGS) $(LDFLAGS) -o $(BIN) $(SRC_MAIN) $(LDLIBS) | ||
50 | |||
51 | $(LIB): $(SRC_APP) $(WATCH_SRC) | ||
52 | $(CC) $(CFLAGS) -shared $(LDFLAGS) -o $@ $(SRC_APP) $(LDLIBS) | ||
53 | |||
54 | $(BUILD_DIR): | ||
55 | mkdir -p $(BUILD_DIR) | ||
56 | |||
57 | run: $(BIN) | ||
58 | exec $(BIN) | ||
59 | |||
60 | clean: | ||
61 | rm -r $(BUILD_DIR) | ||
diff --git a/README.md b/README.md new file mode 100644 index 0000000..56d8b9d --- /dev/null +++ b/README.md | |||
@@ -0,0 +1,118 @@ | |||
1 | # Micro Interactive C Framework (MIC) | ||
2 | |||
3 | MIC is a tiny (<300 cloc) framework to boostrap interactive C projects. | ||
4 | Application code can be dynamically reloaded upon compilation, allowing C to be | ||
5 | used as a pseudo-scripting language. This property can be very useful when | ||
6 | developing, but might not be always desirable for a final release, for this | ||
7 | reason, static compilation can be selected as a make target when needed. The | ||
8 | main limitation of hot code reload is that global state can't be stored in the | ||
9 | main app code, but the framework is designed so the state is passed around when | ||
10 | needed and is always accessible for the relevant functions. | ||
11 | |||
12 | This framework can be easily extended to work with multiple platforms. However, | ||
13 | hot code reload will only be available for platforms that support dynamic | ||
14 | linking. For the moment, only POSIX compliant platforms are supported but | ||
15 | Windows is a target for the near future. | ||
16 | |||
17 | Remember that MIC is only a base, you can (and should) modify anything in here | ||
18 | to adapt it to your particular project. | ||
19 | |||
20 | ## Example usage | ||
21 | |||
22 | Compile and execute the program: | ||
23 | |||
24 | ``` | ||
25 | make && ./build/app | ||
26 | ``` | ||
27 | |||
28 | While the application is running, make changes to the `src/app.c`. For example, | ||
29 | change the `app_step` function from: | ||
30 | |||
31 | ``` | ||
32 | static inline bool | ||
33 | app_step(AppState *state, PlatformAPI platform) { | ||
34 | (void)state; // Unused parameter. | ||
35 | platform.log("STEP"); | ||
36 | platform.sleep(100000); | ||
37 | return true; | ||
38 | } | ||
39 | ``` | ||
40 | |||
41 | To: | ||
42 | |||
43 | ``` | ||
44 | static inline bool | ||
45 | app_step(AppState *state, PlatformAPI platform) { | ||
46 | (void)state; // Unused parameter. | ||
47 | platform.log("Hello world!"); | ||
48 | platform.sleep(100000); | ||
49 | return true; | ||
50 | } | ||
51 | ``` | ||
52 | |||
53 | In another terminal (or using your editor of choice), recompile the program: | ||
54 | |||
55 | ``` | ||
56 | make | ||
57 | ``` | ||
58 | |||
59 | The changes should take effect in the running application. | ||
60 | |||
61 | ## Implementation details | ||
62 | |||
63 | This framework offers two APIs for managing system resources and executing | ||
64 | application logic. The `PlatformAPI` is described in `src/platform.h` and it's | ||
65 | tasked with manage the resources of the platform and/or operating system. For | ||
66 | example, opening/reading files, allocating memory, logging, etc. To support | ||
67 | a new platform it is sufficient to implement the functions described on the API | ||
68 | and make sure to select such platform at compile time. Additionally, the | ||
69 | following internal functions must be implemented for static and dynamic linking: | ||
70 | |||
71 | ``` | ||
72 | // Load the dynamic library and initialize application. Returns the success or | ||
73 | // failure of this operation. | ||
74 | static bool _app_init(AppAPI *api, AppState *state, PlatformAPI platform); | ||
75 | |||
76 | // This function reloads the application code from the dynamic library, | ||
77 | // returning `true` if the AppAPI is ready to be used. | ||
78 | static bool _app_reload(); | ||
79 | |||
80 | // Cleanup resources before exit. | ||
81 | static void _app_destroy(AppAPI *api, AppState *state, PlatformAPI platform); | ||
82 | ``` | ||
83 | |||
84 | The `AppAPI`, described in `src/app.h` and handles app initialization and | ||
85 | destruction, it contains the behaviour in the event of hot code reloading as | ||
86 | well as the main step function. The `init` function typically will be tasked | ||
87 | with initial allocation of resources. The duty of `reload` and `unload` is | ||
88 | application dependent, for example, we might wish to re-compile the shader | ||
89 | programs or reload geometry/sprites when we make modifications to the main | ||
90 | application code. The `step` function is called in every iteration of the loop, | ||
91 | and is where the bulk of the logic will likely be. | ||
92 | |||
93 | The application state is stored in a unique `AppState` structure. MIC has been | ||
94 | architected so that the state and platform functions are available in the | ||
95 | previously mentioned `AppAPI` functions. `AppState` can contain whatever state | ||
96 | is needed and will be persistent when re-compiling. The only limitation is that | ||
97 | if the `AppState` structure changes, the application must be restarted, as the | ||
98 | stored state will become invalid. This can be circumvented by pre-allocating | ||
99 | some memory that can then be partitioned with a pool allocator to suit the | ||
100 | application needs. This design is also conducive for saving state data by simply | ||
101 | serializing the `AppState` structure. | ||
102 | |||
103 | ## Credits | ||
104 | |||
105 | I initially discovered the hot code reloading trick when watching [Casey | ||
106 | Muratori's][casey] [Handmade Hero][handmade] series. It blew my mind! However, | ||
107 | the code he presented was focused solely on Windows and I never end up trying it | ||
108 | myself. Some years later I tried to find a way of making this work in Mac/Linux | ||
109 | when I came across [Chris Wellons'][skeeto] take on this problem with a clever | ||
110 | function pointer API in his [interactive c demo][interac-c-demo] page. This | ||
111 | framework uses a lot of the ideas he presents there, as you can clearly see if | ||
112 | you look at [his code][interac-c-demo]. | ||
113 | |||
114 | [casey]: https://caseymuratori.com | ||
115 | [handmade]: https://handmadehero.org | ||
116 | [skeeto]: https://nullprogram.com | ||
117 | [interac-c-demo]: https://nullprogram.com/blog/2014/12/23/ | ||
118 | [interac-c-demo-github]: https://github.com/skeeto/interactive-c-demo | ||
diff --git a/src/app.c b/src/app.c new file mode 100644 index 0000000..a39bff0 --- /dev/null +++ b/src/app.c | |||
@@ -0,0 +1,44 @@ | |||
1 | #include "app.h" | ||
2 | #include "platform.h" | ||
3 | |||
4 | static inline bool | ||
5 | app_init(AppState *state, PlatformAPI platform) { | ||
6 | platform.log("INIT"); | ||
7 | state->lt_memory = platform.calloc(LT_MEMORY_SIZE, sizeof(u8)); | ||
8 | state->st_memory = platform.calloc(ST_MEMORY_SIZE, sizeof(u8)); | ||
9 | return true; | ||
10 | } | ||
11 | |||
12 | static inline void | ||
13 | app_destroy(AppState *state, PlatformAPI platform) { | ||
14 | (void)state; // Unused parameter. | ||
15 | platform.log("DESTROY"); | ||
16 | } | ||
17 | |||
18 | static inline void | ||
19 | app_reload(AppState *state, PlatformAPI platform) { | ||
20 | (void)state; // Unused parameter. | ||
21 | platform.log("RELOAD"); | ||
22 | } | ||
23 | |||
24 | static inline void | ||
25 | app_unload(AppState *state, PlatformAPI platform) { | ||
26 | (void)state; // Unused parameter. | ||
27 | platform.log("UNLOAD"); | ||
28 | } | ||
29 | |||
30 | static inline bool | ||
31 | app_step(AppState *state, PlatformAPI platform) { | ||
32 | (void)state; // Unused parameter. | ||
33 | platform.log("STEP"); | ||
34 | platform.sleep(100000); | ||
35 | return true; | ||
36 | } | ||
37 | |||
38 | const AppAPI APP_API = { | ||
39 | .init = app_init, | ||
40 | .destroy = app_destroy, | ||
41 | .reload = app_reload, | ||
42 | .step = app_step, | ||
43 | .unload = app_unload, | ||
44 | }; | ||
diff --git a/src/app.h b/src/app.h new file mode 100644 index 0000000..8edbe58 --- /dev/null +++ b/src/app.h | |||
@@ -0,0 +1,37 @@ | |||
1 | #ifndef MIC_APP_H | ||
2 | #define MIC_APP_H | ||
3 | |||
4 | #include "shorthand.h" | ||
5 | #include "platform.h" | ||
6 | |||
7 | #define LT_MEMORY_SIZE GB(2) | ||
8 | #define ST_MEMORY_SIZE MB(100) | ||
9 | |||
10 | typedef struct AppState { | ||
11 | // Long and short term memory. | ||
12 | char *lt_memory; | ||
13 | char *st_memory; | ||
14 | } AppState; | ||
15 | |||
16 | // Function pointers for the AppAPI. | ||
17 | typedef struct AppAPI { | ||
18 | // Initialization code. Meant to be called exactly once before other API | ||
19 | // interactions. Returns the success or failure of the initialization | ||
20 | // process. | ||
21 | bool (*init)(AppState *state, PlatformAPI platform); | ||
22 | |||
23 | // Resource deallocation and cleanup. Meant to be called exactly once at the | ||
24 | // end of the app. | ||
25 | void (*destroy)(AppState *state, PlatformAPI platform); | ||
26 | |||
27 | // In case of hot code reloading, these functions handle the necessary | ||
28 | // resource setup and reloading. | ||
29 | void (*reload)(AppState *state, PlatformAPI platform); | ||
30 | void (*unload)(AppState *state, PlatformAPI platform); | ||
31 | |||
32 | // Main update/step function for the app. Returns if the app should keep | ||
33 | // running. | ||
34 | bool (*step)(AppState *state, PlatformAPI platform); | ||
35 | } AppAPI; | ||
36 | |||
37 | #endif // MIC_APP_H | ||
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..52728ef --- /dev/null +++ b/src/main.c | |||
@@ -0,0 +1,25 @@ | |||
1 | #include "platform_posix.c" | ||
2 | |||
3 | int | ||
4 | main(void) { | ||
5 | // App initialization. | ||
6 | AppAPI api = {0}; | ||
7 | AppState state = {0}; | ||
8 | if (!_app_init(&api, &state, PLATFORM_API)) { | ||
9 | return EXIT_FAILURE; | ||
10 | } | ||
11 | |||
12 | // Main loop. | ||
13 | for (;;) { | ||
14 | if (!_app_reload(&api, &state, PLATFORM_API)) { | ||
15 | continue; | ||
16 | } | ||
17 | if (!api.step(&state, PLATFORM_API)) { | ||
18 | break; | ||
19 | } | ||
20 | } | ||
21 | |||
22 | // Cleanup. | ||
23 | _app_destroy(&api, &state, PLATFORM_API); | ||
24 | return EXIT_SUCCESS; | ||
25 | } | ||
diff --git a/src/platform.h b/src/platform.h new file mode 100644 index 0000000..877453c --- /dev/null +++ b/src/platform.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #ifndef MIC_PLATFORM_H | ||
2 | #define MIC_PLATFORM_H | ||
3 | |||
4 | // Function pointers for the PlatformAPI. This allows the app to call platform | ||
5 | // specific functions that perform IO, memory allocations, etc. | ||
6 | typedef struct PlatformAPI { | ||
7 | // Reads an entire file into a null terminated buffer. It doesn't perform | ||
8 | // memory allocations and may crash if there is not enough memory or if it | ||
9 | // is uninitialized. Returns the number of bytes read. | ||
10 | size_t (*read_file)(const char *path, char *memory); | ||
11 | |||
12 | // Custom memory allocation functions for the platform. | ||
13 | void *(*malloc)(size_t size); | ||
14 | void (*free)(void *ptr); | ||
15 | void *(*calloc)(size_t nmemb, size_t size); | ||
16 | void *(*realloc)(void *ptr, size_t size); | ||
17 | |||
18 | // Sleep/wait for a given number of microseconds. | ||
19 | void (*sleep)(size_t microseconds); | ||
20 | |||
21 | // Logging functions. | ||
22 | void (*log)(const char *format, ...); | ||
23 | } PlatformAPI; | ||
24 | |||
25 | #endif // MIC_PLATFORM_H | ||
diff --git a/src/platform_posix.c b/src/platform_posix.c new file mode 100644 index 0000000..7c2fb6c --- /dev/null +++ b/src/platform_posix.c | |||
@@ -0,0 +1,171 @@ | |||
1 | #define _DEFAULT_SOURCE | ||
2 | #include <stdarg.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <time.h> | ||
6 | #include <unistd.h> | ||
7 | |||
8 | #include "app.h" | ||
9 | |||
10 | // | ||
11 | // PlatformAPI Implementation. | ||
12 | // | ||
13 | |||
14 | size_t | ||
15 | platform_read_file(const char *path, char *memory) { | ||
16 | size_t file_size = 0; | ||
17 | FILE *fp = fopen(path, "rb"); | ||
18 | if (!fp) { | ||
19 | return 0; | ||
20 | } | ||
21 | fseek(fp, 0, SEEK_END); | ||
22 | file_size = ftell(fp); | ||
23 | rewind(fp); | ||
24 | fread(memory, 1, file_size, fp); | ||
25 | fclose(fp); | ||
26 | memory[file_size] = '\0'; | ||
27 | return file_size; | ||
28 | } | ||
29 | |||
30 | void | ||
31 | platform_log(const char *format, ...) { | ||
32 | // Print date. | ||
33 | time_t raw_time = time(NULL); | ||
34 | struct tm *tm = localtime(&raw_time); | ||
35 | char date[64]; | ||
36 | strftime(date, sizeof(date), "%Y-%M-%d | %H:%M:%S", tm); | ||
37 | printf("%s | ", date); | ||
38 | |||
39 | // Print message. | ||
40 | va_list args; | ||
41 | va_start(args, format); | ||
42 | vprintf(format, args); | ||
43 | |||
44 | printf("\n"); | ||
45 | va_end(args); | ||
46 | } | ||
47 | |||
48 | void | ||
49 | platform_sleep(size_t microseconds) { | ||
50 | usleep(microseconds); | ||
51 | } | ||
52 | |||
53 | #if defined(LIB_NAME) && defined(LIB_DIR) | ||
54 | |||
55 | // | ||
56 | // Dynamic linking with hot code reload. | ||
57 | // | ||
58 | |||
59 | #include <dlfcn.h> | ||
60 | #include <sys/stat.h> | ||
61 | #include <sys/types.h> | ||
62 | |||
63 | static ino_t lib_id; | ||
64 | static void * lib_handle; | ||
65 | |||
66 | static const char *lib_path = LIB_DIR "/" LIB_NAME; | ||
67 | |||
68 | static bool | ||
69 | load_lib(AppAPI *api) { | ||
70 | struct stat st; | ||
71 | if (stat(lib_path, &st) != 0) { | ||
72 | return false; | ||
73 | } | ||
74 | |||
75 | if (lib_id != st.st_ino) { | ||
76 | void *handle = dlopen(lib_path, RTLD_NOW); | ||
77 | if (!handle) { | ||
78 | lib_handle = NULL; | ||
79 | lib_id = 0; | ||
80 | return false; | ||
81 | } | ||
82 | lib_handle = handle; | ||
83 | lib_id = st.st_ino; | ||
84 | |||
85 | const AppAPI *app_api = dlsym(lib_handle, "APP_API"); | ||
86 | if (app_api == NULL) { | ||
87 | dlclose(lib_handle); | ||
88 | lib_handle = NULL; | ||
89 | lib_id = 0; | ||
90 | return false; | ||
91 | } | ||
92 | *api = *app_api; | ||
93 | } | ||
94 | |||
95 | return true; | ||
96 | } | ||
97 | |||
98 | static bool | ||
99 | _app_reload(AppAPI *api, AppState *state, PlatformAPI platform) { | ||
100 | struct stat st; | ||
101 | if (stat(lib_path, &st) == 0 && lib_id != st.st_ino) { | ||
102 | if (lib_handle) { | ||
103 | api->unload(state, platform); | ||
104 | dlclose(lib_handle); | ||
105 | } | ||
106 | |||
107 | if (!load_lib(api)) { | ||
108 | return false; | ||
109 | } | ||
110 | api->reload(state, platform); | ||
111 | } | ||
112 | |||
113 | return true; | ||
114 | } | ||
115 | |||
116 | static bool | ||
117 | _app_init(AppAPI *api, AppState *state, PlatformAPI platform) { | ||
118 | if (!load_lib(api)) { | ||
119 | fprintf(stderr, "error: can't open app library file: %s\n", lib_path); | ||
120 | return false; | ||
121 | } | ||
122 | api->init(state, platform); | ||
123 | return true; | ||
124 | } | ||
125 | |||
126 | static void | ||
127 | _app_destroy(AppAPI *api, AppState *state, PlatformAPI platform) { | ||
128 | api->destroy(state, platform); | ||
129 | if (lib_handle) { | ||
130 | dlclose(lib_handle); | ||
131 | lib_handle = NULL; | ||
132 | lib_id = 0; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | #else | ||
137 | |||
138 | // | ||
139 | // Static linking of app code. | ||
140 | // | ||
141 | |||
142 | extern const AppAPI APP_API; | ||
143 | |||
144 | static bool | ||
145 | _app_reload() { | ||
146 | return true; | ||
147 | } | ||
148 | |||
149 | static bool | ||
150 | _app_init(AppAPI *api, AppState *state, PlatformAPI platform) { | ||
151 | *api = APP_API; | ||
152 | api->init(state, platform); | ||
153 | return true; | ||
154 | } | ||
155 | |||
156 | static void | ||
157 | _app_destroy(AppAPI *api, AppState *state, PlatformAPI platform) { | ||
158 | api->destroy(state, platform); | ||
159 | } | ||
160 | |||
161 | #endif | ||
162 | |||
163 | const PlatformAPI PLATFORM_API = { | ||
164 | .read_file = platform_read_file, | ||
165 | .malloc = malloc, | ||
166 | .free = free, | ||
167 | .calloc = calloc, | ||
168 | .realloc = realloc, | ||
169 | .log = platform_log, | ||
170 | .sleep = platform_sleep, | ||
171 | }; | ||
diff --git a/src/shorthand.h b/src/shorthand.h new file mode 100644 index 0000000..9c2e2f0 --- /dev/null +++ b/src/shorthand.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #ifndef MIC_SHORTHAND_H | ||
2 | #define MIC_SHORTHAND_H | ||
3 | |||
4 | #include <assert.h> | ||
5 | #include <stdbool.h> | ||
6 | #include <stddef.h> | ||
7 | #include <stdint.h> | ||
8 | |||
9 | // | ||
10 | // This simple header just typedefs the basic C define types to a shorter name, | ||
11 | // loads the quality of life bool macro for _Bool and defines shorthand macros | ||
12 | // for byte sizes. We need that the targeted architecture uses the floating | ||
13 | // point representation as described on the IEEE-754 standard. | ||
14 | // | ||
15 | |||
16 | _Static_assert(sizeof(double) == 8, "no support for IEEE-754"); | ||
17 | _Static_assert(sizeof(float) == 4, "no support for IEEE-754"); | ||
18 | |||
19 | typedef uint8_t u8; | ||
20 | typedef uint16_t u16; | ||
21 | typedef uint32_t u32; | ||
22 | typedef uint64_t u64; | ||
23 | typedef int8_t s8; | ||
24 | typedef int16_t s16; | ||
25 | typedef int32_t s32; | ||
26 | typedef int64_t s64; | ||
27 | typedef float f32; | ||
28 | typedef double f64; | ||
29 | |||
30 | #define KB(N) ((u64)(N) * 1024) | ||
31 | #define MB(N) ((u64)KB(N) * 1024) | ||
32 | #define GB(N) ((u64)MB(N) * 1024) | ||
33 | #define TB(N) ((u64)GB(N) * 1024) | ||
34 | |||
35 | #endif // MIC_SHORTHAND_H | ||