Some label fixes

This commit is contained in:
2026-06-25 19:56:14 -05:00
parent d85737cc08
commit 722fe2ccfb
30 changed files with 290 additions and 105 deletions
+1 -2
View File
@@ -14,9 +14,8 @@ cmake_policy(SET CMP0079 NEW)
option(DUSK_BUILD_TESTS "Enable tests" OFF) option(DUSK_BUILD_TESTS "Enable tests" OFF)
# Game identity — override these per-project
set(DUSK_GAME_NAME "Dusk" CACHE STRING "Game display name") set(DUSK_GAME_NAME "Dusk" CACHE STRING "Game display name")
set(DUSK_GAME_AUTHOR "YouWish" CACHE STRING "Game author / coder") set(DUSK_GAME_AUTHOR "YourWishes" CACHE STRING "Game author / coder")
set(DUSK_GAME_SHORT_DESCRIPTION "Dusk game" CACHE STRING "One-line description") set(DUSK_GAME_SHORT_DESCRIPTION "Dusk game" CACHE STRING "One-line description")
set(DUSK_GAME_LONG_DESCRIPTION "No description yet." CACHE STRING "Full description") set(DUSK_GAME_LONG_DESCRIPTION "No description yet." CACHE STRING "Full description")
+1 -1
View File
@@ -45,7 +45,7 @@ endif()
# Link libraries. # Link libraries.
# zip/z/lzma use target_link_options (raw flags) to bypass cmake target # zip/z/lzma use target_link_options (raw flags) to bypass cmake target
# resolution pkg-config-generated targets for these carry ZLIB::ZLIB in # resolution - pkg-config-generated targets for these carry ZLIB::ZLIB in
# INTERFACE_LINK_LIBRARIES which breaks the PPC link step. # INTERFACE_LINK_LIBRARIES which breaks the PPC link step.
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
cglm cglm
+29 -14
View File
@@ -18,7 +18,7 @@ void uiSettingsSelected(
const uint8_t index, const uint8_t index,
const uimenuitem_t *item const uimenuitem_t *item
) { ) {
if(item->type == UIMENU_WIDGET_TYPE_CHECKBOX) { if(item->type == UI_MENU_WIDGET_TYPE_CHECKBOX) {
uiCheckboxToggle(&UI_SETTINGS.items[index].checkbox); uiCheckboxToggle(&UI_SETTINGS.items[index].checkbox);
} }
} }
@@ -30,27 +30,42 @@ void uiSettingsClosed(const uimenu_t *menu) {
errorret_t uiSettingsInit(void) { errorret_t uiSettingsInit(void) {
memoryZero(&UI_SETTINGS, sizeof(uisettings_t)); memoryZero(&UI_SETTINGS, sizeof(uisettings_t));
UI_SETTINGS.items[0] = (uimenuitem_t){ uint8_t i = 0;
.type = UIMENU_WIDGET_TYPE_LABEL,
UI_SETTINGS.items[i] = (uimenuitem_t){
.type = UI_MENU_WIDGET_TYPE_LABEL,
.label = "Settings" .label = "Settings"
}; };
UI_SETTINGS.items[1] = (uimenuitem_t){ ++i;
.type = UIMENU_WIDGET_TYPE_LABEL,
.label = "Settings two" UI_SETTINGS.items[i].type = UI_MENU_WIDGET_TYPE_BUTTON;
uiButtonInit(&UI_SETTINGS.items[i].button, "Do thing");
++i;
UI_SETTINGS.items[i].type = UI_MENU_WIDGET_TYPE_BUTTON;
uiButtonInit(&UI_SETTINGS.items[i].button, "Do other thing");
++i;
UI_SETTINGS.items[i].type = UI_MENU_WIDGET_TYPE_SPACER;
++i;
UI_SETTINGS.items[i] = (uimenuitem_t){
.type = UI_MENU_WIDGET_TYPE_LABEL,
.label = "Options"
}; };
UI_SETTINGS.items[2] = (uimenuitem_t){ ++i;
.type = UIMENU_WIDGET_TYPE_LABEL,
.label = "Settings three" UI_SETTINGS.items[i].type = UI_MENU_WIDGET_TYPE_CHECKBOX;
}; uiCheckboxInit(&UI_SETTINGS.items[i].checkbox, "Enable thing?");
UI_SETTINGS.items[3].type = UIMENU_WIDGET_TYPE_CHECKBOX; ++i;
uiCheckboxInit(&UI_SETTINGS.items[3].checkbox, "Enable thing?");
assertTrue(i <= UI_SETTINGS_ITEM_COUNT, "Settings item count mismatch");
uiMenuInit(&UI_SETTINGS.menu, uiSettingsSelected, uiSettingsClosed, NULL); uiMenuInit(&UI_SETTINGS.menu, uiSettingsSelected, uiSettingsClosed, NULL);
uiMenuSetItems( uiMenuSetItems(
&UI_SETTINGS.menu, &UI_SETTINGS.menu,
UI_SETTINGS.items, UI_SETTINGS.items,
UI_SETTINGS_ITEM_COUNT, i, 1
2
); );
errorOk(); errorOk();
+1 -1
View File
@@ -9,7 +9,7 @@
#include "error/error.h" #include "error/error.h"
#include "ui/widget/uimenu.h" #include "ui/widget/uimenu.h"
#define UI_SETTINGS_ITEM_COUNT 4 #define UI_SETTINGS_ITEM_COUNT 6
typedef struct { typedef struct {
uimenu_t menu; uimenu_t menu;
+1
View File
@@ -5,6 +5,7 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME} target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
uibutton.c
uicheckbox.c uicheckbox.c
uimenu.c uimenu.c
) )
+44
View File
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uibutton.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "display/text/text.h"
#include "display/color.h"
void uiButtonInit(uibutton_t *button, const char_t *label) {
assertNotNull(button, "Button cannot be NULL");
assertNotNull(label, "Label cannot be NULL");
memoryZero(button, sizeof(uibutton_t));
button->label = label;
}
bool_t uiButtonIsHighlighted(const uibutton_t *button) {
assertNotNull(button, "Button cannot be NULL");
return button->highlighted;
}
void uiButtonSetHighlighted(uibutton_t *button, const bool_t highlighted) {
assertNotNull(button, "Button cannot be NULL");
button->highlighted = highlighted;
}
errorret_t uiButtonDraw(
const uibutton_t *button,
const float_t x,
const float_t y
) {
assertNotNull(button, "Button cannot be NULL");
errorChain(textDraw(
x, y, button->label,
button->highlighted ? COLOR_RED : COLOR_WHITE,
&FONT_DEFAULT
));
errorOk();
}
+54
View File
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#define UI_BUTTON_LABEL_MAX 32
typedef struct {
const char_t *label;
bool_t highlighted;
} uibutton_t;
/**
* Initializes a button.
*
* @param button The button to initialize.
* @param label Display label.
*/
void uiButtonInit(uibutton_t *button, const char_t *label);
/**
* Returns whether the button is highlighted.
*
* @param button The button to query.
* @returns True if highlighted.
*/
bool_t uiButtonIsHighlighted(const uibutton_t *button);
/**
* Sets the highlighted state of the button.
*
* @param button The button to update.
* @param highlighted The new highlighted state.
*/
void uiButtonSetHighlighted(uibutton_t *button, const bool_t highlighted);
/**
* Draws the button at the given screen position.
*
* @param button The button to draw.
* @param x Screen x position.
* @param y Screen y position.
* @return Any error that occurs.
*/
errorret_t uiButtonDraw(
const uibutton_t *button,
const float_t x,
const float_t y
);
+1 -1
View File
@@ -16,7 +16,7 @@ void uiCheckboxInit(
const char_t *label const char_t *label
) { ) {
memoryZero(checkbox, sizeof(uicheckbox_t)); memoryZero(checkbox, sizeof(uicheckbox_t));
stringCopy(checkbox->label, label, UI_CHECKBOX_LABEL_MAX); checkbox->label = label;
} }
bool_t uiCheckboxIsChecked(const uicheckbox_t *checkbox) { bool_t uiCheckboxIsChecked(const uicheckbox_t *checkbox) {
+2 -4
View File
@@ -8,10 +8,8 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#define UI_CHECKBOX_LABEL_MAX 32
typedef struct uicheckbox_s { typedef struct uicheckbox_s {
char_t label[UI_CHECKBOX_LABEL_MAX]; const char_t *label;
bool_t checked; bool_t checked;
bool_t highlighted; bool_t highlighted;
} uicheckbox_t; } uicheckbox_t;
@@ -20,7 +18,7 @@ typedef struct uicheckbox_s {
* Initializes a checkbox. * Initializes a checkbox.
* *
* @param checkbox The checkbox to initialize. * @param checkbox The checkbox to initialize.
* @param label Display label, truncated to UI_CHECKBOX_LABEL_MAX - 1 chars. * @param label Display label.
*/ */
void uiCheckboxInit( void uiCheckboxInit(
uicheckbox_t *checkbox, uicheckbox_t *checkbox,
+73 -21
View File
@@ -10,6 +10,7 @@
#include "util/memory.h" #include "util/memory.h"
#include "display/text/text.h" #include "display/text/text.h"
#include "display/color.h" #include "display/color.h"
#include "ui/widget/uibutton.h"
void uiMenuInit( void uiMenuInit(
uimenu_t *menu, uimenu_t *menu,
@@ -52,7 +53,9 @@ void uiMenuOpen(uimenu_t *menu) {
assertTrue(menu->columns > 0, "Menu columns must be > 0"); assertTrue(menu->columns > 0, "Menu columns must be > 0");
if(menu->focusItem != NULL) return; if(menu->focusItem != NULL) return;
uint8_t rows = (menu->itemCount + menu->columns - 1) / menu->columns; uint8_t focusable = uiMenuFocusableCount(menu);
uint8_t rows = focusable > 0 ?
(focusable + menu->columns - 1) / menu->columns : 1;
menu->focusItem = uiFocusPush( menu->focusItem = uiFocusPush(
menu->columns, rows, menu->columns, rows,
uiMenuFocusSelected, uiMenuFocusSelected,
@@ -87,34 +90,49 @@ errorret_t uiMenuDraw(
float_t colStep = width / (float_t)menu->columns; float_t colStep = width / (float_t)menu->columns;
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight; float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
uint8_t focusIndex = menu->focusItem != NULL ? uint8_t col = 0;
menu->focusItem->y * menu->columns + menu->focusItem->x : uint8_t row = 0;
0xFF;
for(uint8_t i = 0; i < menu->itemCount; i++) { for(uint8_t i = 0; i < menu->itemCount; i++) {
const uimenuitem_t *item = &menu->items[i]; const uimenuitem_t *item = &menu->items[i];
uint8_t col = i % menu->columns;
uint8_t row = i / menu->columns;
float_t ix = x + (float_t)col * colStep;
float_t iy = y + (float_t)row * rowHeight;
bool_t highlighted = i == focusIndex;
switch(item->type) { if(item->type == UI_MENU_WIDGET_TYPE_LABEL) {
case UIMENU_WIDGET_TYPE_LABEL: if(col > 0) { row++; col = 0; }
errorChain(textDraw( errorChain(textDraw(
ix, iy, item->label, x,
highlighted ? COLOR_RED : COLOR_WHITE, y + (float_t)row * rowHeight,
item->label,
COLOR_WHITE,
&FONT_DEFAULT &FONT_DEFAULT
)); ));
row++;
continue;
}
if(item->type == UI_MENU_WIDGET_TYPE_SPACER) {
if(col > 0) { row++; col = 0; }
row++;
continue;
}
float_t ix = x + (float_t)col * colStep;
float_t iy = y + (float_t)row * rowHeight;
switch(item->type) {
case UI_MENU_WIDGET_TYPE_CHECKBOX:
errorChain(uiCheckboxDraw(&item->checkbox, ix, iy));
break; break;
case UIMENU_WIDGET_TYPE_CHECKBOX: case UI_MENU_WIDGET_TYPE_BUTTON:
errorChain(uiCheckboxDraw(&item->checkbox, ix, iy)); errorChain(uiButtonDraw(&item->button, ix, iy));
break; break;
default: default:
break; break;
} }
col++;
if(col >= menu->columns) { col = 0; row++; }
} }
errorOk(); errorOk();
@@ -126,8 +144,9 @@ bool_t uiMenuFocusSelected(const uifocusitem_t *focusItem) {
uimenu_t *menu = (uimenu_t *)focusItem->user; uimenu_t *menu = (uimenu_t *)focusItem->user;
if(menu->selected == NULL) return true; if(menu->selected == NULL) return true;
uint8_t index = focusItem->y * menu->columns + focusItem->x; uint8_t slot = focusItem->y * menu->columns + focusItem->x;
if(index >= menu->itemCount) return true; uint8_t index = uiMenuFocusSlotToIndex(menu, slot);
if(index == 0xFF) return true;
menu->selected(menu, index, &menu->items[index]); menu->selected(menu, index, &menu->items[index]);
return true; return true;
@@ -138,16 +157,27 @@ bool_t uiMenuFocusChanged(const uifocusitem_t *focusItem) {
assertNotNull(focusItem->user, "Focus item user cannot be NULL"); assertNotNull(focusItem->user, "Focus item user cannot be NULL");
uimenu_t *menu = (uimenu_t *)focusItem->user; uimenu_t *menu = (uimenu_t *)focusItem->user;
uint8_t index = focusItem->y * menu->columns + focusItem->x; uint8_t focusSlot = focusItem->y * menu->columns + focusItem->x;
uint8_t slot = 0;
for(uint8_t i = 0; i < menu->itemCount; i++) { for(uint8_t i = 0; i < menu->itemCount; i++) {
uimenuitem_t *item = &menu->items[i]; uimenuitem_t *item = &menu->items[i];
if(item->type != UIMENU_WIDGET_TYPE_CHECKBOX) continue; if(
uiCheckboxSetHighlighted(&item->checkbox, i == index); item->type == UI_MENU_WIDGET_TYPE_LABEL ||
item->type == UI_MENU_WIDGET_TYPE_SPACER
) continue;
bool_t isActive = (slot == focusSlot);
if(item->type == UI_MENU_WIDGET_TYPE_CHECKBOX) {
uiCheckboxSetHighlighted(&item->checkbox, isActive);
} else if(item->type == UI_MENU_WIDGET_TYPE_BUTTON) {
uiButtonSetHighlighted(&item->button, isActive);
}
slot++;
} }
if(menu->changed == NULL) return true; if(menu->changed == NULL) return true;
if(index >= menu->itemCount) return true; uint8_t index = uiMenuFocusSlotToIndex(menu, focusSlot);
if(index == 0xFF) return true;
menu->changed(menu, index, &menu->items[index]); menu->changed(menu, index, &menu->items[index]);
return true; return true;
@@ -161,3 +191,25 @@ bool_t uiMenuFocusClosed(const uifocusitem_t *focusItem) {
if(menu->closed != NULL) menu->closed(menu); if(menu->closed != NULL) menu->closed(menu);
return true; return true;
} }
uint8_t uiMenuFocusableCount(const uimenu_t *menu) {
assertNotNull(menu, "Menu cannot be NULL");
uint8_t count = 0;
for(uint8_t i = 0; i < menu->itemCount; i++) {
uimenuwidgettype_t t = menu->items[i].type;
if(t != UI_MENU_WIDGET_TYPE_LABEL && t != UI_MENU_WIDGET_TYPE_SPACER) count++;
}
return count;
}
uint8_t uiMenuFocusSlotToIndex(const uimenu_t *menu, const uint8_t slot) {
assertNotNull(menu, "Menu cannot be NULL");
uint8_t focusable = 0;
for(uint8_t i = 0; i < menu->itemCount; i++) {
uimenuwidgettype_t t = menu->items[i].type;
if(t == UI_MENU_WIDGET_TYPE_LABEL || t == UI_MENU_WIDGET_TYPE_SPACER) continue;
if(focusable == slot) return i;
focusable++;
}
return 0xFF;
}
+29 -7
View File
@@ -8,6 +8,7 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "ui/focus/uifocus.h" #include "ui/focus/uifocus.h"
#include "ui/widget/uibutton.h"
#include "ui/widget/uicheckbox.h" #include "ui/widget/uicheckbox.h"
#define UI_MENU_LABEL_MAX UI_CHECKBOX_LABEL_MAX #define UI_MENU_LABEL_MAX UI_CHECKBOX_LABEL_MAX
@@ -15,16 +16,19 @@
typedef struct uimenu_s uimenu_t; typedef struct uimenu_s uimenu_t;
typedef enum { typedef enum {
UIMENU_WIDGET_TYPE_NONE, UI_MENU_WIDGET_TYPE_NONE,
UIMENU_WIDGET_TYPE_LABEL, UI_MENU_WIDGET_TYPE_LABEL,
UIMENU_WIDGET_TYPE_CHECKBOX, UI_MENU_WIDGET_TYPE_SPACER,
UI_MENU_WIDGET_TYPE_CHECKBOX,
UI_MENU_WIDGET_TYPE_BUTTON,
} uimenuwidgettype_t; } uimenuwidgettype_t;
typedef struct { typedef struct {
uimenuwidgettype_t type; uimenuwidgettype_t type;
union { union {
char_t label[UI_MENU_LABEL_MAX]; char_t *label;
uicheckbox_t checkbox; uicheckbox_t checkbox;
uibutton_t button;
}; };
} uimenuitem_t; } uimenuitem_t;
@@ -136,7 +140,25 @@ errorret_t uiMenuDraw(
); );
/** /**
* Internal focus callback — forwards selection to the menu's selected handler. * Returns the number of focusable (non-label) items in the menu.
*
* @param menu The menu to query.
* @returns Count of non-label items.
*/
uint8_t uiMenuFocusableCount(const uimenu_t *menu);
/**
* Maps a flat focus slot index to the corresponding item array index,
* skipping over label items which are not focusable.
*
* @param menu The menu to query.
* @param slot The focus slot index (y * columns + x).
* @returns The item array index, or 0xFF if out of range.
*/
uint8_t uiMenuFocusSlotToIndex(const uimenu_t *menu, const uint8_t slot);
/**
* Internal focus callback - forwards selection to the menu's selected handler.
* *
* @param focusItem The active focus item; user field must point to uimenu_t. * @param focusItem The active focus item; user field must point to uimenu_t.
* @returns True. * @returns True.
@@ -144,7 +166,7 @@ errorret_t uiMenuDraw(
bool_t uiMenuFocusSelected(const uifocusitem_t *focusItem); bool_t uiMenuFocusSelected(const uifocusitem_t *focusItem);
/** /**
* Internal focus callback updates checkbox highlights and fires changed. * Internal focus callback - updates widget highlights and fires changed.
* *
* @param focusItem The active focus item; user field must point to uimenu_t. * @param focusItem The active focus item; user field must point to uimenu_t.
* @returns True. * @returns True.
@@ -152,7 +174,7 @@ bool_t uiMenuFocusSelected(const uifocusitem_t *focusItem);
bool_t uiMenuFocusChanged(const uifocusitem_t *focusItem); bool_t uiMenuFocusChanged(const uifocusitem_t *focusItem);
/** /**
* Internal focus callback clears focusItem and fires the closed handler. * Internal focus callback - clears focusItem and fires the closed handler.
* *
* @param focusItem The active focus item; user field must point to uimenu_t. * @param focusItem The active focus item; user field must point to uimenu_t.
* @returns True. * @returns True.
+1 -1
View File
@@ -108,7 +108,7 @@ static void test_assertStrLenMin(void **state) {
} }
static void test_assertIsMainThread(void **state) { static void test_assertIsMainThread(void **state) {
// The group setup recorded this thread as main assertIsMainThread must pass. // The group setup recorded this thread as main - assertIsMainThread must pass.
assertIsMainThread("test thread is main, should not assert"); assertIsMainThread("test thread is main, should not assert");
// assertNotMainThread must fail when called from the main thread. // assertNotMainThread must fail when called from the main thread.
+10 -10
View File
@@ -40,7 +40,7 @@ static int asset_setup(void **state) {
// Save real callbacks so we can restore them in teardown. // Save real callbacks so we can restore them in teardown.
memoryCopy(saved_callbacks, ASSET_LOADER_CALLBACKS, sizeof(saved_callbacks)); memoryCopy(saved_callbacks, ASSET_LOADER_CALLBACKS, sizeof(saved_callbacks));
// Manually init ASSET no thread, no ZIP. // Manually init ASSET - no thread, no ZIP.
memoryZero(&ASSET, sizeof(ASSET)); memoryZero(&ASSET, sizeof(ASSET));
for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
threadMutexInit(&ASSET.loading[i].mutex); threadMutexInit(&ASSET.loading[i].mutex);
@@ -117,7 +117,7 @@ static void test_getEntry_distinct_names(void **state) {
} }
// ============================================================ // ============================================================
// assetUpdate state machine tests // assetUpdate - state machine tests
// ============================================================ // ============================================================
static void test_update_entry_reaches_loaded(void **state) { static void test_update_entry_reaches_loaded(void **state) {
@@ -252,7 +252,7 @@ static void test_update_overflow_queues_entries(void **state) {
} }
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_NOT_STARTED); assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_NOT_STARTED);
// Unlock first batch the reaper can collect them in update 2. // Unlock first batch - the reaper can collect them in update 2.
for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
assetEntryUnlock(entries[i]); assetEntryUnlock(entries[i]);
} }
@@ -284,7 +284,7 @@ static void test_update_error_slot_stays_occupied(void **state) {
assetUpdate(); assetUpdate();
assert_int_equal(entry->state, ASSET_ENTRY_STATE_ERROR); assert_int_equal(entry->state, ASSET_ENTRY_STATE_ERROR);
// Unlike LOADED, the ERROR case does NOT clear the slot it throws instead. // Unlike LOADED, the ERROR case does NOT clear the slot - it throws instead.
assert_true(loading_slot_has_entry(entry)); assert_true(loading_slot_has_entry(entry));
errorret_t ret = assetUpdate(); errorret_t ret = assetUpdate();
@@ -295,7 +295,7 @@ static void test_update_error_slot_stays_occupied(void **state) {
} }
// ============================================================ // ============================================================
// assetEntryInit input copy // assetEntryInit - input copy
// ============================================================ // ============================================================
static void test_getEntry_null_input_stays_null(void **state) { static void test_getEntry_null_input_stays_null(void **state) {
@@ -312,7 +312,7 @@ static void test_getEntry_input_copied_into_entry(void **state) {
assetentry_t *entry = assetGetEntry("test.texture", ASSET_LOADER_TYPE_TEXTURE, &input); assetentry_t *entry = assetGetEntry("test.texture", ASSET_LOADER_TYPE_TEXTURE, &input);
// input must have been copied entry->input must point inside the entry. // input must have been copied - entry->input must point inside the entry.
assert_non_null(entry->input); assert_non_null(entry->input);
assert_ptr_equal(entry->input, &entry->inputData); assert_ptr_equal(entry->input, &entry->inputData);
assert_int_equal((int)entry->input->texture, 42); assert_int_equal((int)entry->input->texture, 42);
@@ -321,7 +321,7 @@ static void test_getEntry_input_copied_into_entry(void **state) {
} }
// ============================================================ // ============================================================
// assetUpdate re-entrant sync loader // assetUpdate - re-entrant sync loader
// ============================================================ // ============================================================
static assetentry_t *reentrant_inner_entry = NULL; static assetentry_t *reentrant_inner_entry = NULL;
@@ -362,7 +362,7 @@ static void test_update_reentrant_sync_loader(void **state) {
} }
// ============================================================ // ============================================================
// assetGetEntry dedup against non-NOT_STARTED entries // assetGetEntry - dedup against non-NOT_STARTED entries
// ============================================================ // ============================================================
static void test_getEntry_returns_loaded_entry(void **state) { static void test_getEntry_returns_loaded_entry(void **state) {
@@ -464,7 +464,7 @@ static void test_requireLoaded_propagates_error(void **state) {
assetentry_t *entry = assetGetEntry("fail.locale", ASSET_LOADER_TYPE_LOCALE, NULL); assetentry_t *entry = assetGetEntry("fail.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
// requireLoaded spins assetUpdate until LOADED but the loader always fails, // requireLoaded spins assetUpdate until LOADED - but the loader always fails,
// so the second assetUpdate sees ERROR and throws, which errorChain propagates. // so the second assetUpdate sees ERROR and throws, which errorChain propagates.
errorret_t ret = assetRequireLoaded(entry); errorret_t ret = assetRequireLoaded(entry);
assert_true(errorIsNotOk(ret)); assert_true(errorIsNotOk(ret));
@@ -489,7 +489,7 @@ int main(void) {
cmocka_unit_test_setup_teardown(test_getEntry_null_input_stays_null, asset_setup, asset_teardown), cmocka_unit_test_setup_teardown(test_getEntry_null_input_stays_null, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_getEntry_input_copied_into_entry, asset_setup, asset_teardown), cmocka_unit_test_setup_teardown(test_getEntry_input_copied_into_entry, asset_setup, asset_teardown),
// assetUpdate state machine // assetUpdate - state machine
cmocka_unit_test_setup_teardown(test_update_noop_on_empty_table, asset_setup, asset_teardown), cmocka_unit_test_setup_teardown(test_update_noop_on_empty_table, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_update_entry_reaches_loaded, asset_setup, asset_teardown), cmocka_unit_test_setup_teardown(test_update_entry_reaches_loaded, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_update_slot_occupied_after_first_update, asset_setup, asset_teardown), cmocka_unit_test_setup_teardown(test_update_slot_occupied_after_first_update, asset_setup, asset_teardown),
+8 -8
View File
@@ -133,7 +133,7 @@ static int locale_teardown(void **state) {
} }
// ============================================================ // ============================================================
// assetLocaleParseHeader pure tests (no ZIP required) // assetLocaleParseHeader - pure tests (no ZIP required)
// ============================================================ // ============================================================
static void test_parseHeader_english(void **state) { static void test_parseHeader_english(void **state) {
@@ -251,7 +251,7 @@ static void test_parseHeader_error_missing_nplurals(void **state) {
} }
// ============================================================ // ============================================================
// assetLocaleEvaluatePlural pure tests // assetLocaleEvaluatePlural - pure tests
// ============================================================ // ============================================================
static void test_evaluatePlural_english_singular(void **state) { static void test_evaluatePlural_english_singular(void **state) {
@@ -302,7 +302,7 @@ static void test_evaluatePlural_less_than_boundary(void **state) {
} }
// ============================================================ // ============================================================
// assetLocaleGetString ZIP-based tests // assetLocaleGetString - ZIP-based tests
// ============================================================ // ============================================================
static void test_getString_simple(void **state) { static void test_getString_simple(void **state) {
@@ -355,7 +355,7 @@ static void test_getString_missing_id(void **state) {
} }
// ============================================================ // ============================================================
// assetLocaleGetStringWithArgs ZIP-based tests // assetLocaleGetStringWithArgs - ZIP-based tests
// ============================================================ // ============================================================
static void test_getStringWithArgs_int(void **state) { static void test_getStringWithArgs_int(void **state) {
@@ -392,7 +392,7 @@ static void test_getStringWithArgs_string(void **state) {
int main(void) { int main(void) {
const struct CMUnitTest tests[] = { const struct CMUnitTest tests[] = {
// parseHeader pure // parseHeader - pure
cmocka_unit_test(test_parseHeader_english), cmocka_unit_test(test_parseHeader_english),
cmocka_unit_test(test_parseHeader_singular), cmocka_unit_test(test_parseHeader_singular),
cmocka_unit_test(test_parseHeader_less_than), cmocka_unit_test(test_parseHeader_less_than),
@@ -402,20 +402,20 @@ int main(void) {
cmocka_unit_test(test_parseHeader_error_nplurals_too_large), cmocka_unit_test(test_parseHeader_error_nplurals_too_large),
cmocka_unit_test(test_parseHeader_error_missing_nplurals), cmocka_unit_test(test_parseHeader_error_missing_nplurals),
// evaluatePlural pure // evaluatePlural - pure
cmocka_unit_test(test_evaluatePlural_english_singular), cmocka_unit_test(test_evaluatePlural_english_singular),
cmocka_unit_test(test_evaluatePlural_english_plural), cmocka_unit_test(test_evaluatePlural_english_plural),
cmocka_unit_test(test_evaluatePlural_singular_only), cmocka_unit_test(test_evaluatePlural_singular_only),
cmocka_unit_test(test_evaluatePlural_less_than_boundary), cmocka_unit_test(test_evaluatePlural_less_than_boundary),
// getString in-memory ZIP // getString - in-memory ZIP
cmocka_unit_test_setup_teardown(test_getString_simple, locale_setup, locale_teardown), cmocka_unit_test_setup_teardown(test_getString_simple, locale_setup, locale_teardown),
cmocka_unit_test_setup_teardown(test_getString_plural_singular, locale_setup, locale_teardown), cmocka_unit_test_setup_teardown(test_getString_plural_singular, locale_setup, locale_teardown),
cmocka_unit_test_setup_teardown(test_getString_plural_many, locale_setup, locale_teardown), cmocka_unit_test_setup_teardown(test_getString_plural_many, locale_setup, locale_teardown),
cmocka_unit_test_setup_teardown(test_getString_multiple_calls, locale_setup, locale_teardown), cmocka_unit_test_setup_teardown(test_getString_multiple_calls, locale_setup, locale_teardown),
cmocka_unit_test_setup_teardown(test_getString_missing_id, locale_setup, locale_teardown), cmocka_unit_test_setup_teardown(test_getString_missing_id, locale_setup, locale_teardown),
// getStringWithArgs in-memory ZIP // getStringWithArgs - in-memory ZIP
cmocka_unit_test_setup_teardown(test_getStringWithArgs_int, locale_setup, locale_teardown), cmocka_unit_test_setup_teardown(test_getStringWithArgs_int, locale_setup, locale_teardown),
cmocka_unit_test_setup_teardown(test_getStringWithArgs_string, locale_setup, locale_teardown), cmocka_unit_test_setup_teardown(test_getStringWithArgs_string, locale_setup, locale_teardown),
}; };
+3 -3
View File
@@ -45,7 +45,7 @@ static void test_thread_start_stop(void **state) {
} }
static void test_thread_should_stop(void **state) { static void test_thread_should_stop(void **state) {
// threadStop blocks until STOPPED if threadShouldStop is broken the // threadStop blocks until STOPPED - if threadShouldStop is broken the
// looping callback never exits and this test hangs / times out. // looping callback never exits and this test hangs / times out.
thread_t thread; thread_t thread;
threadInit(&thread, helper_loop); threadInit(&thread, helper_loop);
@@ -108,12 +108,12 @@ typedef struct {
static void helper_trylock(thread_t *thread) { static void helper_trylock(thread_t *thread) {
trylock_data_t *data = (trylock_data_t *)thread->data; trylock_data_t *data = (trylock_data_t *)thread->data;
// Phase 1: main holds the lock trylock must fail. // Phase 1: main holds the lock - trylock must fail.
while(data->phase != 1) {} while(data->phase != 1) {}
data->resultWhileLocked = threadMutexTryLock(data->target); data->resultWhileLocked = threadMutexTryLock(data->target);
data->phase = 2; data->phase = 2;
// Phase 3: main released the lock trylock must succeed. // Phase 3: main released the lock - trylock must succeed.
while(data->phase != 3) {} while(data->phase != 3) {}
data->resultAfterUnlock = threadMutexTryLock(data->target); data->resultAfterUnlock = threadMutexTryLock(data->target);
if(data->resultAfterUnlock) { if(data->resultAfterUnlock) {
+3 -3
View File
@@ -4,7 +4,7 @@ Builds GameCube / Wii disc images (NTSC-J, NTSC-U, PAL) using xorrisofs /
mkisofs + Swiss-GC system-area headers. mkisofs + Swiss-GC system-area headers.
The Swiss-GC headers contain a working GCN apploader that boots any DOL via The Swiss-GC headers contain a working GCN apploader that boots any DOL via
the El Torito catalog no custom PPC assembly needed. the El Torito catalog - no custom PPC assembly needed.
Headers are cached in <script_dir>/../buildtools/iso/ and downloaded from the Headers are cached in <script_dir>/../buildtools/iso/ and downloaded from the
Swiss-GC repository on first use. Swiss-GC repository on first use.
@@ -14,8 +14,8 @@ Disc layout (produced by xorrisofs):
sector 16 ISO 9660 PVD sector 16 ISO 9660 PVD
sector 17 El Torito Boot Record sector 17 El Torito Boot Record
... directory records, file data ... directory records, file data
Dusk.dol (El Torito boot image loaded by apploader) Dusk.dol (El Torito boot image - loaded by apploader)
dusk.dsk (asset archive found at runtime via ISO 9660) dusk.dsk (asset archive - found at runtime via ISO 9660)
""" """
import argparse import argparse
+4 -4
View File
@@ -8,11 +8,11 @@
/** /**
* Descriptor for one entry in an `AssetBatch`. * Descriptor for one entry in an `AssetBatch`.
* *
* `format` texture format constant (`Texture.FORMAT_*`). Alias for `input` * `format` - texture format constant (`Texture.FORMAT_*`). Alias for `input`
* when the type is `Asset.TYPE_TEXTURE`. * when the type is `Asset.TYPE_TEXTURE`.
* `axis` mesh axis constant (`Asset.MESH_AXIS_*`). Alias for `input` * `axis` - mesh axis constant (`Asset.MESH_AXIS_*`). Alias for `input`
* when the type is `Asset.TYPE_MESH`. * when the type is `Asset.TYPE_MESH`.
* `input` generic numeric input for all other loader types. * `input` - generic numeric input for all other loader types.
*/ */
interface AssetBatchDescriptor { interface AssetBatchDescriptor {
path: string; path: string;
@@ -48,7 +48,7 @@ interface AssetBatch {
lock(): this; lock(): this;
/** /**
* Releases all locks and clears the batch. * Releases all locks and clears the batch.
* After this call the object is invalid do not use it again. * After this call the object is invalid - do not use it again.
*/ */
unlock(): void; unlock(): void;
/** /**
+7 -7
View File
@@ -13,24 +13,24 @@
interface AssetEntry { interface AssetEntry {
/** Archive-relative path used as the cache key. */ /** Archive-relative path used as the cache key. */
readonly name: string; readonly name: string;
/** Current loading state compare against `AssetEntry.*` state constants. */ /** Current loading state - compare against `AssetEntry.*` state constants. */
readonly state: number; readonly state: number;
/** Loader type one of the `AssetEntry.TYPE_*` constants. */ /** Loader type - one of the `AssetEntry.TYPE_*` constants. */
readonly type: number; readonly type: number;
/** `true` when the entry has fully loaded (`state === AssetEntry.LOADED`). */ /** `true` when the entry has fully loaded (`state === AssetEntry.LOADED`). */
readonly isLoaded: boolean; readonly isLoaded: boolean;
/** /**
* Returns a `Texture` for this entry when it is a loaded texture asset. * Returns a `Texture` for this entry when it is a loaded texture asset.
* The `Texture` holds its own asset lock independent of this `AssetEntry`. * The `Texture` holds its own asset lock - independent of this `AssetEntry`.
* Returns `undefined` if the entry is not of type `Asset.TYPE_TEXTURE` or * Returns `undefined` if the entry is not of type `Asset.TYPE_TEXTURE` or
* is not yet loaded. * is not yet loaded.
*/ */
readonly texture: Texture | undefined; readonly texture: Texture | undefined;
/** Event proxy subscribe up to 4 callbacks for when loading completes. */ /** Event proxy - subscribe up to 4 callbacks for when loading completes. */
readonly onLoaded: AssetEventProxy; readonly onLoaded: AssetEventProxy;
/** Event proxy subscribe up to 4 callbacks for when the entry is disposed. */ /** Event proxy - subscribe up to 4 callbacks for when the entry is disposed. */
readonly onUnloaded: AssetEventProxy; readonly onUnloaded: AssetEventProxy;
/** Event proxy subscribe up to 4 callbacks for when loading fails. */ /** Event proxy - subscribe up to 4 callbacks for when loading fails. */
readonly onError: AssetEventProxy; readonly onError: AssetEventProxy;
/** /**
* Returns a Promise that resolves when the entry is loaded, or rejects on * Returns a Promise that resolves when the entry is loaded, or rejects on
@@ -45,7 +45,7 @@ interface AssetEntry {
requireLoaded(): this; requireLoaded(): this;
/** /**
* Releases the lock immediately. * Releases the lock immediately.
* After this call the object is invalid do not use it again. * After this call the object is invalid - do not use it again.
*/ */
unlock(): void; unlock(): void;
toString(): string; toString(): string;
+1 -1
View File
@@ -6,7 +6,7 @@
*/ */
/** /**
* A loaded texture asset. Holds a lock on the underlying asset entry * A loaded texture asset. Holds a lock on the underlying asset entry -
* the lock is released automatically when the object is garbage collected. * the lock is released automatically when the object is garbage collected.
*/ */
interface Texture { interface Texture {
+2 -2
View File
@@ -8,11 +8,11 @@
/** Overworld character component (player or NPC). */ /** Overworld character component (player or NPC). */
interface Overworld extends Component { interface Overworld extends Component {
/** /**
* Entity type `Overworld.PLAYER` or `Overworld.NPC`. * Entity type - `Overworld.PLAYER` or `Overworld.NPC`.
*/ */
type: number; type: number;
/** /**
* Facing direction one of the `Overworld.FACING_*` constants. * Facing direction - one of the `Overworld.FACING_*` constants.
*/ */
facing: number; facing: number;
/** Component ID of the linked Renderable, or INVALID if none. */ /** Component ID of the linked Renderable, or INVALID if none. */
+1 -1
View File
@@ -21,7 +21,7 @@ interface OverworldCamera extends Component {
/** Orthographic scale factor (larger = wider view). */ /** Orthographic scale factor (larger = wider view). */
scale: number; scale: number;
/** /**
* Convenience setter equivalent to assigning `targetEntity` and * Convenience setter - equivalent to assigning `targetEntity` and
* `targetPositionComponent` individually. * `targetPositionComponent` individually.
*/ */
setTarget(targetEntityId: number, targetPositionComponentId: number): void; setTarget(targetEntityId: number, targetPositionComponentId: number): void;
+2 -2
View File
@@ -7,10 +7,10 @@
/** Physics body component. */ /** Physics body component. */
interface Physics extends Component { interface Physics extends Component {
/** Body simulation type `Physics.STATIC`, `DYNAMIC`, or `KINEMATIC`. */ /** Body simulation type - `Physics.STATIC`, `DYNAMIC`, or `KINEMATIC`. */
bodyType: number; bodyType: number;
/** /**
* Collision shape type one of the `Physics.SHAPE_*` constants. * Collision shape type - one of the `Physics.SHAPE_*` constants.
* Changing the type preserves existing velocity and ground state. * Changing the type preserves existing velocity and ground state.
*/ */
shape: number; shape: number;
+1 -1
View File
@@ -5,7 +5,7 @@
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
/** Transform component local/world PRS with an optional parent hierarchy. */ /** Transform component - local/world PRS with an optional parent hierarchy. */
interface Position extends Component { interface Position extends Component {
/** /**
* Local-space position. Reading returns a Vec3 copy; assigning a Vec3 * Local-space position. Reading returns a Vec3 copy; assigning a Vec3
+2 -2
View File
@@ -7,7 +7,7 @@
/** A Renderable component. Returned by `entity.add(Component.RENDERABLE)`. */ /** A Renderable component. Returned by `entity.add(Component.RENDERABLE)`. */
interface Renderable extends Component { interface Renderable extends Component {
/** Current render type one of the `Renderable.*` type constants. */ /** Current render type - one of the `Renderable.*` type constants. */
type: number; type: number;
/** Render priority. 0 = auto. Higher = drawn later. */ /** Render priority. 0 = auto. Higher = drawn later. */
priority: number; priority: number;
@@ -28,7 +28,7 @@ interface Renderable extends Component {
texture: Texture | undefined; texture: Texture | undefined;
/** /**
* Sprite list. Reading returns a JS array of 10-element sub-arrays * Sprite list. Reading returns a JS array of 10-element sub-arrays
* `[x1,y1,z1, x2,y2,z2, u1,v1, u2,v2]` one per sprite. * `[x1,y1,z1, x2,y2,z2, u1,v1, u2,v2]` - one per sprite.
* *
* Assigning an array replaces all sprites. Each element may be: * Assigning an array replaces all sprites. Each element may be:
* - 10 numbers (3D): `[x1,y1,z1, x2,y2,z2, u1,v1, u2,v2]` * - 10 numbers (3D): `[x1,y1,z1, x2,y2,z2, u1,v1, u2,v2]`
+1 -1
View File
@@ -5,7 +5,7 @@
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
* *
* Root type declarations for the Dusk engine built-in JavaScript modules. * Root type declarations for the Dusk engine built-in JavaScript modules.
* These globals are injected by the native JerryScript runtime they are not * These globals are injected by the native JerryScript runtime - they are not
* ES modules and cannot be imported. * ES modules and cannot be imported.
* *
* Reference this file from a jsconfig.json or tsconfig.json to get * Reference this file from a jsconfig.json or tsconfig.json to get
+1 -1
View File
@@ -37,7 +37,7 @@ interface InputNamespace {
/** Polling-based input system. */ /** Polling-based input system. */
declare var Input: InputNamespace; declare var Input: InputNamespace;
// Input action constants injected as globals by the engine at startup. // Input action constants - injected as globals by the engine at startup.
declare var INPUT_ACTION_UP: InputAction; declare var INPUT_ACTION_UP: InputAction;
declare var INPUT_ACTION_DOWN: InputAction; declare var INPUT_ACTION_DOWN: InputAction;
declare var INPUT_ACTION_LEFT: InputAction; declare var INPUT_ACTION_LEFT: InputAction;
+1 -1
View File
@@ -58,6 +58,6 @@ interface BackpackNamespace {
declare var Backpack: BackpackNamespace; declare var Backpack: BackpackNamespace;
// Inventory sort constants injected as globals by the engine at startup. // Inventory sort constants - injected as globals by the engine at startup.
declare var INVENTORY_SORT_BY_ID: number; declare var INVENTORY_SORT_BY_ID: number;
declare var INVENTORY_SORT_BY_TYPE: number; declare var INVENTORY_SORT_BY_TYPE: number;
+2 -2
View File
@@ -29,11 +29,11 @@ interface ItemNamespace {
declare var Item: ItemNamespace; declare var Item: ItemNamespace;
// Item ID constants injected as globals by the engine at startup. // Item ID constants - injected as globals by the engine at startup.
declare var ITEM_ID_POTION: ItemId; declare var ITEM_ID_POTION: ItemId;
declare var ITEM_ID_POTATO: ItemId; declare var ITEM_ID_POTATO: ItemId;
declare var ITEM_ID_APPLE: ItemId; declare var ITEM_ID_APPLE: ItemId;
// Item type constants injected as globals by the engine at startup. // Item type constants - injected as globals by the engine at startup.
declare var ITEM_TYPE_MEDICINE: ItemType; declare var ITEM_TYPE_MEDICINE: ItemType;
declare var ITEM_TYPE_FOOD: ItemType; declare var ITEM_TYPE_FOOD: ItemType;
+1 -1
View File
@@ -43,7 +43,7 @@ interface SceneNamespace {
*/ */
set(newScene: SceneObject | null): void; set(newScene: SceneObject | null): void;
/** Called each frame by the engine drives `current.update()`. */ /** Called each frame by the engine - drives `current.update()`. */
update(): void; update(): void;
/** Called each frame by the engine for dynamic/physics updates. */ /** Called each frame by the engine for dynamic/physics updates. */
+1 -1
View File
@@ -26,5 +26,5 @@ interface StoryNamespace {
declare var Story: StoryNamespace; declare var Story: StoryNamespace;
// Story flag constants injected as globals by the engine at startup. // Story flag constants - injected as globals by the engine at startup.
declare var STORY_FLAG_TEST: StoryFlag; declare var STORY_FLAG_TEST: StoryFlag;