Currently, each activity has to manage their own `displayTaskLoop` which adds redundant boilerplate code. The loop is a wait loop which is also not the best practice, as the `updateRequested` boolean is not protected by a mutex. In this PR: - Move `displayTaskLoop` to the super `Activity` class - Replace `updateRequested` with freeRTOS's [direct to task notification](https://www.freertos.org/Documentation/02-Kernel/02-Kernel-features/03-Direct-to-task-notifications/01-Task-notifications) - For `ActivityWithSubactivity`, whenever a sub-activity is present, the parent's `render()` automatically goes inactive With this change, activities now only need to expose `render()` function, and anywhere in the code base can call `requestUpdate()` to request a new rendering pass. In theory, this change may also make the battery life a bit better, since one wait loop is removed. Although the equipment in my home lab wasn't been able to verify it (the electric current is too noisy and small). Would appreciate if anyone has any insights on this subject. Update: I managed to hack [a small piece of code](https://github.com/ngxson/crosspoint-reader/tree/xsn/measure_cpu_usage) that allow tracking CPU idle time. The CPU load does decrease a bit (1.47% down to 1.39%), which make sense, because the display task is now sleeping most of the time unless notified. This should translate to a slightly increase in battery life in the long run. ``` PR: [40012] [MEM] Free: 185856 bytes, Total: 231004 bytes, Min Free: 123316 bytes [40012] [IDLE] Idle time: 98.61% (CPU load: 1.39%) [50017] [MEM] Free: 185856 bytes, Total: 231004 bytes, Min Free: 123316 bytes [50017] [IDLE] Idle time: 98.61% (CPU load: 1.39%) [60022] [MEM] Free: 185856 bytes, Total: 231004 bytes, Min Free: 123316 bytes [60022] [IDLE] Idle time: 98.61% (CPU load: 1.39%) master: [20012] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [20012] [IDLE] Idle time: 98.53% (CPU load: 1.47%) [30017] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [30017] [IDLE] Idle time: 98.53% (CPU load: 1.47%) [40022] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [40022] [IDLE] Idle time: 98.53% (CPU load: 1.47%) ``` --- While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? **NO** <!-- This is an auto-generated comment: release notes by coderabbit.ai --> * **Refactor** * Streamlined rendering architecture by consolidating update mechanisms across all activities, improving efficiency and consistency. * Modernized synchronization patterns for display updates to ensure reliable, conflict-free rendering. * **Bug Fixes** * Enhanced rendering stability through improved locking mechanisms and explicit update requests. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: znelson <znelson@users.noreply.github.com>
96 lines
3.2 KiB
C++
96 lines
3.2 KiB
C++
#pragma once
|
|
#include <GfxRenderer.h>
|
|
|
|
#include <functional>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "../Activity.h"
|
|
#include "util/ButtonNavigator.h"
|
|
|
|
/**
|
|
* Reusable keyboard entry activity for text input.
|
|
* Can be started from any activity that needs text entry.
|
|
*
|
|
* Usage:
|
|
* 1. Create a KeyboardEntryActivity instance
|
|
* 2. Set callbacks with setOnComplete() and setOnCancel()
|
|
* 3. Call onEnter() to start the activity
|
|
* 4. Call loop() in your main loop
|
|
* 5. When complete or cancelled, callbacks will be invoked
|
|
*/
|
|
class KeyboardEntryActivity : public Activity {
|
|
public:
|
|
// Callback types
|
|
using OnCompleteCallback = std::function<void(const std::string&)>;
|
|
using OnCancelCallback = std::function<void()>;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param renderer Reference to the GfxRenderer for drawing
|
|
* @param mappedInput Reference to MappedInputManager for handling input
|
|
* @param title Title to display above the keyboard
|
|
* @param initialText Initial text to show in the input field
|
|
* @param startY Y position to start rendering the keyboard
|
|
* @param maxLength Maximum length of input text (0 for unlimited)
|
|
* @param isPassword If true, display asterisks instead of actual characters
|
|
* @param onComplete Callback invoked when input is complete
|
|
* @param onCancel Callback invoked when input is cancelled
|
|
*/
|
|
explicit KeyboardEntryActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
|
std::string title = "Enter Text", std::string initialText = "", const int startY = 10,
|
|
const size_t maxLength = 0, const bool isPassword = false,
|
|
OnCompleteCallback onComplete = nullptr, OnCancelCallback onCancel = nullptr)
|
|
: Activity("KeyboardEntry", renderer, mappedInput),
|
|
title(std::move(title)),
|
|
text(std::move(initialText)),
|
|
startY(startY),
|
|
maxLength(maxLength),
|
|
isPassword(isPassword),
|
|
onComplete(std::move(onComplete)),
|
|
onCancel(std::move(onCancel)) {}
|
|
|
|
// Activity overrides
|
|
void onEnter() override;
|
|
void onExit() override;
|
|
void loop() override;
|
|
void render(Activity::RenderLock&&) override;
|
|
|
|
private:
|
|
std::string title;
|
|
int startY;
|
|
std::string text;
|
|
size_t maxLength;
|
|
bool isPassword;
|
|
|
|
ButtonNavigator buttonNavigator;
|
|
|
|
// Keyboard state
|
|
int selectedRow = 0;
|
|
int selectedCol = 0;
|
|
int shiftState = 0; // 0 = lower case, 1 = upper case, 2 = shift lock)
|
|
|
|
// Callbacks
|
|
OnCompleteCallback onComplete;
|
|
OnCancelCallback onCancel;
|
|
|
|
// Keyboard layout
|
|
static constexpr int NUM_ROWS = 5;
|
|
static constexpr int KEYS_PER_ROW = 13; // Max keys per row (rows 0 and 1 have 13 keys)
|
|
static const char* const keyboard[NUM_ROWS];
|
|
static const char* const keyboardShift[NUM_ROWS];
|
|
static const char* const shiftString[3];
|
|
|
|
// Special key positions (bottom row)
|
|
static constexpr int SPECIAL_ROW = 4;
|
|
static constexpr int SHIFT_COL = 0;
|
|
static constexpr int SPACE_COL = 2;
|
|
static constexpr int BACKSPACE_COL = 7;
|
|
static constexpr int DONE_COL = 9;
|
|
|
|
char getSelectedChar() const;
|
|
void handleKeyPress();
|
|
int getRowLength(int row) const;
|
|
void renderItemWithSelector(int x, int y, const char* item, bool isSelected) const;
|
|
};
|