diff --git a/build.bat b/build.bat index e05bcf3..3f03830 100644 --- a/build.bat +++ b/build.bat @@ -4,6 +4,6 @@ mkdir build pushd build -cl ../build.c /Zi /Fecgame.exe /Od /DDEBUG /MD /DEBUG /std:c11 +cl.exe /Fe:cgame.exe ..\build.c /Od /std:c11 /W4 /wd4273 /wd4018 /wd4100 /Zi gdi32.lib user32.lib opengl32.lib popd \ No newline at end of file diff --git a/build.c b/build.c index 177a155..7a37ad7 100644 --- a/build.c +++ b/build.c @@ -2,9 +2,11 @@ /// // Build config stuff -#define VERY_DEBUG 0 #define RUN_TESTS 0 +// When we need very debug +// #define CONFIGURATION VERY_DEBUG + typedef struct Context_Extra { int monkee; } Context_Extra; diff --git a/build_clang.bat b/build_clang.bat index fb669b4..8a5df6d 100644 --- a/build_clang.bat +++ b/build_clang.bat @@ -4,6 +4,6 @@ mkdir build pushd build -clang -g -o cgame.exe ../build.c -O0 -DDEBUG -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32 +clang -g -o cgame.exe ../build.c -O0 -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32 popd \ No newline at end of file diff --git a/build_release.bat b/build_release.bat index c21a8e9..91385a3 100644 --- a/build_release.bat +++ b/build_release.bat @@ -7,7 +7,7 @@ pushd build mkdir release pushd release -cl ../../build.c /Zi /Fecgame.exe /Ox /DRELEASE /MD /std:c11 +cl.exe /Fe:cgame.exe ..\..\build.c /Ox /std:c11 /W4 /wd4273 /wd4018 /wd4100 gdi32.lib user32.lib opengl32.lib popd popd \ No newline at end of file diff --git a/build_release_clang.bat b/build_release_clang.bat index 39ad891..3caa7dc 100644 --- a/build_release_clang.bat +++ b/build_release_clang.bat @@ -7,7 +7,7 @@ pushd build mkdir release pushd release -clang -o cgame.exe ../../build.c -Ofast -DRELEASE -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32 +clang -o cgame.exe ../../build.c -Ofast -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32 popd popd \ No newline at end of file diff --git a/entry.c b/entry.c index 770603c..ea354af 100644 --- a/entry.c +++ b/entry.c @@ -15,15 +15,30 @@ int start(int argc, char **argv) { reset_temporary_storage(); context.allocator = temp; - os_update(); + os_update(); float64 now = os_get_current_time_in_seconds(); float64 delta = now - last_time; last_time = now; + + if (is_key_just_released(KEY_ESCAPE)) { + window.should_close = true; + } + + if (is_key_just_released('E')) { + print("Mouse pos: %f, %f\n", input_frame.mouse_x, input_frame.mouse_y); + } + + for (u64 i = 0; i < input_frame.number_of_events; i++) { + Input_Event e = input_frame.events[i]; - // Print some random FPS samples every now and then - if ((get_random() % 4000) == 3) - print("%2.f FPS\n", 1.0/delta); + switch (e.kind) { + case INPUT_EVENT_KEY: print("Key event key code %d\n", e.key_code); break; + case INPUT_EVENT_SCROLL: print("Scroll event, x: %.3f, y: %.3f\n", e.xscroll, e.yscroll); break; + case INPUT_EVENT_TEXT: print("Text event, utf32: %d, ascii: %c\n", e.utf32, e.ascii); break; + } + } + draw_rect_rotated(v2(-.25f, -.25f), v2(.5f, .5f), COLOR_RED, v2(.25, .25), (f32)now); diff --git a/oogabooga/base.c b/oogabooga/base.c index 04c5f1e..e75e29a 100644 --- a/oogabooga/base.c +++ b/oogabooga/base.c @@ -21,6 +21,8 @@ typedef u8 bool; #define thread_local _Thread_local +#define local_persist static + #define ifnt(x) if (!(x)) #ifdef _MSC_VER @@ -40,17 +42,20 @@ void printf(const char* fmt, ...); #define assert_line(line, cond, ...) if (!(cond)) { printf("Assertion failed in file " __FILE__ " on line " STR(line) "\nFailed Condition: " #cond ". Message: " __VA_ARGS__); os_break(); } #define assert(cond, ...) assert_line(__LINE__, cond, __VA_ARGS__); -#ifdef RELEASE +#if CONFIGRATION == RELEASE #undef assert #define assert(...) #endif +#define panic(...) { print(__VA_ARGS__); os_break(); } + #define cast(t) (t) #define ZERO(t) (t){0} /// // Compiler specific stuff +// We make inline actually inline. #ifdef _MSC_VER // Microsoft Visual C++ #define inline __forceinline diff --git a/oogabooga/input.c b/oogabooga/input.c index e69de29..36ff98e 100644 --- a/oogabooga/input.c +++ b/oogabooga/input.c @@ -0,0 +1,177 @@ +/* + + Main input juice: + + bool is_key_down(Input_Key_Code code); + bool is_key_up(Input_Key_Code code); + bool is_key_just_pressed(Input_Key_Code code); + bool is_key_just_released(Input_Key_Code code); + + bool consume_key_down(Input_Key_Code code); + bool consume_key_just_pressed(Input_Key_Code code); + bool consume_key_just_released(Input_Key_Code code); + + // To loop through events this frame + for (u64 i = 0; i < input_frame.number_of_events; i++) { + Input_Event e = input_frame.events[i]; + + switch (e.kind) { + case INPUT_EVENT_KEY: ...; break; + case INPUT_EVENT_SCROLL: ...; break; + case INPUT_EVENT_TEXT: ...; break; + case INPUT_EVENT_CLOSE: ...; break; + } + } +*/ + +typedef enum Input_Event_Kind { + INPUT_EVENT_KEY, + INPUT_EVENT_SCROLL, + INPUT_EVENT_TEXT, +} Input_Event_Kind; + +typedef enum Input_Key_Code { + KEY_UNKNOWN = 0, + + // Non-textual keys that have placements in the ASCII table + // (and thus in Unicode): + + KEY_BACKSPACE = 8, + KEY_TAB = 9, + KEY_ENTER = 13, + KEY_ESCAPE = 27, + KEY_SPACEBAR = 32, + + // The letters A-Z live in here as well and may be returned + // by keyboard events. + + KEY_DELETE = 127, + + KEY_ARROW_UP = 128, + KEY_ARROW_DOWN = 129, + KEY_ARROW_LEFT = 130, + KEY_ARROW_RIGHT = 131, + + KEY_PAGE_UP = 132, + KEY_PAGE_DOWN = 133, + + KEY_HOME = 134, + KEY_END = 135, + + KEY_INSERT = 136, + + KEY_PAUSE = 137, + KEY_SCROLL_LOCK = 138, + + KEY_ALT, + KEY_CTRL, + KEY_SHIFT, + KEY_CMD, + KEY_META = KEY_CMD, + + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + + KEY_PRINT_SCREEN, + + MOUSE_BUTTON_LEFT, + MOUSE_BUTTON_MIDDLE, + MOUSE_BUTTON_RIGHT, + + MOUSE_FIRST = MOUSE_BUTTON_LEFT, + MOUSE_LAST = MOUSE_BUTTON_RIGHT, + + INPUT_KEY_CODE_COUNT +} Input_Key_Code; + +typedef enum Input_State_Flags { + INPUT_STATE_DOWN = 1<<0, + INPUT_STATE_JUST_PRESSED = 1<<1, + INPUT_STATE_JUST_RELEASED = 1<<2, + INPUT_STATE_REPEAT = 1<<3, +} Input_State_Flags; + +typedef struct Input_Event { + Input_Event_Kind kind; + + // For INPUT_EVENT_KEY + Input_Key_Code key_code; + Input_State_Flags key_state; + + // For INPUT_EVENT_SCROLL + float64 xscroll; + float64 yscroll; + + // For INPUT_EVENT_TEXT_INPUT + union { u32 utf32; char ascii; }; + +} Input_Event; + +Input_Key_Code os_key_to_key_code(void* os_key); +void* key_code_to_os_key(Input_Key_Code key_code); + +#define MAX_EVENTS_PER_FRAME 10000 +typedef struct Input_Frame { + Input_Event events[MAX_EVENTS_PER_FRAME]; + u64 number_of_events; + + float32 mouse_x; + float32 mouse_y; + + Input_State_Flags key_states[INPUT_KEY_CODE_COUNT]; + +} Input_Frame; +Input_Frame input_frame = ZERO(Input_Frame); + +bool has_key_state(Input_Key_Code code, Input_State_Flags flags) { + assert(code > 0 && code < INPUT_KEY_CODE_COUNT, "Invalid key code %d!", code); + Input_State_Flags state = input_frame.key_states[code]; + +#if CONFIGURATION == DEBUG + { + Input_State_Flags impossible = (INPUT_STATE_JUST_RELEASED | INPUT_STATE_DOWN); + assert((impossible & state) != impossible, "Key state for key '%d' is corrupt!", code); + impossible = (INPUT_STATE_JUST_RELEASED | INPUT_STATE_JUST_PRESSED); + assert((impossible & state) != impossible, "Key state for key '%d' is corrupt!", code); + } +#endif + return (state & flags) == flags; +} +bool is_key_down(Input_Key_Code code) { + return has_key_state(code, INPUT_STATE_DOWN); +} +bool is_key_up(Input_Key_Code code) { + return input_frame.key_states[code] == 0 || has_key_state(code, INPUT_STATE_JUST_RELEASED); +} +bool is_key_just_pressed(Input_Key_Code code) { + return has_key_state(code, INPUT_STATE_JUST_PRESSED); +} +bool is_key_just_released(Input_Key_Code code) { + return has_key_state(code, INPUT_STATE_JUST_RELEASED); +} + +bool consume_key_down(Input_Key_Code code) { + bool result = is_key_down(code); + input_frame.key_states[code] &= ~(INPUT_STATE_DOWN); + return result; +} +bool consume_key_just_pressed(Input_Key_Code code) { + bool result = is_key_just_pressed(code); + input_frame.key_states[code] &= ~(INPUT_STATE_JUST_PRESSED); + return result; +} +bool consume_key_just_released(Input_Key_Code code) { + bool result = is_key_just_released(code); + input_frame.key_states[code] &= ~(INPUT_STATE_JUST_RELEASED); + return result; +} \ No newline at end of file diff --git a/oogabooga/oogabooga.c b/oogabooga/oogabooga.c index d92b91c..4559f6e 100644 --- a/oogabooga/oogabooga.c +++ b/oogabooga/oogabooga.c @@ -25,12 +25,16 @@ void lodepng_free(void* ptr) { ///// -#if !defined(DEBUG) && !defined(RELEASE) +#define DEBUG 0 +#define VERY_DEBUG 1 +#define RELEASE 2 + +#if !defined(CONFIGURATION) #ifdef _DEBUG - #define DEBUG + #define CONFIGURATION DEBUG #elif defined(NDEBUG) - #define RELEASE + #define CONFIGURATION RELEASE #endif #endif @@ -76,6 +80,7 @@ void lodepng_free(void* ptr) { #include "string.c" +#include "unicode.c" #include "string_format.c" #include "os_interface.c" diff --git a/oogabooga/os_impl_windows.c b/oogabooga/os_impl_windows.c index 52ee079..c2b9ea9 100644 --- a/oogabooga/os_impl_windows.c +++ b/oogabooga/os_impl_windows.c @@ -5,8 +5,49 @@ void* heap_alloc(u64); void heap_dealloc(void*); +// Persistent +Input_State_Flags win32_key_states[INPUT_KEY_CODE_COUNT]; +void win32_send_key_event(Input_Key_Code code, Input_State_Flags state) { + Input_Event e; + e.kind = INPUT_EVENT_KEY; + e.key_code = code; + e.key_state = state; + input_frame.events[input_frame.number_of_events] = e; + input_frame.number_of_events += 1; +} +void win32_handle_key_up(Input_Key_Code code) { + if (code == KEY_UNKNOWN) return; + + Input_State_Flags last_state = win32_key_states[code]; + Input_State_Flags state = 0; + + if (last_state & INPUT_STATE_DOWN) state |= INPUT_STATE_JUST_RELEASED; + + win32_key_states[code] = state; + + win32_send_key_event(code, state); +} +void win32_handle_key_down(Input_Key_Code code) { + if (code == KEY_UNKNOWN) return; + + Input_State_Flags last_state = win32_key_states[code]; + Input_State_Flags state = INPUT_STATE_DOWN; + + if (!(last_state & INPUT_STATE_DOWN)) state |= INPUT_STATE_JUST_PRESSED; + + win32_key_states[code] = state; + + win32_send_key_event(code, state); +} +void win32_handle_key_repeat(Input_Key_Code code) { + if (code == KEY_UNKNOWN) return; + + win32_key_states[code] |= INPUT_STATE_REPEAT; + + win32_send_key_event(code, win32_key_states[code]); +} LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wparam, LPARAM lparam) { if (window._initialized) { @@ -15,12 +56,76 @@ LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wpar switch (message) { case WM_CLOSE: + window.should_close = true; DestroyWindow(window._os_handle); + break; case WM_DESTROY: PostQuitMessage(0); break; + case WM_KEYDOWN: + bool is_repeat = (lparam & 0x40000000) != 0; + + if (is_repeat) win32_handle_key_repeat(os_key_to_key_code((void*)wparam)); + else win32_handle_key_down (os_key_to_key_code((void*)wparam)); + goto DEFAULT_HANDLE; + case WM_KEYUP: + win32_handle_key_up(os_key_to_key_code((void*)wparam)); + goto DEFAULT_HANDLE; + case WM_LBUTTONDOWN: + win32_handle_key_down(MOUSE_BUTTON_LEFT); + goto DEFAULT_HANDLE; + case WM_RBUTTONDOWN: + win32_handle_key_down(MOUSE_BUTTON_RIGHT); + goto DEFAULT_HANDLE; + case WM_MBUTTONDOWN: + win32_handle_key_down(MOUSE_BUTTON_MIDDLE); + goto DEFAULT_HANDLE; + case WM_LBUTTONUP: + win32_handle_key_up(MOUSE_BUTTON_LEFT); + goto DEFAULT_HANDLE; + case WM_RBUTTONUP: + win32_handle_key_up(MOUSE_BUTTON_RIGHT); + goto DEFAULT_HANDLE; + case WM_MBUTTONUP: + win32_handle_key_up(MOUSE_BUTTON_MIDDLE); + goto DEFAULT_HANDLE; + case WM_MOUSEWHEEL: { + int delta = GET_WHEEL_DELTA_WPARAM(wparam); + int ticks = delta / WHEEL_DELTA; + Input_Event e; + e.kind = INPUT_EVENT_SCROLL; + e.yscroll = (float64)delta/(float64)WHEEL_DELTA; + e.xscroll = 0; + input_frame.events[input_frame.number_of_events] = e; + input_frame.number_of_events += 1; + goto DEFAULT_HANDLE; + } + case WM_MOUSEHWHEEL: { + int delta = GET_WHEEL_DELTA_WPARAM(wparam); + Input_Event e; + e.kind = INPUT_EVENT_SCROLL; + e.yscroll = 0; + e.xscroll = (float64)delta/(float64)WHEEL_DELTA; + input_frame.events[input_frame.number_of_events] = e; + input_frame.number_of_events += 1; + goto DEFAULT_HANDLE; + } + case WM_CHAR: { + wchar_t utf16 = (wchar_t)wparam; + + Input_Event e; + e.kind = INPUT_EVENT_TEXT; + utf16_to_utf32(&utf16, 1, &e.utf32); + + input_frame.events[input_frame.number_of_events] = e; + input_frame.number_of_events += 1; + + goto DEFAULT_HANDLE; + } default: + + DEFAULT_HANDLE: return DefWindowProc(passed_window, message, wparam, lparam); } return 0; @@ -346,7 +451,7 @@ bool os_compare_and_swap_bool(bool *a, bool b, bool old) { void os_update() { - static Os_Window last_window; + local_persist Os_Window last_window; if (!strings_match(last_window.title, window.title)) { SetWindowText(window._os_handle, temp_convert_to_null_terminated_string(window.title)); @@ -400,9 +505,20 @@ void os_update() { last_window = window; - + // Reflect what the backend did to input state before we query for OS inputs + memcpy(win32_key_states, input_frame.key_states, sizeof(input_frame.key_states)); + input_frame.number_of_events = 0; + + + for (u64 i = 0; i < INPUT_KEY_CODE_COUNT; i++) { + win32_key_states[i] &= ~(INPUT_STATE_REPEAT); + win32_key_states[i] &= ~(INPUT_STATE_JUST_PRESSED); + win32_key_states[i] &= ~(INPUT_STATE_JUST_RELEASED); + } + MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + while (input_frame.number_of_events < MAX_EVENTS_PER_FRAME + && PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { window.should_close = true; break; @@ -410,4 +526,128 @@ void os_update() { TranslateMessage(&msg); DispatchMessage(&msg); } + + memcpy(input_frame.key_states, win32_key_states, sizeof(input_frame.key_states)); + POINT p; + GetCursorPos(&p); + ScreenToClient(window._os_handle, &p); + p.y = window.height - p.y; + input_frame.mouse_x = (float32)p.x; + input_frame.mouse_y = (float32)p.y; + + if (window.should_close) { + win32_window_proc(window._os_handle, WM_CLOSE, 0, 0); + } +} + +Input_Key_Code os_key_to_key_code(void* os_key) { + + UINT win32_key = (UINT)(u64)os_key; + + if (win32_key >= 'A' && win32_key <= 'Z') { + return (Input_Key_Code)win32_key; + } + if (win32_key >= '0' && win32_key <= '9') { + return (Input_Key_Code)win32_key; + } + + switch (win32_key) { + case VK_BACK: return KEY_BACKSPACE; + case VK_TAB: return KEY_TAB; + case VK_RETURN: return KEY_ENTER; + case VK_ESCAPE: return KEY_ESCAPE; + case VK_SPACE: return KEY_SPACEBAR; + case VK_DELETE: return KEY_DELETE; + case VK_UP: return KEY_ARROW_UP; + case VK_DOWN: return KEY_ARROW_DOWN; + case VK_LEFT: return KEY_ARROW_LEFT; + case VK_RIGHT: return KEY_ARROW_RIGHT; + case VK_PRIOR: return KEY_PAGE_UP; + case VK_NEXT: return KEY_PAGE_DOWN; + case VK_HOME: return KEY_HOME; + case VK_END: return KEY_END; + case VK_INSERT: return KEY_INSERT; + case VK_PAUSE: return KEY_PAUSE; + case VK_SCROLL: return KEY_SCROLL_LOCK; + case VK_MENU: return KEY_ALT; + case VK_CONTROL: return KEY_CTRL; + case VK_SHIFT: return KEY_SHIFT; + case VK_LWIN: return KEY_CMD; + case VK_RWIN: return KEY_CMD; + case VK_F1: return KEY_F1; + case VK_F2: return KEY_F2; + case VK_F3: return KEY_F3; + case VK_F4: return KEY_F4; + case VK_F5: return KEY_F5; + case VK_F6: return KEY_F6; + case VK_F7: return KEY_F7; + case VK_F8: return KEY_F8; + case VK_F9: return KEY_F9; + case VK_F10: return KEY_F10; + case VK_F11: return KEY_F11; + case VK_F12: return KEY_F12; + case VK_SNAPSHOT: return KEY_PRINT_SCREEN; + case VK_LBUTTON: return MOUSE_BUTTON_LEFT; + case VK_MBUTTON: return MOUSE_BUTTON_MIDDLE; + case VK_RBUTTON: return MOUSE_BUTTON_RIGHT; + default: return KEY_UNKNOWN; + } +} + +void* key_code_to_os_key(Input_Key_Code key_code) { + + if (key_code >= 'A' && key_code <= 'Z') { + return (void*)key_code; + } + if (key_code >= '0' && key_code <= '9') { + return (void*)key_code; + } + + switch (key_code) { + case KEY_BACKSPACE: return (void*)VK_BACK; + case KEY_TAB: return (void*)VK_TAB; + case KEY_ENTER: return (void*)VK_RETURN; + case KEY_ESCAPE: return (void*)VK_ESCAPE; + case KEY_SPACEBAR: return (void*)VK_SPACE; + case KEY_DELETE: return (void*)VK_DELETE; + case KEY_ARROW_UP: return (void*)VK_UP; + case KEY_ARROW_DOWN: return (void*)VK_DOWN; + case KEY_ARROW_LEFT: return (void*)VK_LEFT; + case KEY_ARROW_RIGHT: return (void*)VK_RIGHT; + case KEY_PAGE_UP: return (void*)VK_PRIOR; + case KEY_PAGE_DOWN: return (void*)VK_NEXT; + case KEY_HOME: return (void*)VK_HOME; + case KEY_END: return (void*)VK_END; + case KEY_INSERT: return (void*)VK_INSERT; + case KEY_PAUSE: return (void*)VK_PAUSE; + case KEY_SCROLL_LOCK: return (void*)VK_SCROLL; + case KEY_ALT: return (void*)VK_MENU; + case KEY_CTRL: return (void*)VK_CONTROL; + case KEY_SHIFT: return (void*)VK_SHIFT; + case KEY_CMD: return (void*)VK_LWIN; // Assuming left win key for both + case KEY_F1: return (void*)VK_F1; + case KEY_F2: return (void*)VK_F2; + case KEY_F3: return (void*)VK_F3; + case KEY_F4: return (void*)VK_F4; + case KEY_F5: return (void*)VK_F5; + case KEY_F6: return (void*)VK_F6; + case KEY_F7: return (void*)VK_F7; + case KEY_F8: return (void*)VK_F8; + case KEY_F9: return (void*)VK_F9; + case KEY_F10: return (void*)VK_F10; + case KEY_F11: return (void*)VK_F11; + case KEY_F12: return (void*)VK_F12; + case KEY_PRINT_SCREEN: return (void*)VK_SNAPSHOT; + case MOUSE_BUTTON_LEFT: return (void*)VK_LBUTTON; + case MOUSE_BUTTON_MIDDLE: return (void*)VK_MBUTTON; + case MOUSE_BUTTON_RIGHT: return (void*)VK_RBUTTON; + + + case INPUT_KEY_CODE_COUNT: + case KEY_UNKNOWN: + break; + } + + panic("Invalid key code %d", key_code); + return 0; } \ No newline at end of file diff --git a/oogabooga/os_interface.c b/oogabooga/os_interface.c index dc8fc4a..54346c5 100644 --- a/oogabooga/os_interface.c +++ b/oogabooga/os_interface.c @@ -58,7 +58,7 @@ inline int crt_vprintf(const char* fmt, va_list args) { return os.crt_vprintf(fmt, args); } -#if !defined(COMPILER_HAS_MEMCPY_INTRINSICS) || defined(DEBUG) +#if !defined(COMPILER_HAS_MEMCPY_INTRINSICS) || CONFIGURATION == DEBUG inline void* naive_memcpy(void* dest, const void* source, size_t size) { for (u64 i = 0; i < (u64)size; i++) ((u8*)dest)[i] = ((u8*)source)[i]; return dest; @@ -139,7 +139,7 @@ void os_lock_mutex(Mutex_Handle m); void os_unlock_mutex(Mutex_Handle m); /// -// Spinlock primitive +// Spinlock "primitive" typedef struct Spinlock { bool locked; } Spinlock; diff --git a/oogabooga/string.c b/oogabooga/string.c index 818efa3..140575a 100644 --- a/oogabooga/string.c +++ b/oogabooga/string.c @@ -11,7 +11,7 @@ Usage: string balls = const_string("balls"); // tprint for temporary allocation - string b = tprint(const_string("Hello, %s!\n"), balls); // %s for string + string b = tprint("Hello, %s!\n", balls); // %s for string // Allocate a new string of length 12 (with context allocator) string c = alloc_string(12); diff --git a/oogabooga/unicode.c b/oogabooga/unicode.c new file mode 100644 index 0000000..a9af51b --- /dev/null +++ b/oogabooga/unicode.c @@ -0,0 +1,38 @@ + + +#define UTF16_SURROGATE_HIGH_START 0xD800 +#define UTF16_SURROGATE_HIGH_END 0xDBFF +#define UTF16_SURROGATE_LOW_START 0xDC00 +#define UTF16_SURROGATE_LOW_END 0xDFFF +#define UTF16_SURROGATE_OFFSET 0x10000 +#define UTF16_SURROGATE_MASK 0x3FF + +// Returns how many utf16 units were converted +int utf16_to_utf32(const u16 *utf16, u64 length, u32 *utf32) { + if (length == 0 || utf16 == NULL || utf32 == NULL) { + return -1; + } + + u16 first = utf16[0]; + + if (first >= UTF16_SURROGATE_HIGH_START && first <= UTF16_SURROGATE_HIGH_END) { + if (length < 2) { + return -1; + } + + u16 second = utf16[1]; + if (second >= UTF16_SURROGATE_LOW_START && second <= UTF16_SURROGATE_LOW_END) { + *utf32 = ((first - UTF16_SURROGATE_HIGH_START) << 10) + + (second - UTF16_SURROGATE_LOW_START) + + UTF16_SURROGATE_OFFSET; + return 2; + } else { + return -1; + } + } else if (first >= UTF16_SURROGATE_LOW_START && first <= UTF16_SURROGATE_LOW_END) { + return -1; + } else { + *utf32 = first; + return 1; + } +} \ No newline at end of file