Refactor app loading and window management (#609)
This commit is contained in:
committed by
GitHub
parent
dc3f6104b8
commit
37c507544b
@@ -0,0 +1,103 @@
|
||||
// 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
|
||||
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/error.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Computes the install directory for @a app_id (does not check whether anything is actually
|
||||
* installed there).
|
||||
* @param[out] path always NULL-terminated on return, even on failure (empty string if
|
||||
* @a path_size == 0 - nothing is written in that case; otherwise at least "" is written)
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_BUFFER_OVERFLOW @a path_size is too small to hold the path (including the
|
||||
* NULL terminator)
|
||||
* @retval ERROR_NOT_FOUND the app install location isn't available (e.g. no SD card)
|
||||
*/
|
||||
error_t app_get_install_path(const char* app_id, char* path, size_t path_size);
|
||||
|
||||
/**
|
||||
* Installs an app from a tarball at @a source_path: extracts it into the app install directory,
|
||||
* parses the extracted manifest.properties (see app/metadata.h) to determine its id, then
|
||||
* registers it with app_manager_add() as an AppLocation{APP_LOCATION_PATH, <install dir>} app.
|
||||
* If an app with the same id is already installed (via a previous app_install() call), it is
|
||||
* uninstalled first - stopped if running, its old install directory removed - before the new
|
||||
* one takes its place.
|
||||
* @param[in] source_path path to a tar file containing the app (must have manifest.properties
|
||||
* at its root)
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_FOUND @a source_path doesn't exist / can't be read
|
||||
* @retval ERROR_INVALID_ARGUMENT the tarball has no valid manifest.properties at its root
|
||||
*/
|
||||
error_t app_install(const char* source_path);
|
||||
|
||||
/**
|
||||
* Uninstalls a previously app_install()-ed app: stops it if currently running, deletes its
|
||||
* install directory, and unregisters it (app_manager_remove()).
|
||||
* @param[in] app_id the id the app was installed under (AppMetadata::app_id)
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_FOUND no such app was installed via app_install()
|
||||
*/
|
||||
error_t app_uninstall(const char* app_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Identifies a running (or previously running) app instance. 0 is never a valid instance id. */
|
||||
typedef uint32_t AppInstanceId;
|
||||
|
||||
/** Lifecycle state of a running (or previously running) app instance. Every app instance owns
|
||||
* its own task for its entire lifetime - there is no "saved, task given up" state. */
|
||||
typedef enum {
|
||||
APP_INSTANCE_STATE_STARTING,
|
||||
APP_INSTANCE_STATE_ACTIVE,
|
||||
APP_INSTANCE_STATE_STOPPING,
|
||||
APP_INSTANCE_STATE_STOPPED,
|
||||
} AppInstanceState;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <app/manifest.h>
|
||||
#include <tactility/error.h>
|
||||
#include <stdint.h>
|
||||
#include "location.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** service-module id the AppLoaderApi implementation for AppManifest::location.type ==
|
||||
* APP_LOCATION_MEMORY must register under. Implemented by app-module itself (source/app_internal_loader.cpp). */
|
||||
#define APP_LOADER_MEMORY_SERVICE_ID "app-loader-memory"
|
||||
|
||||
/** service-module id the AppLoaderApi implementation for AppManifest::location.type ==
|
||||
* APP_LOCATION_PATH must register under. Implemented by a platform module (e.g. app-esp32-module). */
|
||||
#define APP_LOADER_PATH_SERVICE_ID "app-loader-path"
|
||||
|
||||
/**
|
||||
* Entry point signature for an APP_LOCATION_MEMORY app: a function linked directly into this
|
||||
* firmware binary. Called on the dedicated task app-module's scheduler spawns for this instance,
|
||||
* blocking for the app's whole lifetime - same contract as an external app's main(), plus
|
||||
* @a app_instance_id identifying this running instance (use it with
|
||||
* app_event_subscribe()/window_manager_create()/app_manager_finish()/etc.).
|
||||
* AppManifest::location.location holds this cast to void*.
|
||||
*/
|
||||
typedef int32_t (*AppMainFn)(uint32_t app_instance_id, int argc, char* argv[]);
|
||||
|
||||
typedef void* AppRuntime;
|
||||
|
||||
/**
|
||||
* Pluggable mechanism for loading and executing an app.
|
||||
*/
|
||||
struct AppLoaderApi {
|
||||
/**
|
||||
* Prepares an app instance for execution (e.g. read + relocate its binary).
|
||||
* @param[in] location the location to load the elf from
|
||||
* @param[out] out_runtime opaque handle to whatever load() allocated; passed back to run()/unload()
|
||||
*/
|
||||
error_t (*load)(struct AppLocation location, AppRuntime* out_runtime);
|
||||
|
||||
/**
|
||||
* Blocking: runs the app to completion.
|
||||
* @param[in] runtime handle produced by load()
|
||||
* @param[in] app_instance_id the running instance's id
|
||||
* @param[in] argc the amount of arguments in @a argv
|
||||
* @param[in] argv the array of string pointers (can be NULL)
|
||||
*/
|
||||
int32_t (*run)(AppRuntime runtime, uint32_t app_instance_id, int argc, char* argv[]);
|
||||
|
||||
/** Releases whatever load() allocated. Called after run() returns. */
|
||||
void (*unload)(AppRuntime runtime);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum AppLocationType {
|
||||
APP_LOCATION_MEMORY,
|
||||
APP_LOCATION_PATH,
|
||||
};
|
||||
|
||||
struct AppLocation {
|
||||
enum AppLocationType type;
|
||||
/** Meaning depends on `type`; see AppLocationType. */
|
||||
void* location;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,153 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <app/instance.h>
|
||||
#include <app/manifest.h>
|
||||
|
||||
#include <tactility/error.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Register an app manifest.
|
||||
* @retval ERROR_INVALID_ARGUMENT a manifest with the same id is already registered
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_manager_add(const struct AppManifest* manifest);
|
||||
|
||||
/**
|
||||
* Unregister a previously-added manifest.
|
||||
* @retval ERROR_NOT_FOUND no manifest with this id is registered
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_manager_remove(const char* id);
|
||||
|
||||
/** @return the manifest, or NULL if not found. */
|
||||
const struct AppManifest* app_manager_find_manifest(const char* id);
|
||||
|
||||
/**
|
||||
* Calls `@a` visitor once for every registered manifest. Iteration order is unspecified.
|
||||
* `@warning` `@a` visitor runs with app-module's internal registry lock held. Do not call any
|
||||
* app_manager_*() function from inside `@a` visitor - copy out what you need and act on it after
|
||||
* this call returns.
|
||||
*/
|
||||
typedef void (*AppManifestVisitorFn)(const struct AppManifest* manifest, void* context);
|
||||
void app_manager_for_each_manifest(AppManifestVisitorFn visitor, void* context);
|
||||
|
||||
/**
|
||||
* Starts a new instance of the app registered under @a id. Every app instance gets its own
|
||||
* dedicated task for its entire lifetime - starting an app never asks any other app to give up
|
||||
* its task, and multiple instances (of the same or different apps) can be Active at once.
|
||||
* @param[in] id the manifest id to start
|
||||
* @param[out] out_app_instance_id the id of the new app instance
|
||||
* @retval ERROR_NOT_FOUND no manifest with this id is registered, or no AppLoaderApi is registered
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_manager_start(const char* id, AppInstanceId* out_app_instance_id);
|
||||
|
||||
/**
|
||||
* Same as app_manager_start(), but also passes @a argc/@a argv to the new instance's own main
|
||||
* function (see app/loader.h's AppMainFn) - modelled on a C program's main(argc, argv). For
|
||||
* regular (non-modal) navigations that need to pass data to the target app (e.g. "show details
|
||||
* for this app id") without expecting a result back.
|
||||
* @param[in] argv @a argc strings; app-module makes its own deep copy before returning, so
|
||||
* @a argv and the strings it points to may be freed/go out of scope immediately after this call
|
||||
* returns (e.g. safe to pass a stack-local array of a caller's own std::string::c_str()s).
|
||||
*/
|
||||
error_t app_manager_start_with_parameters(const char* id, int argc, const char* const argv[], AppInstanceId* out_app_instance_id);
|
||||
|
||||
/**
|
||||
* Starts @a id as a modal child of @a parent_instance_id, for the purpose of receiving a
|
||||
* result. The parent keeps running (window_manager's own multi-window stack handles burying its
|
||||
* window while the child is shown).
|
||||
*
|
||||
* When the child's task exits, an APP_EVENT_RESULT is delivered to @a parent_instance_id -
|
||||
* result is whatever the child's AppMainFn/AppLoaderApi::run() returned - unless
|
||||
* @a parent_instance_id is 0, in which case no result is delivered (fire-and-forget, for
|
||||
* callers with no app_instance_id of their own). The parent is then responsible for calling
|
||||
* app_manager_stop() on the child's instance id to fully reap it. Children that need to hand
|
||||
* back more than an int32_t (e.g. picked text, a path) expose their own "get last result"
|
||||
* getter for the parent to call after receiving the event - see e.g.
|
||||
* tt::app::inputdialog::getLastText().
|
||||
* @param[in] argv @a argc strings; app-module makes its own deep copy before returning (same as
|
||||
* app_manager_start_with_parameters()), so @a argv and the strings it points to may be
|
||||
* freed/go out of scope immediately after this call returns.
|
||||
* @retval ERROR_NOT_FOUND no manifest with this id is registered, or no AppLoaderApi is registered
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_manager_start_for_result(const char* id, AppInstanceId parent_instance_id, int argc, const char* const argv[], AppInstanceId* out_app_instance_id);
|
||||
|
||||
/**
|
||||
* Stop an app instance permanently. Emits APP_EVENT_CLOSE and bound-waits for its task to exit
|
||||
* if it was running.
|
||||
* @warning Must not be called from the instance's own task (it bound-waits via thread_join(),
|
||||
* which asserts against joining yourself) - an app closing itself must call app_manager_finish()
|
||||
* instead, right before returning from its own AppMainFn/AppLoaderApi::run().
|
||||
*/
|
||||
error_t app_manager_stop(AppInstanceId app_instance_id);
|
||||
|
||||
/**
|
||||
* Called by an app instance, from its own task, right before it returns in response to
|
||||
* APP_EVENT_CLOSE - whether that close was self-initiated (e.g. its own back button) or came
|
||||
* from someone else. Marks this instance Stopped immediately (rather than waiting for its task
|
||||
* to actually exit) so app_manager_get_state()/app_manager_get_topmost_instance_id() reflect the
|
||||
* closure as soon as the app has decided to close, not just once its task has fully unwound.
|
||||
* @warning Does not join or free this instance's own task/ledger entry (can't - this runs on
|
||||
* that very task); those are cleaned up on a later app_manager_stop() call, same as any
|
||||
* self-terminating instance.
|
||||
*/
|
||||
error_t app_manager_finish(AppInstanceId app_instance_id);
|
||||
|
||||
/** @return the instance's current state, or APP_INSTANCE_STATE_STOPPED if the id is unknown. */
|
||||
AppInstanceState app_manager_get_state(AppInstanceId app_instance_id);
|
||||
|
||||
/**
|
||||
* @param[out] out_app_instance_id set to the instance id of the topmost currently-Active app -
|
||||
* the most recently started of whichever instances are Active (a modal child launched via
|
||||
* app_manager_start_for_result() stays Active alongside its parent while shown, so this
|
||||
* correctly picks the child, not the parent, while a dialog is up).
|
||||
* @retval ERROR_NOT_FOUND no app is Active
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_manager_get_topmost_instance_id(AppInstanceId* out_app_instance_id);
|
||||
|
||||
/**
|
||||
* Same as app_manager_get_topmost_instance_id(), but resolves straight to the topmost app's
|
||||
* manifest id string.
|
||||
* @param[out] buffer always NULL-terminated on return, even on failure (empty string if
|
||||
* @a buffer_size == 0 - nothing is written in that case; otherwise at least "" is written)
|
||||
* @retval ERROR_NOT_FOUND no app is Active
|
||||
* @retval ERROR_BUFFER_OVERFLOW @a buffer_size is too small to hold the id (including the NULL
|
||||
* terminator)
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_manager_get_topmost_app_id(char* buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
* Registers @a path as a directory to scan for app manifests - each direct subdirectory of
|
||||
* @a path is expected to hold a manifest.properties (see app/metadata.h), matching the layout
|
||||
* app_install() creates ({install dir}/{app_id}/manifest.properties), though this is not
|
||||
* install/uninstall - it only ever adds/removes manifest registrations, never touches files on
|
||||
* disk or running instances. No-op if @a path is already registered. Does not scan immediately -
|
||||
* call app_manager_install_path_scan() to do that.
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_manager_install_path_add(const char* path);
|
||||
|
||||
/**
|
||||
* Scans every path registered via app_manager_install_path_add(): registers
|
||||
* (app_manager_add()) any direct subdirectory with a valid manifest.properties that isn't
|
||||
* already registered, and unregisters (app_manager_remove() only - does not stop it if running,
|
||||
* does not delete anything) any manifest a previous scan registered whose directory has since
|
||||
* disappeared. Safe to call repeatedly (e.g. after an SD card is mounted/unmounted).
|
||||
*/
|
||||
void app_manager_install_path_scan(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include "location.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Broad classification of an app, used for grouping/launcher presentation. */
|
||||
enum AppCategory {
|
||||
APP_CATEGORY_SYSTEM,
|
||||
APP_CATEGORY_SETTINGS,
|
||||
APP_CATEGORY_USER,
|
||||
};
|
||||
|
||||
/** Bit flags for AppManifest::flags. */
|
||||
enum AppManifestFlags {
|
||||
/** Excluded from generic app-browsing UIs (AppList, Settings) - for apps only ever reached
|
||||
* by direct navigation (modal dialogs, detail views that require parameters, wizard/
|
||||
* bootstrap steps). */
|
||||
APP_MANIFEST_FLAG_HIDDEN = 0b00000001,
|
||||
};
|
||||
|
||||
/** Describes a registrable app. One manifest exists per app id. */
|
||||
struct AppManifest {
|
||||
/** Unique app identifier. Should never be NULL. */
|
||||
const char* id;
|
||||
/** Human-readable name. Should never be NULL. */
|
||||
const char* name;
|
||||
enum AppCategory category;
|
||||
struct AppLocation location;
|
||||
/** Bitmask of AppManifestFlags. Most apps should leave this 0. */
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/error.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define APP_METADATA_TARGET_SDK_LENGTH 16
|
||||
#define APP_METADATA_APP_ID_LENGTH 32
|
||||
#define APP_METADATA_APP_NAME_LENGTH 32
|
||||
#define APP_METADATA_APP_VERSION_NAME_LENGTH 16
|
||||
|
||||
struct AppMetadata {
|
||||
|
||||
/**
|
||||
* The SDK version that was used to compile this app. (e.g. "0.6.0")
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char target_sdk[APP_METADATA_TARGET_SDK_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* The identifier by which the app is launched by the system and other apps.
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char app_id[APP_METADATA_APP_ID_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* The user-readable name of the app. Used in UI.
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char app_name[APP_METADATA_APP_NAME_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* The version as it is displayed to the user (e.g. "1.2.0")
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char app_version_name[APP_METADATA_APP_VERSION_NAME_LENGTH + 1];
|
||||
|
||||
/** The technical version (must be incremented with new releases of the app) */
|
||||
uint64_t app_version_code;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a manifest.properties file at @a path into @a out_metadata, auto-detecting the V1
|
||||
* (sectioned, e.g. "[app]id=...") or V2 (flat dot-notation, e.g. "app.id=...") format from its
|
||||
* first line.
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_FOUND the file doesn't exist / couldn't be opened
|
||||
* @retval ERROR_INVALID_ARGUMENT the file isn't a valid manifest, or a field's value doesn't fit
|
||||
* @a out_metadata's fixed-size buffers
|
||||
*/
|
||||
error_t app_metadata_parse(const char* path, struct AppMetadata* out_metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/module.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern struct Module app_module;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get the user data directory for an app. Survives OS upgrades. No trailing "/".
|
||||
* @param[in] app_id non-null app id
|
||||
* @param[out] out_path buffer to store the path
|
||||
* @param[in] out_path_size size of the output buffer
|
||||
* @retval ERROR_BUFFER_OVERFLOW if out_path_size is too small
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_paths_get_user_data_directory(const char* app_id, char* out_path, size_t out_path_size);
|
||||
|
||||
/**
|
||||
* @brief Get a path within the user data directory for an app.
|
||||
* @param[in] app_id non-null app id
|
||||
* @param[in] child_path path without a "/" prefix
|
||||
* @param[out] out_path buffer to store the path
|
||||
* @param[in] out_path_size size of the output buffer
|
||||
* @retval ERROR_BUFFER_OVERFLOW if out_path_size is too small
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_paths_get_user_data_path(const char* app_id, const char* child_path, char* out_path, size_t out_path_size);
|
||||
|
||||
/**
|
||||
* @brief Get the assets directory for an app. Do not store configuration data here. No trailing "/".
|
||||
* @param[in] app_id non-null app id
|
||||
* @param[out] out_path buffer to store the path
|
||||
* @param[in] out_path_size size of the output buffer
|
||||
* @retval ERROR_BUFFER_OVERFLOW if out_path_size is too small
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_paths_get_assets_directory(const char* app_id, char* out_path, size_t out_path_size);
|
||||
|
||||
/**
|
||||
* @brief Get a path within the assets directory for an app.
|
||||
* @param[in] app_id non-null app id
|
||||
* @param[in] child_path path without a "/" prefix
|
||||
* @param[out] out_path buffer to store the path
|
||||
* @param[in] out_path_size size of the output buffer
|
||||
* @retval ERROR_BUFFER_OVERFLOW if out_path_size is too small
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_paths_get_assets_path(const char* app_id, const char* child_path, char* out_path, size_t out_path_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <app/instance.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @return the app_instance_id of whichever app instance's task is calling this (every app
|
||||
* instance's task stashes it in its own thread-local storage when it starts), or 0 if called
|
||||
* from a task that isn't a running app instance. An app's own main() typically calls this once,
|
||||
* near the top, to learn its own instance id - see e.g. app_event_subscribe()/
|
||||
* window_manager_create(), both of which need it.
|
||||
*/
|
||||
AppInstanceId app_scheduler_current_app_id(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user