1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-13 13:09:49 +04:00

Various fixes

fixes by Willy-JL
nfc parser by zacharyweiss
js widget and path globals by jamisonderek
This commit is contained in:
MX
2024-04-05 01:25:03 +03:00
parent 45e7913435
commit ec4b8b8f5e
14 changed files with 2137 additions and 8 deletions

View File

@@ -128,6 +128,15 @@ App(
sources=["plugins/supported_cards/metromoney.c"],
)
App(
appid="charliecard_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="charliecard_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/charliecard.c"],
)
App(
appid="kazan_parser",
apptype=FlipperAppType.PLUGIN,

File diff suppressed because it is too large Load Diff

View File

@@ -33,8 +33,10 @@ static void desktop_loader_callback(const void* message, void* context) {
const LoaderEvent* event = message;
if(event->type == LoaderEventTypeApplicationStarted) {
desktop->animation_lock = api_lock_alloc_locked();
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalBeforeAppStarted);
furi_check(furi_semaphore_acquire(desktop->animation_semaphore, 3000) == FuriStatusOk);
api_lock_wait_unlock_and_free(desktop->animation_lock);
desktop->animation_lock = NULL;
} else if(event->type == LoaderEventTypeApplicationStopped) {
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished);
}
@@ -126,7 +128,7 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
animation_manager_unload_and_stall_animation(desktop->animation_manager);
}
desktop_auto_lock_inhibit(desktop);
furi_semaphore_release(desktop->animation_semaphore);
api_lock_unlock(desktop->animation_lock);
return true;
case DesktopGlobalAfterAppFinished:
animation_manager_load_and_continue_animation(desktop->animation_manager);
@@ -276,7 +278,6 @@ void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) {
Desktop* desktop_alloc(void) {
Desktop* desktop = malloc(sizeof(Desktop));
desktop->animation_semaphore = furi_semaphore_alloc(1, 0);
desktop->animation_manager = animation_manager_alloc();
desktop->gui = furi_record_open(RECORD_GUI);
desktop->scene_thread = furi_thread_alloc();

View File

@@ -20,6 +20,7 @@
#include <loader/loader.h>
#include <notification/notification_app.h>
#include <toolbox/api_lock.h>
#define STATUS_BAR_Y_SHIFT 13
@@ -81,7 +82,7 @@ struct Desktop {
bool in_transition : 1;
FuriSemaphore* animation_semaphore;
FuriApiLock animation_lock;
};
Desktop* desktop_alloc(void);

View File

@@ -109,3 +109,11 @@ App(
requires=["js_app"],
sources=["modules/js_textbox.c"],
)
App(
appid="js_widget",
apptype=FlipperAppType.PLUGIN,
entry_point="js_widget_ep",
requires=["js_app"],
sources=["modules/js_widget.c"],
)

View File

@@ -0,0 +1,9 @@
let storage = require("storage");
print("script has __dirpath of" + __dirpath);
print("script has __filepath of" + __filepath);
if (storage.exists(__dirpath + "/math.js")) {
print("math.js exist here.");
} else {
print("math.js does not exist here.");
}

View File

@@ -0,0 +1,59 @@
let widget = require("widget");
let demo_seconds = 30;
print("Loading file", __filepath);
print("From directory", __dirpath);
// addText supports "Primary" and "Secondary" font sizes.
widget.addText(10, 10, "Primary", "Example JS widget");
widget.addText(10, 20, "Secondary", "Example widget from JS!");
// load a Xbm file from the same directory as this script.
widget.addText(0, 30, "Secondary", __filepath);
let logo = widget.loadImageXbm(__dirpath + "/widget-js.fxbm");
// add a line (x1, y1, x2, y2)
widget.addLine(10, 35, 120, 35);
// add a circle/disc (x, y, radius)
widget.addCircle(12, 52, 10);
widget.addDisc(12, 52, 5);
// add a frame/box (x, y, width, height)
widget.addFrame(30, 45, 10, 10);
widget.addBox(32, 47, 6, 6);
// add a rounded frame/box (x, y, width, height, radius)
widget.addRframe(50, 45, 15, 15, 3);
widget.addRbox(53, 48, 6, 6, 2);
// add a dot (x, y)
widget.addDot(100, 45);
widget.addDot(102, 44);
widget.addDot(104, 43);
// add a glyph (x, y, glyph)
widget.addGlyph(115, 50, "#".charCodeAt(0));
// Show the widget (drawing the layers in the orderer they were added)
widget.show();
let i = 1;
let bitmap = undefined;
while (widget.isOpen() && i <= demo_seconds) {
// Print statements will only show up once the widget is closed.
print("count is at", i++);
// You can call remove on any added item, it does not impact the other ids.
if (bitmap) { widget.remove(bitmap); bitmap = undefined; }
// All of the addXXX functions return an id that can be used to remove the item.
else { bitmap = widget.addXbm(77, 45, logo); }
delay(1000);
}
// If user did not press the back button, close the widget.
if (widget.isOpen()) {
widget.close();
}

View File

@@ -1,4 +1,5 @@
#include <common/cs_dbg.h>
#include <toolbox/path.h>
#include <toolbox/stream/file_stream.h>
#include <loader/firmware_api/firmware_api.h>
#include <flipper_application/api_hashtable/api_hashtable.h>
@@ -319,6 +320,24 @@ static int32_t js_thread(void* arg) {
struct mjs* mjs = mjs_create(worker);
worker->modules = js_modules_create(mjs, worker->resolver);
mjs_val_t global = mjs_get_global(mjs);
if(worker->path) {
FuriString* dirpath = furi_string_alloc();
path_extract_dirname(furi_string_get_cstr(worker->path), dirpath);
mjs_set(
mjs,
global,
"__filepath",
~0,
mjs_mk_string(
mjs, furi_string_get_cstr(worker->path), furi_string_size(worker->path), true));
mjs_set(
mjs,
global,
"__dirpath",
~0,
mjs_mk_string(mjs, furi_string_get_cstr(dirpath), furi_string_size(dirpath), true));
furi_string_free(dirpath);
}
mjs_set(mjs, global, "print", ~0, MJS_MK_FN(js_print));
mjs_set(mjs, global, "delay", ~0, MJS_MK_FN(js_delay));
mjs_set(mjs, global, "to_string", ~0, MJS_MK_FN(js_global_to_string));

View File

@@ -211,7 +211,7 @@ void js_math_random(struct mjs* mjs) {
mjs_return(mjs, MJS_UNDEFINED);
}
const uint32_t random_val = furi_hal_random_get();
double rnd = (double)random_val / RAND_MAX;
double rnd = (double)random_val / FURI_HAL_RANDOM_MAX;
mjs_return(mjs, mjs_mk_number(mjs, rnd));
}

View File

@@ -0,0 +1,956 @@
#include <assets_icons.h>
#include <gui/view_dispatcher.h>
#include <gui/view.h>
#include <m-array.h>
#include <m-list.h>
#include <string.h>
#include "../js_modules.h"
typedef struct WidgetComponent WidgetComponent;
ARRAY_DEF(ComponentArray, WidgetComponent*, M_PTR_OPLIST);
typedef struct XbmImage XbmImage;
LIST_DEF(XbmImageList, XbmImage*, M_POD_OPLIST);
struct WidgetComponent {
void (*draw)(Canvas* canvas, void* model);
void (*free)(WidgetComponent* component);
void* model;
uint32_t id;
};
struct XbmImage {
uint32_t width;
uint32_t height;
uint8_t data[];
};
typedef struct {
uint8_t x;
uint8_t y;
uint8_t w;
uint8_t h;
} BoxElement;
typedef struct {
uint8_t x;
uint8_t y;
uint8_t r;
} CircleElement;
typedef struct {
uint8_t x;
uint8_t y;
uint8_t r;
} DiscElement;
typedef struct {
uint8_t x;
uint8_t y;
} DotElement;
typedef struct {
uint8_t x;
uint8_t y;
const Icon* icon;
} IconElement;
typedef struct {
uint8_t x;
uint8_t y;
uint8_t w;
uint8_t h;
} FrameElement;
typedef struct {
uint8_t x;
uint8_t y;
uint16_t ch;
} GlyphElement;
typedef struct {
uint8_t x1;
uint8_t y1;
uint8_t x2;
uint8_t y2;
} LineElement;
typedef struct {
uint8_t x;
uint8_t y;
uint8_t w;
uint8_t h;
uint8_t r;
} RboxElement;
typedef struct {
uint8_t x;
uint8_t y;
uint8_t w;
uint8_t h;
uint8_t r;
} RframeElement;
typedef struct {
uint8_t x;
uint8_t y;
Font font;
FuriString* text;
} TextElement;
typedef struct {
uint8_t x;
uint8_t y;
uint32_t index;
View* view;
} XbmElement;
typedef struct {
ComponentArray_t component;
XbmImageList_t image;
uint32_t max_assigned_id;
} WidgetModel;
typedef struct {
View* view;
ViewDispatcher* view_dispatcher;
FuriThread* thread;
} JsWidgetInst;
static JsWidgetInst* get_this_ctx(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsWidgetInst* widget = mjs_get_ptr(mjs, obj_inst);
furi_assert(widget);
return widget;
}
static void ret_bad_args(struct mjs* mjs, const char* error) {
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error);
mjs_return(mjs, MJS_UNDEFINED);
}
static bool check_arg_count(struct mjs* mjs, size_t count) {
size_t num_args = mjs_nargs(mjs);
if(num_args != count) {
ret_bad_args(mjs, "Wrong argument count");
return false;
}
return true;
}
static void js_widget_load_image_xbm(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 1)) {
return;
}
mjs_val_t path_arg = mjs_arg(mjs, 0);
size_t path_len = 0;
const char* path = mjs_get_string(mjs, &path_arg, &path_len);
if(!path) {
ret_bad_args(mjs, "Path must be a string");
return;
}
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
XbmImage* xbm = NULL;
do {
if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
ret_bad_args(mjs, "Failed to open file");
break;
}
uint32_t size = 0;
if(storage_file_read(file, &size, sizeof(size)) != sizeof(size)) {
ret_bad_args(mjs, "Failed to get file size");
break;
}
xbm = malloc(size);
if(storage_file_read(file, xbm, size) != size) {
ret_bad_args(mjs, "Failed to load entire file");
free(xbm);
xbm = NULL;
break;
}
} while(false);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
if(xbm == NULL) {
mjs_return(mjs, MJS_UNDEFINED);
return;
}
uint32_t count = 0;
with_view_model(
widget->view,
WidgetModel * model,
{
count = XbmImageList_size(model->image);
XbmImageList_push_back(model->image, xbm);
},
false);
mjs_return(mjs, mjs_mk_number(mjs, count));
}
static void js_widget_remove(struct mjs* mjs) {
bool removed = false;
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 1)) {
return;
}
with_view_model(
widget->view,
WidgetModel * model,
{
uint32_t id = mjs_get_int32(mjs, mjs_arg(mjs, 0));
ComponentArray_it_t it;
ComponentArray_it(it, model->component);
while(!ComponentArray_end_p(it)) {
WidgetComponent* component = *ComponentArray_ref(it);
if(component->id == id) {
if(component->free) {
component->free(component);
}
ComponentArray_remove(model->component, it);
removed = true;
break;
}
ComponentArray_next(it);
}
},
true);
mjs_return(mjs, mjs_mk_boolean(mjs, removed));
}
static void widget_box_draw(Canvas* canvas, void* model) {
BoxElement* element = model;
canvas_draw_box(canvas, element->x, element->y, element->w, element->h);
}
static void widget_box_free(WidgetComponent* component) {
BoxElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_box(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 4)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t w = mjs_get_int32(mjs, mjs_arg(mjs, 2));
int32_t h = mjs_get_int32(mjs, mjs_arg(mjs, 3));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_box_draw;
component->free = widget_box_free;
component->model = malloc(sizeof(BoxElement));
BoxElement* element = component->model;
element->x = x;
element->y = y;
element->w = w;
element->h = h;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_circle_draw(Canvas* canvas, void* model) {
CircleElement* element = model;
canvas_draw_circle(canvas, element->x, element->y, element->r);
}
static void widget_circle_free(WidgetComponent* component) {
CircleElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_circle(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 3)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t r = mjs_get_int32(mjs, mjs_arg(mjs, 2));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_circle_draw;
component->free = widget_circle_free;
component->model = malloc(sizeof(CircleElement));
CircleElement* element = component->model;
element->x = x;
element->y = y;
element->r = r;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_disc_draw(Canvas* canvas, void* model) {
DiscElement* element = model;
canvas_draw_disc(canvas, element->x, element->y, element->r);
}
static void widget_disc_free(WidgetComponent* component) {
DiscElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_disc(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 3)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t r = mjs_get_int32(mjs, mjs_arg(mjs, 2));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_disc_draw;
component->free = widget_disc_free;
component->model = malloc(sizeof(DiscElement));
DiscElement* element = component->model;
element->x = x;
element->y = y;
element->r = r;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_dot_draw(Canvas* canvas, void* model) {
DotElement* element = model;
canvas_draw_dot(canvas, element->x, element->y);
}
static void widget_dot_free(WidgetComponent* component) {
DotElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_dot(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 2)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_dot_draw;
component->free = widget_dot_free;
component->model = malloc(sizeof(DotElement));
DotElement* element = component->model;
element->x = x;
element->y = y;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_frame_draw(Canvas* canvas, void* model) {
FrameElement* element = model;
canvas_draw_frame(canvas, element->x, element->y, element->w, element->h);
}
static void widget_frame_free(WidgetComponent* component) {
FrameElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_frame(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 4)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t w = mjs_get_int32(mjs, mjs_arg(mjs, 2));
int32_t h = mjs_get_int32(mjs, mjs_arg(mjs, 3));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_frame_draw;
component->free = widget_frame_free;
component->model = malloc(sizeof(FrameElement));
FrameElement* element = component->model;
element->x = x;
element->y = y;
element->w = w;
element->h = h;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_glyph_draw(Canvas* canvas, void* model) {
GlyphElement* element = model;
canvas_draw_glyph(canvas, element->x, element->y, element->ch);
}
static void widget_glyph_free(WidgetComponent* component) {
GlyphElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_glyph(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 3)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t ch = mjs_get_int32(mjs, mjs_arg(mjs, 2));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_glyph_draw;
component->free = widget_glyph_free;
component->model = malloc(sizeof(GlyphElement));
GlyphElement* element = component->model;
element->x = x;
element->y = y;
element->ch = ch;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_line_draw(Canvas* canvas, void* model) {
LineElement* element = model;
canvas_draw_line(canvas, element->x1, element->y1, element->x2, element->y2);
}
static void widget_line_free(WidgetComponent* component) {
LineElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_line(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 4)) return;
int32_t x1 = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y1 = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t x2 = mjs_get_int32(mjs, mjs_arg(mjs, 2));
int32_t y2 = mjs_get_int32(mjs, mjs_arg(mjs, 3));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_line_draw;
component->free = widget_line_free;
component->model = malloc(sizeof(LineElement));
LineElement* element = component->model;
element->x1 = x1;
element->y1 = y1;
element->x2 = x2;
element->y2 = y2;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_rbox_draw(Canvas* canvas, void* model) {
RboxElement* element = model;
canvas_draw_rbox(canvas, element->x, element->y, element->w, element->h, element->r);
}
static void widget_rbox_free(WidgetComponent* component) {
BoxElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_rbox(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 5)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t w = mjs_get_int32(mjs, mjs_arg(mjs, 2));
int32_t h = mjs_get_int32(mjs, mjs_arg(mjs, 3));
int32_t r = mjs_get_int32(mjs, mjs_arg(mjs, 4));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_rbox_draw;
component->free = widget_rbox_free;
component->model = malloc(sizeof(RboxElement));
RboxElement* element = component->model;
element->x = x;
element->y = y;
element->w = w;
element->h = h;
element->r = r;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_rframe_draw(Canvas* canvas, void* model) {
RframeElement* element = model;
canvas_draw_rframe(canvas, element->x, element->y, element->w, element->h, element->r);
}
static void widget_rframe_free(WidgetComponent* component) {
RframeElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_rframe(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 5)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t w = mjs_get_int32(mjs, mjs_arg(mjs, 2));
int32_t h = mjs_get_int32(mjs, mjs_arg(mjs, 3));
int32_t r = mjs_get_int32(mjs, mjs_arg(mjs, 4));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_rframe_draw;
component->free = widget_rframe_free;
component->model = malloc(sizeof(RframeElement));
RframeElement* element = component->model;
element->x = x;
element->y = y;
element->w = w;
element->h = h;
element->r = r;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_text_draw(Canvas* canvas, void* model) {
TextElement* element = model;
canvas_set_font(canvas, element->font);
canvas_draw_str(canvas, element->x, element->y, furi_string_get_cstr(element->text));
}
static void widget_text_free(WidgetComponent* component) {
TextElement* element = component->model;
furi_string_free(element->text);
free(element);
free(component);
}
static void js_widget_add_text(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 4)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
mjs_val_t font_arg = mjs_arg(mjs, 2);
size_t font_name_len = 0;
const char* font_name_text = mjs_get_string(mjs, &font_arg, &font_name_len);
if(!font_name_text) {
ret_bad_args(mjs, "Font name must be a string");
return;
}
Font font = FontTotalNumber;
size_t cmp_str_len = strlen("Primary");
if(font_name_len == cmp_str_len && strncmp(font_name_text, "Primary", cmp_str_len) == 0) {
font = FontPrimary;
} else {
cmp_str_len = strlen("Secondary");
if(font_name_len == cmp_str_len &&
strncmp(font_name_text, "Secondary", cmp_str_len) == 0) {
font = FontSecondary;
}
}
if(font == FontTotalNumber) {
ret_bad_args(mjs, "Unknown font name");
return;
}
mjs_val_t text_arg = mjs_arg(mjs, 3);
size_t text_len = 0;
const char* text = mjs_get_string(mjs, &text_arg, &text_len);
if(!text) {
ret_bad_args(mjs, "Text must be a string");
return;
}
FuriString* text_str = furi_string_alloc_set(text);
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_text_draw;
component->free = widget_text_free;
component->model = malloc(sizeof(TextElement));
TextElement* element = component->model;
element->x = x;
element->y = y;
element->font = font;
element->text = text_str;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_xbm_draw(Canvas* canvas, void* model) {
XbmElement* element = model;
XbmImage* image = NULL;
with_view_model(
element->view,
WidgetModel * widget_model,
{ image = *XbmImageList_get(widget_model->image, element->index); },
false);
if(image) {
canvas_draw_xbm(canvas, element->x, element->y, image->width, image->height, image->data);
}
}
static void widget_xbm_free(WidgetComponent* component) {
XbmElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_xbm(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 3)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t index = mjs_get_int32(mjs, mjs_arg(mjs, 2));
with_view_model(
widget->view,
WidgetModel * widget_model,
{
size_t count = XbmImageList_size(widget_model->image);
if(index < 0 || index >= (int32_t)count) {
ret_bad_args(mjs, "Invalid image index");
return;
}
},
false);
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_xbm_draw;
component->free = widget_xbm_free;
component->model = malloc(sizeof(XbmElement));
XbmElement* element = component->model;
element->x = x;
element->y = y;
element->index = index;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
element->view = widget->view;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void js_widget_is_open(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
mjs_return(mjs, mjs_mk_boolean(mjs, !!widget->thread));
}
static void widget_deinit(void* context) {
JsWidgetInst* widget = context;
if(widget->thread) {
furi_thread_join(widget->thread);
furi_thread_free(widget->thread);
widget->thread = NULL;
furi_assert(widget->view_dispatcher);
view_dispatcher_remove_view(widget->view_dispatcher, 0);
view_dispatcher_free(widget->view_dispatcher);
widget->view_dispatcher = NULL;
furi_record_close(RECORD_GUI);
}
}
static void widget_callback(void* context, uint32_t arg) {
UNUSED(arg);
widget_deinit(context);
}
static bool widget_exit(void* context) {
JsWidgetInst* widget = context;
view_dispatcher_stop(widget->view_dispatcher);
furi_timer_pending_callback(widget_callback, widget, 0);
return true;
}
static int32_t widget_thread(void* context) {
ViewDispatcher* view_dispatcher = context;
view_dispatcher_run(view_dispatcher);
return 0;
}
static void js_widget_show(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
Gui* gui = furi_record_open(RECORD_GUI);
widget->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(widget->view_dispatcher);
view_dispatcher_add_view(widget->view_dispatcher, 0, widget->view);
view_dispatcher_set_event_callback_context(widget->view_dispatcher, widget);
view_dispatcher_set_navigation_event_callback(widget->view_dispatcher, widget_exit);
view_dispatcher_attach_to_gui(widget->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
view_dispatcher_switch_to_view(widget->view_dispatcher, 0);
widget->thread =
furi_thread_alloc_ex("JsWidget", 1024, widget_thread, widget->view_dispatcher);
furi_thread_start(widget->thread);
mjs_return(mjs, MJS_UNDEFINED);
}
static void js_widget_close(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
if(widget->thread) {
view_dispatcher_stop(widget->view_dispatcher);
widget_deinit(widget);
}
mjs_return(mjs, MJS_UNDEFINED);
}
static void widget_draw_callback(Canvas* canvas, void* model) {
WidgetModel* widget_model = model;
canvas_clear(canvas);
ComponentArray_it_t it;
ComponentArray_it(it, widget_model->component);
while(!ComponentArray_end_p(it)) {
WidgetComponent* component = *ComponentArray_ref(it);
if(component->draw != NULL) {
component->draw(canvas, component->model);
}
ComponentArray_next(it);
}
}
static void widget_remove_view(void* context) {
JsWidgetInst* widget = context;
if(widget->view) {
with_view_model(
widget->view,
WidgetModel * model,
{
ComponentArray_it_t it;
ComponentArray_it(it, model->component);
while(!ComponentArray_end_p(it)) {
WidgetComponent* component = *ComponentArray_ref(it);
if(component->free) {
component->free(component);
component->free = NULL;
}
ComponentArray_next(it);
}
ComponentArray_reset(model->component);
ComponentArray_clear(model->component);
},
false);
with_view_model(
widget->view, WidgetModel * model, { XbmImageList_clear(model->image); }, false);
view_free(widget->view);
widget->view = NULL;
}
}
static JsWidgetInst* widget_alloc(void) {
JsWidgetInst* widget = malloc(sizeof(JsWidgetInst));
widget->thread = NULL;
widget->view_dispatcher = NULL;
widget->view = view_alloc();
view_allocate_model(widget->view, ViewModelTypeLockFree, sizeof(WidgetModel));
view_set_draw_callback(widget->view, widget_draw_callback);
with_view_model(
widget->view,
WidgetModel * model,
{
ComponentArray_init(model->component);
XbmImageList_init(model->image);
model->max_assigned_id = 0;
},
true);
return widget;
}
static void* js_widget_create(struct mjs* mjs, mjs_val_t* object) {
JsWidgetInst* widget = widget_alloc();
mjs_val_t widget_obj = mjs_mk_object(mjs);
mjs_set(mjs, widget_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, widget));
// addBox(x: number, y: number, w: number, h: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addBox", ~0, MJS_MK_FN(js_widget_add_box));
// addCircle(x: number, y: number, r: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addCircle", ~0, MJS_MK_FN(js_widget_add_circle));
// addDisc(x: number, y: number, r: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addDisc", ~0, MJS_MK_FN(js_widget_add_disc));
// addDot(x: number, y: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addDot", ~0, MJS_MK_FN(js_widget_add_dot));
// addFrame(x: number, y: number, w: number, h: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addFrame", ~0, MJS_MK_FN(js_widget_add_frame));
// addGlyph(x: number, y: number, ch: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addGlyph", ~0, MJS_MK_FN(js_widget_add_glyph));
// addLine(x1: number, y1: number, x2: number, y2: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addLine", ~0, MJS_MK_FN(js_widget_add_line));
// addRbox(x: number, y: number, w: number, h: number, r: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addRbox", ~0, MJS_MK_FN(js_widget_add_rbox));
// addRframe(x: number, y: number, w: number, h: number, r: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addRframe", ~0, MJS_MK_FN(js_widget_add_rframe));
// addText(x: number, y: number, font: string, text: string): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addText", ~0, MJS_MK_FN(js_widget_add_text));
// addXbm(x: number, y: number, index: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addXbm", ~0, MJS_MK_FN(js_widget_add_xbm));
// loadImageXbm(path: string): number (returns index of the loaded image)
mjs_set(mjs, widget_obj, "loadImageXbm", ~0, MJS_MK_FN(js_widget_load_image_xbm));
// remove(id: number): boolean (returns true if the component was removed)
mjs_set(mjs, widget_obj, "remove", ~0, MJS_MK_FN(js_widget_remove));
// isOpen(): boolean (returns true if the widget is open)
mjs_set(mjs, widget_obj, "isOpen", ~0, MJS_MK_FN(js_widget_is_open));
// show(): void (shows the widget)
mjs_set(mjs, widget_obj, "show", ~0, MJS_MK_FN(js_widget_show));
// close(): void (closes the widget)
mjs_set(mjs, widget_obj, "close", ~0, MJS_MK_FN(js_widget_close));
*object = widget_obj;
return widget;
}
static void js_widget_destroy(void* inst) {
JsWidgetInst* widget = inst;
if(widget->thread) {
view_dispatcher_stop(widget->view_dispatcher);
widget_deinit(widget);
}
widget_remove_view(widget);
free(widget);
}
static const JsModuleDescriptor js_widget_desc = {
"widget",
js_widget_create,
js_widget_destroy,
};
static const FlipperAppPluginDescriptor widget_plugin_descriptor = {
.appid = PLUGIN_APP_ID,
.ep_api_version = PLUGIN_API_VERSION,
.entry_point = &js_widget_desc,
};
const FlipperAppPluginDescriptor* js_widget_ep(void) {
return &widget_plugin_descriptor;
}

View File

@@ -612,7 +612,7 @@ void infrared_worker_set_raw_signal(
furi_check(timings);
furi_check(timings_cnt > 0);
furi_check((frequency <= INFRARED_MAX_FREQUENCY) && (frequency >= INFRARED_MIN_FREQUENCY));
furi_check((duty_cycle < 1.0f) && (duty_cycle > 0.0f));
furi_check((duty_cycle <= 1.0f) && (duty_cycle > 0.0f));
size_t max_copy_num = COUNT_OF(instance->signal.raw.timings) - 1;
furi_check(timings_cnt <= max_copy_num);

View File

@@ -41,7 +41,11 @@
> If for any reason this test is never passed, this means there is a failure in the system and there is no other
> way to recover than applying a device reset.
*/
#define FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS (3000U) /* 3 seconds */
// Was previously 3000U, 3 seconds
// Changing furi_assert() to furi_check() brought timeout crashes
// Internal storage is very slow, and "big" files will often cause a "timeout" with 3 seconds
// 10 seconds seems fine, the file operations complete successfully, albeit slowly
#define FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS (10000U) /* 10 seconds */
#define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__) & 0x7U) == (0x00UL))
#define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \

View File

@@ -6,12 +6,16 @@
extern "C" {
#endif
#define FURI_HAL_RANDOM_MAX 0xFFFFFFFF
/** Initialize random subsystem */
void furi_hal_random_init(void);
/** Get random value
* furi_hal_random_get() gives up to FURI_HAL_RANDOM_MAX
* rand() and random() give up to RAND_MAX
*
* @return random value
* @return 32 bit random value (up to FURI_HAL_RANDOM_MAX)
*/
uint32_t furi_hal_random_get(void);