merge ofw dev missing parts

This commit is contained in:
MX 2024-05-19 03:54:21 +03:00
parent fb6070f29d
commit 21abcb56fd
No known key found for this signature in database
GPG key ID: 7CCC66B7DBDD1C83
5 changed files with 292 additions and 230 deletions

View file

@ -1,47 +1,69 @@
let math = require("math");
let absResult = math.abs(-5);
let acosResult = math.acos(0.5);
let acoshResult = math.acosh(2);
let asinResult = math.asin(0.5);
let asinhResult = math.asinh(2);
let atanResult = math.atan(1);
let atan2Result = math.atan2(1, 1);
let atanhResult = math.atanh(0.5);
let cbrtResult = math.cbrt(27);
let ceilResult = math.ceil(5.3);
let clz32Result = math.clz32(1);
let cosResult = math.cos(math.PI);
let expResult = math.exp(1);
let floorResult = math.floor(5.7);
let maxResult = math.max(3, 5);
let minResult = math.min(3, 5);
let powResult = math.pow(2, 3);
let randomResult = math.random();
let signResult = math.sign(-5);
let sinResult = math.sin(math.PI / 2);
let sqrtResult = math.sqrt(25);
let truncResult = math.trunc(5.7);
print("math.abs(-5):", math.abs(-5));
print("math.acos(0.5):", math.acos(0.5));
print("math.acosh(2):", math.acosh(2));
print("math.asin(0.5):", math.asin(0.5));
print("math.asinh(2):", math.asinh(2));
print("math.atan(1):", math.atan(1));
print("math.atan2(1, 1):", math.atan2(1, 1));
print("math.atanh(0.5):", math.atanh(0.5));
print("math.cbrt(27):", math.cbrt(27));
print("math.ceil(5.3):", math.ceil(5.3));
print("math.clz32(1):", math.clz32(1));
print("math.cos(math.PI):", math.cos(math.PI));
print("math.exp(1):", math.exp(1));
print("math.floor(5.7):", math.floor(5.7));
print("math.max(3, 5):", math.max(3, 5));
print("math.min(3, 5):", math.min(3, 5));
print("math.pow(2, 3):", math.pow(2, 3));
print("math.random():", math.random());
print("math.sign(-5):", math.sign(-5));
print("math.sin(math.PI/2):", math.sin(math.PI / 2));
print("math.sqrt(25):", math.sqrt(25));
print("math.trunc(5.7):", math.trunc(5.7));
print("math.abs(-5):", absResult);
print("math.acos(0.5):", acosResult);
print("math.acosh(2):", acoshResult);
print("math.asin(0.5):", asinResult);
print("math.asinh(2):", asinhResult);
print("math.atan(1):", atanResult);
print("math.atan2(1, 1):", atan2Result);
print("math.atanh(0.5):", atanhResult);
print("math.cbrt(27):", cbrtResult);
print("math.ceil(5.3):", ceilResult);
print("math.clz32(1):", clz32Result);
print("math.cos(math.PI):", cosResult);
print("math.exp(1):", expResult);
print("math.floor(5.7):", floorResult);
print("math.max(3, 5):", maxResult);
print("math.min(3, 5):", minResult);
print("math.pow(2, 3):", powResult);
print("math.random():", randomResult);
print("math.sign(-5):", signResult);
print("math.sin(math.PI/2):", sinResult);
print("math.sqrt(25):", sqrtResult);
print("math.trunc(5.7):", truncResult);
// Unit tests. Please add more if you have time and knowledge.
// math.EPSILON on Flipper Zero is 2.22044604925031308085e-16
let succeeded = 0;
let failed = 0;
function test(text, result, expected, epsilon) {
let is_equal = math.is_equal(result, expected, epsilon);
if (is_equal) {
succeeded += 1;
} else {
failed += 1;
print(text, "expected", expected, "got", result);
}
}
test("math.abs(5)", math.abs(-5), 5, math.EPSILON);
test("math.abs(0.5)", math.abs(-0.5), 0.5, math.EPSILON);
test("math.abs(5)", math.abs(5), 5, math.EPSILON);
test("math.abs(-0.5)", math.abs(0.5), 0.5, math.EPSILON);
test("math.acos(0.5)", math.acos(0.5), 1.0471975511965976, math.EPSILON);
test("math.acosh(2)", math.acosh(2), 1.3169578969248166, math.EPSILON);
test("math.asin(0.5)", math.asin(0.5), 0.5235987755982988, math.EPSILON);
test("math.asinh(2)", math.asinh(2), 1.4436354751788103, math.EPSILON);
test("math.atan(1)", math.atan(1), 0.7853981633974483, math.EPSILON);
test("math.atan2(1, 1)", math.atan2(1, 1), 0.7853981633974483, math.EPSILON);
test("math.atanh(0.5)", math.atanh(0.5), 0.5493061443340549, math.EPSILON);
test("math.cbrt(27)", math.cbrt(27), 3, math.EPSILON);
test("math.ceil(5.3)", math.ceil(5.3), 6, math.EPSILON);
test("math.clz32(1)", math.clz32(1), 31, math.EPSILON);
test("math.floor(5.7)", math.floor(5.7), 5, math.EPSILON);
test("math.max(3, 5)", math.max(3, 5), 5, math.EPSILON);
test("math.min(3, 5)", math.min(3, 5), 3, math.EPSILON);
test("math.pow(2, 3)", math.pow(2, 3), 8, math.EPSILON);
test("math.sign(-5)", math.sign(-5), -1, math.EPSILON);
test("math.sqrt(25)", math.sqrt(25), 5, math.EPSILON);
test("math.trunc(5.7)", math.trunc(5.7), 5, math.EPSILON);
test("math.cos(math.PI)", math.cos(math.PI), -1, math.EPSILON * 18); // Error 3.77475828372553223744e-15
test("math.exp(1)", math.exp(1), 2.718281828459045, math.EPSILON * 2); // Error 4.44089209850062616169e-16
test("math.sin(math.PI / 2)", math.sin(math.PI / 2), 1, math.EPSILON * 4.5); // Error 9.99200722162640886381e-16
if (failed > 0) {
print("!!!", failed, "Unit tests failed !!!");
}

View file

@ -4,9 +4,9 @@ let textbox = require("textbox");
// Focus (start / end), Font (text / hex)
textbox.setConfig("end", "text");
// Can make sure it's empty before showing, in case of reusing in same script
// (Closing textbox already empties the text, but maybe you added more in a loop for example)
textbox.emptyText();
// Can make sure it's cleared before showing, in case of reusing in same script
// (Closing textbox already clears the text, but maybe you added more in a loop for example)
textbox.clearText();
// Add default text
textbox.addText("Example dynamic updating textbox\n");

View file

@ -1,268 +1,313 @@
#include "../js_modules.h"
#include "furi_hal_random.h"
#include <float.h>
#define JS_MATH_PI (double)3.14159265358979323846
#define JS_MATH_E (double)2.7182818284590452354
#define JS_MATH_PI ((double)M_PI)
#define JS_MATH_E ((double)M_E)
#define JS_MATH_EPSILON ((double)DBL_EPSILON)
#define TAG "JsMath"
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_mk_undefined());
mjs_return(mjs, MJS_UNDEFINED);
}
static bool check_arg_count(struct mjs* mjs, size_t count) {
static bool check_args(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;
}
for(size_t i = 0; i < count; i++) {
if(!mjs_is_number(mjs_arg(mjs, i))) {
ret_bad_args(mjs, "Wrong argument type");
return false;
}
}
return true;
}
void js_math_abs(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
void js_math_is_equal(struct mjs* mjs) {
if(!check_args(mjs, 3)) {
return;
}
double a = mjs_get_double(mjs, mjs_arg(mjs, 0));
double b = mjs_get_double(mjs, mjs_arg(mjs, 1));
double e = mjs_get_double(mjs, mjs_arg(mjs, 2));
double f = fabs(a - b);
mjs_return(mjs, mjs_mk_boolean(mjs, (f <= e)));
}
void js_math_abs(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, x < 0 ? mjs_mk_number(mjs, -x) : mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, fabs(x)));
}
void js_math_acos(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
if(x < -1 || x > 1) {
ret_bad_args(mjs, "Invalid input value for Math.acos");
mjs_return(mjs, MJS_UNDEFINED);
if(x < (double)-1. || x > (double)1.) {
ret_bad_args(mjs, "Invalid input value for math.acos");
return;
}
mjs_return(mjs, mjs_mk_number(mjs, JS_MATH_PI / (double)2 - atan(x / sqrt(1 - x * x))));
mjs_return(mjs, mjs_mk_number(mjs, acos(x)));
}
void js_math_acosh(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
if(x < 1) {
ret_bad_args(mjs, "Invalid input value for Math.acosh");
mjs_return(mjs, MJS_UNDEFINED);
if(x < (double)1.) {
ret_bad_args(mjs, "Invalid input value for math.acosh");
return;
}
mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x - 1))));
mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x - (double)1.))));
}
void js_math_asin(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, atan(x / sqrt(1 - x * x))));
mjs_return(mjs, mjs_mk_number(mjs, asin(x)));
}
void js_math_asinh(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x + 1))));
mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x + (double)1.))));
}
void js_math_atan(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, atan(x)));
}
void js_math_atan2(struct mjs* mjs) {
if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) ||
!mjs_is_number(mjs_arg(mjs, 1))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 2)) {
return;
}
double y = mjs_get_double(mjs, mjs_arg(mjs, 0));
double x = mjs_get_double(mjs, mjs_arg(mjs, 1));
mjs_return(mjs, mjs_mk_number(mjs, atan2(y, x)));
}
void js_math_atanh(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
if(x <= -1 || x >= 1) {
ret_bad_args(mjs, "Invalid input value for Math.atanh");
mjs_return(mjs, MJS_UNDEFINED);
if(x < (double)-1. || x > (double)1.) {
ret_bad_args(mjs, "Invalid input value for math.atanh");
return;
}
mjs_return(mjs, mjs_mk_number(mjs, (double)0.5 * log((1 + x) / (1 - x))));
mjs_return(mjs, mjs_mk_number(mjs, (double)0.5 * log(((double)1. + x) / ((double)1. - x))));
}
void js_math_cbrt(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, pow(x, 1.0 / 3.0)));
mjs_return(mjs, mjs_mk_number(mjs, cbrt(x)));
}
void js_math_ceil(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, (int)(x + (double)0.5)));
mjs_return(mjs, mjs_mk_number(mjs, ceil(x)));
}
void js_math_clz32(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
unsigned int x = (unsigned int)mjs_get_int(mjs, mjs_arg(mjs, 0));
int count = 0;
while(x) {
x >>= 1;
count++;
}
mjs_return(mjs, mjs_mk_number(mjs, 32 - count));
}
void js_math_cos(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, cos(x)));
}
void js_math_exp(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
double result = 1;
double term = 1;
for(int i = 1; i < 100; i++) {
term *= x / i;
result += term;
}
mjs_return(mjs, mjs_mk_number(mjs, result));
mjs_return(mjs, mjs_mk_number(mjs, exp(x)));
}
void js_math_floor(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, (int)x));
mjs_return(mjs, mjs_mk_number(mjs, floor(x)));
}
void js_math_log(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
if(x <= 0) {
ret_bad_args(mjs, "Invalid input value for Math.log");
mjs_return(mjs, MJS_UNDEFINED);
ret_bad_args(mjs, "Invalid input value for math.log");
return;
}
double result = 0;
while(x >= JS_MATH_E) {
x /= JS_MATH_E;
result++;
}
mjs_return(mjs, mjs_mk_number(mjs, result + log(x)));
mjs_return(mjs, mjs_mk_number(mjs, log(x)));
}
void js_math_max(struct mjs* mjs) {
if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) ||
!mjs_is_number(mjs_arg(mjs, 1))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 2)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
double y = mjs_get_double(mjs, mjs_arg(mjs, 1));
mjs_return(mjs, mjs_mk_number(mjs, x > y ? x : y));
}
void js_math_min(struct mjs* mjs) {
if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) ||
!mjs_is_number(mjs_arg(mjs, 1))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 2)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
double y = mjs_get_double(mjs, mjs_arg(mjs, 1));
mjs_return(mjs, mjs_mk_number(mjs, x < y ? x : y));
}
void js_math_pow(struct mjs* mjs) {
if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) ||
!mjs_is_number(mjs_arg(mjs, 1))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 2)) {
return;
}
double base = mjs_get_double(mjs, mjs_arg(mjs, 0));
double exponent = mjs_get_double(mjs, mjs_arg(mjs, 1));
double result = 1;
for(int i = 0; i < exponent; i++) {
result *= base;
}
mjs_return(mjs, mjs_mk_number(mjs, result));
mjs_return(mjs, mjs_mk_number(mjs, pow(base, exponent)));
}
void js_math_random(struct mjs* mjs) {
if(!check_arg_count(mjs, 0)) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 0)) {
return;
}
// double clearly provides more bits for entropy then we pack
// 32bit should be enough for now, but fix it maybe
const uint32_t random_val = furi_hal_random_get();
double rnd = (double)random_val / FURI_HAL_RANDOM_MAX;
double rnd = (double)random_val / (double)FURI_HAL_RANDOM_MAX;
mjs_return(mjs, mjs_mk_number(mjs, rnd));
}
void js_math_sign(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, x == 0 ? 0 : (x < 0 ? -1 : 1)));
mjs_return(
mjs,
mjs_mk_number(
mjs, fabs(x) <= JS_MATH_EPSILON ? 0 : (x < (double)0. ? (double)-1.0 : (double)1.0)));
}
void js_math_sin(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
double result = x;
double term = x;
for(int i = 1; i < 10; i++) {
term *= -x * x / ((2 * i) * (2 * i + 1));
result += term;
}
mjs_return(mjs, mjs_mk_number(mjs, result));
mjs_return(mjs, mjs_mk_number(mjs, sin(x)));
}
void js_math_sqrt(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
if(x < 0) {
ret_bad_args(mjs, "Invalid input value for Math.sqrt");
mjs_return(mjs, MJS_UNDEFINED);
if(x < (double)0.) {
ret_bad_args(mjs, "Invalid input value for math.sqrt");
return;
}
double result = 1;
while(result * result < x) {
result += (double)0.001;
}
mjs_return(mjs, mjs_mk_number(mjs, result));
mjs_return(mjs, mjs_mk_number(mjs, sqrt(x)));
}
void js_math_trunc(struct mjs* mjs) {
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
mjs_return(mjs, MJS_UNDEFINED);
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, x < 0 ? ceil(x) : floor(x)));
mjs_return(mjs, mjs_mk_number(mjs, x < (double)0. ? ceil(x) : floor(x)));
}
static void* js_math_create(struct mjs* mjs, mjs_val_t* object) {
mjs_val_t math_obj = mjs_mk_object(mjs);
mjs_set(mjs, math_obj, "is_equal", ~0, MJS_MK_FN(js_math_is_equal));
mjs_set(mjs, math_obj, "abs", ~0, MJS_MK_FN(js_math_abs));
mjs_set(mjs, math_obj, "acos", ~0, MJS_MK_FN(js_math_acos));
mjs_set(mjs, math_obj, "acosh", ~0, MJS_MK_FN(js_math_acosh));
@ -288,6 +333,7 @@ static void* js_math_create(struct mjs* mjs, mjs_val_t* object) {
mjs_set(mjs, math_obj, "trunc", ~0, MJS_MK_FN(js_math_trunc));
mjs_set(mjs, math_obj, "PI", ~0, mjs_mk_number(mjs, JS_MATH_PI));
mjs_set(mjs, math_obj, "E", ~0, mjs_mk_number(mjs, JS_MATH_E));
mjs_set(mjs, math_obj, "EPSILON", ~0, mjs_mk_number(mjs, JS_MATH_EPSILON));
*object = math_obj;
return (void*)1;
}
@ -306,4 +352,4 @@ static const FlipperAppPluginDescriptor plugin_descriptor = {
const FlipperAppPluginDescriptor* js_math_ep(void) {
return &plugin_descriptor;
}
}

View file

@ -1,11 +1,13 @@
#include <gui/modules/submenu.h>
#include <gui/view_dispatcher.h>
#include <gui/view_holder.h>
#include <gui/view.h>
#include <toolbox/api_lock.h>
#include "../js_modules.h"
typedef struct {
Submenu* submenu;
ViewDispatcher* view_dispatcher;
ViewHolder* view_holder;
FuriApiLock lock;
uint32_t result;
bool accepted;
} JsSubmenuInst;
@ -35,15 +37,14 @@ static void submenu_callback(void* context, uint32_t id) {
JsSubmenuInst* submenu = context;
submenu->result = id;
submenu->accepted = true;
view_dispatcher_stop(submenu->view_dispatcher);
api_lock_unlock(submenu->lock);
}
static bool submenu_exit(void* context) {
static void submenu_exit(void* context) {
JsSubmenuInst* submenu = context;
submenu->result = 0;
submenu->accepted = false;
view_dispatcher_stop(submenu->view_dispatcher);
return true;
api_lock_unlock(submenu->lock);
}
static void js_submenu_add_item(struct mjs* mjs) {
@ -89,21 +90,20 @@ static void js_submenu_show(struct mjs* mjs) {
JsSubmenuInst* submenu = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
submenu->lock = api_lock_alloc_locked();
Gui* gui = furi_record_open(RECORD_GUI);
submenu->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(submenu->view_dispatcher);
view_dispatcher_add_view(submenu->view_dispatcher, 0, submenu_get_view(submenu->submenu));
view_dispatcher_set_event_callback_context(submenu->view_dispatcher, submenu);
view_dispatcher_set_navigation_event_callback(submenu->view_dispatcher, submenu_exit);
view_dispatcher_attach_to_gui(submenu->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
view_dispatcher_switch_to_view(submenu->view_dispatcher, 0);
submenu->view_holder = view_holder_alloc();
view_holder_attach_to_gui(submenu->view_holder, gui);
view_holder_set_back_callback(submenu->view_holder, submenu_exit, submenu);
view_dispatcher_run(submenu->view_dispatcher);
view_holder_set_view(submenu->view_holder, submenu_get_view(submenu->submenu));
view_holder_start(submenu->view_holder);
api_lock_wait_unlock(submenu->lock);
view_dispatcher_remove_view(submenu->view_dispatcher, 0);
view_dispatcher_free(submenu->view_dispatcher);
submenu->view_dispatcher = NULL;
view_holder_stop(submenu->view_holder);
view_holder_free(submenu->view_holder);
furi_record_close(RECORD_GUI);
api_lock_free(submenu->lock);
submenu_reset(submenu->submenu);
if(submenu->accepted) {

View file

@ -1,13 +1,12 @@
#include <gui/modules/text_box.h>
#include <gui/view_dispatcher.h>
#include <gui/view.h>
#include <gui/view_holder.h>
#include "../js_modules.h"
typedef struct {
TextBox* text_box;
ViewDispatcher* view_dispatcher;
FuriThread* thread;
ViewHolder* view_holder;
FuriString* text;
bool is_shown;
} JsTextboxInst;
static JsTextboxInst* get_this_ctx(struct mjs* mjs) {
@ -87,6 +86,9 @@ static void js_textbox_add_text(struct mjs* mjs) {
return;
}
// Avoid condition race between GUI and JS thread
text_box_set_text(textbox->text_box, "");
size_t new_len = furi_string_size(textbox->text) + text_len;
if(new_len >= 4096) {
furi_string_right(textbox->text, new_len / 2);
@ -99,10 +101,13 @@ static void js_textbox_add_text(struct mjs* mjs) {
mjs_return(mjs, MJS_UNDEFINED);
}
static void js_textbox_empty_text(struct mjs* mjs) {
static void js_textbox_clear_text(struct mjs* mjs) {
JsTextboxInst* textbox = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
// Avoid condition race between GUI and JS thread
text_box_set_text(textbox->text_box, "");
furi_string_reset(textbox->text);
text_box_set_text(textbox->text_box, furi_string_get_cstr(textbox->text));
@ -114,58 +119,34 @@ static void js_textbox_is_open(struct mjs* mjs) {
JsTextboxInst* textbox = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
mjs_return(mjs, mjs_mk_boolean(mjs, !!textbox->thread));
}
static void textbox_deinit(void* context) {
JsTextboxInst* textbox = context;
furi_thread_join(textbox->thread);
furi_thread_free(textbox->thread);
textbox->thread = NULL;
view_dispatcher_remove_view(textbox->view_dispatcher, 0);
view_dispatcher_free(textbox->view_dispatcher);
textbox->view_dispatcher = NULL;
furi_record_close(RECORD_GUI);
text_box_reset(textbox->text_box);
furi_string_reset(textbox->text);
mjs_return(mjs, mjs_mk_boolean(mjs, textbox->is_shown));
}
static void textbox_callback(void* context, uint32_t arg) {
UNUSED(arg);
textbox_deinit(context);
}
static bool textbox_exit(void* context) {
JsTextboxInst* textbox = context;
view_dispatcher_stop(textbox->view_dispatcher);
furi_timer_pending_callback(textbox_callback, textbox, 0);
return true;
view_holder_stop(textbox->view_holder);
textbox->is_shown = false;
}
static int32_t textbox_thread(void* context) {
ViewDispatcher* view_dispatcher = context;
view_dispatcher_run(view_dispatcher);
return 0;
static void textbox_exit(void* context) {
JsTextboxInst* textbox = context;
// Using timer to schedule view_holder stop, will not work under high CPU load
furi_timer_pending_callback(textbox_callback, textbox, 0);
}
static void js_textbox_show(struct mjs* mjs) {
JsTextboxInst* textbox = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
Gui* gui = furi_record_open(RECORD_GUI);
textbox->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(textbox->view_dispatcher);
view_dispatcher_add_view(textbox->view_dispatcher, 0, text_box_get_view(textbox->text_box));
view_dispatcher_set_event_callback_context(textbox->view_dispatcher, textbox);
view_dispatcher_set_navigation_event_callback(textbox->view_dispatcher, textbox_exit);
view_dispatcher_attach_to_gui(textbox->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
view_dispatcher_switch_to_view(textbox->view_dispatcher, 0);
if(textbox->is_shown) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Textbox is already shown");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
textbox->thread =
furi_thread_alloc_ex("JsTextbox", 1024, textbox_thread, textbox->view_dispatcher);
furi_thread_start(textbox->thread);
view_holder_start(textbox->view_holder);
textbox->is_shown = true;
mjs_return(mjs, MJS_UNDEFINED);
}
@ -174,36 +155,49 @@ static void js_textbox_close(struct mjs* mjs) {
JsTextboxInst* textbox = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
if(textbox->thread) {
view_dispatcher_stop(textbox->view_dispatcher);
textbox_deinit(textbox);
}
view_holder_stop(textbox->view_holder);
textbox->is_shown = false;
mjs_return(mjs, MJS_UNDEFINED);
}
static void* js_textbox_create(struct mjs* mjs, mjs_val_t* object) {
JsTextboxInst* textbox = malloc(sizeof(JsTextboxInst));
mjs_val_t textbox_obj = mjs_mk_object(mjs);
mjs_set(mjs, textbox_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, textbox));
mjs_set(mjs, textbox_obj, "setConfig", ~0, MJS_MK_FN(js_textbox_set_config));
mjs_set(mjs, textbox_obj, "addText", ~0, MJS_MK_FN(js_textbox_add_text));
mjs_set(mjs, textbox_obj, "emptyText", ~0, MJS_MK_FN(js_textbox_empty_text));
mjs_set(mjs, textbox_obj, "clearText", ~0, MJS_MK_FN(js_textbox_clear_text));
mjs_set(mjs, textbox_obj, "isOpen", ~0, MJS_MK_FN(js_textbox_is_open));
mjs_set(mjs, textbox_obj, "show", ~0, MJS_MK_FN(js_textbox_show));
mjs_set(mjs, textbox_obj, "close", ~0, MJS_MK_FN(js_textbox_close));
textbox->text_box = text_box_alloc();
textbox->text = furi_string_alloc();
textbox->text_box = text_box_alloc();
Gui* gui = furi_record_open(RECORD_GUI);
textbox->view_holder = view_holder_alloc();
view_holder_attach_to_gui(textbox->view_holder, gui);
view_holder_set_back_callback(textbox->view_holder, textbox_exit, textbox);
view_holder_set_view(textbox->view_holder, text_box_get_view(textbox->text_box));
*object = textbox_obj;
return textbox;
}
static void js_textbox_destroy(void* inst) {
JsTextboxInst* textbox = inst;
if(textbox->thread) {
view_dispatcher_stop(textbox->view_dispatcher);
textbox_deinit(textbox);
}
view_holder_stop(textbox->view_holder);
view_holder_free(textbox->view_holder);
textbox->view_holder = NULL;
furi_record_close(RECORD_GUI);
text_box_reset(textbox->text_box);
furi_string_reset(textbox->text);
text_box_free(textbox->text_box);
furi_string_free(textbox->text);
free(textbox);
@ -223,4 +217,4 @@ static const FlipperAppPluginDescriptor textbox_plugin_descriptor = {
const FlipperAppPluginDescriptor* js_textbox_ep(void) {
return &textbox_plugin_descriptor;
}
}