The Pikachus of the Clock page are icons

Stored as icons at boot from the firmware's bitmaps (cut to 80 x 19), so
they can be edited; the firmware copies stay the fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-16 22:18:58 +02:00
1 parent 51b8d87e86
commit 839e86e8f1
17 files changed
+68 -6

No files matched your search

+1 -2
View File
@@ -16,8 +16,7 @@ struct BuiltinBitmap
static const BuiltinBitmap BUILTIN[] = {
{"Nintendo", nintendo_bitmap_Nintendo_new},
{"Pikachu", pikachu_clock_bitmap},
{"Pikachu yawning", pikachu_yawning_clock_bitmap},
// The Pikachus of the Clock page are icons (display/icon_store)
};
static bool mounted = false;
+52 -1
View File
@@ -6,11 +6,45 @@
#include <strings.h>
#include "display/bitmap_store.h"
#include "display/bitmaps.h"
static const char *DIR = "/icons";
// Written when the bitmaps were moved (not a valid name, so never listed)
static const char *MIGRATED = "/icons/.migrated";
// Built into the firmware (full screen bitmaps, cut to icons): stored at boot so they can
// be edited, and the fallback when they are not stored
struct BuiltinIcon
{
const char *name;
const uint8_t *bitmap; // PROGMEM, BITMAP_BYTES
};
static const BuiltinIcon BUILTIN[] = {
{"Pikachu", pikachu_clock_bitmap},
{"Pikachu yawning", pikachu_yawning_clock_bitmap},
};
static bool builtinIcon(const char *name, Icon &icon)
{
for (const BuiltinIcon &b : BUILTIN)
if (strcasecmp(b.name, name) == 0)
{
uint8_t bitmap[BITMAP_BYTES];
memcpy_P(bitmap, b.bitmap, BITMAP_BYTES);
iconFromBitmap(bitmap, icon);
return true;
}
return false;
}
bool builtinIconName(const char *name)
{
for (const BuiltinIcon &b : BUILTIN)
if (strcasecmp(b.name, name) == 0)
return true;
return false;
}
static uint32_t revision = 0;
// Counted once (listing the directory is slow), then kept up to date; -1: not yet
static int count = -1;
@@ -37,9 +71,24 @@ static bool ensureDir()
return LittleFS.exists(DIR) || LittleFS.mkdir(DIR);
}
void iconStoreBegin()
{
for (const BuiltinIcon &b : BUILTIN)
{
Icon icon;
if (!bitmapStorageAvailable() || LittleFS.exists(path(b.name)) || !builtinIcon(b.name, icon))
continue;
const char *error = saveIcon(b.name, icon);
Serial.printf("Icon \"%s\" stored from the firmware: %s\n", b.name, error ? error : "ok");
}
}
std::vector<String> listIcons()
{
std::vector<String> names = storedNames(DIR, ".bin");
for (const BuiltinIcon &b : BUILTIN) // not stored (no storage)
if (std::none_of(names.begin(), names.end(), [&b](const String &n) { return strcasecmp(n.c_str(), b.name) == 0; }))
names.push_back(b.name);
std::sort(names.begin(), names.end(),
[](const String &a, const String &b) { return strcasecmp(a.c_str(), b.c_str()) < 0; });
return names;
@@ -59,7 +108,7 @@ bool iconExists(const char *name)
bool loadIcon(const char *name, Icon &icon)
{
if (!bitmapStorageAvailable() || !validBitmapName(name))
return false;
return builtinIcon(name, icon);
auto cached = cache.find(name);
if (cached != cache.end())
{
@@ -88,6 +137,8 @@ bool loadIcon(const char *name, Icon &icon)
file.close();
}
}
if (!ok)
ok = builtinIcon(name, loaded);
if (cache.size() >= CACHE_MAX)
cache.clear();
CachedIcon &entry = cache[name];
+6
View File
@@ -12,6 +12,12 @@
const int MAX_ICONS = 200;
// At boot: stores the icons built into the firmware (the Pikachus of the Clock page) when
// they are not stored, so they can be edited. The built-in ones stay the fallback.
void iconStoreBegin();
// One of them (it always exists)
bool builtinIconName(const char *name);
// Sorted without case
std::vector<String> listIcons();
// Changes each time an icon is saved or deleted