diff --git a/README.md b/README.md index b1cca10..92662ed 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,10 @@ the platform layer prefix. - [x] Add sys_fd for handle - [ ] Class and Title sys_char* -- [ ] Assertions & Testing +- [x] Assertions & Testing + - [x] Static Assertions + - [x] Basic Assertions + - [ ] Add failure flag to cause failure. - [ ] Exceptions & Errors - [ ] Memory Allocations - [ ] Arena Allocations diff --git a/include/ci2.h b/include/ci2.h index 9886612..09a9fd0 100644 --- a/include/ci2.h +++ b/include/ci2.h @@ -24,7 +24,7 @@ SOFTWARE. #define CI2_H #include "ci2_window.h" // Includes base - +#include "ci2_exception.h" #endif // ci2.h diff --git a/include/ci2_exception.h b/include/ci2_exception.h new file mode 100644 index 0000000..6f132e3 --- /dev/null +++ b/include/ci2_exception.h @@ -0,0 +1,128 @@ +/* - | Copyright | -------------------------------------------------------------- + Copyright (c) 2026 Randy Jordan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + * --------------------------------------------------------------------------*/ +#ifndef CI2_EXCEPTIONS_H +#define CI2_EXCEPTIONS_H + +#include "./base/ci2_base.h" // CURRENT_FUNC macro +#include +#include +extern int errno; + +struct CI2_Exception { + int num; + const char *msg; +}; +typedef struct CI2_Exception CI2_Exception; + +struct CI2_Exception_Frame { + struct CI2_Exception_Frame *prev; + jmp_buf env; + const char *func; + const char *file; + int line; + const struct CI2_Exception *exception; +}; +typedef struct CI2_Exception_Frame CI2_Exception_Frame; + +enum { CI2_EXCEPT_STATE_ENTERED= 0, + CI2_EXCEPT_STATE_RAISED, + CI2_EXCEPT_STATE_HANDLED, + CI2_EXCEPT_STATE_FINALIZED, + CI2_EXCEPT_STATE_COUNT +}; + +extern const struct CI2_Exception runtime_assertion_failed; +extern struct CI2_Exception_Frame *ci2_exception_stack; +extern void ci2_raise_exception(const struct CI2_Exception *e, const char *file, int line, const char *func); +extern void ci2_rt_assert(int e); + +#ifdef CI2_WINDOWS + +extern DWORD ci2_exception_index; +extern void ci2_exception_init(void); +extern void ci2_exception_push(struct CI2_Exception_Frame *fp); +extern void ci2_exception_pop(void); + +#define CI2_RAISE(e) ci2_raise_exception(&(e), __FILE__, __LINE__, CURRENT_FUNC) +#define CI2_RERAISE ci2_raise_exception(exception_frame.exception, \ + exception_frame.file, exception_frame.line, CURRENT_FUNC) +#define CI2_RETURN switch (ci2_exception_pop(),0) default: return +#define CI2_TRY do { \ + volatile int ci2_exception_flag; \ + CI2_Exception_Frame exception_frame; \ + if (exception_index == TLS_OUT_OF_INDEXES) \ + ci2_exception_init(); \ + ci2_exception_push(&exception_frame); \ + ci2_exception_flag = setjmp(exception_frame.env); \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) { +#define CI2_EXCEPT(e) \ + if (ci2_except_flag == CI2_EXCEPT_STATE_ENTERED) ci2_exception_pop(); \ + } else if (exception_frame.exception == &(e)) { \ + ci2_exception_flag = CI2_EXCEPT_STATE_HANDLED; +#define CI2_ELSE \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) ci2_exception_pop(); \ + } else { \ + ci2_exception_flag = CI2_EXCEPT_STATE_HANDLED; +#define CI2_FINALLY \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) ci2_exception_pop(); \ + } { \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) \ + ci2_exception_flag = CI2_EXCEPT_STATE_FINALIZED; +#define CI2_END_TRY \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) ci2_exception_pop(); \ + } if (ci2_exception_flag == CI2_EXCEPT_STATE_RAISED) CI2_RERAISE; \ +} while (0) +#else // UNIX +#define CI2_RAISE(e) ci2_raise_exception(&(e), __FILE__, __LINE__, CURRENT_FUNC) +#define CI2_RERAISE ci2_raise_exception(exception_frame.exception, \ + exception_frame.file, exception_frame.line, CURRENT_FUNC) +#define CI2_RETURN switch (ci2_exception_stack = ci2_exception_stack->prev,0) default: return +#define CI2_TRY do { \ + volatile int ci2_exception_flag; \ + CI2_Exception_Frame exception_frame; \ + exception_frame.prev = ci2_exception_stack; \ + ci2_exception_stack = &exception_frame; \ + ci2_exception_flag = setjmp(exception_frame.env); \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) { +#define CI2_EXCEPT(e) \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) ci2_exception_stack = ci2_exception_stack->prev; \ + } else if (exception_frame.exception == &(e)) { \ + ci2_exception_flag = CI2_EXCEPT_STATE_HANDLED; +#define CI2_ELSE \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) ci2_exception_stack = ci2_exception_stack->prev; \ + } else { \ + ci2_exception_flag = CI2_EXCEPT_STATE_HANDLED; +#define CI2_FINALLY \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) ci2_exception_stack = ci2_exception_stack->prev; \ + } { \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) \ + ci2_exception_flag = CI2_EXCEPT_STATE_FINALIZED; +#define CI2_END_TRY \ + if (ci2_exception_flag == CI2_EXCEPT_STATE_ENTERED) ci2_exception_stack = ci2_exception_stack->prev; \ + } if (ci2_exception_flag == CI2_EXCEPT_STATE_RAISED) CI2_RERAISE; \ +} while (0) +#endif + + +#endif // ci2_exceptions.h + diff --git a/src/linux_ci2_exceptions.c b/src/linux_ci2_exceptions.c new file mode 100644 index 0000000..715256b --- /dev/null +++ b/src/linux_ci2_exceptions.c @@ -0,0 +1,62 @@ +/* - | Copyright | -------------------------------------------------------------- + Copyright (c) 2026 Randy Jordan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + * --------------------------------------------------------------------------*/ +#include "../include/ci2_exception.h" + +struct CI2_Exception_Frame *ci2_exception_stack = NULL; +const struct CI2_Exception runtime_assertion_failed = { EFAULT , "CI2 Runtime Assertion Failed" }; + +void ci2_rt_assert(int e){ + if(!e){ + CI2_RAISE(runtime_assertion_failed); + } +} + +void ci2_raise_exception(const struct CI2_Exception *e, const char *file,int line, const char *func){ + + struct CI2_Exception_Frame *p = ci2_exception_stack; + CI2_ASSERT(e != NULL); + if (p == NULL) { + fprintf(stderr, "CI2 Uncaught Exception"); + if(e->num > 0) + fprintf(stderr," %d ",e->num); + if (e->msg) + fprintf(stderr, "| %s | ", e->msg); + if( !e->num || !e->msg ) + fprintf(stderr, "at address 0x%p", (void*)e); + if (file && line > 0) + fprintf(stderr, "raised at line %d in file %s", line, file); + if(func) + printf(" by the function %s()\n", func); + fflush(stderr); + CI2_DEBUG_BREAK(); + } + p->exception = e; + p->file = file; + p->line = line; + p->func = func; + + ci2_exception_stack = ci2_exception_stack->prev; + longjmp(p->env, CI2_EXCEPT_STATE_RAISED); +} + + diff --git a/src/win32_ci2_exceptions.c b/src/win32_ci2_exceptions.c new file mode 100644 index 0000000..8109d45 --- /dev/null +++ b/src/win32_ci2_exceptions.c @@ -0,0 +1,97 @@ +/* - | Copyright | -------------------------------------------------------------- + Copyright (c) 2026 Randy Jordan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + * --------------------------------------------------------------------------*/ +#include "../include/ci2_exception.h" +#include "../include/ci2_assert.h" +// #include + +DWORD ci2_exception_index = -1; +const struct CI2_Exception runtime_assertion_failed = { EFAULT, "CI2 Runtime Assertion Failed" }; + +void ci2_rt_assert(int e){ + if(!e){ + CI2_RAISE(runtime_assertion_failed); + } +} + +#ifdef CI2_OS_WINDOWS +_CRTIMP void __cdecl _assert(void *, void *, unsigned); +#undef assert +#define ci2_rt_assert(e) ((e) || (_assert(#e, __FILE__, __LINE__), 0)) +#endif + +void ci2_raise_exception(const struct CI2_Exception *e, const char *file, int line, const char *func){ + struct CI2_Exception_Frame *p; + + if (ci2_exception_index == TLS_OUT_OF_INDEXES) + ci2_exception_init(); + p = TlsGetValue(ci2_exception_index); + CI2_ASSERT(e != NULL); + if (p == NULL) { + if (p == NULL) { + fprintf(stderr, "CI2 Uncaught Exception"); + if(e->num > 0) + fprintf(stderr," %d ",e->num); + if (e->msg) + fprintf(stderr, "| %s | ", e->msg); + if( !e->num || !e->msg ) + fprintf(stderr, "at address 0x%p", (void*)e); + if (file && line > 0) + fprintf(stderr, "raised at line %d in file %s", line, file); + if(func) + printf(" by the function %s()\n", func); + fflush(stderr); + CI2_DEBUG_BREAK(); + } + } + + p->exception = e; + p->file = file; + p->line = line; + p->func = func; + + ci2_exception_pop(); + longjmp(p->env, CI2_EXCEPT_STATE_RAISED); +} + +void ci2_exception_init(void) { + BOOL cond; + ci2_exception_index = TlsAlloc(); + CI2_ASSERT(ci2_exception_index != TLS_OUT_OF_INDEXES); + cond = TlsSetValue(ci2_exception_index, NULL); + CI2_ASSERT(cond == TRUE); +} + +void ci2_exception_push(struct CI2_Exception_Frame *fp) { + BOOL cond; + fp->prev = TlsGetValue(ci2_exception_index); + cond = TlsSetValue(ci2_exception_index, fp); + CI2_ASSERT(cond == TRUE); +} + +void ci2_exception_pop(void) { + BOOL cond; + struct CI2_Exception_Frame *tos = TlsGetValue(ci2_exception_index); + cond = TlsSetValue(ci2_exception_index, tos->prev); + CI2_ASSERT(cond == TRUE); +} + diff --git a/tests/01_static_asserts.c b/tests/01_static_asserts.c index e5faa98..7ab646c 100644 --- a/tests/01_static_asserts.c +++ b/tests/01_static_asserts.c @@ -81,7 +81,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine // 4) Main loop: run until ci2_poll_events reports the window should close. // This loop is friendly to rendering or other per-frame work. - int result; + int result = -1; while (ci2_poll_events(window)) { // Per-frame update/render would go here. // Example: simple sleep to avoid busy-looping; adjust to suit your app's timing. @@ -110,7 +110,7 @@ int main(int argc, ci2_sys_char *argv[]) CI2_Window* window = ci2_create_window(ci2_window_title, 800, 600); if (!window) return 1; - int result; + int result = -1; while (ci2_poll_events(window)) { // Your rendering / game loop goes here result = ci2_static_assertions(); diff --git a/tests/02_ci2_assertion.c b/tests/02_ci2_assertion.c new file mode 100644 index 0000000..bfec894 --- /dev/null +++ b/tests/02_ci2_assertion.c @@ -0,0 +1,150 @@ +/* - | Copyright | -------------------------------------------------------------- + Copyright (c) 2026 Randy Jordan + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + * --------------------------------------------------------------------------*/ +#define DEBUG +#define GUI_DEBUG_CLOSE + +#include "../include/ci2.h" +#include +#include +static int test_assert_failing(void){ + #ifdef CI2_ASSERT_CRASH + printf("ASSERT_MSG — Deliberate Failure (expect a debug break)"); + printf(" Triggering ASSERT_MSG(0, ...) now...\n"); + fflush(stdout); + + CI2_ASSERT_MSG(0, "This failure is intentional — testing the message path"); + + printf(" [FAIL] Execution continued past a failing ASSERT_MSG!\n"); + #else + + return EXIT_SUCCESS; + #endif +} + +#ifdef CI2_WINDOWS +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + (void)hInstance; + (void)hPrevInstance; + (void)lpCmdLine; + (void) nShowCmd; + if (!ci2_init()) { + MessageBoxA(NULL, "ci2_init failed", "Error", MB_OK | MB_ICONERROR); + return EXIT_FAILURE; + } + static ci2_sys_char *ci2_window_title = L"Win32 CI2 Window Title"; + CI2_Window* window = ci2_create_window(ci2_window_title, 800, 600); + if (!window) { + MessageBoxA(NULL, "ci2_create_window failed", "Error", MB_OK | MB_ICONERROR); + ci2_shutdown(); + return EXIT_FAILURE; + } + + // 4) Main loop: run until ci2_poll_events reports the window should close. + // This loop is friendly to rendering or other per-frame work. + int result = -1; + while (ci2_poll_events(window)) { + // Per-frame update/render would go here. + // Example: simple sleep to avoid busy-looping; adjust to suit your app's timing. + + CI2_ASSERT(ci2_true); + CI2_ASSERT(1); + CI2_ASSERT(1 == 1); + + /* Pointer non-null */ + const char *str = "hello"; + CI2_ASSERT(str != NULL); + + /* Arithmetic */ + int x = 42; + CI2_ASSERT(x > 0); + CI2_ASSERT(x * 2 == 84); + + /* CI2_ASSERT_MSG variants */ + CI2_ASSERT_MSG(x == 42, "x must be 42"); + CI2_ASSERT_MSG(sizeof(int) == 4, "int must be 4 bytes on this platform"); + + /* Boolean */ + bool flag = true; + CI2_ASSERT(flag); + result = test_assert_failing(); + Sleep(1); + #ifdef GUI_DEBUG_CLOSE + break; + #endif + } + + // 5) Destroy window and shutdown ci2. + ci2_destroy_window(window); + ci2_shutdown(); + + return result; +} +#else /* non-Windows (Linux, macOS, etc.) */ +#include +int main(int argc, ci2_sys_char *argv[]) +{ + UNUSED(argc); + UNUSED(argv); + if (!ci2_init()) return 1; + + static ci2_sys_char *ci2_window_title = "Linux CI2 Window Title"; + CI2_Window* window = ci2_create_window(ci2_window_title, 800, 600); + if (!window) return 1; + + int result = -1; + while (ci2_poll_events(window)) { + // Your rendering / game loop goes here + CI2_ASSERT(ci2_true); + + CI2_ASSERT(1); + CI2_ASSERT(1 == 1); + + /* Pointer non-null */ + const char *str = "hello"; + CI2_ASSERT(str != NULL); + + /* Arithmetic */ + int x = 42; + CI2_ASSERT(x > 0); + CI2_ASSERT(x * 2 == 84); + + /* CI2_ASSERT_MSG variants */ + CI2_ASSERT_MSG(x == 42, "x must be 42"); + CI2_ASSERT_MSG(sizeof(int) == 4, "int must be 4 bytes on this platform"); + + /* Boolean */ + bool flag = true; + CI2_ASSERT(flag); + result = test_assert_failing(); + sleep(1); + #ifdef GUI_DEBUG_CLOSE + break; + #endif + } + + ci2_destroy_window(window); + ci2_shutdown(); + return result; +} +#endif diff --git a/tests/03_ci2_exceptions.c b/tests/03_ci2_exceptions.c new file mode 100644 index 0000000..a173302 --- /dev/null +++ b/tests/03_ci2_exceptions.c @@ -0,0 +1,137 @@ +/* - | Copyright | -------------------------------------------------------------- + Copyright (c) 2026 Randy Jordan + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + * --------------------------------------------------------------------------*/ +#define DEBUG +#define GUI_DEBUG_CLOSE + +#include "../include/ci2.h" +#include + +static int test_error_num; +const struct CI2_Exception e = { EOWNERDEAD , "Owner died"}; + +static void test_exception(){ + test_error_num = EOWNERDEAD; // Oops better revive them. + CI2_RAISE(e); +} +static int test_caught_exception(){ + CI2_TRY { + printf("Trying to catch a failure.\n"); + // Oops + test_exception(); + } + CI2_EXCEPT(e) { + /* handle error failure gracefully */ + fprintf(stderr, "Caught: %d: %s.\n", e.num, e.msg); + test_error_num = 0; + } + CI2_FINALLY { + /* Runs no matter what */ + printf("Hope we caught something.\n"); + } + CI2_END_TRY; + + return test_error_num; +} +static int test_uncaught_exception(void){ +#ifdef CI2_EXCEPTION_CRASH + printf("CI2_MSG — Deliberate Failure (expect a debug break)\n"); + printf(" Triggering ci2_raise_exception() now...\n"); + fflush(stdout); + + struct CI2_Exception u = { EFAULT, "Deliberate uncaught exception test."}; + CI2_RAISE(u); + printf(" [FAIL] Execution continued past a failing ASSERT_MSG!\n"); + return EXIT_SUCCESS; // Shouldn't but still. +#endif + return EXIT_SUCCESS; +} + +#ifdef CI2_WINDOWS +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + (void)hInstance; + (void)hPrevInstance; + (void)lpCmdLine; + (void) nShowCmd; + if (!ci2_init()) { + MessageBoxA(NULL, "ci2_init failed", "Error", MB_OK | MB_ICONERROR); + return EXIT_FAILURE; + } + static ci2_sys_char *ci2_window_title = L"Win32 CI2 Window Title"; + CI2_Window* window = ci2_create_window(ci2_window_title, 800, 600); + if (!window) { + MessageBoxA(NULL, "ci2_create_window failed", "Error", MB_OK | MB_ICONERROR); + ci2_shutdown(); + return EXIT_FAILURE; + } + + // 4) Main loop: run until ci2_poll_events reports the window should close. + // This loop is friendly to rendering or other per-frame work. + int result = -1; + while (ci2_poll_events(window)) { + // Per-frame update/render would go here. + // Example: simple sleep to avoid busy-looping; adjust to suit your app's timing. + + result = test_uncaught_exception(); + result = test_caught_exception(); + Sleep(1); + #ifdef GUI_DEBUG_CLOSE + break; + #endif + } + + // 5) Destroy window and shutdown ci2. + ci2_destroy_window(window); + ci2_shutdown(); + + return result; +} +#else /* non-Windows (Linux, macOS, etc.) */ +#include +int main(int argc, ci2_sys_char *argv[]) +{ + UNUSED(argc); + UNUSED(argv); + if (!ci2_init()) return 1; + + static ci2_sys_char *ci2_window_title = "Linux CI2 Window Title"; + CI2_Window* window = ci2_create_window(ci2_window_title, 800, 600); + if (!window) return 1; + + int result = -1; + while (ci2_poll_events(window)) { + // Your rendering / game loop goes here + + result = test_uncaught_exception(); + result = test_caught_exception(); + sleep(1); + #ifdef GUI_DEBUG_CLOSE + break; + #endif + } + + ci2_destroy_window(window); + ci2_shutdown(); + return result; +} +#endif