Taglists for creating GUIs! Some memory leak fixes.

This commit is contained in:
Scott Duensing 2021-11-09 19:51:03 -06:00
parent cf12d340bd
commit 7cf6a7112a
20 changed files with 660 additions and 7 deletions

View file

@ -49,6 +49,7 @@ HEADERS = \
$$LINUX_HEADERS \
src/system/color.h \
src/system/surface.h \
src/system/taglist.h \
src/thirdparty/stb_ds.h \
src/thirdparty/stb_image.h \
src/thirdparty/memwatch/memwatch.h \
@ -84,6 +85,7 @@ HEADERS = \
SOURCES = \
$$LINUX_SOURCES \
src/system/surface.c \
src/system/taglist.c \
src/thirdparty/memwatch/memwatch.c \
src/system/memory.c \
src/system/array.c \

View file

@ -34,9 +34,11 @@ void buttonClickHandlerSet(ButtonT *button, widgetCallback callback) {
static void buttonDel(WidgetT **widget) {
ButtonT *b = (ButtonT *)*widget;
arrfree(b->base.children);
if (b->title) free(b->title);
free(b);
b = NULL;
*widget = W(b);
}

View file

@ -34,9 +34,11 @@ void checkboxClickHandlerSet(CheckboxT *checkbox, widgetCallback callback) {
static void checkboxDel(WidgetT **widget) {
CheckboxT *c = (CheckboxT *)*widget;
arrfree(c->base.children);
if (c->title) free(c->title);
free(c);
c = NULL;
*widget = W(c);
}

View file

@ -29,9 +29,11 @@ static void desktopPaint(WidgetT *desktop, RectT pos);
static void desktopDel(WidgetT **widget) {
DesktopT *d = (DesktopT *)*widget;
arrfree(d->base.children);
surfaceDestroy(&d->base.surface);
free(d);
d = NULL;
*widget = W(d);
}

View file

@ -28,9 +28,11 @@ static void framePaint(WidgetT *widget, RectT pos);
static void frameDel(WidgetT **widget) {
FrameT *f = (FrameT *)*widget;
arrfree(f->base.children);
if (f->title) free(f->title);
free(f);
f = NULL;
*widget = W(f);
}

View file

@ -52,9 +52,11 @@ void labelColorForegroundSet(LabelT *label, ColorT color) {
static void labelDel(WidgetT **widget) {
LabelT *l = (LabelT *)*widget;
arrfree(l->base.children);
if (l->title) free(l->title);
free(l);
l = NULL;
*widget = W(l);
}

View file

@ -51,10 +51,13 @@ static void listboxDel(WidgetT **widget) {
free(l->values[x]);
}
}
arrfree(l->values);
arrfree(l->base.children);
if (l->title) free(l->title);
free(l);
l = NULL;
*widget = W(l);
}

View file

@ -34,10 +34,12 @@ void pictureClickHandlerSet(PictureT *picture, widgetCallback callback) {
static void pictureDel(WidgetT **widget) {
PictureT *p = (PictureT *)*widget;
arrfree(p->base.children);
if (p->image) imageUnload(&p->image);
if (p->filename) free(p->filename);
free(p);
p = NULL;
*widget = W(p);
}

View file

@ -36,9 +36,11 @@ void radioClickHandlerSet(RadioT *radio, widgetCallback callback) {
static void radioDel(WidgetT **widget) {
RadioT *r = (RadioT *)*widget;
arrfree(r->base.children);
if (r->title) free(r->title);
free(r);
r = NULL;
*widget = W(r);
}

View file

@ -503,8 +503,10 @@ static void terminalDel(WidgetT **widget) {
}
free(t->cells);
arrfree(t->base.children);
free(t);
t = NULL;
*widget = W(t);
}

View file

@ -32,10 +32,12 @@ static void textboxPaint(WidgetT *widget, RectT pos);
static void textboxDel(WidgetT **widget) {
TextboxT *t = (TextboxT *)*widget;
arrfree(t->base.children);
if (t->title) free(t->title);
if (t->value) free(t->value);
free(t);
t = NULL;
*widget = W(t);
}

View file

@ -47,9 +47,11 @@ static void updownVisibleSet(UpdownT *updown);
static void updownDel(WidgetT **widget) {
UpdownT *u = (UpdownT *)*widget;
arrfree(u->base.children);
if (u->title) free(u->title);
free(u);
u = NULL;
*widget = W(u);
}

View file

@ -28,8 +28,10 @@ static void widgetDel(WidgetT **widget);
static void widgetDel(WidgetT **widget) {
WidgetT *w = (WidgetT *)*widget;
arrfree(w->children);
free(w);
w = NULL;
*widget = w;
}

View file

@ -80,9 +80,11 @@ static void windowDel(WidgetT **widget) {
WindowT *w = (WindowT *)*widget;
surfaceDestroy(&w->base.surface);
arrfree(w->base.children);
if (w->title) free(w->title);
free(w);
w = NULL;
*widget = W(w);
}

View file

@ -31,7 +31,7 @@
* - No thumb in listbox scrollbar
* - Window dragging is being screwball
*
* - Random crash on exit - looks memwatch / task related
* - Random crash on exit - looks memwatch / task related - hasn't happened on DOS yet
*/
@ -61,6 +61,8 @@
#include "listbox.h"
#include "terminal.h"
#include "taglist.h"
static TerminalT *t1 = NULL;
static uint8_t lastKey = 0;
@ -68,8 +70,9 @@ static uint8_t lastKey = 0;
static void buttonClick(WidgetT *widget);
static void mainLoop(void *data);
static void terminalTest(void *data);
static void test(void *data);
//static void test(void *data);
static void testTagList(void *data);
static void testTerminal(void *data);
static void widgetDebugDraw(WidgetT *widget, uint8_t debugToggle);
@ -122,6 +125,7 @@ static void mainLoop(void *data) {
}
/*
static void test(void *data) {
DesktopT *desktop = (DesktopT *)guiRootGet();
WindowT *w1 = NULL;
@ -211,11 +215,153 @@ static void test(void *data) {
t1 = terminalNew(0, 0, 80, 24);
guiAttach(W(w4), W(t1));
taskCreate(terminalTest, "terminalTest");
taskCreate(testTerminal, "terminalTest");
}
*/
static void testTagList(void *data) {
WindowT *w1 = NULL;
WindowT *w2 = NULL;
WindowT *w3 = NULL;
WindowT *w4 = NULL;
ButtonT *b1 = NULL;
LabelT *l1 = NULL;
CheckboxT *c1 = NULL;
RadioT *r1a = NULL;
RadioT *r2a = NULL;
RadioT *r3a = NULL;
RadioT *r1b = NULL;
RadioT *r2b = NULL;
RadioT *r3b = NULL;
PictureT *p1 = NULL;
FrameT *f1 = NULL;
TextboxT *tb1 = NULL;
TextboxT *tb2 = NULL;
UpdownT *u1 = NULL;
ListboxT *lb1 = NULL;
(void)data;
TagItemT ui[] = {
T_START,
T_WINDOW, O(w1),
T_TITLE, P("Window 1"),
T_X, 300, T_Y, 25, T_WIDTH, 300, T_HEIGHT, 200,
T_PICTURE, O(p1),
T_X, 0, T_Y, 0,
T_FILENAME, P("kanga.png"),
T_PICTURE, T_DONE,
T_LISTBOX, O(lb1),
T_TITLE, P("Listbox"),
T_X, 155, T_Y, 10, T_WIDTH, 120, T_HEIGHT, 140,
T_ITEM, P("One"),
T_ITEM, P("Two"),
T_ITEM, P("Three"),
T_ITEM, P("Four"),
T_ITEM, P("Five"),
T_ITEM, P("Six"),
T_ITEM, P("Seven"),
T_ITEM, P("Eight"),
T_ITEM, P("Nine"),
T_ITEM, P("Ten"),
T_STEP, 3,
T_LISTBOX, T_DONE,
T_WINDOW, T_DONE,
T_WINDOW, O(w2),
T_TITLE, P("Window 2"),
T_X, 150, T_Y, 150, T_WIDTH, 300, T_HEIGHT, 200,
T_RADIOBUTTON, O(r1a),
T_TITLE, P("Radio 1a"),
T_X, 10, T_Y, 10,
T_GROUP, 1,
T_SELECTED, 1,
T_RADIOBUTTON, T_DONE,
T_RADIOBUTTON, O(r2a),
T_TITLE, P("Radio 2a"),
T_X, 20 + 80, T_Y, 10,
T_GROUP, 1,
T_RADIOBUTTON, T_DONE,
T_RADIOBUTTON, O(r3a),
T_TITLE, P("Radio 3a"),
T_X, 30 + 80 * 2, T_Y, 10,
T_GROUP, 1,
T_RADIOBUTTON, T_DONE,
T_RADIOBUTTON, O(r1b),
T_TITLE, P("Radio 1b"),
T_X, 10, T_Y, 35,
T_GROUP, 2,
T_RADIOBUTTON, T_DONE,
T_RADIOBUTTON, O(r2b),
T_TITLE, P("Radio 2b"),
T_X, 20 + 80, T_Y, 35,
T_GROUP, 2,
T_SELECTED, 1,
T_RADIOBUTTON, T_DONE,
T_RADIOBUTTON, O(r3b),
T_TITLE, P("Radio 3b"),
T_X, 30 + 80 * 2, T_Y, 35,
T_GROUP, 2,
T_RADIOBUTTON, T_DONE,
T_TEXTBOX, O(tb1),
T_TITLE, P("Test Textbox"),
T_X, 10, T_Y, 60, T_WIDTH, 265,
T_VALUE, P("Really long text string to edit!"),
T_TEXTBOX, T_DONE,
T_TEXTBOX, O(tb2),
T_TITLE, P("Test Textbox"),
T_X, 10, T_Y, 85, T_WIDTH, 265,
T_VALUE, P("Short String."),
T_TEXTBOX, T_DONE,
T_UPDOWN, O(u1),
T_TITLE, P("UpDown"),
T_X, 10, T_Y, 120,
T_MINIMUM, 0, T_MAXIMUM, 1024, T_STEP, 5,
T_UPDOWN, T_DONE,
T_WINDOW, T_DONE,
T_WINDOW, O(w3),
T_TITLE, P("Window 3"),
T_X, 300, T_Y, 300, T_WIDTH, 300, T_HEIGHT, 200,
T_FRAME, O(f1),
T_TITLE, P("Test Frame"),
T_X, 10, T_Y, 5, T_WIDTH, 175, T_HEIGHT, 125,
T_BUTTON, O(b1),
T_TITLE, P("Test Button"),
T_X, 0, T_Y, 0,
T_CLICK, P(buttonClick),
T_BUTTON, T_DONE,
T_LABEL, O(l1),
T_TITLE, P("Test Label"),
T_X, 10, T_Y, 40,
T_LABEL, T_DONE,
T_CHECKBOX, O(c1),
T_TITLE, P("Test Checkbox"),
T_X, 10, T_Y, 65,
T_CHECKBOX, T_DONE,
T_FRAME, T_DONE,
T_WINDOW, T_DONE,
T_WINDOW, O(w4),
T_TITLE, P("Terminal"),
T_X, 10, T_Y, 10, T_WIDTH, 7 + 8 + (80 * 8), T_HEIGHT, 26 + 8 + (24 * 14),
T_TERMINAL, O(t1),
T_X, 0, T_Y, 0, T_WIDTH, 80, T_HEIGHT, 24,
T_TERMINAL, T_DONE,
T_WINDOW, T_DONE,
T_END
};
tagListRun(ui);
taskCreate(testTerminal, "terminalTest");
}
static void terminalTest(void *data) {
static void testTerminal(void *data) {
FILE *in = NULL;
char *buffer = NULL;
uint16_t length = 0;
@ -317,7 +463,7 @@ int main(int argc, char *argv[]) {
guiStartup();
taskStartup();
taskCreate(test, "test");
taskCreate(testTagList, "testTagList");
taskCreate(mainLoop, "mainLoop");
taskRun();

View file

@ -18,5 +18,7 @@
*/
#include "array.h"
#define STB_DS_IMPLEMENTATION
#include "stb_ds.h"

View file

@ -34,11 +34,15 @@
#ifdef __linux__
#define BITS64
// Linux DOS replacements.
long biostime(int cmd, long newtime);
#else
#define BITS32
// DOS includes.
#include <dos.h>
#include <dpmi.h>

383
client/src/system/taglist.c Normal file
View file

@ -0,0 +1,383 @@
/*
* Kangaroo Punch MultiPlayer Game Server Mark II
* Copyright (C) 2020-2021 Scott Duensing
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "taglist.h"
#include "gui.h"
#include "widget.h"
#include "button.h"
#include "checkbox.h"
#include "frame.h"
#include "label.h"
#include "listbox.h"
#include "picture.h"
#include "radio.h"
#include "terminal.h"
#include "textbox.h"
#include "updown.h"
#include "window.h"
typedef struct WidgetItemS {
TagItemT type;
WidgetT **widget;
WidgetT **parent;
TagItemT *tagList;
} WidgetItemT;
static WidgetItemT **_widgetList = NULL;
static void tagListWidgetAttributeHandle(void);
static uint32_t tagListWidgetHandle(TagItemT *list, uint32_t index, WidgetT **parent);
uint8_t tagListRun(TagItemT *list) {
uint32_t index = 1;
// Is this a list? Lame check.
if (list[0] != T_START) return 1;
// Process until we hit the end of the list.
while (list[index] != T_END) {
// Is this a widget?
if (list[index] > T_META_START_OF_WIDGETS && list[index] < T_META_END_OF_WIDGETS) {
index = tagListWidgetHandle(list, index, NULL);
tagListWidgetAttributeHandle();
}
} // while
return 0;
}
static void tagListWidgetAttributeHandle(void) {
WidgetItemT *w;
TagItemT t;
TagItemT v;
uint32_t i;
uint32_t j;
WidgetT *widget;
WidgetT *parent;
char *title;
char *filename;
RectT pos;
widgetCallback click;
void *userdata;
int32_t valueInt;
char *valueString;
uint8_t hasValue;
ColorT colorActive;
ColorT colorForeground;
ColorT colorBackground;
uint8_t hasColorActive;
uint8_t hasColorForeground;
uint8_t hasColorBackground;
int8_t selected;
uint32_t group;
int32_t length;
int32_t maximum;
int32_t minimum;
int32_t step;
char **items;
uint32_t indexAttribute;
uint8_t hasIndex;
// Process generated lists in reverse.
while (arrlen(_widgetList) > 0) {
w = arrpop(_widgetList);
// Reset all attributes for this widget.
title = NULL;
filename = NULL;
pos.x = 0;
pos.y = 0;
pos.w = 0;
pos.h = 0;
click = NULL;
userdata = NULL;
hasValue = 0;
valueInt = 0;
valueString = NULL;
colorActive = 0;
colorBackground = 0;
colorForeground = 0;
hasColorActive = 0;
hasColorBackground = 0;
hasColorForeground = 0;
selected = -1;
group = 0;
length = -1;
minimum = 0;
maximum = 255;
step = 10;
items = NULL;
indexAttribute = 0;
hasIndex = 0;
// Parse provided attributes.
for (i=0; i<arrlen(w->tagList); i+=2) {
t = w->tagList[i];
v = w->tagList[i + 1];
switch (t) {
case T_COLOR_ACTIVE:
colorActive = (ColorT)v;
hasColorActive = 1;
break;
case T_COLOR_BACKGROUND:
colorBackground = (ColorT)v;
hasColorBackground = 1;
break;
case T_COLOR_FOREGROUND:
colorForeground = (ColorT)v;
hasColorForeground = 1;
break;
case T_CLICK:
click = (widgetCallback)v;
break;
case T_FILENAME:
filename = (char *)v;
break;
case T_GROUP:
group = v;
break;
case T_HEIGHT:
pos.h = v;
break;
case T_INDEX:
indexAttribute = v;
hasIndex = 1;
break;
case T_ITEM:
arrput(items, (char *)v);
break;
case T_LENGTH:
length = v;
break;
case T_MAXIMUM:
maximum = v;
break;
case T_MINIMUM:
minimum = v;
break;
case T_SELECTED:
selected = (v != 0 ? 1 : 0);
break;
case T_STEP:
step = v;
break;
case T_TITLE:
title = (char *)v;
break;
case T_USER_DATA:
userdata = (void *)v;
break;
case T_VALUE:
valueInt = v;
valueString = (void *)v;
hasValue = 1;
break;
case T_WIDTH:
pos.w = v;
break;
case T_X:
pos.x = v;
break;
case T_Y:
pos.y = v;
break;
}
}
// Clear taglist after processing.
while (arrlen(w->tagList) > 0) {
arrpop(w->tagList);
}
arrfree(w->tagList);
// Find our widget object and parent.
widget = *w->widget;
if (w->parent == NULL) {
parent = NULL;
} else {
parent = *w->parent;
}
// Create the widget.
switch (w->type) {
case T_BUTTON:
widget = W(buttonNew(pos.x, pos.y, title, click));
break;
case T_CHECKBOX:
widget = W(checkboxNew(pos.x, pos.y, title));
if (hasValue) checkboxValueSet((CheckboxT *)widget, valueInt);
if (selected >= 0) checkboxValueSet((CheckboxT *)widget, selected);
break;
case T_FRAME:
widget = W(frameNew(pos.x, pos.y, pos.w, pos.h, title));
break;
case T_LABEL:
widget = W(labelNew(pos.x, pos.y, title));
if (hasColorActive) labelColorActiveSet((LabelT *)widget, colorActive);
if (hasColorBackground) labelColorBackgroundSet((LabelT *)widget, colorBackground);
if (hasColorForeground) labelColorForegroundSet((LabelT *)widget, colorForeground);
if (click != NULL) labelClickHandlerSet((LabelT *)widget, click);
break;
case T_LISTBOX:
widget = W(listboxNew(pos.x, pos.y, pos.w, pos.h, title));
listboxStepSet((ListboxT *)widget, step);
for (j=0; j<arrlen(items); j++) {
listboxItemAdd((ListboxT *)widget, items[j]);
}
if (hasValue) listboxValueSet((ListboxT *)widget, valueString);
if (hasIndex) listboxIndexSet((ListboxT *)widget, indexAttribute);
break;
case T_PICTURE:
widget = W(pictureNew(pos.x, pos.y, filename));
if (click != NULL) pictureClickHandlerSet((PictureT *)widget, click);
break;
case T_RADIOBUTTON:
widget = W(radioNew(pos.x, pos.y, title, group));
if (hasValue && valueInt != 0) radioSelectedSet((RadioT *)widget);
if (selected == 1) radioSelectedSet((RadioT *)widget);
break;
case T_TERMINAL:
widget = W(terminalNew(pos.x, pos.y, pos.w, pos.h));
if (hasColorBackground) terminalBackgroundSet((TerminalT *)widget, colorBackground);
if (hasColorForeground) terminalForegroundSet((TerminalT *)widget, colorForeground);
if (hasColorBackground || hasColorForeground) terminalScreenClear((TerminalT *)widget);
break;
case T_TEXTBOX:
widget = W(textboxNew(pos.x, pos.y, pos.w, title));
textboxValueSet((TextboxT *)widget, valueString);
if (length > 0) textboxLengthMaxSet((TextboxT *)widget, length);
break;
case T_UPDOWN:
widget = W(updownNew(pos.x, pos.y, minimum, maximum, step, title));
if (hasValue) updownValueSet((UpdownT *)widget, valueInt);
break;
case T_WINDOW:
widget = W(windowNew(pos.x, pos.y, pos.w, pos.h, title));
break;
}
guiUserDataSet(widget, userdata);
// Store everything we did.
*w->widget = widget;
// Attach it.
if (parent == NULL) {
guiAttach(guiRootGet(), widget);
} else {
guiAttach(parent, widget);
}
// If we allocated items, release them.
if (items != NULL) {
arrfree(items);
items = NULL;
}
// Free this widget list.
free(w);
}
arrfree(_widgetList);
_widgetList = NULL;
}
static uint32_t tagListWidgetHandle(TagItemT *list, uint32_t index, WidgetT **parent) {
TagItemT widgetType = 0;
WidgetItemT *wi = (WidgetItemT *)malloc(sizeof(WidgetItemT));
// Lists at this point consist of:
// WidgetType, WidgetObjectPointer, WidgetAttributeTag, WidgetAttributeValue, WidgetAttributeTag, WidgetAttributeValue, ..., WidgetType, T_END, ...
widgetType = list[index++];
wi->type = widgetType; // Save widget type.
wi->widget = (WidgetT **)list[index++]; // Save widget object pointer.
wi->parent = parent; // Save parent widget.
wi->tagList = NULL; // Prepare widget tag item array.
while (1) {
// End of widget definition?
if (list[index] == widgetType && list[index+1] == T_DONE) {
// Skip over widget T_END tag pair.
index += 2;
// Save this widget data.
arrput(_widgetList, wi);
break;
}
// Start of nested widget?
if (list[index] > T_META_START_OF_WIDGETS && list[index] < T_META_END_OF_WIDGETS) {
index = tagListWidgetHandle(list, index, wi->widget);
continue;
}
// Save this tag item pair for later.
arrput(wi->tagList, list[index++]);
arrput(wi->tagList, list[index++]);
} // while
return index;
}

View file

@ -0,0 +1,88 @@
/*
* Kangaroo Punch MultiPlayer Game Server Mark II
* Copyright (C) 2020-2021 Scott Duensing
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#ifndef TAGLIST_H
#define TAGLIST_H
#include "os.h"
#ifdef BITS64
typedef uint64_t TagItemT;
#else
typedef uint32_t TagItemT;
#endif
#define O(x) ((TagItemT)&x) // Widget Objects
#define P(x) ((TagItemT)x) // Pointers
enum TagItemsE {
T_END = 0,
T_START,
T_DONE,
T_META_START_OF_WIDGETS,
T_BUTTON, // 4
T_CHECKBOX,
T_FRAME,
T_LABEL,
T_LISTBOX,
T_PICTURE,
T_RADIOBUTTON,
T_TERMINAL,
T_TEXTBOX,
T_UPDOWN,
T_WINDOW,
T_META_END_OF_WIDGETS,
T_META_START_OF_ATTRIBUTES,
T_CLICK, // 17
T_COLOR_ACTIVE,
T_COLOR_BACKGROUND,
T_COLOR_FOREGROUND,
T_FILENAME,
T_GROUP,
T_HEIGHT,
T_INDEX,
T_ITEM,
T_LENGTH,
T_MAXIMUM,
T_MINIMUM,
T_SELECTED,
T_STEP,
T_TITLE,
T_USER_DATA,
T_VALUE,
T_WIDTH,
T_X,
T_Y,
T_META_END_OF_ATTRIBUTES
};
uint8_t tagListRun(TagItemT *list);
#endif // TAGLIST_H

View file

@ -20,6 +20,7 @@
#define MINICORO_IMPL
#define MCO_USE_ASM
//#define MCO_DEFAULT_STACK_SIZE 114688 // Default is 57344
#include "minicoro/minicoro.h"
@ -95,7 +96,7 @@ void taskRun(void) {
} // while tasks exist
//arrfree(_taskList);
arrfree(_taskList);
_taskList = NULL;
}