Files
firmware/src/graphics/TFTColorRegions.h
T
8d1fbbf55f Snake! (#10936)
* Snake!

* Add spiLock to snake score saving

* Check fixes

* More careful locking

* WIP: Big Display Node

* Update src/graphics/HUB75Display.cpp

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Add HUB75 Native

* Add Tetris game module with core logic and UI integration

- Implement TetrisGame class for game logic, including piece movement, rotation, and line clearing.
- Create TetrisModule class to manage game state, input handling, and rendering on OLED display.
- Introduce high score tracking with persistence and optional mesh broadcasting.
- Define UI states for title, playing, paused, game over, and high scores.
- Implement input handling for game controls and state transitions.
- Add rendering functions for the game board, high scores, and title screen.

* feat(snake): add snake graphics and update display logic in SnakeModule

* Prompt for Initials for Tetris, too

* refactor games module

* Games refactor

* hub75 native double buffer

* Games tuning

* Make joystick repeat events on held button

* Add clouds and colors

* Fix breakout and more color

* difficulty tuning

* trunk

* refactor game announcements, etc

* Scale chirpy gravity as game speed increases

* Portduino, check for hub75 display before reading in hub75 options

* Final(?) games tuning

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Properly ignore input when games screen not shown

* Fail gracefully when HUB75 is selected but not supported.

---------

Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Ixitxachitl <kramerfm@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-16 18:35:33 -05:00

186 lines
7.6 KiB
C++

#pragma once
#include "configuration.h"
#include <stdint.h>
namespace graphics
{
struct TFTColorRegion {
int16_t x;
int16_t y;
int16_t width;
int16_t height;
uint16_t onColorBe;
uint16_t offColorBe;
// Required by ST7789 driver: it scans until the first disabled entry.
bool enabled = false;
};
#ifndef MAX_TFT_COLOR_REGIONS
static constexpr size_t MAX_TFT_COLOR_REGIONS = 48;
#endif
extern TFTColorRegion colorRegions[MAX_TFT_COLOR_REGIONS];
enum class TFTColorRole : uint8_t {
HeaderBackground = 0,
HeaderTitle,
HeaderStatus,
SignalBars,
ConnectionIcon,
UtilizationFill,
FavoriteNode,
ActionMenuBorder,
ActionMenuBody,
ActionMenuTitle,
FrameMono,
BootSplash,
FavoriteNodeBGHighlight,
NavigationBar,
NavigationArrow,
Count
};
#if HAS_TFT || defined(HAS_SPI_TFT) || defined(HAS_HUB75_NATIVE)
#define GRAPHICS_TFT_COLORING_ENABLED 1
#else
#define GRAPHICS_TFT_COLORING_ENABLED 0
#endif
static constexpr bool kTFTColoringEnabled = GRAPHICS_TFT_COLORING_ENABLED != 0;
constexpr bool isTFTColoringEnabled()
{
return kTFTColoringEnabled;
}
void setTFTColorRole(TFTColorRole role, uint16_t onColor, uint16_t offColor);
void registerTFTColorRegion(TFTColorRole role, int16_t x, int16_t y, int16_t width, int16_t height);
// Convenience helper for the common "set role then register one region" flow.
void setAndRegisterTFTColorRole(TFTColorRole role, uint16_t onColor, uint16_t offColor, int16_t x, int16_t y, int16_t width,
int16_t height);
// Register a region using explicit colors (no role lookup). Use when the
// color comes from a theme field rather than a role (e.g. battery fill).
void registerTFTColorRegionDirect(int16_t x, int16_t y, int16_t width, int16_t height, uint16_t onColor, uint16_t offColor);
void registerTFTActionMenuRegions(int16_t boxLeft, int16_t boxTop, int16_t boxWidth, int16_t boxHeight);
uint32_t getTFTColorFrameSignature();
uint8_t getTFTColorRegionCount();
void clearTFTColorRegions();
uint16_t resolveTFTColorPixel(int16_t x, int16_t y, bool isset, uint16_t defaultOnColor, uint16_t defaultOffColor);
// Resolve effective region-mapped OFF color at a coordinate in native-endian RGB565.
uint16_t resolveTFTOffColorAt(int16_t x, int16_t y, uint16_t defaultOffColor);
// -- Per-row fast path for the hot pixel loops in TFTDisplay::display() --------
// resolveTFTColorPixel() is O(regions) per pixel; for a full 800x480 repaint that
// dominates redraw time. Regions are vertically localized, so cull to the regions
// overlapping the current row once per row, then resolve each pixel against only
// those (inlined here, so no per-pixel cross-TU call).
extern uint8_t tftColorRowRegions[MAX_TFT_COLOR_REGIONS]; // indices into colorRegions[], ascending
extern uint8_t tftColorRowCount;
// Build tftColorRowRegions for row y. Call once per row before resolveTFTColorPixelRow().
void beginTFTColorRow(int16_t y);
// Resolve one pixel against the current row's active regions (set by beginTFTColorRow()).
// Highest-index region wins, matching resolveTFTColorPixel()'s precedence.
inline uint16_t resolveTFTColorPixelRow(int16_t x, bool isset, uint16_t defaultOnColor, uint16_t defaultOffColor)
{
for (int j = static_cast<int>(tftColorRowCount) - 1; j >= 0; j--) {
const TFTColorRegion &r = colorRegions[tftColorRowRegions[j]];
if (x >= r.x && x < r.x + r.width)
return isset ? r.onColorBe : r.offColorBe;
}
return isset ? defaultOnColor : defaultOffColor;
}
// -- Theme engine ------------------------------------------------------
// Each theme has four fields that work together:
//
// id - ThemeID:: constant, used for in-code references.
// name - human-readable label shown in the theme picker.
// uniqueIdentifier - the stable numeric value persisted to
// uiconfig.screen_rgb_color and restored at boot.
// This is a CONTRACT with saved configs on disk - once
// assigned, never reuse or renumber, even if the theme is
// deleted or the kThemes[] array is reordered.
// visible - controls whether a theme appears in the picker menu.
// Hidden themes can still be restored and applied if their
// uniqueIdentifier is persisted.
//
// Display order in the menu is controlled by kThemes[] array order among
// themes where visible == true, NOT by any numeric value above.
//
// To add a new theme:
// 1. Add a unique constant in ThemeID below (next unused value).
// 2. Add a kThemes[] entry at the desired menu position, with a unique
// uniqueIdentifier that has never been used by any prior theme.
// 3. Set visible=true if it should appear in the picker.
//
// To retire a theme without breaking saved configs:
// - Preferred: keep the entry and set visible=false so existing saved
// uniqueIdentifier values still resolve to the same theme.
// - If you remove the entry, resolveThemeIndex() falls back to DefaultDark
// when the persisted uniqueIdentifier no longer matches any theme.
// - Do NOT reuse a retired uniqueIdentifier for a future theme.
namespace ThemeID
{
constexpr uint32_t DefaultDark = 0;
constexpr uint32_t DefaultLight = 1;
constexpr uint32_t Christmas = 2;
constexpr uint32_t Pink = 3;
constexpr uint32_t Blue = 4;
constexpr uint32_t Creamsicle = 5;
constexpr uint32_t MeshtasticGreen = 6;
constexpr uint32_t ClassicRed = 7;
constexpr uint32_t MonochromeWhite = 8;
} // namespace ThemeID
// Per-role color pair stored in native (little-endian) RGB565 format.
struct TFTThemeRoleColor {
uint16_t onColor;
uint16_t offColor;
};
// Complete theme definition.
struct TFTThemeDef {
uint32_t id; // ThemeID constant - in-code identifier for this theme.
const char *name; // Human-readable label shown in the theme picker.
uint32_t uniqueIdentifier; // Stable persisted value copied into uiconfig.screen_rgb_color.
// Never reuse or renumber - see file-level notes above.
TFTThemeRoleColor roles[static_cast<size_t>(TFTColorRole::Count)];
uint16_t batteryFillGood;
uint16_t batteryFillMedium;
uint16_t batteryFillBad;
bool fullFrameInvert; // Apply full-frame FrameMono inversion (ST7789 light themes)
bool visible; // Show in the theme picker menu. Hidden themes still apply
// correctly if their uniqueIdentifier is persisted (dev/legacy themes).
};
// Count of themes whose .visible flag is true. Use this when building menus.
size_t getVisibleThemeCount();
// Access the Nth visible theme (0 .. getVisibleThemeCount()-1). Hidden themes
// are skipped, preserving kThemes[] order among the visible entries.
const TFTThemeDef &getVisibleThemeByIndex(size_t visibleIndex);
// Return the theme that matches uiconfig.screen_rgb_color (falls back to Dark).
const TFTThemeDef &getActiveTheme();
// Return the visible-theme index for the currently active theme, or SIZE_MAX
// if the active theme is hidden (so menus can show "no selection").
size_t getActiveVisibleThemeIndex();
// Convenience accessors - safe to call even when coloring is compiled out.
uint16_t getThemeHeaderBg();
uint16_t getThemeHeaderText();
uint16_t getThemeHeaderStatus();
uint16_t getThemeBodyBg();
uint16_t getThemeBodyFg();
bool isThemeFullFrameInvert();
uint16_t getThemeBatteryFillColor(int batteryPercent);
// Reinitialise default roleColors from the active theme. Call after a
// theme change so that any role registered without a prior setTFTColorRole()
// picks up theme-appropriate defaults.
void loadThemeDefaults();
} // namespace graphics