Files
firmware/src/graphics/Screen.h
Jonathan Bennett 4feaec651f Unify the native display config between legacy display and MUI (#6838)
* Add missed include

* Another Warning fix

* Add another HAS_SCREEN

* Namespace fixes

* Removed depricated destination types and re-factored destination screen

* Get rid of Arduino Strings

* Clean up after Copilot

* SixthLine Def, Screen Rename

Added Sixth Line Definition Screen Rename, and Automatic Line Adjustment

* Consistency is hard - fixed "Sixth"

* System Frame Updates

Adjusted line construction to ensure we fit maximum content per screen.

* Fix up notifications

* Add a couple more ifdef HAS_SCREEN lines

* Add screen->isOverlayBannerShowing()

* Don't forget the invert!

* Adjust Nodelist Center Divider

Adjust Nodelist Center Divider

* Fix variable casting

* Fix entryText variable as empty before update to fix validation

* Altitude is int32_t

* Update PowerTelemetry to have correct data type

* Fix cppcheck warnings (#6945)

* Fix cppcheck warnings

* Adjust logic in Power.cpp for power sensor

---------

Co-authored-by: Jason P <applewiz@mac.com>

* More pixel wrangling so things line up NodeList edition

* Adjust NodeList alignments and plumb some background padding for a possible title fix

* Better alignment for banner notifications

* Move title into drawCommonHeader; initial screen tested

* Fonts make spacing items difficult

* Improved beeping booping and other buzzer based feedback (#6947)

* Improved beeping booping and other buzzer based feedback

* audible button feedback (#6949)

* Refactor

---------

Co-authored-by: todd-herbert <herbert.todd@gmail.com>

* Sandpapered the corners of the notification popup

* Finalize drawCommonHeader migration

* Update Title of Favorite Node Screens

* Update node metric alignment on LoRa screen

* Update the border for popups to separate it from background

* Update PaxcounterModule.cpp with CommonHeader

* Update WiFi screen with CommonHeader and related data reflow

* It was not, in fact, pointing up

* Fix build on wismeshtap

* T-deck trackball debounce

* Fix uptime on Device Focused page to actually detail

* Update Sys screen for new uptime, add label to Freq/Chan on LoRa

* Don't display DOP any longer, make Uptime consistent

* Revert Uptime change on Favorites, Apply to Device Focused

* Label the satelite number to avoid confusion

* Boop boop boop boop

* Correct GPS positioning and string consistency across strings for GPS

* Fix GPS text alignment

* Enable canned messages by default

* Don't wake screen on new nodes

* Cannedmessage list emote support added

* Fn+e emote picker for freetext screen

* Actually block CannedInput actions while display is shown

* Add selection menu to bannerOverlay

* Off by one

* Move to unified text layouts and spacing

* Still my Fav without an "e"

* Fully remove EVENT_NODEDB_UPDATED

* Simply LoRa screen

* Make some char pointers const to fix compilation on native targets

* Update drawCompassNorth to include radius

* Fix warning

* button thread cleanup

* Pull OneButton handling from PowerFSM and add MUI switch (#6973)

* Trunk

* Onebutton Menu Support

* Add temporary clock icon

* Add gps location to fsi

* Banner message state reset

* Cast to char to satisfy compiler

* Better fast handling of input during banner

* Fix warning

* Derp

* oops

* Update ref

* Wire buzzer_mode

* remove legacy string->print()

* Only init screen if one found

* Unsigned Char

* More buttonThread cleaning

* screen.cpp button handling cleanup

* The Great Event Rename of 2025

* Fix the Radiomaster

* Missed trackball type change

* Remove unused function

* Make ButtonThread an InputBroker

* Coffee hadn't kicked in yet

* Add clock icon for Navigation Bar

* Restore clock screen definition code - whoops

* ExternalNotifications now observe inputBroker

* Clock rework (#6992)

* Move Clock bits into ClockRenderer space

* Rework clock into all device navigation

* T-Watch Actually Builds Different

* Compile fix

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>

* Add AM/PM to Digital Clock

* Flip Seconds and AM/PM on Clock Display

* Tik-tok pixels are hard

* Fix builds on Thinknode M1

* Check for GPS and don't crash

* Don't endif til the end

* Rework the OneButton thread to be much less of a mess. (#6997)

* Rework the OneButton thread to be much less of a mess. And break lots of targets temporarily

* Update src/input/ButtonThread.h

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

* fix GPS toggle

* Send the shutdown event, not just the kbchar

* Honor the back button in a notificaiton popup

* Draw the right size box for popup with options

* Try to un-break all the things

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* 24-hour Clock Should have leading zero, but not 12-hour

* Fixup some compile errors

* Add intRoutine to ButtonThread init, to get more responsive user button back

* Add Timezone picker

* Fix Warning

* Optionally set the initial selection for the chooser popup

* Make back buttons work in canned messages

* Drop the wrapper classes

* LonPressTime now configurable

* Clock Frame can not longer be blank; just add valid time

* Back buttons everywhere!

* Key Verification confirm banner

* Make Elecrow M* top button a back button

* Add settings saves

* EInk responsiveness fixes

* Linux Input Fixes

* Add Native Trackball/Joystick support, and move UserButton to Input

* No Flight Stick Mode

* Send input event

* Add Channel Utilization to Device Focused frame

* Don't shift screens when we draw new ones

* Add showOverlayBanner arguments to no-op

* trunk

* Default Native trackball to NC

* Fix crash in simulator mode

* Add longLong button press

* Get the args right

* Adjust Bluetooth Pairing Screen to account for bottom navigation.

* Trackball everywhere, and unPhone buttons

* Remap visionmaster secondary button to TB_UP

* Kill ScanAndSelect

* trunk

* No longer need the canned messages input filter

* All Canned All the time

* Fix stm32 compile error regarding inputBroker

* Unify tft lineheights (#7033)

* Create variable line heights based upon SCREEN_HEIGHT

* Refactor textPositions into method -> getTextPositions

* Update SharedUIDisplay.h

---------

Co-authored-by: Jason P <applewiz@mac.com>

* Adjust top distance for larger displays

* Adjust icon sizes for larger displays

* Fix Paxcounter compile errors after code updates

* Pixel wrangling to make larger screens fit better

* Alert frame has precedence over banner -- for now

* Unify on ALT_BUTTON

* Align AM/PM to the digit, not the segment on larger displays

* Move some global pin defines into configuration.h

* Scaffolding for BMM150 9-axis gyro

* Alt button behavior

* Don't add the blank GPS frames without HAS_GPS

* EVENT_NODEDB_UPDATED has been retired

* Clean out LOG_WARN messages from debugging

* Add dismiss message function

* Minor buttonThread cleanup

* Add BMM150 support

* Clean up last warning from dev

* Simplify bmm150 init return logic

* Add option to reply to messages

* Add minimal menu upon selecting home screen

* Move Messages to slot 2, rename GPS to Position, move variables nearer functional usage in Screen.cpp

* Properly dismiss message

* T-Deck Trackball press is not user button

* Add select on favorite frame to launch cannedMessage DM

* Minor wording change

* Less capital letters

* Fix empty message check, time isn't reliable

* drop dead code

* Make UIRenderer a static class instead of namespace

* Fix the select on favorite

* Check if message is empty early and then 'return'

* Add kb_found, and show the option to launch freetype if appropriate

* Ignore impossible touchscreen touches

* Auto scroll fix

* Move linebreak after "from" for banners to maximize screen usage.

* Center "No messages to show" on Message frame

* Start consolidating buzzer behavior

* Fixed signed / unsigned warning

* Cast second parameter of max() to make some targets happy

* Cast kbchar to (char) to make arduino string happy

* Shorten the notice of "No messages"

* Add buzzer mode chooser

* Add regionPicker to Lora icon

* Reduce line spacing and reorder Position screen to resolve overlapping issues

* Update message titles, fix GPS icons, add Back options

* Leftover boops

* Remove chirp

* Make the region selection dismissable when a region is already set

* Add read-aloud functionality on messages w/ esp8266sam

* "Last Heard" is a better label

* tweak the beep

* 5 options

* properly tear down freetext upon cancel

* de-convelute canned messages just a bit

* Correct height of Mail icon in navigation bar

* Remove unused warning

* Consolidate time methods into TimeFormatters

* Oops

* Change LoRa Picker Cancel to Back

* Tweak selection characters on Banner

* Message render not scrolling on 5th line

* More fixes for message scrolling

* Remove the safety next on text overflow - we found that root cause

* Add pin definitions to fix compilation for obscure target

* Don't let the touchscreen send unitialized kbchar values

* Make virtual KB just a bit quicker

* No more double tap, swipe!

* Left is left, and Right is right

* Update horizontal lightning bolt design

* Move from solid to dashed separator for Message Frame

* Single emote feature fix

* Manually sort overlapping elements for now

* Freetext and clearer choices

* Fix ESP32 InkHUD builds on the unify-tft branch (#7087)

* Remove BaseUI branding

* Capitalization is fun

* Revert Meshtastic Boot Frame Changes

* Add ANZ_433 LoRa region to picker

* Update settings.json

---------

Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: todd-herbert <herbert.todd@gmail.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-21 06:36:04 -05:00

682 lines
21 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include "configuration.h"
#include "detect/ScanI2C.h"
#include "mesh/generated/meshtastic/config.pb.h"
#include <OLEDDisplay.h>
#include <string>
#include <vector>
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
#if !HAS_SCREEN
#include "power.h"
namespace graphics
{
// Noop class for boards without screen.
class Screen
{
public:
enum FrameFocus : uint8_t {
FOCUS_DEFAULT, // No specific frame
FOCUS_PRESERVE, // Return to the previous frame
FOCUS_FAULT,
FOCUS_TEXTMESSAGE,
FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
};
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
void onPress() {}
void setup() {}
void setOn(bool) {}
void doDeepSleep() {}
void forceDisplay(bool forceUiUpdate = false) {}
void startFirmwareUpdateScreen() {}
void increaseBrightness() {}
void decreaseBrightness() {}
void setFunctionSymbol(std::string) {}
void removeFunctionSymbol(std::string) {}
void startAlert(const char *) {}
void showOverlayBanner(const char *message, uint32_t durationMs = 3000, uint8_t options = 0,
std::function<void(int)> bannerCallback = NULL, int8_t InitialSelected = 0)
{
}
void setFrames(FrameFocus focus) {}
void endAlert() {}
};
} // namespace graphics
#else
#include <cstring>
#include <OLEDDisplayUi.h>
#include "../configuration.h"
#include "gps/GeoCoord.h"
#include "graphics/ScreenFonts.h"
#ifdef USE_ST7567
#include <ST7567Wire.h>
#elif defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
#include <SH1106Wire.h>
#elif defined(USE_SSD1306)
#include <SSD1306Wire.h>
#elif defined(USE_ST7789)
#include <ST7789Spi.h>
#else
// the SH1106/SSD1306 variant is auto-detected
#include <AutoOLEDWire.h>
#endif
#include "EInkDisplay2.h"
#include "EInkDynamicDisplay.h"
#include "PointStruct.h"
#include "TFTDisplay.h"
#include "TypedQueue.h"
#include "commands.h"
#include "concurrency/LockGuard.h"
#include "concurrency/OSThread.h"
#include "input/InputBroker.h"
#include "mesh/MeshModule.h"
#include "power.h"
#include <string>
#include <vector>
// 0 to 255, though particular variants might define different defaults
#ifndef BRIGHTNESS_DEFAULT
#define BRIGHTNESS_DEFAULT 150
#endif
// Meters to feet conversion
#ifndef METERS_TO_FEET
#define METERS_TO_FEET 3.28
#endif
// Feet to miles conversion
#ifndef MILES_TO_FEET
#define MILES_TO_FEET 5280
#endif
// Intuitive colors. E-Ink display is inverted from OLED(?)
#define EINK_BLACK OLEDDISPLAY_COLOR::WHITE
#define EINK_WHITE OLEDDISPLAY_COLOR::BLACK
// Base segment dimensions for T-Watch segmented display
#define SEGMENT_WIDTH 16
#define SEGMENT_HEIGHT 4
/// Convert an integer GPS coords to a floating point
#define DegD(i) (i * 1e-7)
extern bool hasUnreadMessage;
namespace
{
/// A basic 2D point class for drawing
class Point
{
public:
float x, y;
Point(float _x, float _y) : x(_x), y(_y) {}
/// Apply a rotation around zero (standard rotation matrix math)
void rotate(float radian)
{
float cos = cosf(radian), sin = sinf(radian);
float rx = x * cos + y * sin, ry = -x * sin + y * cos;
x = rx;
y = ry;
}
void translate(int16_t dx, int dy)
{
x += dx;
y += dy;
}
void scale(float f)
{
// We use -f here to counter the flip that happens
// on the y axis when drawing and rotating on screen
x *= f;
y *= -f;
}
};
} // namespace
namespace graphics
{
// Forward declarations
class Screen;
/// Handles gathering and displaying debug information.
class DebugInfo
{
public:
DebugInfo(const DebugInfo &) = delete;
DebugInfo &operator=(const DebugInfo &) = delete;
private:
friend Screen;
DebugInfo() {}
/// Renders the debug screen.
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
/// Protects all of internal state.
concurrency::Lock lock;
};
/**
* @brief This class deals with showing things on the screen of the device.
*
* @details Other than setup(), this class is thread-safe as long as drawFrame is not called
* multiple times simultaneously. All state-changing calls are queued and executed
* when the main loop calls us.
*/
class Screen : public concurrency::OSThread
{
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> gpsStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver =
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules
CallbackObserver<Screen, const InputEvent *> inputObserver =
CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent);
CallbackObserver<Screen, const meshtastic_AdminMessage *> adminMessageObserver =
CallbackObserver<Screen, const meshtastic_AdminMessage *>(this, &Screen::handleAdminMessage);
public:
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
size_t frameCount = 0; // Total number of active frames
~Screen();
// Which frame we want to be displayed, after we regen the frameset by calling setFrames
enum FrameFocus : uint8_t {
FOCUS_DEFAULT, // No specific frame
FOCUS_PRESERVE, // Return to the previous frame
FOCUS_FAULT,
FOCUS_TEXTMESSAGE,
FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
};
// Regenerate the normal set of frames, focusing a specific frame if requested
// Call when a frame should be added / removed, or custom frames should be cleared
void setFrames(FrameFocus focus = FOCUS_DEFAULT);
std::vector<const uint8_t *> indicatorIcons; // Per-frame custom icon pointers
Screen(const Screen &) = delete;
Screen &operator=(const Screen &) = delete;
ScanI2C::DeviceAddress address_found;
meshtastic_Config_DisplayConfig_OledType model;
OLEDDISPLAY_GEOMETRY geometry;
bool isOverlayBannerShowing();
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
// FIXME: Needs refactoring and getMacAddr needs to be moved to a utility class
char ourId[5];
/// Initializes the UI, turns on the display, starts showing boot screen.
//
// Not thread safe - must be called before any other methods are called.
void setup();
/// Turns the screen on/off. Optionally, pass a custom screensaver frame for E-Ink
void setOn(bool on, FrameCallback einkScreensaver = NULL)
{
if (!on)
// We handle off commands immediately, because they might be called because the CPU is shutting down
handleSetOn(false, einkScreensaver);
else
enqueueCmd(ScreenCmd{.cmd = Cmd::SET_ON});
}
/**
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
*/
void doDeepSleep();
void blink();
// Draw north
float estimatedHeading(double lat, double lon);
/// Handle button press, trackball or swipe action)
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); }
void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); }
// generic alert start
void startAlert(FrameCallback _alertFrame)
{
alertFrame = _alertFrame;
ScreenCmd cmd;
cmd.cmd = Cmd::START_ALERT_FRAME;
enqueueCmd(cmd);
}
void startAlert(const char *_alertMessage)
{
startAlert([_alertMessage](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
uint16_t x_offset = display->width() / 2;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, 26 + y, _alertMessage);
});
}
void endAlert()
{
ScreenCmd cmd;
cmd.cmd = Cmd::STOP_ALERT_FRAME;
enqueueCmd(cmd);
}
void showOverlayBanner(const char *message, uint32_t durationMs = 3000, uint8_t options = 0,
std::function<void(int)> bannerCallback = NULL, int8_t InitialSelected = 0);
void startFirmwareUpdateScreen()
{
ScreenCmd cmd;
cmd.cmd = Cmd::START_FIRMWARE_UPDATE_SCREEN;
enqueueCmd(cmd);
}
// Function to allow the AccelerometerThread to set the heading if a sensor provides it
// Mutex needed?
void setHeading(long _heading)
{
hasCompass = true;
compassHeading = _heading;
}
bool hasHeading() { return hasCompass; }
long getHeading() { return compassHeading; }
void setEndCalibration(uint32_t _endCalibrationAt) { endCalibrationAt = _endCalibrationAt; }
uint32_t getEndCalibration() { return endCalibrationAt; }
// functions for display brightness
void increaseBrightness();
void decreaseBrightness();
void setFunctionSymbol(std::string sym);
void removeFunctionSymbol(std::string sym);
/// Stops showing the boot screen.
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
/// Overrides the default utf8 character conversion, to replace empty space with question marks
static char customFontTableLookup(const uint8_t ch)
{
// UTF-8 to font table index converter
// Code from http://playground.arduino.cc/Main/Utf8ascii
static uint8_t LASTCHAR;
static bool SKIPREST; // Only display a single unconvertable-character symbol per sequence of unconvertable characters
if (ch < 128) { // Standard ASCII-set 0..0x7F handling
LASTCHAR = 0;
SKIPREST = false;
return ch;
}
uint8_t last = LASTCHAR; // get last char
LASTCHAR = ch;
switch (last) {
case 0xC2: {
SKIPREST = false;
return (uint8_t)ch;
}
case 0xC3: {
SKIPREST = false;
return (uint8_t)(ch | 0xC0);
}
}
// We want to strip out prefix chars for two-byte char formats
if (ch == 0xC2 || ch == 0xC3)
return (uint8_t)0;
#if defined(OLED_PL)
switch (last) {
case 0xC3: {
if (ch == 147)
return (uint8_t)(ch); // Ó
else if (ch == 179)
return (uint8_t)(148); // ó
else
return (uint8_t)(ch | 0xC0);
break;
}
case 0xC4: {
SKIPREST = false;
return (uint8_t)(ch);
}
case 0xC5: {
SKIPREST = false;
if (ch == 132)
return (uint8_t)(136); // ń
else if (ch == 186)
return (uint8_t)(137); // ź
else
return (uint8_t)(ch);
break;
}
}
// We want to strip out prefix chars for two-byte char formats
if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5)
return (uint8_t)0;
#endif
#if defined(OLED_UA) || defined(OLED_RU)
switch (last) {
case 0xC3: {
SKIPREST = false;
return (uint8_t)(ch | 0xC0);
}
// map UTF-8 cyrillic chars to it Windows-1251 (CP-1251) ASCII codes
// note: in this case we must use compatible font - provided ArialMT_Plain_10/16/24 by 'ThingPulse/esp8266-oled-ssd1306'
// library have empty chars for non-latin ASCII symbols
case 0xD0: {
SKIPREST = false;
if (ch == 132)
return (uint8_t)(170); // Є
if (ch == 134)
return (uint8_t)(178); // І
if (ch == 135)
return (uint8_t)(175); // Ї
if (ch == 129)
return (uint8_t)(168); // Ё
if (ch > 143 && ch < 192)
return (uint8_t)(ch + 48);
break;
}
case 0xD1: {
SKIPREST = false;
if (ch == 148)
return (uint8_t)(186); // є
if (ch == 150)
return (uint8_t)(179); // і
if (ch == 151)
return (uint8_t)(191); // ї
if (ch == 145)
return (uint8_t)(184); // ё
if (ch > 127 && ch < 144)
return (uint8_t)(ch + 112);
break;
}
case 0xD2: {
SKIPREST = false;
if (ch == 144)
return (uint8_t)(165); // Ґ
if (ch == 145)
return (uint8_t)(180); // ґ
break;
}
}
// We want to strip out prefix chars for two-byte char formats
if (ch == 0xC2 || ch == 0xC3 || ch == 0x82 || ch == 0xD0 || ch == 0xD1)
return (uint8_t)0;
#endif
#if defined(OLED_CS)
switch (last) {
case 0xC2: {
SKIPREST = false;
return (uint8_t)ch;
}
case 0xC3: {
SKIPREST = false;
return (uint8_t)(ch | 0xC0);
}
case 0xC4: {
SKIPREST = false;
if (ch == 140)
return (uint8_t)(129); // Č
if (ch == 141)
return (uint8_t)(138); // č
if (ch == 142)
return (uint8_t)(130); // Ď
if (ch == 143)
return (uint8_t)(139); // ď
if (ch == 154)
return (uint8_t)(131); // Ě
if (ch == 155)
return (uint8_t)(140); // ě
// Slovak specific glyphs
if (ch == 185)
return (uint8_t)(147); // Ĺ
if (ch == 186)
return (uint8_t)(148); // ĺ
if (ch == 189)
return (uint8_t)(149); // Ľ
if (ch == 190)
return (uint8_t)(150); // ľ
break;
}
case 0xC5: {
SKIPREST = false;
if (ch == 135)
return (uint8_t)(132); // Ň
if (ch == 136)
return (uint8_t)(141); // ň
if (ch == 152)
return (uint8_t)(133); // Ř
if (ch == 153)
return (uint8_t)(142); // ř
if (ch == 160)
return (uint8_t)(134); // Š
if (ch == 161)
return (uint8_t)(143); // š
if (ch == 164)
return (uint8_t)(135); // Ť
if (ch == 165)
return (uint8_t)(144); // ť
if (ch == 174)
return (uint8_t)(136); // Ů
if (ch == 175)
return (uint8_t)(145); // ů
if (ch == 189)
return (uint8_t)(137); // Ž
if (ch == 190)
return (uint8_t)(146); // ž
// Slovak specific glyphs
if (ch == 148)
return (uint8_t)(151); // Ŕ
if (ch == 149)
return (uint8_t)(152); // ŕ
break;
}
}
// We want to strip out prefix chars for two-byte char formats
if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5)
return (uint8_t)0;
#endif
// If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the
// rest of it
if (SKIPREST)
return (uint8_t)0;
SKIPREST = true;
return (uint8_t)191; // otherwise: return ¿ if character can't be converted (note that the font map we're using doesn't
// stick to standard EASCII codes)
}
/// Returns a handle to the DebugInfo screen.
//
// Use this handle to set things like battery status, user count, GPS status, etc.
DebugInfo *debug_info() { return &debugInfo; }
// Handle observer events
int handleStatusUpdate(const meshtastic::Status *arg);
int handleTextMessage(const meshtastic_MeshPacket *arg);
int handleUIFrameEvent(const UIFrameEvent *arg);
int handleInputEvent(const InputEvent *arg);
int handleAdminMessage(const meshtastic_AdminMessage *arg);
/// Used to force (super slow) eink displays to draw critical frames
void forceDisplay(bool forceUiUpdate = false);
/// Draws our SSL cert screen during boot (called from WebServer)
void setSSLFrames();
// Dismiss the currently focussed frame, if possible (e.g. text message, waypoint)
void dismissCurrentFrame();
#ifdef USE_EINK
/// Draw an image to remain on E-Ink display after screen off
void setScreensaverFrames(FrameCallback einkScreensaver = NULL);
#endif
protected:
/// Updates the UI.
//
// Called periodically from the main loop.
int32_t runOnce() final;
bool isAUTOOled = false;
// Screen dimensions (for convenience)
// Defined during Screen::setup
uint16_t displayWidth = 0;
uint16_t displayHeight = 0;
private:
FrameCallback alertFrames[1];
struct ScreenCmd {
Cmd cmd;
union {
uint32_t bluetooth_pin;
char *print_text;
};
};
/// Enques given command item to be processed by main loop().
bool enqueueCmd(const ScreenCmd &cmd)
{
if (!useDisplay)
return false; // not enqueued if our display is not in use
else {
bool success = cmdQueue.enqueue(cmd, 0);
enabled = true; // handle ASAP (we are the registered reader for cmdQueue, but might have been disabled)
return success;
}
}
// Implementations of various commands, called from doTask().
void handleSetOn(bool on, FrameCallback einkScreensaver = NULL);
void handleOnPress();
void handleShowNextFrame();
void handleShowPrevFrame();
void handleStartFirmwareUpdateScreen();
void TZPicker();
void LoraRegionPicker(uint32_t duration = 30000);
// Info collected by setFrames method.
// Index location of specific frames.
// - Used to apply the FrameFocus parameter of setFrames
// - Used to dismiss the currently shown frame (txt; waypoint) by CardKB combo
struct FramesetInfo {
struct FramePositions {
uint8_t fault = 255;
uint8_t textMessage = 255;
uint8_t waypoint = 255;
uint8_t focusedModule = 255;
uint8_t log = 255;
uint8_t settings = 255;
uint8_t wifi = 255;
uint8_t deviceFocused = 255;
uint8_t memory = 255;
uint8_t gps = 255;
uint8_t home = 255;
uint8_t clock = 255;
uint8_t firstFavorite = 255;
uint8_t lastFavorite = 255;
uint8_t lora = 255;
} positions;
uint8_t frameCount = 0;
} framesetInfo;
struct DismissedFrames {
bool textMessage = false;
bool waypoint = false;
bool wifi = false;
bool memory = false;
} dismissedFrames;
/// Try to start drawing ASAP
void setFastFramerate();
// Sets frame up for immediate drawing
void setFrameImmediateDraw(FrameCallback *drawFrames);
/// callback for current alert frame
FrameCallback alertFrame;
/// Queue of commands to execute in doTask.
TypedQueue<ScreenCmd> cmdQueue;
/// Whether we are using a display
bool useDisplay = false;
/// Whether the display is currently powered
bool screenOn = false;
// Whether we are showing the regular screen (as opposed to booth screen or
// Bluetooth PIN screen)
bool showingNormalScreen = false;
// Implementation to Adjust Brightness
uint8_t brightness = BRIGHTNESS_DEFAULT; // H = 254, MH = 192, ML = 130 L = 103
bool hasCompass = false;
float compassHeading;
uint32_t endCalibrationAt;
/// Holds state for debug information
DebugInfo debugInfo;
/// Display device
OLEDDisplay *dispdev;
/// UI helper for rendering to frames and switching between them
OLEDDisplayUi *ui;
};
} // namespace graphics
// Extern declarations for function symbols used in UIRenderer
extern std::vector<std::string> functionSymbol;
extern std::string functionSymbolString;
#endif