dep-mongoose/tutorials/device-dashboard/array/dashboard.c
2026-06-09 11:56:40 +01:00

116 lines
3.6 KiB
C

// SPDX-FileCopyrightText: 2026 Cesanta Software Limited
// SPDX-License-Identifier: GPL-2.0-only or commercial
#include "mongoose.h"
// Array elements are stored in a dynamically reallocated array
struct entry {
char text[20]; // Modifiable via GUI
bool checked; // Modifiable via GUI
};
static struct entry *s_arr; // Backing storage, grown/shrunk via realloc()
static size_t s_count; // Number of elements in s_arr
static struct entry s_entry; // Staging area: holds values for read/write/add
static int s_index; // Current element index, see struct mg_field_set
static struct mg_field fields_arr[] = {
{"index", MG_VAL_INT, &s_index, sizeof(s_index)},
{"text", MG_VAL_STR, &s_entry.text, sizeof(s_entry.text)},
{"checked", MG_VAL_BOOL, &s_entry.checked, sizeof(s_entry.checked)},
{NULL, MG_VAL_INT, NULL, 0},
};
// Grow s_arr by one slot and append the staged entry. False on OOM
static bool entry_append(void) {
struct entry *arr =
(struct entry *) realloc(s_arr, (s_count + 1) * sizeof(*arr));
if (arr == NULL) return false;
s_arr = arr;
s_arr[s_count++] = s_entry;
return true;
}
// Remove the entry at the given index, shifting the tail down and shrinking
// s_arr by one slot. False if the index is out of range
static bool entry_remove(int index) {
if (index < 0 || (size_t) index >= s_count) return false;
memmove(&s_arr[index], &s_arr[index + 1],
(s_count - (size_t) index - 1) * sizeof(*s_arr));
s_count--;
if (s_count == 0) {
free(s_arr);
s_arr = NULL;
} else {
struct entry *arr = (struct entry *) realloc(s_arr, s_count * sizeof(*arr));
if (arr != NULL) s_arr = arr;
}
return true;
}
// Read, write, delete or append an array element. The framework sets s_index
// before the call: a negative value asks for the total count, an
// out-of-range value means "end of iteration" - both are answered by
// setting s_index accordingly. MG_DASH_ADD builds a new entry from the
// staged fields and appends it regardless of s_index
static bool arr_fn(enum mg_dash_op op, struct mg_dash_user *u) {
(void) u;
if (op == MG_DASH_READ) {
if (s_index < 0) {
s_index = (int) s_count;
} else if ((size_t) s_index >= s_count) {
s_index = -1;
} else {
s_entry = s_arr[s_index];
}
return true;
}
if (op == MG_DASH_WRITE && s_index >= 0 && (size_t) s_index < s_count) {
s_arr[s_index] = s_entry;
return true;
}
if (op == MG_DASH_DELETE) return entry_remove(s_index);
if (op == MG_DASH_ADD) return entry_append();
return false;
}
static struct mg_field_set field_set_arr = {
"event", fields_arr, arr_fn, &s_index, NULL, NULL,
};
void mg_dash_init(struct mg_mgr *mgr) {
static struct mg_dash dash; // Important: keep it static!
static const char *demo[] = {"Wake up", "Make coffee", "Write code",
"Run tests", "Ship it", "Relax", "Sleep"};
size_t i;
for (i = 0; i < sizeof(demo) / sizeof(demo[0]); i++) {
mg_snprintf(s_entry.text, sizeof(s_entry.text), "%s", demo[i]);
s_entry.checked = (i == 0);
entry_append();
}
MG_DASH_ADD_FIELD_SET(&dash, &field_set_arr);
mg_mem_files = mg_packed_files;
mg_http_listen(mgr, MG_HTTP_ADDR, mg_dash_ev_handler, &dash);
}
void mg_dash_poll(struct mg_mgr *mgr) {
(void) mgr;
}
// On desktop, build with -DMAIN flag to include main().
// On embedded system, run this code in your main() function
#ifdef MAIN
int main(void) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_dash_init(&mgr);
for (;;) {
mg_mgr_poll(&mgr, 1);
mg_dash_poll(&mgr);
}
return 0;
}
#endif