2026-08-06 23:54:23 +02:00

104 lines
3.8 KiB
C

// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <tactility/error.h>
#include <tactility/freertos/freertos.h>
#include <tactility/freertos/task.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Identifies the kind of app-lifecycle event delivered through app_event_await(). */
enum AppEventType {
APP_EVENT_RESULT, // struct AppResultEventData
APP_EVENT_CLOSE, // no data - terminate now, permanently
};
/** Data for APP_EVENT_RESULT. */
struct AppResultEventData {
uint32_t launch_id;
/** The child app instance's own AppMainFn/AppLoaderApi::run() return value. By convention:
* 0 = Ok, 1 = Cancelled, 2 = Error. Apps that need to hand back more than this (e.g. picked
* text, a path) expose their own "get last result" getter instead - see e.g.
* tt::app::inputdialog::getLastText(). */
int32_t result;
};
struct AppEvent {
enum AppEventType type;
/** Stamped by app_event_emit(); any value passed in by the caller is ignored. */
uint64_t timestamp;
/** Valid only when type == APP_EVENT_RESULT. */
struct AppResultEventData result;
};
/**
* Number of events that can be queued per subscription before app_event_emit() starts
* returning ERROR_RESOURCE (dropping the newest event, preserving FIFO order of what's
* already queued). Deliberately generous: app-module's scheduler is the only emitter and it
* serializes app-lifecycle transitions, so a given app can't realistically receive events
* faster than the scheduler produces them one at a time.
*/
#define APP_EVENT_QUEUE_CAPACITY 4
/**
* Caller-owned subscription node. Unlike TactilityKernel's system_event poll subscription
* (which coalesces to the latest value), this queues events by value (FIFO) since dropping an
* APP_EVENT_RESULT would be unacceptable.
* @warning Fields other than `app_instance_id` are for internal use only; do not read or write
* them directly.
*/
struct AppEventSubscription {
/** The app instance this subscription receives events for; set by the caller before app_event_subscribe(). */
uint32_t app_instance_id;
TaskHandle_t task;
struct AppEvent queue[APP_EVENT_QUEUE_CAPACITY];
uint8_t head;
uint8_t count;
struct AppEventSubscription* next;
};
/**
* Register a subscription for events addressed to @a sub->app_instance_id.
* @warning Does not work in ISR context.
* @param[in,out] sub subscription to register; caller sets @a sub->app_instance_id beforehand,
* owns the storage, and must keep it alive (and stationary) until unsubscribed
* @return ERROR_NONE on success
*/
error_t app_event_subscribe(struct AppEventSubscription* sub);
/**
* Remove a previously registered subscription.
* @warning Does not work in ISR context.
* @return ERROR_NONE on success, ERROR_NOT_FOUND if no matching subscription exists
*/
error_t app_event_unsubscribe(struct AppEventSubscription* sub);
/**
* Deliver @a event to every subscription registered for @a app_instance_id (normally exactly one).
* @warning Does not work in ISR context.
* @retval ERROR_NONE delivered to at least one subscription
* @retval ERROR_NOT_FOUND no subscription is registered for @a app_instance_id
* @retval ERROR_RESOURCE at least one matching subscription's queue was full; the event was
* dropped for that subscription (still delivered to any other matching subscription)
*/
error_t app_event_emit(uint32_t app_instance_id, const struct AppEvent* event);
/**
* Pop the next event for @a sub, blocking up to @a timeout if the queue is currently empty.
* @retval ERROR_NONE @a out_event was filled
* @retval ERROR_TIMEOUT no event arrived before the timeout elapsed
*/
error_t app_event_await(struct AppEventSubscription* sub, struct AppEvent* out_event, TickType_t timeout);
#ifdef __cplusplus
}
#endif