feat: Add touch coordinate mapping and RTOS task yielding (#2481)

Co-authored-by: Julia Nguyen <julia@uxj.io>
This commit is contained in:
Justin Mitchell
2026-07-20 16:31:07 -04:00
committed by GitHub
co-authored by Julia Nguyen
parent c9188a7347
commit f42fab1c66
123 changed files with 3564 additions and 1380 deletions
+61 -3
View File
@@ -2,8 +2,10 @@
#include <CrossPointSettings.h>
#include <GfxRenderer.h>
#include <HalGPIO.h>
#include <HalTiltSensor.h>
#include <Logging.h>
#include <components/bars/tap-zones.h>
#include "MappedInputManager.h"
#include "activities/ActivityManager.h"
@@ -16,6 +18,11 @@ constexpr unsigned long SKIP_HOLD_MS = 700;
constexpr unsigned long BOOKMARK_HOLD_MS = 400;
constexpr unsigned long BOOKMARK_MESSAGE_DURATION_MS = 2500;
enum ReaderTouchAction : freeink::ui::ActionId {
READER_TOUCH_PREV = 1,
READER_TOUCH_NEXT = 3,
};
inline void applyOrientation(GfxRenderer& renderer, const uint8_t orientation) {
switch (orientation) {
case CrossPointSettings::ORIENTATION::PORTRAIT:
@@ -61,12 +68,63 @@ inline PageTurnResult detectPageTurn(const MappedInputManager& input) {
return {prev, next, tiltPrev || tiltNext};
}
inline void displayWithRefreshCycle(const GfxRenderer& renderer, int& pagesUntilFullRefresh) {
struct TouchPageTurn {
bool prev;
bool next;
unsigned long heldMs;
};
inline TouchPageTurn detectTouchPageTurn(GfxRenderer& renderer, const MappedInputManager& input) {
TouchPageTurn result{false, false, 0};
if (!SETTINGS.touchReaderControls || !input.hasTouch()) {
return result;
}
int x = 0;
int y = 0;
if (!input.wasScreenTapped(x, y)) {
return result;
}
const int16_t width = static_cast<int16_t>(renderer.getScreenWidth());
const int16_t height = static_cast<int16_t>(renderer.getScreenHeight());
const int16_t previousZoneWidth = width / 3;
const freeink::ui::TapZone zones[] = {
{freeink::ui::Rect{0, 0, previousZoneWidth, height}, READER_TOUCH_PREV},
{freeink::ui::Rect{previousZoneWidth, 0, static_cast<int16_t>(width - previousZoneWidth), height},
READER_TOUCH_NEXT},
};
for (const auto& zone : zones) {
if (!zone.enabled || !zone.rect.contains(static_cast<int16_t>(x), static_cast<int16_t>(y))) continue;
result.prev = zone.action == READER_TOUCH_PREV;
result.next = zone.action == READER_TOUCH_NEXT;
break;
}
result.heldMs = gpio.lastTouchHeldMs();
return result;
}
// Reader menu opens on a downward swipe from the top edge (replaces the old center tap-and-hold).
inline bool isTouchMenuGesture(const MappedInputManager& input) {
return SETTINGS.touchReaderControls && input.hasTouch() && input.wasMenuGesture();
}
// One helper, blocking or deferred: the async form starts the refresh and
// returns so the caller can overlap CPU work with the panel's refresh time.
// Async callers must not touch the framebuffer until
// renderer.waitRefreshComplete() and must rebuild the differential baseline
// before the next page turn (the tiled grayscale cleanup does).
inline void displayWithRefreshCycle(const GfxRenderer& renderer, int& pagesUntilFullRefresh, bool async = false) {
const auto mode = (pagesUntilFullRefresh <= 1) ? HalDisplay::HALF_REFRESH : HalDisplay::FAST_REFRESH;
if (async) {
renderer.displayBufferAsync(mode);
} else {
renderer.displayBuffer(mode);
}
if (pagesUntilFullRefresh <= 1) {
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
pagesUntilFullRefresh = SETTINGS.getRefreshFrequency();
} else {
renderer.displayBuffer();
pagesUntilFullRefresh--;
}
}