diff --git a/apps/linux_cardputer_zero/APP_SHELL_MANIFEST.md b/apps/linux_cardputer_zero/APP_SHELL_MANIFEST.md index c3dac805..6b6f2225 100644 --- a/apps/linux_cardputer_zero/APP_SHELL_MANIFEST.md +++ b/apps/linux_cardputer_zero/APP_SHELL_MANIFEST.md @@ -23,6 +23,20 @@ framebuffer and evdev owner are promoted into this shell. - LoRa: SX1262 on `/dev/spidev0.1`, 500000 Hz, Reset=26, IRQ/DIO1=23, Busy=22, DIO2 RF switch enabled, DIO3 TCXO enabled +## Linux Session Component Facts + +- notifications: standard `org.freedesktop.Notifications` D-Bus provider + supplied by the Cardputer Zero user session +- notification shell IPC: `$XDG_RUNTIME_DIR/cardputer-zero/notifyd.sock`, + status/control only, never a notification creation path +- input method: Fcitx5 text input through the normal Linux frontend path +- IME display bridge: `cardputerzero-ui` Fcitx5 UI addon exports panel state to + `$XDG_RUNTIME_DIR/cardputer-zero/ime-panel.sock` +- IME panel: `cardputer-zero-ime-panel` renders candidate/preedit state as a + Wayland layer-shell projection and does not submit text +- expected session environment: `XMODIFIERS=@im=fcitx`, + `SDL_IM_MODULE=fcitx` + ## Build Entrypoint - `builds/linux_cmake` @@ -37,6 +51,9 @@ May: - own future framebuffer, evdev, packaging, and launch details for this device - own future Trail Mate Linux SX1262 packet-radio wiring for the documented Cardputer Zero LoRa endpoint +- route application notifications through the standard freedesktop notification + contract +- respect the Cardputer Zero Fcitx5 user-session boundary for text input Must not: @@ -45,6 +62,12 @@ Must not: - define protocol, chat, map, or storage semantics - absorb shared Linux runtime code that belongs in `platform/linux/common` - hide missing real-device hardware integration behind simulator naming +- use Cardputer Zero notifyd shell IPC to create notifications +- embed or fork the Cardputer Zero notifyd store, toast, or notification-center + implementation +- turn the IME panel socket into a text submission path +- embed the Cardputer Zero Fcitx5 addon, panel renderer, input method engine, + dictionary, or candidate selection policy inside Trail Mate ## Thin App Shell Entrypoint Declaration diff --git a/apps/linux_cardputer_zero/CMakeLists.txt b/apps/linux_cardputer_zero/CMakeLists.txt index 19e00d87..9f784605 100644 --- a/apps/linux_cardputer_zero/CMakeLists.txt +++ b/apps/linux_cardputer_zero/CMakeLists.txt @@ -12,6 +12,8 @@ include("${TRAIL_MATE_REPO_ROOT}/cmake/TrailMateUxPacks.cmake") trailmate_add_ui_lvgl_ux_packs(trailmate_ui_lvgl_ux_packs) add_library(trailmate_linux_cardputer_zero_shell + src/cardputer_zero_input_method_port.cpp + src/cardputer_zero_notification_port.cpp src/linux_cardputer_zero_app_shell.cpp "${TRAIL_MATE_REPO_ROOT}/modules/product_composition/src/target_profile.cpp" "${TRAIL_MATE_REPO_ROOT}/modules/product_composition/src/target_ux_binding.cpp") diff --git a/apps/linux_cardputer_zero/README.md b/apps/linux_cardputer_zero/README.md index f8c48ba4..0906189d 100644 --- a/apps/linux_cardputer_zero/README.md +++ b/apps/linux_cardputer_zero/README.md @@ -17,4 +17,19 @@ Known hardware facts in this repo: - LoRa: SX1262 on `/dev/spidev0.1`, 500000 Hz, Reset=26, IRQ/DIO1=23, Busy=22, DIO2 RF switch and DIO3 TCXO enabled +Known Cardputer Zero Linux session facts in this shell: + +- notifications are created through the standard + `org.freedesktop.Notifications` D-Bus interface; the + `$XDG_RUNTIME_DIR/cardputer-zero/notifyd.sock` channel is status/control only +- text entry uses Fcitx5 through the normal Linux toolkit/frontend path +- the Cardputer Zero IME addon/panel pair uses + `$XDG_RUNTIME_DIR/cardputer-zero/ime-panel.sock` only to display preedit, + candidates, and input-method state +- Trail Mate does not own the notification daemon implementation, Fcitx5 UI + addon, IME panel renderer, input method engine, dictionaries, or text commit + path +- the shared embedded touch/pinyin IME is not the Cardputer Zero Linux input + method strategy + The simulator remains a separate development shell under `apps/linux_sim_shell`. diff --git a/apps/linux_cardputer_zero/src/cardputer_zero_input_method_port.cpp b/apps/linux_cardputer_zero/src/cardputer_zero_input_method_port.cpp new file mode 100644 index 00000000..f555600b --- /dev/null +++ b/apps/linux_cardputer_zero/src/cardputer_zero_input_method_port.cpp @@ -0,0 +1,82 @@ +#include "cardputer_zero_input_method_port.h" + +#include + +namespace trailmate +{ +namespace apps +{ +namespace linux_cardputer_zero +{ +namespace +{ + +constexpr CardputerZeroInputMethodContract kContract = { + "fcitx5", + "Fcitx5 -> Linux toolkit frontend -> application", + "cardputerzero-ui", + "libcardputerzero-ui", + "cardputer-zero-ime-panel", + "cardputer-zero-ime-session", + "cardputer-zero-ime.service", + "cardputer-zero/ime-panel.sock", + "unix-jsonl-addon-to-panel", + "panel.update", + "panel.hide", + "state.update", + "@im=fcitx", + "fcitx", + "Control+space", + 320, + 170, + 3, + false, + false, + false, + false, + false, + false, + false, +}; + +} // namespace + +const CardputerZeroInputMethodContract& CardputerZeroInputMethodPort::contract() const +{ + return kContract; +} + +bool CardputerZeroInputMethodPort::validate() const +{ + const auto& c = contract(); + return std::strcmp(c.framework_name, "fcitx5") == 0 && + std::strcmp(c.text_submission_path, + "Fcitx5 -> Linux toolkit frontend -> application") == 0 && + std::strcmp(c.ui_addon_name, "cardputerzero-ui") == 0 && + std::strcmp(c.ui_addon_library, "libcardputerzero-ui") == 0 && + std::strcmp(c.panel_executable, "cardputer-zero-ime-panel") == 0 && + std::strcmp(c.session_executable, "cardputer-zero-ime-session") == 0 && + std::strcmp(c.user_service, "cardputer-zero-ime.service") == 0 && + std::strcmp(c.panel_socket_suffix, "cardputer-zero/ime-panel.sock") == 0 && + std::strcmp(c.panel_transport, "unix-jsonl-addon-to-panel") == 0 && + std::strcmp(c.panel_update_message, "panel.update") == 0 && + std::strcmp(c.panel_hide_message, "panel.hide") == 0 && + std::strcmp(c.state_update_message, "state.update") == 0 && + std::strcmp(c.xmodifiers, "@im=fcitx") == 0 && + std::strcmp(c.sdl_im_module, "fcitx") == 0 && + std::strcmp(c.trigger_key, "Control+space") == 0 && + c.screen_width == 320 && + c.screen_height == 170 && + c.max_exported_candidates == 3 && + !c.app_owns_input_method_engine && + !c.app_owns_candidate_display && + !c.app_owns_text_commit && + !c.panel_socket_commits_text && + !c.panel_requests_keyboard_focus && + !c.missing_panel_blocks_text_input && + !c.uses_embedded_touch_ime; +} + +} // namespace linux_cardputer_zero +} // namespace apps +} // namespace trailmate diff --git a/apps/linux_cardputer_zero/src/cardputer_zero_input_method_port.h b/apps/linux_cardputer_zero/src/cardputer_zero_input_method_port.h new file mode 100644 index 00000000..bcac7a12 --- /dev/null +++ b/apps/linux_cardputer_zero/src/cardputer_zero_input_method_port.h @@ -0,0 +1,48 @@ +#pragma once + +namespace trailmate +{ +namespace apps +{ +namespace linux_cardputer_zero +{ + +struct CardputerZeroInputMethodContract +{ + const char* framework_name; + const char* text_submission_path; + const char* ui_addon_name; + const char* ui_addon_library; + const char* panel_executable; + const char* session_executable; + const char* user_service; + const char* panel_socket_suffix; + const char* panel_transport; + const char* panel_update_message; + const char* panel_hide_message; + const char* state_update_message; + const char* xmodifiers; + const char* sdl_im_module; + const char* trigger_key; + int screen_width; + int screen_height; + int max_exported_candidates; + bool app_owns_input_method_engine; + bool app_owns_candidate_display; + bool app_owns_text_commit; + bool panel_socket_commits_text; + bool panel_requests_keyboard_focus; + bool missing_panel_blocks_text_input; + bool uses_embedded_touch_ime; +}; + +class CardputerZeroInputMethodPort +{ + public: + const CardputerZeroInputMethodContract& contract() const; + bool validate() const; +}; + +} // namespace linux_cardputer_zero +} // namespace apps +} // namespace trailmate diff --git a/apps/linux_cardputer_zero/src/cardputer_zero_notification_port.cpp b/apps/linux_cardputer_zero/src/cardputer_zero_notification_port.cpp new file mode 100644 index 00000000..7a5c7fc5 --- /dev/null +++ b/apps/linux_cardputer_zero/src/cardputer_zero_notification_port.cpp @@ -0,0 +1,107 @@ +#include "cardputer_zero_notification_port.h" + +#include + +namespace trailmate +{ +namespace apps +{ +namespace linux_cardputer_zero +{ +namespace +{ + +constexpr FreedesktopNotificationsContract kContract = { + "org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + "Notify", + "susssasa{sv}i", + "u", + "CloseNotification", + "GetCapabilities", + "GetServerInformation", + "NotificationClosed", + "ActionInvoked", + "body", + "actions", + "persistence", + "urgency", + "desktop-entry", + "transient", + "category", + "resident", + "cardputer-zero/notifyd.sock", + false, +}; + +const char* safeString(const char* value) +{ + return value != nullptr ? value : ""; +} + +std::uint8_t urgencyHint(NotificationUrgency urgency) +{ + switch (urgency) + { + case NotificationUrgency::Low: + return 0; + case NotificationUrgency::Critical: + return 2; + case NotificationUrgency::Normal: + default: + return 1; + } +} + +} // namespace + +const FreedesktopNotificationsContract& CardputerZeroNotificationPort::contract() const +{ + return kContract; +} + +FreedesktopNotifyCall +CardputerZeroNotificationPort::makeNotifyCall(const NotificationRequest& request) const +{ + FreedesktopNotifyCall call; + call.contract = &contract(); + call.app_name = safeString(request.app_name); + call.replaces_id = request.replaces_id; + call.app_icon = safeString(request.app_icon); + call.summary = safeString(request.summary); + call.body = safeString(request.body); + call.urgency_hint = urgencyHint(request.urgency); + call.expire_timeout_ms = request.expire_timeout_ms; + return call; +} + +bool CardputerZeroNotificationPort::validate() const +{ + const auto& c = contract(); + return std::strcmp(c.bus_name, "org.freedesktop.Notifications") == 0 && + std::strcmp(c.object_path, "/org/freedesktop/Notifications") == 0 && + std::strcmp(c.interface_name, "org.freedesktop.Notifications") == 0 && + std::strcmp(c.notify_method, "Notify") == 0 && + std::strcmp(c.notify_signature, "susssasa{sv}i") == 0 && + std::strcmp(c.notify_return_signature, "u") == 0 && + std::strcmp(c.close_method, "CloseNotification") == 0 && + std::strcmp(c.get_capabilities_method, "GetCapabilities") == 0 && + std::strcmp(c.get_server_information_method, "GetServerInformation") == 0 && + std::strcmp(c.notification_closed_signal, "NotificationClosed") == 0 && + std::strcmp(c.action_invoked_signal, "ActionInvoked") == 0 && + std::strcmp(c.capability_body, "body") == 0 && + std::strcmp(c.capability_actions, "actions") == 0 && + std::strcmp(c.capability_persistence, "persistence") == 0 && + std::strcmp(c.hint_urgency, "urgency") == 0 && + std::strcmp(c.hint_desktop_entry, "desktop-entry") == 0 && + std::strcmp(c.hint_transient, "transient") == 0 && + std::strcmp(c.hint_category, "category") == 0 && + std::strcmp(c.hint_resident, "resident") == 0 && + std::strcmp(c.shell_ipc_socket_suffix, "cardputer-zero/notifyd.sock") == 0 && + !c.shell_ipc_creates_notifications; +} + +} // namespace linux_cardputer_zero +} // namespace apps +} // namespace trailmate diff --git a/apps/linux_cardputer_zero/src/cardputer_zero_notification_port.h b/apps/linux_cardputer_zero/src/cardputer_zero_notification_port.h new file mode 100644 index 00000000..535c534d --- /dev/null +++ b/apps/linux_cardputer_zero/src/cardputer_zero_notification_port.h @@ -0,0 +1,77 @@ +#pragma once + +#include + +namespace trailmate +{ +namespace apps +{ +namespace linux_cardputer_zero +{ + +enum class NotificationUrgency : std::uint8_t +{ + Low = 0, + Normal = 1, + Critical = 2, +}; + +struct NotificationRequest +{ + const char* app_name = "Trail Mate"; + const char* summary = ""; + const char* body = ""; + const char* app_icon = ""; + std::uint32_t replaces_id = 0; + NotificationUrgency urgency = NotificationUrgency::Normal; + int expire_timeout_ms = -1; +}; + +struct FreedesktopNotificationsContract +{ + const char* bus_name; + const char* object_path; + const char* interface_name; + const char* notify_method; + const char* notify_signature; + const char* notify_return_signature; + const char* close_method; + const char* get_capabilities_method; + const char* get_server_information_method; + const char* notification_closed_signal; + const char* action_invoked_signal; + const char* capability_body; + const char* capability_actions; + const char* capability_persistence; + const char* hint_urgency; + const char* hint_desktop_entry; + const char* hint_transient; + const char* hint_category; + const char* hint_resident; + const char* shell_ipc_socket_suffix; + bool shell_ipc_creates_notifications; +}; + +struct FreedesktopNotifyCall +{ + const FreedesktopNotificationsContract* contract = nullptr; + const char* app_name = ""; + std::uint32_t replaces_id = 0; + const char* app_icon = ""; + const char* summary = ""; + const char* body = ""; + std::uint8_t urgency_hint = 1; + int expire_timeout_ms = -1; +}; + +class CardputerZeroNotificationPort +{ + public: + const FreedesktopNotificationsContract& contract() const; + FreedesktopNotifyCall makeNotifyCall(const NotificationRequest& request) const; + bool validate() const; +}; + +} // namespace linux_cardputer_zero +} // namespace apps +} // namespace trailmate diff --git a/apps/linux_cardputer_zero/src/linux_cardputer_zero_app_shell.cpp b/apps/linux_cardputer_zero/src/linux_cardputer_zero_app_shell.cpp index 03553008..c2d6d730 100644 --- a/apps/linux_cardputer_zero/src/linux_cardputer_zero_app_shell.cpp +++ b/apps/linux_cardputer_zero/src/linux_cardputer_zero_app_shell.cpp @@ -47,6 +47,16 @@ LinuxCardputerZeroAppShell::boardFacts() const return boards::cardputerzero::kBoardFacts; } +const CardputerZeroNotificationPort& LinuxCardputerZeroAppShell::notificationPort() const +{ + return notification_port_; +} + +const CardputerZeroInputMethodPort& LinuxCardputerZeroAppShell::inputMethodPort() const +{ + return input_method_port_; +} + bool LinuxCardputerZeroAppShell::validate() const { const auto& facts = boardFacts(); @@ -76,7 +86,9 @@ bool LinuxCardputerZeroAppShell::validate() const product_composition::findTargetUxBinding(targetId()) != nullptr && config_.ux_pack_id != nullptr && std::strcmp(config_.ux_pack_id, activeUxPackId()) == 0 && - ui_lvgl_ux::findUxPackById(activeUxPackId()) != nullptr; + ui_lvgl_ux::findUxPackById(activeUxPackId()) != nullptr && + notificationPort().validate() && + inputMethodPort().validate(); } } // namespace linux_cardputer_zero diff --git a/apps/linux_cardputer_zero/src/linux_cardputer_zero_app_shell.h b/apps/linux_cardputer_zero/src/linux_cardputer_zero_app_shell.h index 82a3c0fa..0c16b644 100644 --- a/apps/linux_cardputer_zero/src/linux_cardputer_zero_app_shell.h +++ b/apps/linux_cardputer_zero/src/linux_cardputer_zero_app_shell.h @@ -1,6 +1,8 @@ #pragma once #include "boards/cardputerzero/board_facts.h" +#include "cardputer_zero_input_method_port.h" +#include "cardputer_zero_notification_port.h" #include "product_composition/target_profile.h" @@ -28,10 +30,14 @@ class LinuxCardputerZeroAppShell const product_composition::TargetProfile* targetProfile() const; const char* activeUxPackId() const; const boards::cardputerzero::CardputerZeroBoardFacts& boardFacts() const; + const CardputerZeroNotificationPort& notificationPort() const; + const CardputerZeroInputMethodPort& inputMethodPort() const; bool validate() const; private: LinuxCardputerZeroAppShellConfig config_{}; + CardputerZeroNotificationPort notification_port_{}; + CardputerZeroInputMethodPort input_method_port_{}; }; } // namespace linux_cardputer_zero diff --git a/apps/linux_cardputer_zero/tests/linux_cardputer_zero_app_shell_smoke.cpp b/apps/linux_cardputer_zero/tests/linux_cardputer_zero_app_shell_smoke.cpp index f0c67408..eae20811 100644 --- a/apps/linux_cardputer_zero/tests/linux_cardputer_zero_app_shell_smoke.cpp +++ b/apps/linux_cardputer_zero/tests/linux_cardputer_zero_app_shell_smoke.cpp @@ -38,6 +38,49 @@ int main() assert(facts.lora_dio2_as_rf_switch); assert(facts.lora_dio3_tcxo_voltage); + const auto& notifications = shell.notificationPort().contract(); + assert(std::strcmp(notifications.bus_name, "org.freedesktop.Notifications") == 0); + assert(std::strcmp(notifications.object_path, "/org/freedesktop/Notifications") == 0); + assert(std::strcmp(notifications.notify_signature, "susssasa{sv}i") == 0); + assert(std::strcmp(notifications.capability_body, "body") == 0); + assert(std::strcmp(notifications.capability_actions, "actions") == 0); + assert(std::strcmp(notifications.capability_persistence, "persistence") == 0); + assert(std::strcmp(notifications.shell_ipc_socket_suffix, + "cardputer-zero/notifyd.sock") == 0); + assert(!notifications.shell_ipc_creates_notifications); + + trailmate::apps::linux_cardputer_zero::NotificationRequest notification_request; + notification_request.summary = "Mesh"; + notification_request.body = "Message received"; + notification_request.urgency = + trailmate::apps::linux_cardputer_zero::NotificationUrgency::Critical; + const auto notify_call = shell.notificationPort().makeNotifyCall(notification_request); + assert(notify_call.contract == ¬ifications); + assert(std::strcmp(notify_call.summary, "Mesh") == 0); + assert(std::strcmp(notify_call.body, "Message received") == 0); + assert(notify_call.urgency_hint == 2); + + const auto& input_method = shell.inputMethodPort().contract(); + assert(std::strcmp(input_method.framework_name, "fcitx5") == 0); + assert(std::strcmp(input_method.ui_addon_name, "cardputerzero-ui") == 0); + assert(std::strcmp(input_method.panel_socket_suffix, + "cardputer-zero/ime-panel.sock") == 0); + assert(std::strcmp(input_method.panel_update_message, "panel.update") == 0); + assert(std::strcmp(input_method.panel_hide_message, "panel.hide") == 0); + assert(std::strcmp(input_method.state_update_message, "state.update") == 0); + assert(std::strcmp(input_method.xmodifiers, "@im=fcitx") == 0); + assert(std::strcmp(input_method.sdl_im_module, "fcitx") == 0); + assert(input_method.screen_width == 320); + assert(input_method.screen_height == 170); + assert(input_method.max_exported_candidates == 3); + assert(!input_method.app_owns_input_method_engine); + assert(!input_method.app_owns_candidate_display); + assert(!input_method.app_owns_text_commit); + assert(!input_method.panel_socket_commits_text); + assert(!input_method.panel_requests_keyboard_focus); + assert(!input_method.missing_panel_blocks_text_input); + assert(!input_method.uses_embedded_touch_ime); + const auto* profile = shell.targetProfile(); assert(profile != nullptr); assert(profile->platform == product_composition::TargetPlatform::Linux); diff --git a/boards/cardputerzero/BOARD.md b/boards/cardputerzero/BOARD.md index ebd09bb5..ba8b6d64 100644 --- a/boards/cardputerzero/BOARD.md +++ b/boards/cardputerzero/BOARD.md @@ -26,6 +26,11 @@ This record describes current repo evidence for the Linux Cardputer Zero route. - LoRa SPI speed: 500000 Hz - LoRa GPIO control lines: Reset=26, IRQ/DIO1=23, Busy=22 - LoRa DIO2 RF switch and DIO3 TCXO voltage control are required +- Cardputer Zero user-session notifications are provided through the standard + `org.freedesktop.Notifications` D-Bus interface, not through a private app API +- Cardputer Zero user-session input method display is provided by a Fcitx5 UI + addon plus Wayland panel; text submission stays on the normal Linux input + method path - display/input ownership for the real Pi OS path is not yet closed - current dedicated app shell baseline is build-owned by `apps/linux_cardputer_zero` @@ -36,6 +41,7 @@ This record describes current repo evidence for the Linux Cardputer Zero route. - evdev keyboard mapping sampled from the real device - Trail Mate Linux SX1262 runtime validation against `/dev/spidev0.1` and the documented GPIO lines +- notification daemon and Fcitx5 user-session integration on real device image - launch/package route for the portable Linux device The Linux simulator remains a separate target under `apps/linux_sim_shell`. diff --git a/docs/targets/TARGET_MATRIX.md b/docs/targets/TARGET_MATRIX.md index 7137000f..26639268 100644 --- a/docs/targets/TARGET_MATRIX.md +++ b/docs/targets/TARGET_MATRIX.md @@ -19,7 +19,7 @@ routes. | `twatch` | `twatch` | `builds/esp_idf` | `apps/esp32_lvgl` | ESP-IDF | LVGL | `watch_compact` | `compatibility` | `watch_compact_ui` | `watch_compact_manifest` | `watch_compact` | `PendingHardwareValidation` | current repo evidence is board and variant data, not migrated IDF defaults | `apps/esp32_lvgl + builds/esp_idf + boards/twatch` | | `uconsole` | `uconsole` | `builds/linux_cmake` | `apps/linux_uconsole_gtk` | Linux | GTK | `uconsole_desktop` | `uconsole_desktop` | `uconsole_desktop_ui` | `uconsole_desktop_manifest` | `uconsole_desktop` | `Active` | none for UX pack selection | `apps/linux_uconsole_gtk + builds/linux_cmake + boards/uconsole` | | `linux_sim` | `linux_sim` | `builds/linux_cmake` | `apps/linux_sim_shell` | Linux | ASCII | `simulator_full` | `simulator_full` | `simulator_full_ui` | `simulator_full_manifest` | `simulator_full` | `Active` | none for simulator UX pack selection | `apps/linux_sim_shell + builds/linux_cmake` | -| `cardputerzero` | `cardputerzero` | `builds/linux_cmake` | `apps/linux_cardputer_zero` | Linux | ASCII | `cardputer_compact` | `cardputer_compact` | `cardputer_compact_ui` | `cardputer_compact_manifest` | `cardputer_compact` | `PendingHardwareValidation` | framebuffer, evdev, launch, and packaging still need real device validation | `apps/linux_cardputer_zero + builds/linux_cmake + boards/cardputerzero` | +| `cardputerzero` | `cardputerzero` | `builds/linux_cmake` | `apps/linux_cardputer_zero` | Linux | ASCII | `cardputer_compact` | `cardputer_compact` | `cardputer_compact_ui` | `cardputer_compact_manifest` | `cardputer_compact` | `PendingHardwareValidation` | framebuffer, evdev, notifyd session, Fcitx5 session, launch, and packaging still need real device validation | `apps/linux_cardputer_zero + builds/linux_cmake + boards/cardputerzero` | | `gat562_mesh_evb_pro` | `gat562_mesh_evb_pro` | `builds/pio_nrf52` | `apps/nrf52_node` | PlatformIO | Headless | `node_headless` | `tiny_node_status` | `node_headless_ui` | `node_headless_manifest` | `headless_node` | `Headless` | current executable UX pack is `tiny_node_status`; board hardware still records a 128 x 64 display | `apps/nrf52_node + builds/pio_nrf52 + boards/gat562_mesh_evb_pro` | ## Pending Evidence @@ -29,7 +29,7 @@ routes. | `tlora_pager` | ESP-IDF target defaults and hardware validation for the requested IDF route | `builds/esp_idf + boards/tlora_pager` | | `tdeck` | ESP-IDF target defaults and hardware validation for the requested IDF route | `builds/esp_idf + boards/tdeck` | | `twatch` | ESP-IDF target defaults and hardware validation for the requested IDF route | `builds/esp_idf + boards/twatch` | -| `cardputerzero` | real Cardputer Zero framebuffer, evdev, launch, and packaging validation | `apps/linux_cardputer_zero + boards/cardputerzero` | +| `cardputerzero` | real Cardputer Zero framebuffer, evdev, notifyd session, Fcitx5 session, launch, and packaging validation | `apps/linux_cardputer_zero + boards/cardputerzero` | ## Batch 2 Status Tokens diff --git a/docs/targets/linux_targets.md b/docs/targets/linux_targets.md index a686e6c3..e88c6239 100644 --- a/docs/targets/linux_targets.md +++ b/docs/targets/linux_targets.md @@ -35,7 +35,8 @@ absorb GTK, simulator, framebuffer, device-shell, or runtime behavior into `cardputerzero` is no longer described as a future Raspberry Pi simulator path. Its current owner is `apps/linux_cardputer_zero`; the remaining work is real -Cardputer Zero framebuffer, evdev, launch, and package validation. +Cardputer Zero framebuffer, evdev, notification daemon session, Fcitx5 session, +launch, and package validation. ## Non-Goals diff --git a/docs/ux_profiles/cardputer_compact.md b/docs/ux_profiles/cardputer_compact.md index ee32fe5c..c9740f5d 100644 --- a/docs/ux_profiles/cardputer_compact.md +++ b/docs/ux_profiles/cardputer_compact.md @@ -9,6 +9,11 @@ Compact 320 x 170 landscape keyboard device. Keyboard-first input. Pointer, touch, and trackball are absent in the current Cardputer Zero board facts. +On the Linux device route, text composition is owned by the user-session Fcitx5 +stack. Trail Mate's compact UX accepts committed text from the normal Linux +input frontend path; the Cardputer Zero IME panel socket is display-only and is +not a Trail Mate text submission API. + ## Feature Set Compact chat/status workflow with compact map and practical GPS/team actions. @@ -44,7 +49,7 @@ uses ASCII renderer until framebuffer ownership is validated. ## Deferred Decisions -Real-device keyboard sampling, framebuffer handoff, launch, and packaging remain -deferred. +Real-device keyboard sampling, framebuffer handoff, notification daemon session +validation, Fcitx5 session validation, launch, and packaging remain deferred. Board describes. Target chooses. UX Pack presents. Renderer draws.