Files
vicliu 71f10ae6d0 Feature/cardputer zero (#27)
* fix(tdeck): improve display startup and brightness handling

* fix(energy-sweep): use instant RSSI and only lock LoRa while scanning

* feat(cardputer-zero): add linux shells and M5 SDK baseline

* feat(cardputer-zero): add linux runtime baseline and shell ui simulator

* feat(cardputer-zero): unify linux shell boot and polish simulator

* docs(cardputer-zero): define final-shape adaptation spec

* feat(cardputer-zero): integrate shared linux runtimes and pages

* fix(linux-sim): mount repo root in dev container

* Fix GPS runtime semantics and transport init

Add a GPS specification and align platform runtimes around explicit GPS enable, power, receiver configuration, and external NMEA export semantics.

Keep internal NMEA parsing independent from external export settings, stop treating gps_mode as an enable flag, and update phone/UI config paths to use gps_enabled.

Decouple board-level GPS transport readiness from UBX receiver probing on T-Deck, T-Deck Pro, and T-LoRa Pager, and let boards own UART teardown.

Verified with pio run -e tdeck, pio run -e tlora_pager_sx1262, pio run -e gat562_mesh_evb_pro, and pio run -e tdeck_pro_a7682e.

* Add Russian localization pack

Add an installable European Cyrillic Extended locale bundle with Russian translations, Cyrillic font metadata, and package catalog entry.

Credit polarikus for the Russian translations based on the polarikus/trail-mate localization PR.

* Prepare 0.1.23-alpha release

* Fix T-Watch Morse release build

* Format CI-checked sources

* Fix Cardputer Linux CI dependencies

* Fix WSL validation smoke target build

* Prepare 0.1.24-alpha release

* fix: unblock Cardputer Zero Linux CI
2026-05-09 14:03:49 +08:00

178 lines
5.2 KiB
C++
Raw Permalink 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 <chrono>
#include <cstdint>
#include <deque>
#include <memory>
#include <vector>
#include "app/linux_app_facade.h"
#include "core/canvas.h"
#include "platform/surface_presenter.h"
#include "lvgl.h"
namespace trailmate::cardputer_zero
{
namespace app
{
struct InputEvent;
}
namespace linux_ui
{
// ---------------------------------------------------------------------------
// ShellSession
//
// Owns the app facade, shared UI startup sequence, input event queue, and
// per-frame tick logic. Does NOT own LVGL lifecycle (lv_init/lv_deinit),
// display, or framebuffer. That responsibility lives in the *LvglHost
// classes.
//
// Both the simulator and the real device shell share this session object.
// ---------------------------------------------------------------------------
class ShellSession
{
public:
ShellSession();
~ShellSession();
ShellSession(const ShellSession&) = delete;
ShellSession& operator=(const ShellSession&) = delete;
/// Initialize the app facade and begin the shared UI startup sequence.
/// Must be called after LVGL has been initialized by the host.
bool begin();
/// Drive one frame worth of app-layer work: update core services, tick
/// the event runtime, dispatch pending events.
void tick();
/// Push simulated or evdev input events into the session queue.
void enqueueInputs(const std::vector<app::InputEvent>& events);
/// Returns true once the startup sequence has finalized.
bool ready() const noexcept;
/// Access the underlying app facade.
MinimalLinuxAppFacade& facade() noexcept { return app_facade_; }
const MinimalLinuxAppFacade& facade() const noexcept
{
return app_facade_;
}
private:
friend class CanvasLvglHost;
struct QueuedKeyEvent
{
std::uint32_t key{};
lv_indev_state_t state{};
};
/// Called by the LVGL host's read-input callback to dequeue one key event.
/// Returns false when the queue is empty.
bool dequeueKeyEvent(std::uint32_t* key, lv_indev_state_t* state);
/// Peek: returns true if at least one event is enqueued (used to set
/// continue_reading without consuming the next event).
bool hasPendingKeyEvent() const noexcept;
struct Impl;
std::unique_ptr<Impl> impl_;
MinimalLinuxAppFacade app_facade_;
bool initialized_{false};
};
// ---------------------------------------------------------------------------
// CanvasLvglHost
//
// Owns the LVGL lifecycle (lv_init / lv_deinit), display, RGB565 buffer,
// keypad input device, and the framebuffer-to-Canvas copy step.
//
// Suitable for the SDL simulator and the custom Linux framebuffer shell.
// ---------------------------------------------------------------------------
class CanvasLvglHost
{
public:
explicit CanvasLvglHost(ShellSession& shell);
~CanvasLvglHost();
CanvasLvglHost(const CanvasLvglHost&) = delete;
CanvasLvglHost& operator=(const CanvasLvglHost&) = delete;
/// Drive one frame: tick the shell session, run lv_timer_handler(), and
/// (if dirty) copy the LVGL framebuffer to the output canvas.
void tick();
/// Read-only access to the output canvas for the surface presenter.
const core::Canvas& canvas() const noexcept;
/// LVGL callbacks — exact typed signatures matching LVGL's typedefs.
/// Implemented in the .cpp, where lvgl.h provides the full definitions.
static void flushCb(lv_display_t* display, const lv_area_t* area,
uint8_t* px_map);
static void readInputCb(lv_indev_t* indev, lv_indev_data_t* data);
private:
void copyFrameBufferToCanvas();
ShellSession& shell_;
lv_display_t* display_{};
lv_indev_t* keypad_{};
core::Canvas canvas_;
std::vector<std::uint16_t> frame_buffer_{};
bool dirty_{true};
};
// ---------------------------------------------------------------------------
// NativeLvglHost
//
// Thin host for the M5Stack Linux SDK path where LVGL display and input are
// already owned by the SDK. Only drives the shell session tick each frame.
//
// Usage in SDK main loop:
//
// lv_init();
// initLinuxDisplay(); // SDK-owned fbdev / evdev
//
// ShellSession shell;
// shell.begin();
//
// NativeLvglHost host{shell};
// while (true) {
// host.tick();
// lv_timer_handler();
// usleep(1000);
// }
// ---------------------------------------------------------------------------
class NativeLvglHost
{
public:
explicit NativeLvglHost(ShellSession& shell) : shell_(shell) {}
/// Drive the shell session tick. Caller is responsible for
/// lv_timer_handler() and the main-loop sleep.
void tick() { shell_.tick(); }
private:
ShellSession& shell_;
};
// ---------------------------------------------------------------------------
// Convenience desktop simulator / custom-fbdev main loop
// ---------------------------------------------------------------------------
/// Create a ShellSession + CanvasLvglHost and run the classic frame-paced
/// main loop driven by a SurfacePresenter. Backward-compatible entrypoint.
void runShellUi(
platform::SurfacePresenter& presenter,
std::chrono::milliseconds auto_exit_after = std::chrono::milliseconds::zero());
} // namespace linux_ui
} // namespace trailmate::cardputer_zero