From ef1e347456e4192eda693adc18f0f75e34f03db8 Mon Sep 17 00:00:00 2001 From: Timofey Kirichenko Date: Sat, 1 Nov 2025 00:07:32 +0300 Subject: [PATCH 1/5] Signal rewroted to using a vector to store connections, instead of ChunkMemoryAllocator --- src/types/signal.c | 116 ++++++++++++++++++++++++++++++--------------- src/types/signal.h | 19 ++++---- 2 files changed, 85 insertions(+), 50 deletions(-) diff --git a/src/types/signal.c b/src/types/signal.c index 52c8637..0b34a5f 100644 --- a/src/types/signal.c +++ b/src/types/signal.c @@ -1,23 +1,29 @@ #include "signal.h" #include "error.h" #include "ex_alloc/chunk_allocator.h" +#include "log.h" #include "platform/memory.h" #include "types/types.h" +#include "types/vector.h" -boolean signal_constructor(Signal* self, const u32 chunk_min_siz, const u8 minimal_chunks_count) { - return chunk_memory_allocator_constructor( - &self->allocator, sizeof(SignalCallback), chunk_min_siz, minimal_chunks_count - ); +vector_template_impl(SignalCallback, SignalCallback); + +boolean signal_constructor(Signal* self) { + ERROR_ARGS_CHECK_1(self, { return false; }); + self->data = vec_SignalCallback_init(); + return true; } boolean signal_destructor(Signal* self) { - return chunk_memory_allocator_destructor(&self->allocator); + ERROR_ARGS_CHECK_1(self, { return false; }); + vec_SignalCallback_free(&self->data); + return true; } -Signal* signal_new_with_params(const u32 chunk_min_size, const u8 minimal_chunks_count) { +Signal* signal_new(void) { Signal* self = tmalloc(sizeof(Signal)); ERROR_ALLOC_CHECK(self, { return NULL; }); - boolean s = signal_constructor(self, chunk_min_size, minimal_chunks_count); + boolean s = signal_constructor(self); if (s) { return self; } else { @@ -26,9 +32,6 @@ Signal* signal_new_with_params(const u32 chunk_min_size, const u8 minimal_chunks } } -Signal* signal_new(void) { - return signal_new_with_params(8, 1); -} boolean signal_free(Signal* self) { if (signal_destructor(self)) { @@ -42,48 +45,83 @@ boolean signal_free(Signal* self) { boolean signal_emit(Signal* self, void* args) { ERROR_ARG_CHECK(self, { return false; }); - const usize chunks_count = self->allocator.chunks.size; - const void** const chunks_data = (const void** const) self->allocator.chunks.data; - const u8* const bitfield_data = self->allocator.chunks_bitfield.data; - const u32 chunk_bitfield_size = self->allocator.chunk_bitfield_size; - - for (usize i = 0; i < chunks_count; ++i) { - const SignalCallback* const chunk = chunks_data[i]; - const u8* const bitfield = bitfield_data + i * chunk_bitfield_size; - - if (!chunk) - continue; - - for (usize b_i = 0; b_i < chunk_bitfield_size; ++b_i) { - if (bitfield[b_i] == 0x00) - continue; - for (usize bit = 0; bit < 8; ++bit) { - if (bitfield[b_i] & (1 << bit)) { - const SignalCallback* const callback = &chunk[b_i * chunk_bitfield_size + bit]; - callback->func(args, callback->ctx); - } - } + const SignalCallback* const data_end_ptr = self->data.data + self->data.size; + for (const SignalCallback* ptr = self->data.data; ptr < data_end_ptr; ++ptr) { + if (ptr->func != NULL) { + ptr->func(args, ptr->ctx); } } return true; } + +// static find_in_vec(const Signal* const self, ) + SignalCallbackHandler signal_connect(Signal* self, SignalCallbackFunc func, void* ctx) { ERROR_ARGS_CHECK_2(self, func, { return 0; }); - chunk_allocator_ptr ptr = chunk_memory_allocator_alloc_mem(&self->allocator); - if (ptr == 0) { + // Finding empty cell in the data vector + const SignalCallback* const data_end_ptr = self->data.data + self->data.size; + for (SignalCallback* ptr = self->data.data; ptr < data_end_ptr; ++ptr) { + if (ptr->func == NULL) { + ptr->func = func; + ptr->ctx = ctx; + return (SignalCallbackHandler) (data_end_ptr - ptr - 1); + } + } + + // Otherwise push func to the data vector + const unsigned char s = + vec_SignalCallback_push_back(&self->data, (SignalCallback) {.func = func, .ctx = ctx}); + if (!s) { + LOG_ERROR("Pushing callback to the data vector is failed. I gues is the out-of-memory"); + set_error(ERROR_ALLOCATION_FAILED); return 0; - } else { - SignalCallback* callback = chunk_memory_allocator_get_real_ptr(&self->allocator, ptr); - callback->func = func; - callback->ctx = ctx; - return ptr; } + + return self->data.size - 1; } boolean signal_disconnect(Signal* self, SignalCallbackHandler handler) { ERROR_ARGS_CHECK_2(self, handler, { return false; }); - return chunk_memory_allocator_free_mem(&self->allocator, handler); + if (handler >= self->data.size) { + LOG_ERROR( + "Handler is out from index boundary. 'handler' = %u, but bonddary is %u (incl.)", + (unsigned int) handler, (unsigned int) (self->data.size - 1) + ); + set_error(ERROR_NOT_FOUND); + return false; + } + + // SignalCallbackHandler shouldn't be changed on freing other callback + + SignalCallback* callback = &self->data.data[handler]; + if (callback->func == NULL) { + LOG_ERROR("Handler (%u) already disconnected", (unsigned int) handler); + set_error(ERROR_NOT_FOUND); + return false; + } + + callback->func = NULL; + callback->ctx = NULL; + + // Removing disconnected handler from end of the data vector + usize count_to_remove = 0; + const SignalCallback* const data_start_ptr = self->data.data; + for (SignalCallback* ptr = self->data.data + self->data.size; ptr-- > data_start_ptr;) { + if (ptr->func == NULL) + ++count_to_remove; + else + break; + } + + if (count_to_remove != 0) { + // It's always will be successful. There is no point in checking erasing + vec_SignalCallback_erase_range(&self->data, self->data.size - count_to_remove, count_to_remove); + // It's can fails on memory reallocation, but it's not broke any logic. + vec_SignalCallback_shrink_to_fit(&self->data); + } + + return true; } diff --git a/src/types/signal.h b/src/types/signal.h index 5c11486..da84a71 100644 --- a/src/types/signal.h +++ b/src/types/signal.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -16,6 +17,9 @@ typedef struct SignalCallback { void* ctx; } SignalCallback; + +vector_template_def(SignalCallback, SignalCallback); + /** * @brief It's just a signal * @@ -26,7 +30,8 @@ typedef struct SignalCallback { * @api */ typedef struct Signal { - ChunkMemoryAllocator allocator; + // ChunkMemoryAllocator allocator; + vec_SignalCallback data; } Signal; /** @@ -41,7 +46,7 @@ typedef chunk_allocator_ptr SignalCallbackHandler; * @error "InvalidArgument" * @error "AllocationFailed" */ -boolean signal_constructor(Signal* self, const u32 chunk_min_siz, const u8 minimal_chunks_count); +boolean signal_constructor(Signal* self); /** * @brief It's like 'signal_free', but for your allocated Signals. Available only in engine scope. @@ -59,15 +64,6 @@ boolean signal_destructor(Signal* self); */ Signal* signal_new(void); -/** - * @brief Create a Signal instance with custom ChunkMemoryAllocator parameters - * - * @error "InvalidArgument" - * @error "AllocationFailed" - * @api - */ -Signal* signal_new_with_params(const u32 chunk_min_size, const u8 minimal_chunks_count); - /** * @brief Free Signal instance * @@ -97,6 +93,7 @@ SignalCallbackHandler signal_connect(Signal* self, SignalCallbackFunc func, void * @brief Disconnect callback from the signal. * * @error "InvalidArgument" + * @error "NotFound" if handler out of index boundary or callback by this handler is NULL * @api */ boolean signal_disconnect(Signal* self, SignalCallbackHandler handler); From 982de9dbf59cd57d6fca35d3bc852c16274aa295 Mon Sep 17 00:00:00 2001 From: Timofey Kirichenko Date: Sun, 2 Nov 2025 02:19:35 +0300 Subject: [PATCH 2/5] Now Signal is safe and with mutexes. Added SignalUnsafe without any mutexes --- examples/signal.c | 14 +++---- src/platform/mutex.h | 5 +++ src/types/signal.c | 98 +++++++++++++++++++++++++++++++++++++++----- src/types/signal.h | 87 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 182 insertions(+), 22 deletions(-) diff --git a/examples/signal.c b/examples/signal.c index ea1145f..60acc31 100644 --- a/examples/signal.c +++ b/examples/signal.c @@ -6,11 +6,11 @@ #include void signal_a_fn1(void* args, void* ctx) { - printf("Hello from Function 1, Signal A\n"); + printf("Hello from Function 1, Unsafe Signal A\n"); } void signal_a_fn2(void* args, void* ctx) { - printf("Hello from Function 2, Signal A\n"); + printf("Hello from Function 2, Unasfe Signal A\n"); } @@ -25,20 +25,20 @@ void signal_b_fn(void* args, void* ctx) { } -Signal* g_signalA; +SignalUnsafe* g_signalA; Signal* g_signalB; PUBLIC void _ready(void) { - g_signalA = signal_new(); + g_signalA = signal_unsafe_new(); g_signalB = signal_new(); - signal_connect(g_signalA, signal_a_fn1, NULL); - signal_connect(g_signalA, signal_a_fn2, NULL); + signal_unsafe_connect(g_signalA, signal_a_fn1, NULL); + signal_unsafe_connect(g_signalA, signal_a_fn2, NULL); int* ctx = tmalloc(sizeof(int)); *ctx = 0; signal_connect(g_signalB, signal_b_fn, ctx); - signal_emit(g_signalA, NULL); + signal_unsafe_emit(g_signalA, NULL); } PUBLIC void _process(double delta) { diff --git a/src/platform/mutex.h b/src/platform/mutex.h index 2a81cd0..8e6cb94 100644 --- a/src/platform/mutex.h +++ b/src/platform/mutex.h @@ -2,6 +2,11 @@ #include +// MACROS API START +#define ERROR_MUTEX_INIT_FAILED "MutexInitFailed" +// MACROS API END + + /** * @brief Opaque mutex handle * @api diff --git a/src/types/signal.c b/src/types/signal.c index 0b34a5f..329361b 100644 --- a/src/types/signal.c +++ b/src/types/signal.c @@ -3,27 +3,28 @@ #include "ex_alloc/chunk_allocator.h" #include "log.h" #include "platform/memory.h" +#include "platform/mutex.h" #include "types/types.h" #include "types/vector.h" vector_template_impl(SignalCallback, SignalCallback); -boolean signal_constructor(Signal* self) { +boolean signal_unsafe_constructor(SignalUnsafe* self) { ERROR_ARGS_CHECK_1(self, { return false; }); self->data = vec_SignalCallback_init(); return true; } -boolean signal_destructor(Signal* self) { +boolean signal_unsafe_destructor(SignalUnsafe* self) { ERROR_ARGS_CHECK_1(self, { return false; }); vec_SignalCallback_free(&self->data); return true; } -Signal* signal_new(void) { - Signal* self = tmalloc(sizeof(Signal)); +SignalUnsafe* signal_unsafe_new(void) { + SignalUnsafe* self = tmalloc(sizeof(SignalUnsafe)); ERROR_ALLOC_CHECK(self, { return NULL; }); - boolean s = signal_constructor(self); + boolean s = signal_unsafe_constructor(self); if (s) { return self; } else { @@ -33,8 +34,8 @@ Signal* signal_new(void) { } -boolean signal_free(Signal* self) { - if (signal_destructor(self)) { +boolean signal_unsafe_free(SignalUnsafe* self) { + if (signal_unsafe_destructor(self)) { tfree(self); return true; } else { @@ -42,7 +43,7 @@ boolean signal_free(Signal* self) { } } -boolean signal_emit(Signal* self, void* args) { +boolean signal_unsafe_emit(SignalUnsafe* self, void* args) { ERROR_ARG_CHECK(self, { return false; }); const SignalCallback* const data_end_ptr = self->data.data + self->data.size; @@ -56,9 +57,11 @@ boolean signal_emit(Signal* self, void* args) { } -// static find_in_vec(const Signal* const self, ) +// static find_in_vec(const SignalUnsafe* const self, ) -SignalCallbackHandler signal_connect(Signal* self, SignalCallbackFunc func, void* ctx) { +SignalCallbackHandler signal_unsafe_connect( + SignalUnsafe* const self, SignalCallbackFunc func, void* ctx +) { ERROR_ARGS_CHECK_2(self, func, { return 0; }); // Finding empty cell in the data vector @@ -83,7 +86,7 @@ SignalCallbackHandler signal_connect(Signal* self, SignalCallbackFunc func, void return self->data.size - 1; } -boolean signal_disconnect(Signal* self, SignalCallbackHandler handler) { +boolean signal_unsafe_disconnect(SignalUnsafe* self, SignalCallbackHandler handler) { ERROR_ARGS_CHECK_2(self, handler, { return false; }); if (handler >= self->data.size) { LOG_ERROR( @@ -125,3 +128,76 @@ boolean signal_disconnect(Signal* self, SignalCallbackHandler handler) { return true; } + + +/* ============== Safe functions ================ */ +boolean signal_constructor(Signal* self) { + ERROR_ARG_CHECK(self, { return false; }); + boolean s = signal_unsafe_constructor(&self->unsafe); + if (!s) { + LOG_ERROR("'signal_constructor': SignalUnsafe construction failed"); + return false; + } + + // Mutex init + self->mutex = mutex_new(); + if (self->mutex == NULL) { + LOG_ERROR("'signal_constructor': Mutex initialization failed"); + set_error(ERROR_MUTEX_INIT_FAILED); + return false; + } + + return true; +} + +boolean signal_destructor(Signal* self) { + ERROR_ARGS_CHECK_1(self, { return false; }); + signal_unsafe_destructor(&self->unsafe); + mutex_free(self->mutex); + return true; +} + +Signal* signal_new(void) { + Signal* self = tmalloc(sizeof(Signal)); + ERROR_ALLOC_CHECK(self, { return NULL; }); + boolean s = signal_constructor(self); + if (s) { + return self; + } else { + tfree(self); + return NULL; + } +} + +boolean signal_free(Signal* self) { + if (signal_destructor(self)) { + tfree(self); + return true; + } else { + return false; + } +} + +boolean signal_emit(Signal* self, void* args) { + ERROR_ARG_CHECK(self, { return false; }); + mutex_lock(self->mutex); + boolean st = signal_unsafe_emit(&self->unsafe, args); + mutex_unlock(self->mutex); + return st; +} + +SignalCallbackHandler signal_connect(Signal* const self, SignalCallbackFunc func, void* ctx) { + ERROR_ARGS_CHECK_2(self, func, { return 0; }); + mutex_lock(self->mutex); + SignalCallbackHandler handler = signal_unsafe_connect(&self->unsafe, func, ctx); + mutex_unlock(self->mutex); + return handler; +} + +boolean signal_disconnect(Signal* self, SignalCallbackHandler handler) { + ERROR_ARGS_CHECK_2(self, handler, { return false; }); + mutex_lock(self->mutex); + boolean st = signal_unsafe_disconnect(&self->unsafe, handler); + mutex_unlock(self->mutex); + return st; +} diff --git a/src/types/signal.h b/src/types/signal.h index da84a71..1fd0a81 100644 --- a/src/types/signal.h +++ b/src/types/signal.h @@ -3,6 +3,7 @@ #include #include #include +#include "platform/mutex.h" /** @@ -27,24 +28,102 @@ vector_template_def(SignalCallback, SignalCallback); * You can connect some functions (callbacks) to the signal. Then in other piece of code, you can emit * the signal with your args and all callbacks will be executed. * + * @warning FOR ONE THREAD USE ONLY. It's not guarded with any mutexes + * * @api */ -typedef struct Signal { - // ChunkMemoryAllocator allocator; +typedef struct SignalUnsafe { vec_SignalCallback data; -} Signal; +} SignalUnsafe; /** * @api */ typedef chunk_allocator_ptr SignalCallbackHandler; +/** + * @brief It's like 'signal_unsafe_new_with_params', but for your allocated Signals. Available only in + * engine scope; + * + * @error "InvalidArgument" + * @error "AllocationFailed" + */ +boolean signal_unsafe_constructor(SignalUnsafe* self); + +/** + * @brief It's like 'signal_unsafe_free', but for your allocated Signals. Available only in engine scope. + * + * @error "InvalidArgument" + */ +boolean signal_unsafe_destructor(SignalUnsafe* self); + +/** + * @brief Create a SignalUnsafe instance. + * + * @error "InvalidArgument" + * @error "AllocationFailed" + * @api + */ +SignalUnsafe* signal_unsafe_new(void); + +/** + * @brief Free SignalUnsafe instance + * + * @error "InvalidArgument" + * @api + */ +boolean signal_unsafe_free(SignalUnsafe* self); + +/** + * @brief Emit signal. All callbacks will be executed. + * + * @error "InvalidArgument" + * @api + */ +boolean signal_unsafe_emit(SignalUnsafe* self, void* args); + +/** + * @breif Connect callback to the signal. It will be executed, when the signal is emitted. + * + * @error "InvalidArgument" + * @error "AllocationFailed" + * @api + */ +SignalCallbackHandler signal_unsafe_connect( + SignalUnsafe* const self, SignalCallbackFunc func, void* ctx +); + +/** + * @brief Disconnect callback from the signal. + * + * @error "InvalidArgument" + * @error "NotFound" if handler out of index boundary or callback by this handler is NULL + * @api + */ +boolean signal_unsafe_disconnect(SignalUnsafe* self, SignalCallbackHandler handler); + + +/** + * @brief It's just a signal with some thread safety (instead of SignalUnsafe) + * + * I don't know what I should write here, it's intuitively understandable. + * You can connect some functions (callbacks) to the signal. Then in other piece of code, you can emit + * the signal with your args and all callbacks will be executed. + * + * @api + */ +typedef struct Signal { + SignalUnsafe unsafe; + mutex_handle mutex; +} Signal; + /** * @brief It's like 'signal_new_with_params', but for your allocated Signals. Available only in engine * scope; * * @error "InvalidArgument" * @error "AllocationFailed" + * @error "MutexInitFailed" */ boolean signal_constructor(Signal* self); @@ -87,7 +166,7 @@ boolean signal_emit(Signal* self, void* args); * @error "AllocationFailed" * @api */ -SignalCallbackHandler signal_connect(Signal* self, SignalCallbackFunc func, void* ctx); +SignalCallbackHandler signal_connect(Signal* const self, SignalCallbackFunc func, void* ctx); /** * @brief Disconnect callback from the signal. From c4a24e30e2df057b6561c7762b98ff0471014cae Mon Sep 17 00:00:00 2001 From: Timofey Kirichenko Date: Sun, 2 Nov 2025 12:18:34 +0300 Subject: [PATCH 3/5] Adder signal system to RenderContext --- .../sdl3/render_context_opengl_13_sdl3.c | 114 ++++++++++++++++-- src/servers/render_context/render_context.h | 7 ++ 2 files changed, 114 insertions(+), 7 deletions(-) diff --git a/src/servers/render_context/opengl_13/sdl3/render_context_opengl_13_sdl3.c b/src/servers/render_context/opengl_13/sdl3/render_context_opengl_13_sdl3.c index 79d5564..d294cc9 100644 --- a/src/servers/render_context/opengl_13/sdl3/render_context_opengl_13_sdl3.c +++ b/src/servers/render_context/opengl_13/sdl3/render_context_opengl_13_sdl3.c @@ -9,6 +9,7 @@ #include "servers/render_context/render_context.h" #include "servers/window_server/sdl3/window_server_sdl3.h" #include "servers/window_server/window_server.h" +#include "types/signal.h" #include "types/types.h" /*struct RenderContextSurface { @@ -25,7 +26,8 @@ do { \ if (!SDL_IsMainThread()) { \ LOG_ERROR( \ - "WindowServer(SDL3)::" #function " must be called only from main thread or " \ + "RenderContext(OpenGL 1.3, SDL3)::" #function \ + " must be called only from main thread or " \ "via call_deferred/call_deferred_async" \ ) \ set_error(NOT_MAIN_THREAD_ERROR); \ @@ -37,6 +39,21 @@ const static u32 INIT_FLAGS = SDL_INIT_VIDEO | SDL_INIT_EVENTS; static u8 g_isInit = 0; +struct ServerSignal { + Signal* signal; + c_str name; + c_str description; +}; + +// clang-format off +static Signal g_signalGlContextCreated; + +#define SERVER_SIGNAL(_signal, _name, _description) (struct ServerSignal) {.signal = _signal, .name = _name, .description = _description} +static struct ServerSignal g_signals[] = { + SERVER_SIGNAL(&g_signalGlContextCreated, "gl_context_created", "") +}; +// clang-format on + static boolean _init(void) { if (!SDL_WasInit(INIT_FLAGS)) { if (!SDL_Init(INIT_FLAGS)) { @@ -46,6 +63,13 @@ static boolean _init(void) { g_isInit = 1; } + for (usize i = 0; i < (sizeof(g_signals) / sizeof(g_signals[0])); ++i) { + if (!signal_constructor(g_signals[i].signal)) { + LOG_ERROR("RenderContext::_init: Signal initializaito failed"); + return false; + } + } + return true; } @@ -53,10 +77,69 @@ static boolean _quit(void) { if (g_isInit) { SDL_QuitSubSystem(INIT_FLAGS); } + + for (usize i = 0; i < (sizeof(g_signals) / sizeof(g_signals[0])); ++i) { + signal_destructor(g_signals[i].signal); + } return true; } +static SignalCallbackHandler impl_signal_connect(c_str name, SignalCallbackFunc func, void* ctx) { + ERROR_ARGS_CHECK_2(name, func, { return 0; }); + + for (usize i = 0; i < (sizeof(g_signals) / sizeof(g_signals[0])); ++i) { + if (strcmp(name, g_signals[i].name) == 0) { + return signal_connect(g_signals[i].signal, func, ctx); + } + } + + LOG_WARN("RenderContext(OpenGL 1.3, SDL3)::signal_connect: Signal '%s' not found", name); + set_error(ERROR_NOT_FOUND); + return 0; +} + +static boolean impl_signal_disconnect(c_str name, SignalCallbackHandler handler) { + ERROR_ARGS_CHECK_2(name, handler, { return false; }); + + for (usize i = 0; i < (sizeof(g_signals) / sizeof(g_signals[0])); ++i) { + if (strcmp(name, g_signals[i].name) == 0) { + return signal_disconnect(g_signals[i].signal, handler); + } + } + + LOG_WARN("RenderContext.signal_disconnect: Signal '%s' not found", name); + set_error(ERROR_NOT_FOUND); + return false; +} + +static i32 get_available_signals(c_str* names_buff, c_str* descriptions_buff) { + const usize signals_count = sizeof(g_signals) / sizeof(g_signals[0]); + + if (names_buff) { + names_buff = tmalloc(sizeof(c_str) * signals_count); + ERROR_ALLOC_CHECK(names_buff, { return 0; }); + } + + if (descriptions_buff) { + descriptions_buff = tmalloc(sizeof(c_str) * signals_count); + ERROR_ALLOC_CHECK(descriptions_buff, { + tfree(names_buff); + return 0; + }); + } + + for (usize i = 0; i < (sizeof(g_signals) / sizeof(g_signals[0])); ++i) { + if (names_buff) + names_buff[i] = g_signals[i].name; + if (descriptions_buff) + descriptions_buff[i] = g_signals[i].description; + } + + return signals_count; +} + + static SDL_GLContext g_openglSharedContext = NULL; static RenderContextSurface* create_surface(WindowServerWindow* window) { ERROR_ARGS_CHECK_1(window, { return NULL; }); @@ -66,10 +149,16 @@ static RenderContextSurface* create_surface(WindowServerWindow* window) { g_openglSharedContext = SDL_GL_CreateContext((SDL_Window*) window); if (!g_openglSharedContext) { - LOG_FATAL("Failed to create shared OpenGL context. SDL Error: %s", SDL_GetError()); + LOG_FATAL( + "RenderContext.create_surface: Failed to create shared OpenGL context. SDL Error: " + "%s", + SDL_GetError() + ); set_error(ANY_ERROR); return NULL; } + + signal_emit(&g_signalGlContextCreated, NULL); } return (RenderContextSurface*) window; @@ -92,7 +181,10 @@ static boolean surface_make_current(RenderContextSurface* surface) { } if (!SDL_GL_MakeCurrent((SDL_Window*) surface, g_openglSharedContext)) { - LOG_FATAL("Failed to change OpenGL context. SDL Error: %s", SDL_GetError()); + LOG_FATAL( + "RenderContext.surface_make_current: Failed to change OpenGL context. SDL Error: %s", + SDL_GetError() + ); set_error(ANY_ERROR); return false; } @@ -111,27 +203,35 @@ static boolean surface_present(RenderContextSurface* surface) { return false; } if (!SDL_GL_SwapWindow((SDL_Window*) surface)) { - LOG_FATAL("Failed to swap window's buffers. SDL Error: %s", SDL_GetError()); + LOG_FATAL( + "RenderContext.surface_present: Failed to swap window's buffers. SDL Error: %s", + SDL_GetError() + ); set_error(ANY_ERROR); return false; } return true; } - -#define REGISTER(fn) render_context_backend_set_function(backend, #fn, (fptr) fn) +#define REGISTER_WITH_NAME(name, fn) render_context_backend_set_function(backend, name, (fptr) fn) +#define REGISTER(fn) REGISTER_WITH_NAME(#fn, fn) void render_context_opengl_13_sdl3_backend_register(void) { RenderContextBackend* backend = render_context_backend_new(); REGISTER(_init); REGISTER(_quit); + + REGISTER_WITH_NAME("signal_connect", impl_signal_connect); + REGISTER_WITH_NAME("signal_disconnect", impl_signal_disconnect); + REGISTER(get_available_signals); + REGISTER(create_surface); REGISTER(destroy_surface); REGISTER(surface_make_current); REGISTER(surface_present); - render_context_backend_set_function(backend, "get_proc_addr", (fptr) SDL_GL_GetProcAddress); + REGISTER_WITH_NAME("get_proc_addr", SDL_GL_GetProcAddress); render_context_register_backend("OpenGL 1.3", "SDL3", backend); } diff --git a/src/servers/render_context/render_context.h b/src/servers/render_context/render_context.h index 0e2ba05..db18003 100644 --- a/src/servers/render_context/render_context.h +++ b/src/servers/render_context/render_context.h @@ -2,6 +2,7 @@ #include #include +#include #include @@ -23,6 +24,12 @@ typedef struct { boolean (*_init)(void); boolean (*_quit)(void); + // TODO: change to StringSlice when fix/string PR is will be allowed + SignalCallbackHandler (*signal_connect)(c_str name, SignalCallbackFunc func, void* ctx); + boolean (*signal_disconnect)(c_str name, SignalCallbackHandler); + i32 (*get_available_signals)(c_str* names_buff, c_str* descriptions_buff); + + RenderContextSurface* (*create_surface)(WindowServerWindow* window); boolean (*destroy_surface)(RenderContextSurface* surface); From 2de2e4051e18175d7f15d969481f9c5571c4b3e1 Mon Sep 17 00:00:00 2001 From: Timofey Kirichenko Date: Sun, 2 Nov 2025 12:51:08 +0300 Subject: [PATCH 4/5] Registred new functions --- src/servers/render_context/render_context.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/servers/render_context/render_context.c b/src/servers/render_context/render_context.c index 45f4bdb..5e99566 100644 --- a/src/servers/render_context/render_context.c +++ b/src/servers/render_context/render_context.c @@ -136,10 +136,16 @@ static boolean backend_set_get( FnT function; }; - // TODO: generate with API Generator +// TODO: generate with API Generator +// clang-format off + #define FN_PAIR(fn) {#fn, (FnT)&backend-> fn} + // clang-format on const struct FnPair pairs[] = { {"_init", (FnT) &backend->_init}, {"_quit", (FnT) &backend->_quit}, + FN_PAIR(signal_connect), + FN_PAIR(signal_disconnect), + FN_PAIR(get_available_signals), {"create_surface", (FnT) &backend->create_surface}, {"destroy_surface", (FnT) &backend->destroy_surface}, {"surface_make_current", (FnT) &backend->surface_make_current}, From db85d1e42a74846a386a2a7fa15ac3aca2f9ac6c Mon Sep 17 00:00:00 2001 From: Timofey Kirichenko Date: Sun, 2 Nov 2025 12:54:08 +0300 Subject: [PATCH 5/5] Registred new functions. Fix --- src/servers/render_context/render_context.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/servers/render_context/render_context.c b/src/servers/render_context/render_context.c index 5e99566..9f520ce 100644 --- a/src/servers/render_context/render_context.c +++ b/src/servers/render_context/render_context.c @@ -136,21 +136,21 @@ static boolean backend_set_get( FnT function; }; -// TODO: generate with API Generator -// clang-format off + // TODO: generate with API Generator + // clang-format off #define FN_PAIR(fn) {#fn, (FnT)&backend-> fn} // clang-format on const struct FnPair pairs[] = { - {"_init", (FnT) &backend->_init}, - {"_quit", (FnT) &backend->_quit}, + FN_PAIR(_init), + FN_PAIR(_quit), FN_PAIR(signal_connect), FN_PAIR(signal_disconnect), FN_PAIR(get_available_signals), - {"create_surface", (FnT) &backend->create_surface}, - {"destroy_surface", (FnT) &backend->destroy_surface}, - {"surface_make_current", (FnT) &backend->surface_make_current}, - {"surface_present", (FnT) &backend->surface_present}, - {"get_proc_addr", (FnT) &backend->get_proc_addr}, + FN_PAIR(create_surface), + FN_PAIR(destroy_surface), + FN_PAIR(surface_make_current), + FN_PAIR(surface_present), + FN_PAIR(get_proc_addr) }; for (usize i = 0; i < sizeof(pairs) / sizeof(pairs[0]); i++) {