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
+3 -4
View File
@@ -14,10 +14,9 @@ cmake_policy(SET CMP0079 NEW)
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_AUTHOR "YouWish" CACHE STRING "Game author / coder")
set(DUSK_GAME_SHORT_DESCRIPTION "Dusk game" CACHE STRING "One-line description")
set(DUSK_GAME_NAME "Dusk" CACHE STRING "Game display name")
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_LONG_DESCRIPTION "No description yet." CACHE STRING "Full description")
# Prep cache
+1 -1
View File
@@ -45,7 +45,7 @@ endif()
# Link libraries.
# 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.
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
cglm
+29 -14
View File
@@ -18,7 +18,7 @@ void uiSettingsSelected(
const uint8_t index,
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);
}
}
@@ -30,27 +30,42 @@ void uiSettingsClosed(const uimenu_t *menu) {
errorret_t uiSettingsInit(void) {
memoryZero(&UI_SETTINGS, sizeof(uisettings_t));
UI_SETTINGS.items[0] = (uimenuitem_t){
.type = UIMENU_WIDGET_TYPE_LABEL,
uint8_t i = 0;
UI_SETTINGS.items[i] = (uimenuitem_t){
.type = UI_MENU_WIDGET_TYPE_LABEL,
.label = "Settings"
};
UI_SETTINGS.items[1] = (uimenuitem_t){
.type = UIMENU_WIDGET_TYPE_LABEL,
.label = "Settings two"
++i;
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){
.type = UIMENU_WIDGET_TYPE_LABEL,
.label = "Settings three"
};
UI_SETTINGS.items[3].type = UIMENU_WIDGET_TYPE_CHECKBOX;
uiCheckboxInit(&UI_SETTINGS.items[3].checkbox, "Enable thing?");
++i;
UI_SETTINGS.items[i].type = UI_MENU_WIDGET_TYPE_CHECKBOX;
uiCheckboxInit(&UI_SETTINGS.items[i].checkbox, "Enable thing?");
++i;
assertTrue(i <= UI_SETTINGS_ITEM_COUNT, "Settings item count mismatch");
uiMenuInit(&UI_SETTINGS.menu, uiSettingsSelected, uiSettingsClosed, NULL);
uiMenuSetItems(
&UI_SETTINGS.menu,
UI_SETTINGS.items,
UI_SETTINGS_ITEM_COUNT,
2
i, 1
);
errorOk();
+1 -1
View File
@@ -9,7 +9,7 @@
#include "error/error.h"
#include "ui/widget/uimenu.h"
#define UI_SETTINGS_ITEM_COUNT 4
#define UI_SETTINGS_ITEM_COUNT 6
typedef struct {
uimenu_t menu;
+1
View File
@@ -5,6 +5,7 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
uibutton.c
uicheckbox.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
) {
memoryZero(checkbox, sizeof(uicheckbox_t));
stringCopy(checkbox->label, label, UI_CHECKBOX_LABEL_MAX);
checkbox->label = label;
}
bool_t uiCheckboxIsChecked(const uicheckbox_t *checkbox) {
+2 -4
View File
@@ -8,10 +8,8 @@
#pragma once
#include "error/error.h"
#define UI_CHECKBOX_LABEL_MAX 32
typedef struct uicheckbox_s {
char_t label[UI_CHECKBOX_LABEL_MAX];
const char_t *label;
bool_t checked;
bool_t highlighted;
} uicheckbox_t;
@@ -20,7 +18,7 @@ typedef struct uicheckbox_s {
* Initializes a checkbox.
*
* @param checkbox The checkbox to initialize.
* @param label Display label, truncated to UI_CHECKBOX_LABEL_MAX - 1 chars.
* @param label Display label.
*/
void uiCheckboxInit(
uicheckbox_t *checkbox,
+73 -21
View File
@@ -10,6 +10,7 @@
#include "util/memory.h"
#include "display/text/text.h"
#include "display/color.h"
#include "ui/widget/uibutton.h"
void uiMenuInit(
uimenu_t *menu,
@@ -52,7 +53,9 @@ void uiMenuOpen(uimenu_t *menu) {
assertTrue(menu->columns > 0, "Menu columns must be > 0");
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->columns, rows,
uiMenuFocusSelected,
@@ -87,34 +90,49 @@ errorret_t uiMenuDraw(
float_t colStep = width / (float_t)menu->columns;
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
uint8_t focusIndex = menu->focusItem != NULL ?
menu->focusItem->y * menu->columns + menu->focusItem->x :
0xFF;
uint8_t col = 0;
uint8_t row = 0;
for(uint8_t i = 0; i < menu->itemCount; i++) {
const uimenuitem_t *item = &menu->items[i];
uint8_t col = i % menu->columns;
uint8_t row = i / menu->columns;
if(item->type == UI_MENU_WIDGET_TYPE_LABEL) {
if(col > 0) { row++; col = 0; }
errorChain(textDraw(
x,
y + (float_t)row * rowHeight,
item->label,
COLOR_WHITE,
&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;
bool_t highlighted = i == focusIndex;
switch(item->type) {
case UIMENU_WIDGET_TYPE_LABEL:
errorChain(textDraw(
ix, iy, item->label,
highlighted ? COLOR_RED : COLOR_WHITE,
&FONT_DEFAULT
));
case UI_MENU_WIDGET_TYPE_CHECKBOX:
errorChain(uiCheckboxDraw(&item->checkbox, ix, iy));
break;
case UIMENU_WIDGET_TYPE_CHECKBOX:
errorChain(uiCheckboxDraw(&item->checkbox, ix, iy));
case UI_MENU_WIDGET_TYPE_BUTTON:
errorChain(uiButtonDraw(&item->button, ix, iy));
break;
default:
break;
}
col++;
if(col >= menu->columns) { col = 0; row++; }
}
errorOk();
@@ -126,8 +144,9 @@ bool_t uiMenuFocusSelected(const uifocusitem_t *focusItem) {
uimenu_t *menu = (uimenu_t *)focusItem->user;
if(menu->selected == NULL) return true;
uint8_t index = focusItem->y * menu->columns + focusItem->x;
if(index >= menu->itemCount) return true;
uint8_t slot = focusItem->y * menu->columns + focusItem->x;
uint8_t index = uiMenuFocusSlotToIndex(menu, slot);
if(index == 0xFF) return true;
menu->selected(menu, index, &menu->items[index]);
return true;
@@ -138,16 +157,27 @@ bool_t uiMenuFocusChanged(const uifocusitem_t *focusItem) {
assertNotNull(focusItem->user, "Focus item user cannot be NULL");
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++) {
uimenuitem_t *item = &menu->items[i];
if(item->type != UIMENU_WIDGET_TYPE_CHECKBOX) continue;
uiCheckboxSetHighlighted(&item->checkbox, i == index);
if(
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(index >= menu->itemCount) return true;
uint8_t index = uiMenuFocusSlotToIndex(menu, focusSlot);
if(index == 0xFF) return true;
menu->changed(menu, index, &menu->items[index]);
return true;
@@ -161,3 +191,25 @@ bool_t uiMenuFocusClosed(const uifocusitem_t *focusItem) {
if(menu->closed != NULL) menu->closed(menu);
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
#include "error/error.h"
#include "ui/focus/uifocus.h"
#include "ui/widget/uibutton.h"
#include "ui/widget/uicheckbox.h"
#define UI_MENU_LABEL_MAX UI_CHECKBOX_LABEL_MAX
@@ -15,16 +16,19 @@
typedef struct uimenu_s uimenu_t;
typedef enum {
UIMENU_WIDGET_TYPE_NONE,
UIMENU_WIDGET_TYPE_LABEL,
UIMENU_WIDGET_TYPE_CHECKBOX,
UI_MENU_WIDGET_TYPE_NONE,
UI_MENU_WIDGET_TYPE_LABEL,
UI_MENU_WIDGET_TYPE_SPACER,
UI_MENU_WIDGET_TYPE_CHECKBOX,
UI_MENU_WIDGET_TYPE_BUTTON,
} uimenuwidgettype_t;
typedef struct {
uimenuwidgettype_t type;
union {
char_t label[UI_MENU_LABEL_MAX];
char_t *label;
uicheckbox_t checkbox;
uibutton_t button;
};
} 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.
* @returns True.
@@ -144,7 +166,7 @@ errorret_t uiMenuDraw(
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.
* @returns True.
@@ -152,7 +174,7 @@ bool_t uiMenuFocusSelected(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.
* @returns True.
+1 -1
View File
@@ -108,7 +108,7 @@ static void test_assertStrLenMin(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");
// 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.
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));
for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
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) {
@@ -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);
// 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++) {
assetEntryUnlock(entries[i]);
}
@@ -284,7 +284,7 @@ static void test_update_error_slot_stays_occupied(void **state) {
assetUpdate();
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));
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) {
@@ -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);
// 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_ptr_equal(entry->input, &entry->inputData);
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;
@@ -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) {
@@ -464,7 +464,7 @@ static void test_requireLoaded_propagates_error(void **state) {
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.
errorret_t ret = assetRequireLoaded(entry);
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_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_entry_reaches_loaded, 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) {
@@ -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) {
@@ -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) {
@@ -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) {
@@ -392,7 +392,7 @@ static void test_getStringWithArgs_string(void **state) {
int main(void) {
const struct CMUnitTest tests[] = {
// parseHeader — pure
// parseHeader - pure
cmocka_unit_test(test_parseHeader_english),
cmocka_unit_test(test_parseHeader_singular),
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_missing_nplurals),
// evaluatePlural — pure
// evaluatePlural - pure
cmocka_unit_test(test_evaluatePlural_english_singular),
cmocka_unit_test(test_evaluatePlural_english_plural),
cmocka_unit_test(test_evaluatePlural_singular_only),
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_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_multiple_calls, 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_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) {
// 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.
thread_t thread;
threadInit(&thread, helper_loop);
@@ -108,12 +108,12 @@ typedef struct {
static void helper_trylock(thread_t *thread) {
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) {}
data->resultWhileLocked = threadMutexTryLock(data->target);
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) {}
data->resultAfterUnlock = threadMutexTryLock(data->target);
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.
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
Swiss-GC repository on first use.
@@ -14,8 +14,8 @@ Disc layout (produced by xorrisofs):
sector 16 ISO 9660 PVD
sector 17 El Torito Boot Record
... directory records, file data
Dusk.dol (El Torito boot image — loaded by apploader)
dusk.dsk (asset archive — found at runtime via ISO 9660)
Dusk.dol (El Torito boot image - loaded by apploader)
dusk.dsk (asset archive - found at runtime via ISO 9660)
"""
import argparse
+4 -4
View File
@@ -8,11 +8,11 @@
/**
* 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`.
* `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`.
* `input` — generic numeric input for all other loader types.
* `input` - generic numeric input for all other loader types.
*/
interface AssetBatchDescriptor {
path: string;
@@ -48,7 +48,7 @@ interface AssetBatch {
lock(): this;
/**
* 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;
/**
+7 -7
View File
@@ -13,24 +13,24 @@
interface AssetEntry {
/** Archive-relative path used as the cache key. */
readonly name: string;
/** Current loading state — compare against `AssetEntry.*` state constants. */
/** Current loading state - compare against `AssetEntry.*` state constants. */
readonly state: number;
/** Loader type — one of the `AssetEntry.TYPE_*` constants. */
/** Loader type - one of the `AssetEntry.TYPE_*` constants. */
readonly type: number;
/** `true` when the entry has fully loaded (`state === AssetEntry.LOADED`). */
readonly isLoaded: boolean;
/**
* 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
* is not yet loaded.
*/
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;
/** 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;
/** 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;
/**
* Returns a Promise that resolves when the entry is loaded, or rejects on
@@ -45,7 +45,7 @@ interface AssetEntry {
requireLoaded(): this;
/**
* 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;
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.
*/
interface Texture {
+2 -2
View File
@@ -8,11 +8,11 @@
/** Overworld character component (player or NPC). */
interface Overworld extends Component {
/**
* Entity type — `Overworld.PLAYER` or `Overworld.NPC`.
* Entity type - `Overworld.PLAYER` or `Overworld.NPC`.
*/
type: number;
/**
* Facing direction — one of the `Overworld.FACING_*` constants.
* Facing direction - one of the `Overworld.FACING_*` constants.
*/
facing: number;
/** 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). */
scale: number;
/**
* Convenience setter — equivalent to assigning `targetEntity` and
* Convenience setter - equivalent to assigning `targetEntity` and
* `targetPositionComponent` individually.
*/
setTarget(targetEntityId: number, targetPositionComponentId: number): void;
+2 -2
View File
@@ -7,10 +7,10 @@
/** Physics body component. */
interface Physics extends Component {
/** Body simulation type — `Physics.STATIC`, `DYNAMIC`, or `KINEMATIC`. */
/** Body simulation type - `Physics.STATIC`, `DYNAMIC`, or `KINEMATIC`. */
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.
*/
shape: number;
+1 -1
View File
@@ -5,7 +5,7 @@
* 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 {
/**
* 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)`. */
interface Renderable extends Component {
/** Current render type — one of the `Renderable.*` type constants. */
/** Current render type - one of the `Renderable.*` type constants. */
type: number;
/** Render priority. 0 = auto. Higher = drawn later. */
priority: number;
@@ -28,7 +28,7 @@ interface Renderable extends Component {
texture: Texture | undefined;
/**
* 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:
* - 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
*
* 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.
*
* 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. */
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_DOWN: InputAction;
declare var INPUT_ACTION_LEFT: InputAction;
+1 -1
View File
@@ -58,6 +58,6 @@ interface 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_TYPE: number;
+2 -2
View File
@@ -29,11 +29,11 @@ interface 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_POTATO: 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_FOOD: ItemType;
+1 -1
View File
@@ -43,7 +43,7 @@ interface SceneNamespace {
*/
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;
/** Called each frame by the engine for dynamic/physics updates. */
+1 -1
View File
@@ -26,5 +26,5 @@ interface 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;