mirror of
https://github.com/cesanta/mongoose
synced 2026-08-25 22:26:06 -04:00
116 lines
3.6 KiB
C
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
|