Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions Documentation/ideas.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,8 @@

## Higher Priority

- Wi-Fi and Bluetooth settings app: scanning should start and gather results, then it should stop refreshing the scan results automatically. When scanning is finished, a paginated view should be shown with the results (as many as fit on the screen, but not more). There should be an icon on the toolbar to refresh scanning (looking glass? something else?) and this icon is only visible when the radio is enabled. When radio is enabled by the user, automatically start a scan. If the radio was already enabled and the app starts, start scanning.
- Update to LVGL 9.6.x (ESP-IDF and POSIX)
- Terminal app: on devices with pointer device: LVGL rendering mode should hide the toolbar by default, but tapping the terminal should toggle the visibility of the toolbar.
- Terminal app: should always run in non-LVGL mode if no PSRAM is present.
- Crash app should work without LVGL, like Boot app. It should wait for any input (keyboard or pointer) to continue.
- NimBLE looses pairing key after reboot
- CrashDiagnostics shouldn't show a QR when there's no callstack
- Move USB host task stacks to SPIRAM when available: esp32_usbhost*.cpp
- Add bold fonts for e-ink readability improvement
- Improve Setup: Add keyboard/keypad/encoder navigation explanation.
Expand Down Expand Up @@ -49,7 +44,6 @@
- Consider implementing LVGL gridnav in apps https://lvgl.io/docs/open/9.3/details/auxiliary-modules/gridnav.html
- Make USB host driver disabled by default, so it doesn't consume memory
- TactilityTool: Make API compatibility table (and check for compatibility in the tool itself)
- Bug: Crash handling app cannot be exited with an EncoderDevice. (current work-around is to manually reset the device)
- Refactor HttpServer into C code and move implementation to http-module
- Use GPS time to set/update the current time
- Make a URL handler. Use it for handling local files. Match file types with apps.
Expand Down
16 changes: 16 additions & 0 deletions Tactility/Private/Tactility/app/boot/BootScreen.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <cstdint>
#include <functional>
#include <string>
#include <vector>

Expand Down Expand Up @@ -30,6 +32,12 @@ class BootScreen {
int logicalWidth() const { return (rotation % 2 == 0) ? panelWidth : panelHeight; }
int logicalHeight() const { return (rotation % 2 == 0) ? panelHeight : panelWidth; }

/** @return true when the image has a pixel at (x, y), with its colour as RGB565 */
using ImagePixel = std::function<bool(int x, int y, uint16_t& color)>;

/** Draws an image with text lines below it, centered on a black background */
void draw(int imageWidth, int imageHeight, const ImagePixel& imagePixel, const std::vector<std::string>& lines);

public:

~BootScreen();
Expand All @@ -49,6 +57,14 @@ class BootScreen {
* @param[in] lines text lines, wrapped when they're too long for the display
*/
void show(const std::string& logoPath, const std::vector<std::string>& lines);

/**
* Shows a QR code with text lines below it, centered on a black background. The set modules are white.
* @param[in] moduleCount the width and height of the QR code in modules
* @param[in] isModuleSet whether the module at (x, y) is set
* @param[in] lines text lines, wrapped when they're too long for the display
*/
void showQrCode(int moduleCount, const std::function<bool(int x, int y)>& isModuleSet, const std::vector<std::string>& lines);
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@

#ifdef ESP_PLATFORM

namespace tt::app::boot {
class BootScreen;
}

namespace tt::app::crashdiagnostics {

void start();
/**
* Writes the crash log and shows the crash with a QR code for reporting it, after a panic reboot.
* Returns when the user continues with any input, and blocks forever when there's no input device to continue with.
* @param[in] screen the boot screen, which may have no display
*/
void showCrashScreen(boot::BootScreen& screen);

}

Expand Down
7 changes: 0 additions & 7 deletions Tactility/Private/Tactility/app/launcher/Launcher.h

This file was deleted.

2 changes: 0 additions & 2 deletions Tactility/Source/InitApps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ namespace app {
#ifdef ESP_PLATFORM
namespace apwebserver { extern const ::AppManifest manifest; }
namespace camera { extern const ::AppManifest manifest; }
namespace crashdiagnostics { extern const ::AppManifest manifest; }
#if CONFIG_TT_TDECK_WORKAROUND == 1
namespace keyboardsettings { extern const ::AppManifest manifest; } // T-Deck only for now
#endif
Expand Down Expand Up @@ -152,7 +151,6 @@ static void registerInternalApps() {
if (device_exists_of_type(&CAMERA_TYPE)) {
app_manager_add(&app::camera::manifest);
}
app_manager_add(&app::crashdiagnostics::manifest);
#if defined(CONFIG_TT_TDECK_WORKAROUND)
app_manager_add(&app::keyboardsettings::manifest);
#endif
Expand Down
13 changes: 6 additions & 7 deletions Tactility/Source/app/boot/BootInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,6 @@ void waitForInputAndReboot() {
}

void startNextApp() {
#ifdef ESP_PLATFORM
if (esp_reset_reason() == ESP_RST_PANIC) {
crashdiagnostics::start(); // fire-and-forget; no result expected back
return;
}
#endif

auto launcher_app_id = getLauncherAppId();
uint32_t launcher_instance_id = 0;
AppStartContext context;
Expand Down Expand Up @@ -271,6 +264,12 @@ bool bootInit(TickType_t startTime) {
return false;
}

#ifdef ESP_PLATFORM
if (esp_reset_reason() == ESP_RST_PANIC) {
crashdiagnostics::showCrashScreen(screen);
}
#endif

LOG_I(TAG, "Loading fonts");
lvgl::loadFonts(lvgl::loadFontConfiguration());
lvgl::configureTheme(settings::appearance::loadOrGetDefault());
Expand Down
57 changes: 44 additions & 13 deletions Tactility/Source/app/boot/BootScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,41 @@ void BootScreen::show(const std::string& logoPath, const std::vector<std::string
}
}

// Layout in logical (rotated) coordinates: logo with text below it, centered as a whole
draw(static_cast<int>(logo.width), static_cast<int>(logo.height), [&logo](int x, int y, uint16_t& color) {
const unsigned char* pixel = logo.rgba + (static_cast<size_t>(y) * logo.width + x) * 4;
const uint32_t alpha = pixel[3];
if (alpha == 0) {
return false;
}
// Blended onto the black background
color = toRgb565(pixel[0] * alpha / 255, pixel[1] * alpha / 255, pixel[2] * alpha / 255);
return true;
}, lines);
}

void BootScreen::showQrCode(int moduleCount, const std::function<bool(int x, int y)>& isModuleSet, const std::vector<std::string>& lines) {
if (target == nullptr || moduleCount <= 0) {
return;
}

// About 60% of the display, which leaves room for the text
const int module_size = std::max(1, getSmallestDimension() * 6 / 10 / moduleCount);
const int size = module_size * moduleCount;
draw(size, size, [&isModuleSet, module_size](int x, int y, uint16_t& color) {
if (!isModuleSet(x / module_size, y / module_size)) {
return false;
}
color = 0xFFFF;
return true;
}, lines);
}

void BootScreen::draw(int imageWidth, int imageHeight, const ImagePixel& imagePixel, const std::vector<std::string>& lines) {
if (target == nullptr) {
return;
}

// Layout in logical (rotated) coordinates: the image with text below it, centered as a whole
if (!lines.empty() && textFont == nullptr) {
textFont = lvgl::loadMonoFont();
if (textFont == nullptr) {
Expand All @@ -179,10 +213,10 @@ void BootScreen::show(const std::string& logoPath, const std::vector<std::string
}
}
const int text_height = static_cast<int>(wrapped_lines.size()) * line_height;
const int gap = (logo.height > 0 && !wrapped_lines.empty()) ? line_height : 0;
const int content_top = (logical_height - static_cast<int>(logo.height) - gap - text_height) / 2;
const int logo_left = (logical_width - static_cast<int>(logo.width)) / 2;
const int text_top = content_top + static_cast<int>(logo.height) + gap;
const int gap = (imageHeight > 0 && !wrapped_lines.empty()) ? line_height : 0;
const int content_top = (logical_height - imageHeight - gap - text_height) / 2;
const int image_left = (logical_width - imageWidth) / 2;
const int text_top = content_top + imageHeight + gap;

std::vector<uint8_t> glyph_bitmap;
for (int band_top = 0; band_top < panelHeight; band_top += bandHeight) {
Expand All @@ -204,14 +238,11 @@ void BootScreen::show(const std::string& logoPath, const std::vector<std::string
}
};

for (unsigned y = 0; y < logo.height; y++) {
for (unsigned x = 0; x < logo.width; x++) {
const unsigned char* pixel = logo.rgba + (static_cast<size_t>(y) * logo.width + x) * 4;
const uint32_t alpha = pixel[3];
if (alpha != 0) {
// Blended onto the black background
const uint16_t color = toRgb565(pixel[0] * alpha / 255, pixel[1] * alpha / 255, pixel[2] * alpha / 255);
plot(logo_left + static_cast<int>(x), content_top + static_cast<int>(y), color, PIXEL_BUFFER_CONVERSION_LUMA_THRESHOLD);
for (int y = 0; y < imageHeight; y++) {
for (int x = 0; x < imageWidth; x++) {
uint16_t color;
if (imagePixel(x, y, color)) {
plot(image_left + x, content_top + y, color, PIXEL_BUFFER_CONVERSION_LUMA_THRESHOLD);
}
}
}
Expand Down
Loading
Loading