diff --git a/demo/src/main.c b/demo/src/main.c index 3966402..7b363ff 100644 --- a/demo/src/main.c +++ b/demo/src/main.c @@ -60,16 +60,9 @@ void draw() { point(mouseX(), mouseY()); } -void deinit() { - soundDestroy(beep); - groupDestroy(edges); - groupDestroy(movement); -} - int main() { worldSetInit(&init); worldSetDraw(&draw); - worldSetDeinit(&deinit); worldRun(); return 0; } diff --git a/src/array.c b/src/array.c index 20ff47e..26b7dac 100644 --- a/src/array.c +++ b/src/array.c @@ -8,9 +8,11 @@ void heliPairInit(HeliPair *p) { } void heliPairDestroy(HeliPair *p) { - if (p->key && p->freeKey) p->freeKey(p->key); - if (p->value && p->freeValue) p->freeValue(p->value); + HeliPair pp; + memcpy(&pp, p, sizeof(pp)); heliPairInit(p); + if (pp.key && pp.freeKey) pp.freeKey(pp.key); + if (pp.value && pp.freeValue) pp.freeValue(pp.value); } void heliArrayInit(HeliArray *a) { @@ -37,7 +39,7 @@ void* heliArrayGetValue(HeliArray *a, int i) { return item ? item->value : NULL; } -HeliPair* heliArrayInsertPair(HeliArray *a, int i, void *k, void *v, HeliFreeCallback fk, HeliFreeCallback fv) { +HeliPair* heliArrayInsertPair(HeliArray *a, int i, void *k, HeliFreeCallback fk, void *v, HeliFreeCallback fv) { if (i < 0 || i > a->count) i = a->count; if (a->allocated < a->count + 1) { @@ -60,14 +62,16 @@ HeliPair* heliArrayInsertPair(HeliArray *a, int i, void *k, void *v, HeliFreeCal } HeliPair* heliArrayInsert(HeliArray *a, int i, void *v, HeliFreeCallback fv) - { return heliArrayInsertPair(a, i, NULL, v, NULL, fv); } + { return heliArrayInsertPair(a, i, NULL, NULL, v, fv); } void heliArrayRemove(HeliArray *a, int i) { if (i < 0 || i >= a->count) return; - heliPairDestroy(&a->items[i]); + HeliPair item; + memcpy(&item, &a->items[i], sizeof(item)); memmove(&a->items[i], &a->items[i+1], (a->count-i-1)*sizeof(a->items[0])); --a->count; memset(&a->items[a->count], 0, sizeof(a->items[0])); + heliPairDestroy(&item); } void heliArrayClear(HeliArray *a) { @@ -75,59 +79,81 @@ void heliArrayClear(HeliArray *a) { heliArrayRemove(a, a->count-1); } -HeliPair* heliStringmapFind(HeliArray *a, const char *k, int *gtOrEqIndex) { + +HeliPair* heliMapFind(HeliArray *a, const void *k, HeliCompareCallback cmp, int *gtOrEqIndex) { int i0 = 0; int i1 = a->count - 1; - int cmp = i0 < a->count ? strcmp((char*)a->items[i0].key, k) : -1; - if (cmp >= 0) { + int c = i0 < a->count ? cmp(a->items[i0].key, k) : -1; + if (c >= 0) { if (gtOrEqIndex) *gtOrEqIndex = i0; - return cmp ? NULL : &a->items[i0]; + return c ? NULL : &a->items[i0]; } - if (i1 > i0) cmp = strcmp((char*)a->items[i1].key, k); - if (cmp < 0) { + if (i1 > i0) c = cmp(a->items[i1].key, k); + if (c < 0) { if (gtOrEqIndex) *gtOrEqIndex = a->count; return NULL; } - if (cmp == 0) { + if (c == 0) { if (gtOrEqIndex) *gtOrEqIndex = i1; return &a->items[i1]; } while(i0 + 1 < i1) { int i = (i0 + i1)/2; - cmp = strcmp((char*)a->items[i].key, k); - if (cmp == 0) { + c = cmp(a->items[i].key, k); + if (c == 0) { if (gtOrEqIndex) *gtOrEqIndex = i; return &a->items[i]; } - if (cmp < 0) i0 = i; else i1 = i; + if (c < 0) i0 = i; else i1 = i; } if (gtOrEqIndex) *gtOrEqIndex = i1; return NULL; } -HeliPair* heliStringmapGet(HeliArray *a, const char *k) - { return heliStringmapFind(a, k, NULL); } +HeliPair* heliMapGet(HeliArray *a, const void *k, HeliCompareCallback cmp) + { return heliMapFind(a, k, cmp, NULL); } -HeliPair* heliStringmapAdd(HeliArray *a, const char *k, void *v, HeliFreeCallback fv) { +HeliPair* heliMapAdd( + HeliArray *a, const void *k, HeliCompareCallback cmp, + HeliCloneCallback ck, HeliFreeCallback fk, + void *v, HeliFreeCallback fv ) +{ int i; - HeliPair *item = heliStringmapFind(a, k, &i); + HeliPair *item = heliMapFind(a, k, cmp, &i); if (item) { - if (item->value && item->freeValue) item->freeValue(item->value); - item->value = v; - item->freeValue = fv; + if (item->value != v) { + void *vv = item->value; + HeliFreeCallback ffvv = item->freeValue; + item->value = v; + item->freeValue = fv; + if (vv && ffvv) ffvv(vv); + } } else { - item = heliArrayInsertPair(a, i, heliStringCopy(k), v, &free, fv); + item = heliArrayInsertPair(a, i, ck(k), fk, v, fv); } return item; } -int heliStringmapRemove(HeliArray *a, const char *k) { +int heliMapRemove(HeliArray *a, const void *k, HeliCompareCallback cmp) { int i; - if (heliStringmapFind(a, k, &i)) { + if (heliMapFind(a, k, cmp, &i)) { heliArrayRemove(a, i); return TRUE; } return FALSE; } + +HeliPair* heliStringmapFind(HeliArray *a, const char *k, int *gtOrEqIndex) + { return heliMapFind(a, k, (HeliCompareCallback)&strcmp, gtOrEqIndex); } + +HeliPair* heliStringmapGet(HeliArray *a, const char *k) + { return heliStringmapFind(a, k, NULL); } + +HeliPair* heliStringmapAdd(HeliArray *a, const char *k, void *v, HeliFreeCallback fv) + { return heliMapAdd(a, k, (HeliCompareCallback)&strcmp, (HeliCloneCallback)&heliStringCopy, &free, v, fv); } + +int heliStringmapRemove(HeliArray *a, const char *k) + { return heliMapRemove(a, k, (HeliCompareCallback)&strcmp); } + diff --git a/src/common.c b/src/common.c index 21344e4..ec5e7fa 100644 --- a/src/common.c +++ b/src/common.c @@ -5,6 +5,8 @@ cairo_t *heliCairo; +int heliInitialized; +HeliArray heliObjectsSet; struct _Directory { @@ -43,6 +45,8 @@ double randomFloat() Directory openDirectory(const char *path) { + if (!heliInitialized) return NULL; + GDir *dir = g_dir_open(path, 0, NULL); if (!dir) return NULL; @@ -55,11 +59,13 @@ Directory openDirectory(const char *path) { } g_dir_close(dir); + heliObjectRegister(d, (HeliFreeCallback)&closeDirectory); return d; } void closeDirectory(Directory directory) { if (!directory) return; + heliObjectUnregister(directory); heliArrayDestroy(&directory->files); free(directory); } @@ -136,3 +142,20 @@ void heliParseColor(const char *x, double *color) { } +static int objectsCompare(const void *a, const void *b) + { return a < b ? -1 : b < a ? 1 : 0; } + +static void* objectsKeyClone(void *k) + { return k; } + +void heliObjectRegister(void *o, HeliFreeCallback fo) + { heliMapAdd(&heliObjectsSet, o, &objectsCompare, (HeliCloneCallback)objectsKeyClone, fo, NULL, NULL); } + +void heliObjectUnregister(void *o) { + int i; + HeliPair *item = heliMapFind(&heliObjectsSet, o, &objectsCompare, &i); + if (item) { + item->freeKey = NULL; + heliArrayRemove(&heliObjectsSet, i); + } +} diff --git a/src/group.c b/src/group.c index d443050..fb94a7b 100644 --- a/src/group.c +++ b/src/group.c @@ -9,11 +9,14 @@ struct _Group { Group createGroup() { + if (!heliInitialized) return NULL; Group g = calloc(1, sizeof(*g)); + heliObjectRegister(g, (HeliFreeCallback)&groupDestroy); return g; } Group createEdgesGroupEx(double x1, double y1, double x2, double y2, double borderWidth) { + if (!heliInitialized) return NULL; if (x2 < x1) { double x = x2; x2 = x1; x1 = x; } if (y2 < y1) { double y = y2; y2 = y1; y1 = y; } if (borderWidth < 0.1) borderWidth = 0.1; @@ -37,6 +40,7 @@ Group createEdgesGroup() void groupDestroy(Group group) { + heliObjectUnregister(group); heliArrayDestroy(&group->sprites); free(group); } diff --git a/src/private.h b/src/private.h index 4509224..b8acfe4 100644 --- a/src/private.h +++ b/src/private.h @@ -32,6 +32,8 @@ void heliParseColor(const char *x, double *color); // pointer array typedef void (*HeliFreeCallback)(void*); +typedef void* (*HeliCloneCallback)(const void*); +typedef int (*HeliCompareCallback)(const void*, const void*); typedef struct _HeliPair { void *key; @@ -56,15 +58,31 @@ HeliPair* heliArrayGet(HeliArray *a, int i); void* heliArrayGetKey(HeliArray *a, int i); void* heliArrayGetValue(HeliArray *a, int i); HeliPair* heliArrayInsert(HeliArray *a, int i, void *v, HeliFreeCallback fv); -HeliPair* heliArrayInsertPair(HeliArray *a, int i, void *k, void *v, HeliFreeCallback fk, HeliFreeCallback fv); +HeliPair* heliArrayInsertPair(HeliArray *a, int i, void *k, HeliFreeCallback fk, void *v, HeliFreeCallback fv); void heliArrayRemove(HeliArray *a, int i); +HeliPair* heliMapFind(HeliArray *a, const void *k, HeliCompareCallback cmp, int *gtOrEqIndex); +HeliPair* heliMapGet(HeliArray *a, const void *k, HeliCompareCallback cmp); +HeliPair* heliMapAdd( + HeliArray *a, const void *k, HeliCompareCallback cmp, + HeliCloneCallback ck, HeliFreeCallback fk, + void *v, HeliFreeCallback fv ); +int heliMapRemove(HeliArray *a, const void *k, HeliCompareCallback cmp); + HeliPair* heliStringmapFind(HeliArray *a, const char *k, int *gtOrEqIndex); HeliPair* heliStringmapGet(HeliArray *a, const char *k); HeliPair* heliStringmapAdd(HeliArray *a, const char *k, void *v, HeliFreeCallback fv); int heliStringmapRemove(HeliArray *a, const char *k); +// all objects + +extern int heliInitialized; +extern HeliArray heliObjectsSet; +void heliObjectRegister(void *o, HeliFreeCallback fo); +void heliObjectUnregister(void *o); + + // animation typedef struct _HeliAnimation { diff --git a/src/sound.c b/src/sound.c index a064a3a..bc5b5a5 100644 --- a/src/sound.c +++ b/src/sound.c @@ -116,6 +116,7 @@ static void unload(HeliSoundInstance *s) { Sound createSound(const char *path) { + if (!heliInitialized) return NULL; init(); HeliPair *item = heliStringmapGet(&cache, path); if (!item) item = heliStringmapAdd(&cache, path, load(path), (HeliFreeCallback)&unload); @@ -124,10 +125,13 @@ Sound createSound(const char *path) { Sound sound = calloc(1, sizeof(*sound)); sound->instance = s; + + heliObjectRegister(sound, (HeliFreeCallback)&soundDestroy); return sound; } void soundDestroy(Sound sound) { + heliObjectUnregister(sound); soundStop(sound); if (--sound->instance->refcount <= 0) heliStringmapRemove(&cache, sound->instance->path); diff --git a/src/sprite.c b/src/sprite.c index c2ac2cb..a8341a7 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -90,6 +90,8 @@ static void prepareCollider(Sprite sprite, HeliCollider *collider) { Sprite createSpriteEx(double x, double y, double width, double height) { + if (!heliInitialized) return NULL; + Sprite s = calloc(1, sizeof(*s)); s->x = x; s->y = y; @@ -113,6 +115,7 @@ Sprite createSpriteEx(double x, double y, double width, double height) { heliArrayInsert(&sprites, -1, s, NULL); heliArrayInsert(&spritesSorted, -1, s, NULL); + heliObjectRegister(s, (HeliFreeCallback)&spriteDestroy); return s; } @@ -120,6 +123,7 @@ Sprite createSprite(double x, double y) { return createSpriteEx(x, y, 100, 100); } void spriteDestroy(Sprite sprite) { + heliObjectUnregister(sprite); spriteSetNoAnimation(sprite); while(sprite->groups.count > 0) groupRemove((Group)sprite->groups.items[0].value, sprite); diff --git a/src/world.c b/src/world.c index 30c5218..8f50864 100644 --- a/src/world.c +++ b/src/world.c @@ -8,7 +8,6 @@ #include "world.h" -static int resizable; static GtkWidget *window; static GtkWidget *dialogMessage; @@ -35,6 +34,9 @@ static Callback deinitCallback; static cairo_surface_t *cairoSurface; static int width = 400; static int height = 400; +static int resizable; +static char title[1000]; +static int titleSize = (int)(sizeof(title)/sizeof(*title)); static double frameRate = 30; static int cameraEnabled; @@ -108,7 +110,7 @@ int mousePressedOver(Sprite sprite) static void resize(int w, int h) { width = w > 200 ? w : 100; height = h > 200 ? h : 200; - if (started && window) { + if (started && !stopped && window) { gtk_window_set_default_size(GTK_WINDOW(window), width, height); gtk_widget_set_size_request(window, width, height); } @@ -124,6 +126,28 @@ int worldGetHeight() void worldSetHeight(int h) { resize(width, h); } +int worldGetResizable() + { return resizable; } +void worldSetResizable(int r) { + if (resizable == r) return; + resizable = r ? TRUE : FALSE; + if (started && !stopped && window) + gtk_window_set_resizable(GTK_WINDOW(window), resizable); +} + +const char* worldGetTitle() + { return title; } +void worldSetTitle(const char *t) { + int changed = FALSE; + for(int i = 0; i < titleSize-1; ++i) { + if (title[i] != t[i]) changed = TRUE; + title[i] = t[i]; + if (!t[i]) break; + } + if (changed && started && !stopped && window) + gtk_window_set_title(GTK_WINDOW(window), title); +} + double worldGetFrameRate() { return frameRate; } void worldSetFrameRate(double fps) { @@ -382,6 +406,7 @@ static void activate(GtkApplication* app, gpointer data G_GNUC_UNUSED) { g_signal_connect(window, "event", G_CALLBACK(handleEvent), NULL); gtk_window_set_resizable(GTK_WINDOW(window), resizable); gtk_window_set_default_size(GTK_WINDOW(window), width, height); + gtk_window_set_title(GTK_WINDOW(window), title); gtk_widget_add_events( window, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK @@ -437,16 +462,13 @@ static void activate(GtkApplication* app, gpointer data G_GNUC_UNUSED) { askMultilineBox ); currentTime = startTime = 0; + heliInitialized = TRUE; if (initCallback) initCallback(); g_idle_add(idle, NULL); } -void worldRun() - { worldRunEx(FALSE); } - -void worldRunEx(int rs) { +void worldRun() { if (started) return; - resizable = rs; started = TRUE; stopped = FALSE; @@ -469,10 +491,14 @@ void worldRunEx(int rs) { if (deinitCallback) deinitCallback(); + heliArrayClear(&heliObjectsSet); heliSpriteFinish(); heliDrawingFinish(); heliAnimationFinish(); heliSoundFinish(); + heliArrayDestroy(&heliObjectsSet); + + heliInitialized = FALSE; if (cairoSurface) cairo_surface_destroy(cairoSurface); cairoSurface = NULL; diff --git a/src/world.h b/src/world.h index 2d81a95..f9bd4f1 100644 --- a/src/world.h +++ b/src/world.h @@ -55,6 +55,12 @@ void worldSetWidth(int width); int worldGetHeight(); void worldSetHeight(int height); +int worldGetResizable(); +void worldSetResizable(int resizable); + +const char* worldGetTitle(); +void worldSetTitle(const char *title); + double worldGetFrameRate(); void worldSetFrameRate(double frameRate); @@ -67,7 +73,6 @@ void worldSetInit(Callback init); void worldSetDraw(Callback draw); void worldSetDeinit(Callback deinit); void worldRun(); -void worldRunEx(int resizable); void worldStop();