Skip to content

Commit

Permalink
app/service vs appdata/servicedata
Browse files Browse the repository at this point in the history
  • Loading branch information
KenVanHoeylandt committed Jan 13, 2024
1 parent 5bc348a commit 200c223
Show file tree
Hide file tree
Showing 26 changed files with 496 additions and 262 deletions.
125 changes: 125 additions & 0 deletions components/furi/src/app.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#include "app_i.h"

#include <stdio.h>

static AppFlags app_get_flags_default(AppType type);

// region Alloc/free

App app_alloc(const AppManifest* manifest, Bundle* _Nullable bundle) {
AppData* data = malloc(sizeof(AppData));
*data = (AppData) {
.mutex = furi_mutex_alloc(FuriMutexTypeRecursive),
.state = APP_STATE_INITIAL,
.flags = app_get_flags_default(manifest->type),
.manifest = manifest,
.bundle = bundle,
.data = NULL
};
return (App*)data;
}

void app_free(App app) {
AppData* data = (AppData*)app;
furi_mutex_free(data->mutex);
free(data);
}

// endregion

// region Internal

static void app_lock(AppData* data) {
furi_mutex_acquire(data->mutex, FuriMutexTypeRecursive);
}

static void app_unlock(AppData* data) {
furi_mutex_release(data->mutex);
}

static AppFlags app_get_flags_default(AppType type) {
static const AppFlags DEFAULT_DESKTOP_FLAGS = {
.show_toolbar = false,
.show_statusbar = true
};

static const AppFlags DEFAULT_APP_FLAGS = {
.show_toolbar = true,
.show_statusbar = true
};

return type == AppTypeDesktop
? DEFAULT_DESKTOP_FLAGS
: DEFAULT_APP_FLAGS;
}

// endregion Internal

// region Public getters & setters

void app_set_state(App app, AppState state) {
AppData* data = (AppData*)app;
app_lock(data);
data->state = state;
app_unlock(data);
}

AppState app_get_state(App app) {
AppData* data = (AppData*)app;
app_lock(data);
AppState state = data->state;
app_unlock(data);
return state;
}

const AppManifest* app_get_manifest(App app) {
AppData* data = (AppData*)app;
// No need to lock const data;
return data->manifest;
}

AppFlags app_get_flags(App app) {
AppData* data = (AppData*)app;
app_lock(data);
AppFlags flags = data->flags;
app_unlock(data);
return flags;
}

void app_set_flags(App app, AppFlags flags) {
AppData* data = (AppData*)app;
app_lock(data);
data->flags = flags;
app_unlock(data);
}

void* app_get_data(App app) {
AppData* data = (AppData*)app;
app_lock(data);
void* value = data->data;
app_unlock(data);
return value;
}

void app_set_data(App app, void* value) {
AppData* data = (AppData*)app;
app_lock(data);
data->data = value;
app_unlock(data);
}

/** TODO: Make this thread-safe.
* In practice, the bundle is writeable, so someone could be writing to it
* while it is being accessed from another thread.
* Consider creating MutableBundle vs Bundle.
* Consider not exposing bundle, but expose `app_get_bundle_int(key)` methods with locking in it.
*/
Bundle* _Nullable app_get_bundle(App app) {
AppData* data = (AppData*)app;
app_lock(data);
Bundle* bundle = data->bundle;
app_unlock(data);
return bundle;
}

// endregion Public getters & setters
51 changes: 51 additions & 0 deletions components/furi/src/app.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

#include "app_manifest.h"
#include "bundle.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef enum {
APP_STATE_INITIAL, // App is being activated in loader
APP_STATE_STARTED, // App is in memory
APP_STATE_SHOWING, // App view is created
APP_STATE_HIDING, // App view is destroyed
APP_STATE_STOPPED // App is not in memory
} AppState;

typedef union {
struct {
bool show_statusbar : 1;
bool show_toolbar : 1;
};
unsigned char flags;
} AppFlags;

typedef void* App;

/** @brief Create an app
* @param manifest
* @param bundle optional bundle. memory ownership is transferred to App
* @return
*/
App app_alloc(const AppManifest* manifest, Bundle* _Nullable bundle);
void app_free(App app);

void app_set_state(App app, AppState state);
AppState app_get_state(App app);

const AppManifest* app_get_manifest(App app);

AppFlags app_get_flags(App app);
void app_set_flags(App app, AppFlags flags);

void* _Nullable app_get_data(App app);
void app_set_data(App app, void* data);

Bundle* _Nullable app_get_bundle(App app);

#ifdef __cplusplus
}
#endif
35 changes: 16 additions & 19 deletions components/furi/src/app_i.h
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
#pragma once

#include "app.h"

#include "app_manifest.h"
#include "context.h"
#include "mutex.h"
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef enum {
APP_STATE_INITIAL, // App is being activated in loader
APP_STATE_STARTED, // App is in memory
APP_STATE_SHOWING, // App view is created
APP_STATE_HIDING, // App view is destroyed
APP_STATE_STOPPED // App is not in memory
} AppState;

typedef union {
struct {
bool show_statusbar : 1;
bool show_toolbar : 1;
};
unsigned char flags;
} AppFlags;

typedef struct {
FuriMutex* mutex;
const AppManifest* manifest;
AppState state;
AppFlags flags;
const AppManifest* manifest;
Context context;
} App;
/** @brief Optional arguments
* When these are stored in the app struct, the struct takes ownership.
*/
Bundle* _Nullable bundle;
/** @brief @brief Contextual data related to the running app's instance
* The app can attach its data to this.
* The lifecycle is determined by the on_start and on_stop methods in the AppManifest.
* These manifest methods can optionally allocate/free data that is attached here.
*/
void* _Nullable data;
} AppData;

#ifdef __cplusplus
}
Expand Down
10 changes: 5 additions & 5 deletions components/furi/src/app_manifest.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once

#include "context.h"
#include <stdio.h>

#ifdef __cplusplus
Expand All @@ -9,6 +8,7 @@ extern "C" {

// Forward declarations
typedef struct _lv_obj_t lv_obj_t;
typedef void* App;

typedef enum {
AppTypeDesktop,
Expand All @@ -17,10 +17,10 @@ typedef enum {
AppTypeUser
} AppType;

typedef void (*AppOnStart)(Context* context);
typedef void (*AppOnStop)(Context* context);
typedef void (*AppOnShow)(Context* context, lv_obj_t* parent);
typedef void (*AppOnHide)(Context* context);
typedef void (*AppOnStart)(App app);
typedef void (*AppOnStop)(App app);
typedef void (*AppOnShow)(App app, lv_obj_t* parent);
typedef void (*AppOnHide)(App app);

typedef struct {
/**
Expand Down
61 changes: 35 additions & 26 deletions components/furi/src/bundle.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "m_cstr_dup.h"
#include "check.h"

// region BundleEntry

typedef enum {
BUNDLE_ENTRY_TYPE_BOOL,
BUNDLE_ENTRY_TYPE_INT,
Expand Down Expand Up @@ -60,7 +62,12 @@ void bundle_entry_free(BundleEntry* entry) {
free(entry);
}

// endregion BundleEntry

// region Bundle

DICT_DEF2(BundleDict, const char*, M_CSTR_DUP_OPLIST, BundleEntry*, M_PTR_OPLIST)

typedef struct {
BundleDict_t dict;
} BundleData;
Expand All @@ -71,6 +78,33 @@ Bundle bundle_alloc() {
return bundle;
}

Bundle bundle_alloc_copy(Bundle source) {
BundleData* source_data = (BundleData*)source;
BundleData* target_data = bundle_alloc();

BundleDict_it_t it;
for (BundleDict_it(it, source_data->dict); !BundleDict_end_p(it); BundleDict_next(it)) {
const char* key = BundleDict_cref(it)->key;
BundleEntry* entry = BundleDict_cref(it)->value;
BundleEntry* entry_copy = bundle_entry_alloc_copy(entry);
BundleDict_set_at(target_data->dict, key, entry_copy);
}

return target_data;
}

void bundle_free(Bundle bundle) {
BundleData* data = (BundleData*)bundle;

BundleDict_it_t it;
for (BundleDict_it(it, data->dict); !BundleDict_end_p(it); BundleDict_next(it)) {
bundle_entry_free(BundleDict_cref(it)->value);
}

BundleDict_clear(data->dict);
free(data);
}

bool bundle_get_bool(Bundle bundle, const char* key) {
BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key);
Expand Down Expand Up @@ -168,29 +202,4 @@ void bundle_put_string(Bundle bundle, const char* key, const char* value) {
}
}

Bundle bundle_alloc_copy(Bundle source) {
BundleData* source_data = (BundleData*)source;
BundleData* target_data = bundle_alloc();

BundleDict_it_t it;
for (BundleDict_it(it, source_data->dict); !BundleDict_end_p(it); BundleDict_next(it)) {
const char* key = BundleDict_cref(it)->key;
BundleEntry* entry = BundleDict_cref(it)->value;
BundleEntry* entry_copy = bundle_entry_alloc_copy(entry);
BundleDict_set_at(target_data->dict, key, entry_copy);
}

return target_data;
}

void bundle_free(Bundle bundle) {
BundleData* data = (BundleData*)bundle;

BundleDict_it_t it;
for (BundleDict_it(it, data->dict); !BundleDict_end_p(it); BundleDict_next(it)) {
bundle_entry_free(BundleDict_cref(it)->value);
}

BundleDict_clear(data->dict);
free(data);
}
// endregion Bundle
8 changes: 1 addition & 7 deletions components/furi/src/context.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
#pragma once

typedef struct {
/** Contextual data related to the running app's instance
*
* The app can attach its data to this.
* The lifecycle is determined by the on_start and on_stop methods in the AppManifest.
* These manifest methods can optionally allocate/free data that is attached here.
*/
void* data;

} Context;
Loading

0 comments on commit 200c223

Please sign in to comment.