diff --git a/src/dusk/CMakeLists.txt b/src/dusk/CMakeLists.txt index 900072b8..70894fd9 100644 --- a/src/dusk/CMakeLists.txt +++ b/src/dusk/CMakeLists.txt @@ -53,7 +53,6 @@ target_sources(${DUSK_BINARY_TARGET_NAME} # Subdirs add_subdirectory(animation) -add_subdirectory(event) add_subdirectory(assert) add_subdirectory(asset) add_subdirectory(console) diff --git a/src/dusk/asset/CMakeLists.txt b/src/dusk/asset/CMakeLists.txt index 0e87c96f..39e32cab 100644 --- a/src/dusk/asset/CMakeLists.txt +++ b/src/dusk/asset/CMakeLists.txt @@ -7,7 +7,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC asset.c - assetbatch.c assetfile.c ) diff --git a/src/dusk/asset/asset.c b/src/dusk/asset/asset.c index 1a26fc16..bcb33dfc 100644 --- a/src/dusk/asset/asset.c +++ b/src/dusk/asset/asset.c @@ -322,7 +322,9 @@ errorret_t assetUpdate(void) { } else if(loading->entry->state == ASSET_ENTRY_STATE_LOADED) { assetentry_t *loadedEntry = loading->entry; loading->entry = NULL; - eventInvoke(&loadedEntry->onLoaded, loadedEntry); + if(loadedEntry->onLoaded) { + loadedEntry->onLoaded(loadedEntry, loadedEntry->onLoadedUser); + } } loading++; @@ -346,8 +348,8 @@ errorret_t assetUpdate(void) { assetentry_t *errEntry = loading->entry; loading->entry = NULL; threadMutexUnlock(&loading->mutex); - eventInvoke(&errEntry->onError, errEntry); - errorThrow("Failed to load asset asynchronously."); + if(errEntry->onError) errEntry->onError(errEntry, errEntry->onErrorUser); + loading++; break; } diff --git a/src/dusk/asset/assetbatch.c b/src/dusk/asset/assetbatch.c deleted file mode 100644 index 3bb27927..00000000 --- a/src/dusk/asset/assetbatch.c +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetbatch.h" -#include "asset.h" -#include "assert/assert.h" -#include "util/memory.h" -#include - -void assetBatchInit( - assetbatch_t *batch, - const uint16_t count, - const assetbatchdesc_t *descs -) { - assertNotNull(batch, "Batch cannot be NULL."); - assertNotNull(descs, "Descs cannot be NULL."); - assertTrue(count > 0, "Count must be greater than 0."); - assertTrue( - count <= ASSET_BATCH_COUNT_MAX, "Count exceeds ASSET_BATCH_COUNT_MAX." - ); - - memoryZero(batch, sizeof(assetbatch_t)); - batch->count = count; - - eventInit( - &batch->onLoaded, - batch->onLoadedCallbacks, batch->onLoadedUsers, ASSET_BATCH_EVENT_MAX - ); - eventInit( - &batch->onEntryLoaded, - batch->onEntryLoadedCallbacks, - batch->onEntryLoadedUsers, - ASSET_BATCH_EVENT_MAX - ); - eventInit( - &batch->onError, - batch->onErrorCallbacks, batch->onErrorUsers, ASSET_BATCH_EVENT_MAX - ); - eventInit( - &batch->onEntryError, - batch->onEntryErrorCallbacks, - batch->onEntryErrorUsers, - ASSET_BATCH_EVENT_MAX - ); - - for(uint16_t i = 0; i < count; i++) { - batch->inputs[i] = descs[i].input; - batch->entries[i] = assetLock( - descs[i].path, descs[i].type, &batch->inputs[i] - ); - - if(batch->entries[i]->state == ASSET_ENTRY_STATE_LOADED) { - // Already loaded (cached) - count it now, no subscription needed. - batch->loadedCount++; - } else if(batch->entries[i]->state == ASSET_ENTRY_STATE_ERROR) { - batch->errorCount++; - } else { - eventSubscribe( - &batch->entries[i]->onLoaded, assetBatchEntryOnLoadedCb, batch - ); - eventSubscribe( - &batch->entries[i]->onError, assetBatchEntryOnErrorCb, batch - ); - } - } -} - -void assetBatchLock(assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - for(uint16_t i = 0; i < batch->count; i++) { - assetEntryLock(batch->entries[i]); - } -} - -void assetBatchUnlock(assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - for(uint16_t i = 0; i < batch->count; i++) { - assetEntryUnlock(batch->entries[i]); - } -} - -bool_t assetBatchIsLoaded(const assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - for(uint16_t i = 0; i < batch->count; i++) { - if(batch->entries[i]->state != ASSET_ENTRY_STATE_LOADED) return false; - } - return true; -} - -bool_t assetBatchHasError(const assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - for(uint16_t i = 0; i < batch->count; i++) { - if(batch->entries[i]->state == ASSET_ENTRY_STATE_ERROR) return true; - } - return false; -} - -errorret_t assetBatchRequireLoaded(assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - - bool_t allDone; - do { - allDone = true; - for(uint16_t i = 0; i < batch->count; i++) { - const assetentrystate_t state = batch->entries[i]->state; - if(state == ASSET_ENTRY_STATE_ERROR) { - errorThrow("Asset '%s' failed to load.", batch->entries[i]->name); - } - if(state != ASSET_ENTRY_STATE_LOADED) { - allDone = false; - } - } - if(!allDone) { - usleep(1000); - errorChain(assetUpdate()); - } - } while(!allDone); - - errorOk(); -} - -void assetBatchDispose(assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - for(uint16_t i = 0; i < batch->count; i++) { - if(batch->entries[i]) { - // Unsubscribe while we still hold a lock so the entry is live. - eventUnsubscribe(&batch->entries[i]->onLoaded, assetBatchEntryOnLoadedCb); - eventUnsubscribe(&batch->entries[i]->onError, assetBatchEntryOnErrorCb); - assetUnlockEntry(batch->entries[i]); - } - } - memoryZero(batch, sizeof(assetbatch_t)); -} - -void assetBatchEntryOnLoadedCb(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)params; - assetbatch_t *batch = (assetbatch_t *)user; - - batch->loadedCount++; - eventInvoke(&batch->onEntryLoaded, entry); - - if((uint16_t)(batch->loadedCount + batch->errorCount) >= batch->count) { - if(batch->errorCount == 0) { - eventInvoke(&batch->onLoaded, batch); - } else { - eventInvoke(&batch->onError, batch); - } - } -} - -void assetBatchEntryOnErrorCb(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)params; - assetbatch_t *batch = (assetbatch_t *)user; - - batch->errorCount++; - eventInvoke(&batch->onEntryError, entry); - - if((uint16_t)(batch->loadedCount + batch->errorCount) >= batch->count) { - eventInvoke(&batch->onError, batch); - } -} diff --git a/src/dusk/asset/assetbatch.h b/src/dusk/asset/assetbatch.h deleted file mode 100644 index 20aaac2e..00000000 --- a/src/dusk/asset/assetbatch.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/loader/assetentry.h" -#include "asset/loader/assetloader.h" -#include "event/event.h" - -#define ASSET_BATCH_COUNT_MAX 64 -#define ASSET_BATCH_EVENT_MAX 4 - -typedef struct { - const char_t *path; - assetloadertype_t type; - assetloaderinput_t input; -} assetbatchdesc_t; - -typedef struct { - assetentry_t *entries[ASSET_BATCH_COUNT_MAX]; - assetloaderinput_t inputs[ASSET_BATCH_COUNT_MAX]; - uint16_t count; - uint16_t loadedCount; - uint16_t errorCount; - - /** Fires once when every entry loaded. params = assetbatch_t * */ - event_t onLoaded; - eventcallback_t onLoadedCallbacks[ASSET_BATCH_EVENT_MAX]; - void *onLoadedUsers[ASSET_BATCH_EVENT_MAX]; - - /** Fires each time a single entry loads. params = assetentry_t * */ - event_t onEntryLoaded; - eventcallback_t onEntryLoadedCallbacks[ASSET_BATCH_EVENT_MAX]; - void *onEntryLoadedUsers[ASSET_BATCH_EVENT_MAX]; - - /** Fires when all entries finish (any with errors). params: assetbatch_t * */ - event_t onError; - eventcallback_t onErrorCallbacks[ASSET_BATCH_EVENT_MAX]; - void *onErrorUsers[ASSET_BATCH_EVENT_MAX]; - - /** Fires each time a single entry errors. params = assetentry_t * */ - event_t onEntryError; - eventcallback_t onEntryErrorCallbacks[ASSET_BATCH_EVENT_MAX]; - void *onEntryErrorUsers[ASSET_BATCH_EVENT_MAX]; -} assetbatch_t; - -/** - * Initialises the batch from an array of descriptors. Each entry is locked - * and queued for loading immediately. - * - * @param batch Batch to initialise. - * @param descs Array of entry descriptors (need not outlive this call). - * @param count Number of descriptors (must be <= ASSET_BATCH_COUNT_MAX). - */ -void assetBatchInit( - assetbatch_t *batch, - uint16_t count, - const assetbatchdesc_t *descs -); - -/** - * Acquires one additional lock on every entry in the batch. - * - * @param batch Batch to lock. - */ -void assetBatchLock(assetbatch_t *batch); - -/** - * Releases one lock from every entry in the batch. When an entry's lock - * count reaches zero it will be reaped on the next assetUpdate. - * - * @param batch Batch to unlock. - */ -void assetBatchUnlock(assetbatch_t *batch); - -/** - * Returns true if every entry in the batch has finished loading. - * - * @param batch Batch to query. - */ -bool_t assetBatchIsLoaded(const assetbatch_t *batch); - -/** - * Returns true if any entry in the batch is in an error state. - * - * @param batch Batch to query. - */ -bool_t assetBatchHasError(const assetbatch_t *batch); - -/** - * Blocks until every entry is loaded. Returns an error if any entry fails. - * - * @param batch Batch to wait on. - */ -errorret_t assetBatchRequireLoaded(assetbatch_t *batch); - -/** - * Releases the batch's lock on every entry and clears the batch. After this - * call the batch struct may be reused with assetBatchInit. - * - * @param batch Batch to dispose. - */ -void assetBatchDispose(assetbatch_t *batch); - -/** - * Event trampoline invoked when a batch entry finishes loading. - * Increments the loaded counter and fires batch-level events. - * - * @param params The loaded assetentry_t pointer. - * @param user The owning assetbatch_t pointer. - */ -void assetBatchEntryOnLoadedCb(void *params, void *user); - -/** - * Event trampoline invoked when a batch entry fails to load. - * Increments the error counter and fires batch-level events. - * - * @param params The errored assetentry_t pointer. - * @param user The owning assetbatch_t pointer. - */ -void assetBatchEntryOnErrorCb(void *params, void *user); diff --git a/src/dusk/asset/loader/assetentry.c b/src/dusk/asset/loader/assetentry.c index 9e9ad477..cfa5697f 100644 --- a/src/dusk/asset/loader/assetentry.c +++ b/src/dusk/asset/loader/assetentry.c @@ -35,22 +35,6 @@ void assetEntryInit( entry->input = NULL; } refInit(&entry->refs, entry, NULL, NULL, NULL); - - eventInit( - &entry->onLoaded, - entry->onLoadedCallbacks, entry->onLoadedUsers, - ASSET_ENTRY_EVENT_MAX - ); - eventInit( - &entry->onUnloaded, - entry->onUnloadedCallbacks, entry->onUnloadedUsers, - ASSET_ENTRY_EVENT_MAX - ); - eventInit( - &entry->onError, - entry->onErrorCallbacks, entry->onErrorUsers, - ASSET_ENTRY_EVENT_MAX - ); } void assetEntryLock(assetentry_t *entry) { @@ -97,7 +81,7 @@ errorret_t assetEntryDispose(assetentry_t *entry) { "Asset entry still refed at dispose time." ); - eventInvoke(&entry->onUnloaded, entry); + if(entry->onUnloaded) entry->onUnloaded(entry, entry->onUnloadedUser); errorChain(ASSET_LOADER_CALLBACKS[entry->type].dispose(entry)); memoryZero(entry, sizeof(assetentry_t)); errorOk(); diff --git a/src/dusk/asset/loader/assetentry.h b/src/dusk/asset/loader/assetentry.h index 94eff3fc..493af0b1 100644 --- a/src/dusk/asset/loader/assetentry.h +++ b/src/dusk/asset/loader/assetentry.h @@ -7,7 +7,6 @@ #pragma once #include "asset/loader/assetloading.h" -#include "event/event.h" #include "util/ref.h" typedef enum { @@ -20,11 +19,17 @@ typedef enum { ASSET_ENTRY_STATE_ERROR } assetentrystate_t; -/** Maximum number of subscribers for each per-entry event. */ -#define ASSET_ENTRY_EVENT_MAX 2 - typedef struct assetentry_s assetentry_t; +/** + * A single asset entry callback. Each entry supports at most one subscriber + * per event - a second assignment without clearing the first is a bug. + * + * @param entry The assetentry_t the event fired on. + * @param user The user pointer passed alongside the callback. + */ +typedef void (*assetentrycallback_t)(assetentry_t *entry, void *user); + struct assetentry_s { char_t name[ASSET_FILE_NAME_MAX]; assetloadertype_t type; @@ -33,30 +38,27 @@ struct assetentry_s { ref_t refs; assetloaderinput_t *input; assetloaderinput_t inputData; - /** - * Fired once when loading completes successfully (params = assetentry_t *). - * Always invoked on the main thread. - */ - event_t onLoaded; - eventcallback_t onLoadedCallbacks[ASSET_ENTRY_EVENT_MAX]; - void *onLoadedUsers[ASSET_ENTRY_EVENT_MAX]; /** - * Fired once when the entry is disposed/reaped (params = assetentry_t *). - * The asset data is still accessible when the callback runs. + * Fired once when loading completes successfully. * Always invoked on the main thread. */ - event_t onUnloaded; - eventcallback_t onUnloadedCallbacks[ASSET_ENTRY_EVENT_MAX]; - void *onUnloadedUsers[ASSET_ENTRY_EVENT_MAX]; + assetentrycallback_t onLoaded; + void *onLoadedUser; /** - * Fired once when loading fails (params = assetentry_t *). + * Fired once when the entry is disposed/reaped. The asset data is still + * accessible when the callback runs. Always invoked on the main thread. + */ + assetentrycallback_t onUnloaded; + void *onUnloadedUser; + + /** + * Fired once when loading fails. * Always invoked on the main thread. */ - event_t onError; - eventcallback_t onErrorCallbacks[ASSET_ENTRY_EVENT_MAX]; - void *onErrorUsers[ASSET_ENTRY_EVENT_MAX]; + assetentrycallback_t onError; + void *onErrorUser; }; /** diff --git a/src/dusk/event/CMakeLists.txt b/src/dusk/event/CMakeLists.txt deleted file mode 100644 index 69958ead..00000000 --- a/src/dusk/event/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - event.c -) diff --git a/src/dusk/event/event.c b/src/dusk/event/event.c deleted file mode 100644 index 1051fb51..00000000 --- a/src/dusk/event/event.c +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "event.h" -#include "assert/assert.h" -#include "util/memory.h" - -void eventInit( - event_t *event, - eventcallback_t *callbacks, - void **users, - size_t size -) { - assertNotNull(event, "event must not be NULL"); - assertNotNull((void *)callbacks, "callbacks must not be NULL"); - assertTrue(size > 0, "size must be greater than 0"); - - event->callbacks = callbacks; - event->users = users; - event->size = size; - event->count = 0; - memoryZero(callbacks, sizeof(eventcallback_t) * size); - if(users) memoryZero(users, sizeof(void *) * size); -} - -void eventSubscribe(event_t *event, eventcallback_t callback, void *user) { - assertNotNull(event, "event must not be NULL"); - assertNotNull(callback, "callback must not be NULL"); - - // Ensure callback isn't already susbcribed - for(uint32_t i = 0; i < event->count; i++) { - if(event->callbacks[i] != callback) continue; - assertUnreachable("Callback already registered, cannot subscribe twice."); - } - - assertTrue(event->count < event->size, "event subscriber capacity exceeded"); - - event->callbacks[event->count] = callback; - if(user) { - assertNotNull(event->users, "Cannot add user pointer."); - event->users[event->count] = user; - } - event->count++; -} - -void eventUnsubscribe(event_t *event, eventcallback_t callback) { - assertNotNull(event, "event must not be NULL"); - assertNotNull(callback, "callback must not be NULL"); - - for(uint32_t i = 0; i < event->count; i++) { - if(event->callbacks[i] != callback) continue; - - uint32_t last = event->count - 1; - if(i != last) { - event->callbacks[i] = event->callbacks[last]; - if(event->users) event->users[i] = event->users[last]; - } - event->callbacks[last] = NULL; - if(event->users) event->users[last] = NULL; - event->count--; - return; - } -} - -void eventInvoke(const event_t *event, void *params) { - assertNotNull(event, "event must not be NULL"); - - for(uint32_t i = 0; i < event->count; i++) { - void *u = event->users ? event->users[i] : NULL; - event->callbacks[i](params, u); - } -} diff --git a/src/dusk/event/event.h b/src/dusk/event/event.h deleted file mode 100644 index b94b3f59..00000000 --- a/src/dusk/event/event.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef void (*eventcallback_t)(void *params, void *user); - -typedef struct { - eventcallback_t *callbacks; - void **users; - size_t size; - uint32_t count; -} event_t; - -/** - * Initializes an event, binding it to the provided backing arrays and clearing - * all subscribers. May also be called to reset an event (re-clears subscribers - * without changing the backing arrays or size). - * - * @param event The event to initialize. - * @param callbacks Caller-owned array of at least `size` callback slots. - * @param users Array of user pointers, matching each callback, or NULL. - * @param size Capacity of both arrays, must match. - */ -void eventInit( - event_t *event, - eventcallback_t *callbacks, - void **users, - size_t size -); - -/** - * Subscribes a callback to an event. The callback is invoked with params and - * the provided user pointer each time the event fires. The same (callback, - * user) pair may only be subscribed once. - * - * @param event The event to subscribe to. - * @param callback The function to call when the event fires. - * @param user Arbitrary pointer forwarded to the callback unchanged. - */ -void eventSubscribe(event_t *event, eventcallback_t callback, void *user); - -/** - * Removes a previously subscribed (callback, user) pair. Does nothing if the - * pair is not currently subscribed. - * - * @param event The event to unsubscribe from. - * @param callback The callback that was passed to eventSubscribe. - */ -void eventUnsubscribe(event_t *event, eventcallback_t callback); - -/** - * Invokes all subscribed callbacks, passing params and each subscriber's user - * pointer. - * - * @param event The event to invoke. - * @param params Arbitrary pointer forwarded to every callback unchanged. - */ -void eventInvoke(const event_t *event, void *params); diff --git a/src/dusk/input/input.c b/src/dusk/input/input.c index ad9c0bd7..5ef56e81 100644 --- a/src/dusk/input/input.c +++ b/src/dusk/input/input.c @@ -11,7 +11,6 @@ #include "util/string.h" #include "util/math.h" #include "time/time.h" -#include "event/event.h" input_t INPUT; diff --git a/src/dusk/rpg/overworld/map.c b/src/dusk/rpg/overworld/map.c index c4f44824..c3c3b56c 100644 --- a/src/dusk/rpg/overworld/map.c +++ b/src/dusk/rpg/overworld/map.c @@ -11,7 +11,6 @@ #include "asset/asset.h" #include "asset/loader/assetloader.h" #include "console/console.h" -#include "event/event.h" #include "util/string.h" #include "rpg/entity/global/entityglobal.h" #include "rpg/entity/item/entityitem.h" @@ -137,8 +136,10 @@ void mapChunkUnload(chunk_t *chunk) { memorySet(chunk->areas, 0xFF, sizeof(chunk->areas)); if(chunk->dcfEntry != NULL) { - eventUnsubscribe(&chunk->dcfEntry->onLoaded, mapChunkLoaded); - eventUnsubscribe(&chunk->dcfEntry->onError, mapChunkLoadError); + chunk->dcfEntry->onLoaded = NULL; + chunk->dcfEntry->onLoadedUser = NULL; + chunk->dcfEntry->onError = NULL; + chunk->dcfEntry->onErrorUser = NULL; assetUnlockEntry(chunk->dcfEntry); chunk->dcfEntry = NULL; } @@ -159,8 +160,10 @@ errorret_t mapChunkLoad(chunk_t *chunk) { mapChunkLoadingSlotClear(chunk); if(chunk->dcfEntry != NULL) { - eventUnsubscribe(&chunk->dcfEntry->onLoaded, mapChunkLoaded); - eventUnsubscribe(&chunk->dcfEntry->onError, mapChunkLoadError); + chunk->dcfEntry->onLoaded = NULL; + chunk->dcfEntry->onLoadedUser = NULL; + chunk->dcfEntry->onError = NULL; + chunk->dcfEntry->onErrorUser = NULL; assetUnlockEntry(chunk->dcfEntry); chunk->dcfEntry = NULL; } @@ -244,8 +247,12 @@ void mapChunkLoadNext() { continue; } - eventSubscribe(&entry->onLoaded, mapChunkLoaded, chunk); - eventSubscribe(&entry->onError, mapChunkLoadError, chunk); + assertNull(entry->onLoaded, "Entry already has an onLoaded subscriber."); + assertNull(entry->onError, "Entry already has an onError subscriber."); + entry->onLoaded = mapChunkLoaded; + entry->onLoadedUser = chunk; + entry->onError = mapChunkLoadError; + entry->onErrorUser = chunk; } } @@ -385,10 +392,9 @@ void mapRebuildChunkOrder() { } } -void mapChunkLoadError(void *params, void *user) { - assertNotNull(params, "mapChunkLoadError: params cannot be NULL"); +void mapChunkLoadError(assetentry_t *entry, void *user) { + assertNotNull(entry, "mapChunkLoadError: entry cannot be NULL"); assertNotNull(user, "mapChunkLoadError: user cannot be NULL"); - assetentry_t *entry = (assetentry_t *)params; chunk_t *chunk = (chunk_t *)user; if(chunk->dcfEntry != entry) return; consolePrint( @@ -397,8 +403,10 @@ void mapChunkLoadError(void *params, void *user) { (int32_t)chunk->position.y, (int32_t)chunk->position.z ); - eventUnsubscribe(&entry->onLoaded, mapChunkLoaded); - eventUnsubscribe(&entry->onError, mapChunkLoadError); + entry->onLoaded = NULL; + entry->onLoadedUser = NULL; + entry->onError = NULL; + entry->onErrorUser = NULL; assetUnlockEntry(chunk->dcfEntry); chunk->dcfEntry = NULL; memorySet(chunk->tiles, 0x00, sizeof(chunk->tiles)); @@ -407,10 +415,9 @@ void mapChunkLoadError(void *params, void *user) { mapChunkLoadNext(); } -void mapChunkLoaded(void *params, void *user) { - assertNotNull(params, "mapChunkLoaded: params cannot be NULL"); +void mapChunkLoaded(assetentry_t *entry, void *user) { + assertNotNull(entry, "mapChunkLoaded: entry cannot be NULL"); assertNotNull(user, "mapChunkLoaded: user cannot be NULL"); - assetentry_t *entry = (assetentry_t *)params; chunk_t *chunk = (chunk_t *)user; if(chunk->dcfEntry != entry) return; // consolePrint( @@ -457,8 +464,10 @@ void mapChunkLoaded(void *params, void *user) { // modelEntries must still be intact for that next reuse to copy from. chunk->modelEntries[m] = entry->data.chunk.modelEntries[m]; } - eventUnsubscribe(&entry->onLoaded, mapChunkLoaded); - eventUnsubscribe(&entry->onError, mapChunkLoadError); + entry->onLoaded = NULL; + entry->onLoadedUser = NULL; + entry->onError = NULL; + entry->onErrorUser = NULL; // Deliberately keep chunk->dcfEntry locked and set - it is what keeps the // chunk asset entry (and therefore its model locks) alive for as long as // this chunk_t is displaying it. Released in mapChunkUnload instead. diff --git a/src/dusk/rpg/overworld/map.h b/src/dusk/rpg/overworld/map.h index 154f32f6..35e221d7 100644 --- a/src/dusk/rpg/overworld/map.h +++ b/src/dusk/rpg/overworld/map.h @@ -101,19 +101,19 @@ void mapChunkLoadQueueRemove(chunk_t *chunk); * chunk tiles with TILE_SHAPE_GROUND as a fallback. * Always invoked on the main thread. * - * @param params The failed assetentry_t. + * @param entry The failed assetentry_t. * @param user The chunk_t that owns the entry. */ -void mapChunkLoadError(void *params, void *user); +void mapChunkLoadError(assetentry_t *entry, void *user); /** * Callback invoked when a chunk DCF asset finishes loading. * Always invoked on the main thread. * - * @param params The loaded assetentry_t. + * @param entry The loaded assetentry_t. * @param user The chunk_t that owns the entry. */ -void mapChunkLoaded(void *params, void *user); +void mapChunkLoaded(assetentry_t *entry, void *user); /** * Rebuilds chunkOrder from the loaded chunks that fall within the diff --git a/src/dusk/ui/overlay/uifullbox.c b/src/dusk/ui/overlay/uifullbox.c index 0fdb6e74..dab307ba 100644 --- a/src/dusk/ui/overlay/uifullbox.c +++ b/src/dusk/ui/overlay/uifullbox.c @@ -20,12 +20,6 @@ uifullbox_t UI_FULLBOX_OVER; void uiFullboxInit(uifullbox_t *fullbox) { assertNotNull(fullbox, "fullbox must not be NULL"); memoryZero(fullbox, sizeof(uifullbox_t)); - eventInit( - &fullbox->onTransitionEnd, - fullbox->onTransitionEndCallbacks, - fullbox->onTransitionEndUsers, - 4 - ); } void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta) { @@ -35,7 +29,9 @@ void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta) { fullbox->time += delta; if(fullbox->time >= fullbox->duration) { fullbox->time = fullbox->duration; - eventInvoke(&fullbox->onTransitionEnd, fullbox); + if(fullbox->onTransitionEnd) { + fullbox->onTransitionEnd(fullbox, fullbox->onTransitionEndUser); + } } } diff --git a/src/dusk/ui/overlay/uifullbox.h b/src/dusk/ui/overlay/uifullbox.h index 5c1accd3..46950a43 100644 --- a/src/dusk/ui/overlay/uifullbox.h +++ b/src/dusk/ui/overlay/uifullbox.h @@ -9,18 +9,26 @@ #include "error/error.h" #include "display/color.h" #include "animation/easing.h" -#include "event/event.h" -typedef struct { +typedef struct uifullbox_s uifullbox_t; + +/** + * Callback fired once when a fullbox transition completes. + * + * @param fullbox The uifullbox_t the transition ran on. + * @param user The user pointer passed alongside the callback. + */ +typedef void (*uifullboxcallback_t)(uifullbox_t *fullbox, void *user); + +struct uifullbox_s { color_t fromColor; color_t toColor; float_t duration; float_t time; easingtype_t easing; - eventcallback_t onTransitionEndCallbacks[4]; - void *onTransitionEndUsers[4]; - event_t onTransitionEnd; -} uifullbox_t; + uifullboxcallback_t onTransitionEnd; + void *onTransitionEndUser; +}; extern uifullbox_t UI_FULLBOX_UNDER; extern uifullbox_t UI_FULLBOX_OVER; diff --git a/src/dusk/ui/overlay/uiloading.c b/src/dusk/ui/overlay/uiloading.c index 3f6a045b..ad52345a 100644 --- a/src/dusk/ui/overlay/uiloading.c +++ b/src/dusk/ui/overlay/uiloading.c @@ -20,12 +20,6 @@ uiloading_t UI_LOADING; errorret_t uiLoadingInit(void) { memoryZero(&UI_LOADING, sizeof(uiloading_t)); - eventInit( - &UI_LOADING.onTransitionEnd, - UI_LOADING.onTransitionEndCallbacks, - UI_LOADING.onTransitionEndUsers, - 4 - ); errorChain(assetLocaleGetString( &LOCALE.entry->data.locale, @@ -43,7 +37,9 @@ errorret_t uiLoadingUpdate(void) { UI_LOADING.time += TIME.delta; if(UI_LOADING.time >= UI_LOADING.duration) { UI_LOADING.time = UI_LOADING.duration; - eventInvoke(&UI_LOADING.onTransitionEnd, &UI_LOADING); + if(UI_LOADING.onTransitionEnd) { + UI_LOADING.onTransitionEnd(&UI_LOADING, UI_LOADING.onTransitionEndUser); + } } } errorOk(); @@ -76,26 +72,21 @@ errorret_t uiLoadingDraw(void) { return spriteBatchFlush(); } -static void uiLoadingTransition( +void uiLoadingTransition( const float_t from, const float_t to, - const eventcallback_t callback, + const uiloadingcallback_t callback, void *user ) { UI_LOADING.fromAlpha = from; UI_LOADING.toAlpha = to; UI_LOADING.duration = UI_LOADING_FADE_DURATION; UI_LOADING.time = 0.0f; - eventInit( - &UI_LOADING.onTransitionEnd, - UI_LOADING.onTransitionEndCallbacks, - UI_LOADING.onTransitionEndUsers, - 4 - ); - if(callback) eventSubscribe(&UI_LOADING.onTransitionEnd, callback, user); + UI_LOADING.onTransitionEnd = callback; + UI_LOADING.onTransitionEndUser = user; } -void uiLoadingShow(eventcallback_t callback, void *user) { +void uiLoadingShow(uiloadingcallback_t callback, void *user) { uiLoadingTransition(0.0f, 1.0f, callback, user); uiFullboxTransition( &UI_FULLBOX_OVER, @@ -106,7 +97,7 @@ void uiLoadingShow(eventcallback_t callback, void *user) { ); } -void uiLoadingHide(eventcallback_t callback, void *user) { +void uiLoadingHide(uiloadingcallback_t callback, void *user) { uiLoadingTransition(1.0f, 0.0f, callback, user); uiFullboxTransition( &UI_FULLBOX_OVER, diff --git a/src/dusk/ui/overlay/uiloading.h b/src/dusk/ui/overlay/uiloading.h index 096cc57b..531d4f23 100644 --- a/src/dusk/ui/overlay/uiloading.h +++ b/src/dusk/ui/overlay/uiloading.h @@ -7,22 +7,30 @@ #pragma once #include "error/error.h" -#include "event/event.h" #define UI_LOADING_FADE_DURATION 0.5f #define UI_LOADING_MARGIN 8.0f #define UI_LOADING_TEXT_MAX 32 -typedef struct { +typedef struct uiloading_s uiloading_t; + +/** + * Callback fired once when a loading indicator fade transition completes. + * + * @param loading The uiloading_t the transition ran on. + * @param user The user pointer passed alongside the callback. + */ +typedef void (*uiloadingcallback_t)(uiloading_t *loading, void *user); + +struct uiloading_s { float_t fromAlpha; float_t toAlpha; float_t duration; float_t time; - eventcallback_t onTransitionEndCallbacks[4]; - void *onTransitionEndUsers[4]; - event_t onTransitionEnd; + uiloadingcallback_t onTransitionEnd; + void *onTransitionEndUser; char_t text[UI_LOADING_TEXT_MAX]; -} uiloading_t; +}; extern uiloading_t UI_LOADING; @@ -48,13 +56,29 @@ errorret_t uiLoadingUpdate(void); */ errorret_t uiLoadingDraw(void); +/** + * Begins a loading indicator fade transition, replacing any transition + * already in progress along with its pending callback. + * + * @param from Starting alpha. + * @param to Ending alpha. + * @param callback Called when the transition completes. May be NULL. + * @param user Forwarded to the callback unchanged. + */ +void uiLoadingTransition( + const float_t from, + const float_t to, + const uiloadingcallback_t callback, + void *user +); + /** * Fades the loading indicator in. Invokes callback when fully visible. * * @param callback Called when the fade-in completes. May be NULL. * @param user Forwarded to the callback unchanged. */ -void uiLoadingShow(eventcallback_t callback, void *user); +void uiLoadingShow(uiloadingcallback_t callback, void *user); /** * Fades the loading indicator out. Invokes callback when fully hidden. @@ -62,4 +86,4 @@ void uiLoadingShow(eventcallback_t callback, void *user); * @param callback Called when the fade-out completes. May be NULL. * @param user Forwarded to the callback unchanged. */ -void uiLoadingHide(eventcallback_t callback, void *user); +void uiLoadingHide(uiloadingcallback_t callback, void *user); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 309ad5e6..986b88bf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,7 +8,6 @@ add_subdirectory(assert) add_subdirectory(asset) add_subdirectory(console) add_subdirectory(error) -add_subdirectory(event) add_subdirectory(thread) add_subdirectory(display) add_subdirectory(rpg) diff --git a/test/event/CMakeLists.txt b/test/event/CMakeLists.txt deleted file mode 100644 index 8614a292..00000000 --- a/test/event/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -include(dusktest) - -# Tests -dusktest(test_event.c) diff --git a/test/event/test_event.c b/test/event/test_event.c deleted file mode 100644 index 350daeb7..00000000 --- a/test/event/test_event.c +++ /dev/null @@ -1,443 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "dusktest.h" -#include "event/event.h" - -typedef struct { - int32_t callCount; - void *lastParams; - void *lastUser; -} eventrecord_t; - -// Distinct function pointers are required per-subscriber - the same callback -// pointer cannot be subscribed twice, even with a different user pointer. -static void helper_recordA(void *params, void *user) { - eventrecord_t *record = (eventrecord_t *)user; - record->callCount++; - record->lastParams = params; - record->lastUser = user; -} -static void helper_recordB(void *params, void *user) { - eventrecord_t *record = (eventrecord_t *)user; - record->callCount++; - record->lastParams = params; - record->lastUser = user; -} -static void helper_recordC(void *params, void *user) { - eventrecord_t *record = (eventrecord_t *)user; - record->callCount++; - record->lastParams = params; - record->lastUser = user; -} - -// Records via `params` instead of `user` - for cases exercising a NULL user -// (e.g. no users array), where dereferencing `user` would crash. -static void helper_recordViaParams(void *params, void *user) { - eventrecord_t *record = (eventrecord_t *)params; - record->callCount++; - record->lastParams = params; - record->lastUser = user; -} - -#define EVENT_CAPACITY 4 - -// --- eventInit --- - -static void test_eventInitSetsUpBackingArrays(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - memset(callbacks, 0xAA, sizeof(callbacks)); - memset(users, 0xAA, sizeof(users)); - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - - assert_ptr_equal(event.callbacks, callbacks); - assert_ptr_equal(event.users, users); - assert_int_equal(event.size, EVENT_CAPACITY); - assert_int_equal(event.count, 0); - for(int32_t i = 0; i < EVENT_CAPACITY; i++) { - assert_null(callbacks[i]); - assert_null(users[i]); - } -} - -static void test_eventInitAllowsNullUsersArray(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - - event_t event; - eventInit(&event, callbacks, NULL, EVENT_CAPACITY); - - assert_null(event.users); -} - -static void test_eventInitResetClearsSubscribersOnly(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t record = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &record); - assert_int_equal(event.count, 1); - - // Re-init (reset) with the same backing arrays. - eventInit(&event, callbacks, users, EVENT_CAPACITY); - - assert_ptr_equal(event.callbacks, callbacks); - assert_int_equal(event.count, 0); - assert_null(callbacks[0]); - assert_null(users[0]); -} - -static void test_eventInitNullEventAsserts(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - expect_assert_failure(eventInit(NULL, callbacks, NULL, EVENT_CAPACITY)); -} - -static void test_eventInitNullCallbacksAsserts(void **state) { - event_t event; - expect_assert_failure(eventInit(&event, NULL, NULL, EVENT_CAPACITY)); -} - -static void test_eventInitZeroSizeAsserts(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - event_t event; - expect_assert_failure(eventInit(&event, callbacks, NULL, 0)); -} - -// --- eventSubscribe --- - -static void test_eventSubscribeAddsCallback(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t record = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &record); - - assert_int_equal(event.count, 1); - assert_ptr_equal(event.callbacks[0], helper_recordA); - assert_ptr_equal(event.users[0], &record); -} - -static void test_eventSubscribeMultiple(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t recordA = { 0 }, recordB = { 0 }, recordC = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &recordA); - eventSubscribe(&event, helper_recordB, &recordB); - eventSubscribe(&event, helper_recordC, &recordC); - - assert_int_equal(event.count, 3); -} - -static void test_eventSubscribeNullUserLeavesSlotNull(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, NULL); - - assert_null(event.users[0]); -} - -static void test_eventSubscribeNullUserAfterUnsubscribeStaysNull(void **state) { - // The vacated slot must actually be cleared, not left holding a stale - // user pointer from whatever previously occupied it. - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t recordA = { 0 }, recordB = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &recordA); - eventSubscribe(&event, helper_recordB, &recordB); - eventUnsubscribe(&event, helper_recordA); - - eventSubscribe(&event, helper_recordC, NULL); - - for(int32_t i = 0; i < event.count; i++) { - if(event.callbacks[i] == helper_recordC) { - assert_null(event.users[i]); - return; - } - } - fail_msg("helper_recordC was not found after subscribing."); -} - -static void test_eventSubscribeUserWithoutUsersArrayAsserts(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - eventrecord_t record = { 0 }; - - event_t event; - eventInit(&event, callbacks, NULL, EVENT_CAPACITY); - - expect_assert_failure(eventSubscribe(&event, helper_recordA, &record)); -} - -static void test_eventSubscribeDuplicateCallbackAsserts(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t record = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &record); - - expect_assert_failure(eventSubscribe(&event, helper_recordA, &record)); -} - -static void test_eventSubscribeSameCallbackDifferentUserStillAsserts(void **state) { - // Documented as "the same (callback, user) pair may only be subscribed - // once", implying a different user should be fine - but the actual - // uniqueness check only looks at the callback pointer, so this asserts - // too. Captures actual behavior; flag if the doc/implementation should - // instead match on the (callback, user) pair. - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t recordA = { 0 }, recordB = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &recordA); - - expect_assert_failure(eventSubscribe(&event, helper_recordA, &recordB)); -} - -static void test_eventSubscribeCapacityExceededAsserts(void **state) { - eventcallback_t callbacks[2]; - void *users[2]; - eventrecord_t recordA = { 0 }, recordB = { 0 }, recordC = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, 2); - eventSubscribe(&event, helper_recordA, &recordA); - eventSubscribe(&event, helper_recordB, &recordB); - - expect_assert_failure(eventSubscribe(&event, helper_recordC, &recordC)); -} - -static void test_eventSubscribeNullEventAsserts(void **state) { - expect_assert_failure(eventSubscribe(NULL, helper_recordA, NULL)); -} - -static void test_eventSubscribeNullCallbackAsserts(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - event_t event; - eventInit(&event, callbacks, NULL, EVENT_CAPACITY); - expect_assert_failure(eventSubscribe(&event, NULL, NULL)); -} - -// --- eventUnsubscribe --- - -static void test_eventUnsubscribeMiddleSwapsLastIntoPlace(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t recordA = { 0 }, recordB = { 0 }, recordC = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &recordA); - eventSubscribe(&event, helper_recordB, &recordB); - eventSubscribe(&event, helper_recordC, &recordC); - - eventUnsubscribe(&event, helper_recordB); - - assert_int_equal(event.count, 2); - assert_ptr_equal(event.callbacks[0], helper_recordA); - // recordC (previously last) was swapped into the vacated middle slot. - assert_ptr_equal(event.callbacks[1], helper_recordC); - assert_ptr_equal(event.users[1], &recordC); - // The old tail slot is fully cleared. - assert_null(event.callbacks[2]); - assert_null(event.users[2]); -} - -static void test_eventUnsubscribeLastElement(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t recordA = { 0 }, recordB = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &recordA); - eventSubscribe(&event, helper_recordB, &recordB); - - eventUnsubscribe(&event, helper_recordB); - - assert_int_equal(event.count, 1); - assert_ptr_equal(event.callbacks[0], helper_recordA); - assert_null(event.callbacks[1]); -} - -static void test_eventUnsubscribeNotSubscribedIsNoop(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t recordA = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &recordA); - - eventUnsubscribe(&event, helper_recordB); - - assert_int_equal(event.count, 1); - assert_ptr_equal(event.callbacks[0], helper_recordA); -} - -static void test_eventUnsubscribeThenResubscribeSucceeds(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t recordA = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &recordA); - eventUnsubscribe(&event, helper_recordA); - - // Would have asserted (duplicate) had the removal not actually happened. - eventSubscribe(&event, helper_recordA, &recordA); - - assert_int_equal(event.count, 1); -} - -static void test_eventUnsubscribeWithNullUsersArrayIsSafe(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - - event_t event; - eventInit(&event, callbacks, NULL, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, NULL); - eventUnsubscribe(&event, helper_recordA); - - assert_int_equal(event.count, 0); -} - -static void test_eventUnsubscribeNullEventAsserts(void **state) { - expect_assert_failure(eventUnsubscribe(NULL, helper_recordA)); -} - -static void test_eventUnsubscribeNullCallbackAsserts(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - event_t event; - eventInit(&event, callbacks, NULL, EVENT_CAPACITY); - expect_assert_failure(eventUnsubscribe(&event, NULL)); -} - -// --- eventInvoke --- - -static void test_eventInvokeCallsAllSubscribersWithParamsAndUser(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t recordA = { 0 }, recordB = { 0 }; - int32_t params = 123; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &recordA); - eventSubscribe(&event, helper_recordB, &recordB); - - eventInvoke(&event, ¶ms); - - assert_int_equal(recordA.callCount, 1); - assert_ptr_equal(recordA.lastParams, ¶ms); - assert_ptr_equal(recordA.lastUser, &recordA); - - assert_int_equal(recordB.callCount, 1); - assert_ptr_equal(recordB.lastParams, ¶ms); - assert_ptr_equal(recordB.lastUser, &recordB); -} - -static void test_eventInvokeWithNoSubscribersDoesNothing(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - - // No subscribers exist, so there is nothing to observe beyond "no crash". - eventInvoke(&event, NULL); -} - -static void test_eventInvokeWithNullUsersArrayPassesNull(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - eventrecord_t record = { 0 }; - - event_t event; - eventInit(&event, callbacks, NULL, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordViaParams, NULL); - - eventInvoke(&event, &record); - - assert_int_equal(record.callCount, 1); - assert_ptr_equal(record.lastParams, &record); - assert_null(record.lastUser); -} - -static void test_eventInvokeSkipsUnsubscribedCallback(void **state) { - eventcallback_t callbacks[EVENT_CAPACITY]; - void *users[EVENT_CAPACITY]; - eventrecord_t recordA = { 0 }, recordB = { 0 }; - - event_t event; - eventInit(&event, callbacks, users, EVENT_CAPACITY); - eventSubscribe(&event, helper_recordA, &recordA); - eventSubscribe(&event, helper_recordB, &recordB); - eventUnsubscribe(&event, helper_recordA); - - eventInvoke(&event, NULL); - - assert_int_equal(recordA.callCount, 0); - assert_int_equal(recordB.callCount, 1); -} - -static void test_eventInvokeNullEventAsserts(void **state) { - expect_assert_failure(eventInvoke(NULL, NULL)); -} - -int main(void) { - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_eventInitSetsUpBackingArrays), - cmocka_unit_test(test_eventInitAllowsNullUsersArray), - cmocka_unit_test(test_eventInitResetClearsSubscribersOnly), - cmocka_unit_test(test_eventInitNullEventAsserts), - cmocka_unit_test(test_eventInitNullCallbacksAsserts), - cmocka_unit_test(test_eventInitZeroSizeAsserts), - - cmocka_unit_test(test_eventSubscribeAddsCallback), - cmocka_unit_test(test_eventSubscribeMultiple), - cmocka_unit_test(test_eventSubscribeNullUserLeavesSlotNull), - cmocka_unit_test(test_eventSubscribeNullUserAfterUnsubscribeStaysNull), - cmocka_unit_test(test_eventSubscribeUserWithoutUsersArrayAsserts), - cmocka_unit_test(test_eventSubscribeDuplicateCallbackAsserts), - cmocka_unit_test(test_eventSubscribeSameCallbackDifferentUserStillAsserts), - cmocka_unit_test(test_eventSubscribeCapacityExceededAsserts), - cmocka_unit_test(test_eventSubscribeNullEventAsserts), - cmocka_unit_test(test_eventSubscribeNullCallbackAsserts), - - cmocka_unit_test(test_eventUnsubscribeMiddleSwapsLastIntoPlace), - cmocka_unit_test(test_eventUnsubscribeLastElement), - cmocka_unit_test(test_eventUnsubscribeNotSubscribedIsNoop), - cmocka_unit_test(test_eventUnsubscribeThenResubscribeSucceeds), - cmocka_unit_test(test_eventUnsubscribeWithNullUsersArrayIsSafe), - cmocka_unit_test(test_eventUnsubscribeNullEventAsserts), - cmocka_unit_test(test_eventUnsubscribeNullCallbackAsserts), - - cmocka_unit_test(test_eventInvokeCallsAllSubscribersWithParamsAndUser), - cmocka_unit_test(test_eventInvokeWithNoSubscribersDoesNothing), - cmocka_unit_test(test_eventInvokeWithNullUsersArrayPassesNull), - cmocka_unit_test(test_eventInvokeSkipsUnsubscribedCallback), - cmocka_unit_test(test_eventInvokeNullEventAsserts), - }; - return cmocka_run_group_tests(tests, NULL, NULL); -}