mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-29 14:10:53 +00:00
* TwoButtonExtened mirrors TwoButton but added joystick functionality * basic ui navigation with a joystick settings->joystick.enabled setting added and SETTINGS_VERSION incremented by one in InkHUD/Persistence.h in seeed_wio_tracker_L1_eink/nicheGraphics.h enable joystick and disable "Next Tile" menu item in implement prevTile and prevApplet functions in InkHUD/WindowManager.h,cpp and InkHUD/InkHUD.h,cpp onStickCenterShort, onStickCenterLong, onStickUp, onStickDown, onStickLeft, and onStickRight functions added to: - InkHUD/InkHUD.h,cpp - InkHUD/Events.h,cpp - InkHUD/Applet.h change navigation actions in InkHUD/Events.cpp events based on whether the joystick is enabled or not in seeed_wio_tracker_L1_eink/nicheGraphics.h connect joystick events to the new joystick handler functions * handle joystick input in NotificationApplet and TipsApplet Both the joystick center short press and the user button short press can be used to advance through the Tips applet. dismiss notifications with any joystick input * MenuApplet controls allows menu navigation including a back button * add AlignStickApplet for aligning the joystick with the screen add joystick.aligned and joystick.alignment to InkHUD/Persistence.h for storing alignment status and relative angle create AlignStick applet that prompts the user for a joystick input and rotates the controls to align with the screen AlignStick applet is run after the tips applet if the joystick is enabled and not aligned add menu item for opening the AlignStick applet * update tips applet with joystick controls * format InkHUD additions * fix stroke consistency when resizing joystick graphic * tweak button tips for order consistency * increase joystick debounce * fix comments * remove unnecessary '+' * remap joystick controls to match standard inkHUD behavior Input with a joystick now behaves as follows User Button (joystick center): - short press in applet -> opens menu - long press in applet -> opens menu - short press in menu -> selects - long press in menu -> selects Exit Button: - short press in applet -> switches tile - long press in applet -> nothing for now - short press in menu -> closes menu - long press in menu -> nothing for now --------- Co-authored-by: scobert <scobert57@gmail.com> Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
334 lines
10 KiB
C++
334 lines
10 KiB
C++
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
|
|
|
#include "./InkHUD.h"
|
|
|
|
#include "./Applet.h"
|
|
#include "./Events.h"
|
|
#include "./Persistence.h"
|
|
#include "./Renderer.h"
|
|
#include "./SystemApplet.h"
|
|
#include "./Tile.h"
|
|
#include "./WindowManager.h"
|
|
|
|
using namespace NicheGraphics;
|
|
|
|
// Get or create the singleton
|
|
InkHUD::InkHUD *InkHUD::InkHUD::getInstance()
|
|
{
|
|
// Create the singleton instance of our class, if not yet done
|
|
static InkHUD *instance = nullptr;
|
|
if (!instance) {
|
|
instance = new InkHUD;
|
|
|
|
instance->persistence = new Persistence;
|
|
instance->windowManager = new WindowManager;
|
|
instance->renderer = new Renderer;
|
|
instance->events = new Events;
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
// Connect the (fully set-up) E-Ink driver to InkHUD
|
|
// Should happen in your variant's nicheGraphics.h file, before InkHUD::begin is called
|
|
void InkHUD::InkHUD::setDriver(Drivers::EInk *driver)
|
|
{
|
|
renderer->setDriver(driver);
|
|
}
|
|
|
|
// Set the target number of FAST display updates in a row, before a FULL update is used for display health
|
|
// This value applies only to updates with an UNSPECIFIED update type
|
|
// If explicitly requested FAST updates exceed this target, the stressMultiplier parameter determines how many
|
|
// subsequent FULL updates will be performed, in an attempt to restore the display's health
|
|
void InkHUD::InkHUD::setDisplayResilience(uint8_t fastPerFull, float stressMultiplier)
|
|
{
|
|
renderer->setDisplayResilience(fastPerFull, stressMultiplier);
|
|
}
|
|
|
|
// Register a user applet with InkHUD
|
|
// A variant's nicheGraphics.h file should instantiate your chosen applets, then pass them to this method
|
|
// Passing an applet to this method is all that is required to make it available to the user in your InkHUD build
|
|
void InkHUD::InkHUD::addApplet(const char *name, Applet *a, bool defaultActive, bool defaultAutoshow, uint8_t onTile)
|
|
{
|
|
windowManager->addApplet(name, a, defaultActive, defaultAutoshow, onTile);
|
|
}
|
|
|
|
// Start InkHUD!
|
|
// Call this only after you have configured InkHUD
|
|
void InkHUD::InkHUD::begin()
|
|
{
|
|
persistence->loadSettings();
|
|
persistence->loadLatestMessage();
|
|
|
|
windowManager->begin();
|
|
events->begin();
|
|
renderer->begin();
|
|
// LogoApplet shows boot screen here
|
|
}
|
|
|
|
// Call this when your user button gets a short press
|
|
// Should be connected to an input source in nicheGraphics.h (NicheGraphics::Inputs::TwoButton?)
|
|
void InkHUD::InkHUD::shortpress()
|
|
{
|
|
events->onButtonShort();
|
|
}
|
|
|
|
// Call this when your user button gets a long press
|
|
// Should be connected to an input source in nicheGraphics.h (NicheGraphics::Inputs::TwoButton?)
|
|
void InkHUD::InkHUD::longpress()
|
|
{
|
|
events->onButtonLong();
|
|
}
|
|
|
|
// Call this when your exit button gets a short press
|
|
void InkHUD::InkHUD::exitShort()
|
|
{
|
|
events->onExitShort();
|
|
}
|
|
|
|
// Call this when your exit button gets a long press
|
|
void InkHUD::InkHUD::exitLong()
|
|
{
|
|
events->onExitLong();
|
|
}
|
|
|
|
// Call this when your joystick gets an up input
|
|
void InkHUD::InkHUD::navUp()
|
|
{
|
|
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
|
|
case 1: // 90 deg
|
|
events->onNavLeft();
|
|
break;
|
|
case 2: // 180 deg
|
|
events->onNavDown();
|
|
break;
|
|
case 3: // 270 deg
|
|
events->onNavRight();
|
|
break;
|
|
default: // 0 deg
|
|
events->onNavUp();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Call this when your joystick gets a down input
|
|
void InkHUD::InkHUD::navDown()
|
|
{
|
|
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
|
|
case 1: // 90 deg
|
|
events->onNavRight();
|
|
break;
|
|
case 2: // 180 deg
|
|
events->onNavUp();
|
|
break;
|
|
case 3: // 270 deg
|
|
events->onNavLeft();
|
|
break;
|
|
default: // 0 deg
|
|
events->onNavDown();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Call this when your joystick gets a left input
|
|
void InkHUD::InkHUD::navLeft()
|
|
{
|
|
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
|
|
case 1: // 90 deg
|
|
events->onNavDown();
|
|
break;
|
|
case 2: // 180 deg
|
|
events->onNavRight();
|
|
break;
|
|
case 3: // 270 deg
|
|
events->onNavUp();
|
|
break;
|
|
default: // 0 deg
|
|
events->onNavLeft();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Call this when your joystick gets a right input
|
|
void InkHUD::InkHUD::navRight()
|
|
{
|
|
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
|
|
case 1: // 90 deg
|
|
events->onNavUp();
|
|
break;
|
|
case 2: // 180 deg
|
|
events->onNavLeft();
|
|
break;
|
|
case 3: // 270 deg
|
|
events->onNavDown();
|
|
break;
|
|
default: // 0 deg
|
|
events->onNavRight();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Cycle the next user applet to the foreground
|
|
// Only activated applets are cycled
|
|
// If user has a multi-applet layout, the applets will cycle on the "focused tile"
|
|
void InkHUD::InkHUD::nextApplet()
|
|
{
|
|
windowManager->nextApplet();
|
|
}
|
|
|
|
// Cycle the previous user applet to the foreground
|
|
// Only activated applets are cycled
|
|
// If user has a multi-applet layout, the applets will cycle on the "focused tile"
|
|
void InkHUD::InkHUD::prevApplet()
|
|
{
|
|
windowManager->prevApplet();
|
|
}
|
|
|
|
// Show the menu (on the the focused tile)
|
|
// The applet previously displayed there will be restored once the menu closes
|
|
void InkHUD::InkHUD::openMenu()
|
|
{
|
|
windowManager->openMenu();
|
|
}
|
|
|
|
// Bring AlignStick applet to the foreground
|
|
void InkHUD::InkHUD::openAlignStick()
|
|
{
|
|
windowManager->openAlignStick();
|
|
}
|
|
|
|
// In layouts where multiple applets are shown at once, change which tile is focused
|
|
// The focused tile in the one which cycles applets on button short press, and displays menu on long press
|
|
void InkHUD::InkHUD::nextTile()
|
|
{
|
|
windowManager->nextTile();
|
|
}
|
|
|
|
// In layouts where multiple applets are shown at once, change which tile is focused
|
|
// The focused tile in the one which cycles applets on button short press, and displays menu on long press
|
|
void InkHUD::InkHUD::prevTile()
|
|
{
|
|
windowManager->prevTile();
|
|
}
|
|
|
|
// Rotate the display image by 90 degrees
|
|
void InkHUD::InkHUD::rotate()
|
|
{
|
|
windowManager->rotate();
|
|
}
|
|
|
|
// rotate the joystick in 90 degree increments
|
|
void InkHUD::InkHUD::rotateJoystick(uint8_t angle)
|
|
{
|
|
persistence->settings.joystick.alignment += angle;
|
|
persistence->settings.joystick.alignment %= 4;
|
|
}
|
|
|
|
// Show / hide the battery indicator in top-right
|
|
void InkHUD::InkHUD::toggleBatteryIcon()
|
|
{
|
|
windowManager->toggleBatteryIcon();
|
|
}
|
|
|
|
// An applet asking for the display to be updated
|
|
// This does not occur immediately
|
|
// Instead, rendering is scheduled ASAP, for the next Renderer::runOnce call
|
|
// This allows multiple applets to observe the same event, and then share the same opportunity to update
|
|
// Applets should requestUpdate, whether or not they are currently displayed ("foreground")
|
|
// This is because they *might* be automatically brought to foreground by WindowManager::autoshow
|
|
void InkHUD::InkHUD::requestUpdate()
|
|
{
|
|
renderer->requestUpdate();
|
|
}
|
|
|
|
// Demand that the display be updated
|
|
// Ignores all diplomacy:
|
|
// - the display *will* update
|
|
// - the specified update type *will* be used
|
|
// If the async parameter is false, code flow is blocked while the update takes place
|
|
void InkHUD::InkHUD::forceUpdate(EInk::UpdateTypes type, bool async)
|
|
{
|
|
renderer->forceUpdate(type, async);
|
|
}
|
|
|
|
// Wait for any in-progress display update to complete before continuing
|
|
void InkHUD::InkHUD::awaitUpdate()
|
|
{
|
|
renderer->awaitUpdate();
|
|
}
|
|
|
|
// Ask the window manager to potentially bring a different user applet to foreground
|
|
// An applet will be brought to foreground if it has just received new and relevant info
|
|
// For Example: AllMessagesApplet has just received a new text message
|
|
// Permission for this autoshow behavior is granted by the user, on an applet-by-applet basis
|
|
// If autoshow brings an applet to foreground, an InkHUD notification will not be generated for the same event
|
|
void InkHUD::InkHUD::autoshow()
|
|
{
|
|
windowManager->autoshow();
|
|
}
|
|
|
|
// Tell the window manager that the Persistence::Settings value for applet activation has changed,
|
|
// and that it should reconfigure accordingly.
|
|
// This is triggered at boot, or when the user enables / disabled applets via the on-screen menu
|
|
void InkHUD::InkHUD::updateAppletSelection()
|
|
{
|
|
windowManager->changeActivatedApplets();
|
|
}
|
|
|
|
// Tell the window manager that the Persistence::Settings value for layout or rotation has changed,
|
|
// and that it should reconfigure accordingly.
|
|
// This is triggered at boot, or by rotate / layout options in the on-screen menu
|
|
void InkHUD::InkHUD::updateLayout()
|
|
{
|
|
windowManager->changeLayout();
|
|
}
|
|
|
|
// Width of the display, in the context of the current rotation
|
|
uint16_t InkHUD::InkHUD::width()
|
|
{
|
|
return renderer->width();
|
|
}
|
|
|
|
// Height of the display, in the context of the current rotation
|
|
uint16_t InkHUD::InkHUD::height()
|
|
{
|
|
return renderer->height();
|
|
}
|
|
|
|
// A collection of any user tiles which do not have a valid user applet
|
|
// This can occur in various situations, such as when a user enables fewer applets than their layout has tiles
|
|
// The tiles (and which regions the occupy) are private information of the window manager
|
|
// The renderer needs to know which regions (if any) are empty,
|
|
// in order to fill them with a "placeholder" pattern.
|
|
// -- There may be a tidier way to accomplish this --
|
|
std::vector<InkHUD::Tile *> InkHUD::InkHUD::getEmptyTiles()
|
|
{
|
|
return windowManager->getEmptyTiles();
|
|
}
|
|
|
|
// Get a system applet by its name
|
|
// This isn't particularly elegant, but it does avoid:
|
|
// - passing around a big set of references
|
|
// - having two sets of references (systemApplet vector for iteration)
|
|
InkHUD::SystemApplet *InkHUD::InkHUD::getSystemApplet(const char *name)
|
|
{
|
|
for (SystemApplet *sa : systemApplets) {
|
|
if (strcmp(name, sa->name) == 0)
|
|
return sa;
|
|
}
|
|
|
|
assert(false); // Invalid name
|
|
}
|
|
|
|
// Place a pixel into the image buffer
|
|
// The x and y coordinates are in the context of the current display rotation
|
|
// - Applets pass "relative" pixels to tiles
|
|
// - Tiles pass translated pixels to this method
|
|
// - this methods (Renderer) places rotated pixels into the image buffer
|
|
// This method provides the final formatting step required. The image buffer is suitable for writing to display
|
|
void InkHUD::InkHUD::drawPixel(int16_t x, int16_t y, Color c)
|
|
{
|
|
renderer->handlePixel(x, y, c);
|
|
}
|
|
|
|
#endif |