mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2025-10-10 23:22:35 +02:00
* JS: Fix file select for fbt launch js_app * JS: badusb: Add numpad keys Co-authored-by: oldip <oldip@users.noreply.github.com> * JS: badusb: Layout support * JS: badusb: altPrint() and altPrintln() Co-authored-by: oldip <oldip@users.noreply.github.com> * JS: badusb: quit() * JS: serial: readAny() * JS: serial: end() * JS: serial: Auto disable expansion service * JS: storage: Add example script * JS: gui: text_input: Fix NULL ptr when no prop given * JS: gui: text_input: Default text props Co-authored-by: xMasterX <xMasterX@users.noreply.github.com> * JS: gui: byte_input Co-authored-by: xMasterX <xMasterX@users.noreply.github.com> * JS: gui: file_picker * JS: gui: viewDispatcher.currentView * JS: gui: view.hasProperty() * JS: gui: Add some missing typedefs comments * JS: globals: Fix toString() with negative numbers * JS: globals: parseInt() Co-authored-by: Spooks4576 <Spooks4576@users.noreply.github.com> * JS: globals: toUpperCase() and toLowerCase() Co-authored-by: Spooks4576 <Spooks4576@users.noreply.github.com> * JS: globals: Add some missing typedefs * JS: Add example for string functions Co-authored-by: Spooks4576 <Spooks4576@users.noreply.github.com> * JS: globals: __dirpath and __filepath Co-authored-by: jamisonderek <jamisonderek@users.noreply.github.com> * JS: globals: load() typedef missing scope param * JS: Add interactive REPL script example * JS: Add missing icon for file picker * JS: Rename to __filename and __dirname * JS: Move toUpperCase() and toLowerCase() to string class * JS: parseInt() refactor * JS: Typedef base param for parseInt() * Revert "JS: gui: view.hasProperty()" This reverts commit 1967ec06d4f2e9cafc4e18384ad370f7a7c44468. * JS: Move toString() to Number class * JS: Fix duplicate plugin files in plugins, `requires` is used to determine which app to distribute the .fal under `apps_data/appid/plugins` * JS: math: Missing typedefs, use camelCase * JS: badusb: layoutPath is optional in typedef * Fix ASS scoping * Rename mjs term prop type value * Change load() description * Enlarge buffers in default prop assign * More checks for default data/text size * Make PVS happy * Fix icon symbol * Update types for JS SDK * toString() was moved to number class Co-authored-by: oldip <oldip@users.noreply.github.com> Co-authored-by: xMasterX <xMasterX@users.noreply.github.com> Co-authored-by: Spooks4576 <Spooks4576@users.noreply.github.com> Co-authored-by: jamisonderek <jamisonderek@users.noreply.github.com> Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: あく <alleteam@gmail.com>
189 lines
6.0 KiB
C
189 lines
6.0 KiB
C
#include "../../js_modules.h" // IWYU pragma: keep
|
|
#include "js_gui.h"
|
|
#include "../js_event_loop/js_event_loop.h"
|
|
#include <gui/modules/text_input.h>
|
|
|
|
#define DEFAULT_BUF_SZ 33
|
|
|
|
typedef struct {
|
|
char* buffer;
|
|
size_t buffer_size;
|
|
size_t default_text_size;
|
|
FuriString* header;
|
|
bool default_text_clear;
|
|
FuriSemaphore* input_semaphore;
|
|
JsEventLoopContract contract;
|
|
} JsKbdContext;
|
|
|
|
static mjs_val_t
|
|
input_transformer(struct mjs* mjs, FuriSemaphore* semaphore, JsKbdContext* context) {
|
|
furi_check(furi_semaphore_acquire(semaphore, 0) == FuriStatusOk);
|
|
return mjs_mk_string(mjs, context->buffer, ~0, true);
|
|
}
|
|
|
|
static void input_callback(JsKbdContext* context) {
|
|
furi_semaphore_release(context->input_semaphore);
|
|
}
|
|
|
|
static bool
|
|
header_assign(struct mjs* mjs, TextInput* input, JsViewPropValue value, JsKbdContext* context) {
|
|
UNUSED(mjs);
|
|
furi_string_set(context->header, value.string);
|
|
text_input_set_header_text(input, furi_string_get_cstr(context->header));
|
|
return true;
|
|
}
|
|
|
|
static bool min_len_assign(
|
|
struct mjs* mjs,
|
|
TextInput* input,
|
|
JsViewPropValue value,
|
|
JsKbdContext* context) {
|
|
UNUSED(mjs);
|
|
UNUSED(context);
|
|
text_input_set_minimum_length(input, (size_t)value.number);
|
|
return true;
|
|
}
|
|
|
|
static bool max_len_assign(
|
|
struct mjs* mjs,
|
|
TextInput* input,
|
|
JsViewPropValue value,
|
|
JsKbdContext* context) {
|
|
UNUSED(mjs);
|
|
size_t new_buffer_size = value.number + 1;
|
|
if(new_buffer_size < context->default_text_size) {
|
|
// Avoid confusing parameters from user
|
|
mjs_prepend_errorf(
|
|
mjs, MJS_BAD_ARGS_ERROR, "maxLength must be larger than defaultText length");
|
|
return false;
|
|
}
|
|
context->buffer_size = new_buffer_size;
|
|
context->buffer = realloc(context->buffer, context->buffer_size); //-V701
|
|
text_input_set_result_callback(
|
|
input,
|
|
(TextInputCallback)input_callback,
|
|
context,
|
|
context->buffer,
|
|
context->buffer_size,
|
|
context->default_text_clear);
|
|
return true;
|
|
}
|
|
|
|
static bool default_text_assign(
|
|
struct mjs* mjs,
|
|
TextInput* input,
|
|
JsViewPropValue value,
|
|
JsKbdContext* context) {
|
|
UNUSED(mjs);
|
|
UNUSED(input);
|
|
|
|
if(value.string) {
|
|
context->default_text_size = strlen(value.string) + 1;
|
|
if(context->buffer_size < context->default_text_size) {
|
|
// Ensure buffer is large enough for defaultData
|
|
context->buffer_size = context->default_text_size;
|
|
context->buffer = realloc(context->buffer, context->buffer_size); //-V701
|
|
}
|
|
// Also trim excess previous data with strlcpy()
|
|
strlcpy(context->buffer, value.string, context->buffer_size); //-V575
|
|
text_input_set_result_callback(
|
|
input,
|
|
(TextInputCallback)input_callback,
|
|
context,
|
|
context->buffer,
|
|
context->buffer_size,
|
|
context->default_text_clear);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool default_text_clear_assign(
|
|
struct mjs* mjs,
|
|
TextInput* input,
|
|
JsViewPropValue value,
|
|
JsKbdContext* context) {
|
|
UNUSED(mjs);
|
|
|
|
context->default_text_clear = value.boolean;
|
|
text_input_set_result_callback(
|
|
input,
|
|
(TextInputCallback)input_callback,
|
|
context,
|
|
context->buffer,
|
|
context->buffer_size,
|
|
context->default_text_clear);
|
|
return true;
|
|
}
|
|
|
|
static JsKbdContext* ctx_make(struct mjs* mjs, TextInput* input, mjs_val_t view_obj) {
|
|
JsKbdContext* context = malloc(sizeof(JsKbdContext));
|
|
*context = (JsKbdContext){
|
|
.buffer_size = DEFAULT_BUF_SZ,
|
|
.buffer = malloc(DEFAULT_BUF_SZ),
|
|
.header = furi_string_alloc(),
|
|
.default_text_clear = false,
|
|
.input_semaphore = furi_semaphore_alloc(1, 0),
|
|
};
|
|
context->contract = (JsEventLoopContract){
|
|
.magic = JsForeignMagic_JsEventLoopContract,
|
|
.object_type = JsEventLoopObjectTypeSemaphore,
|
|
.object = context->input_semaphore,
|
|
.non_timer =
|
|
{
|
|
.event = FuriEventLoopEventIn,
|
|
.transformer = (JsEventLoopTransformer)input_transformer,
|
|
.transformer_context = context,
|
|
},
|
|
};
|
|
text_input_set_result_callback(
|
|
input,
|
|
(TextInputCallback)input_callback,
|
|
context,
|
|
context->buffer,
|
|
context->buffer_size,
|
|
context->default_text_clear);
|
|
mjs_set(mjs, view_obj, "input", ~0, mjs_mk_foreign(mjs, &context->contract));
|
|
return context;
|
|
}
|
|
|
|
static void ctx_destroy(TextInput* input, JsKbdContext* context, FuriEventLoop* loop) {
|
|
UNUSED(input);
|
|
furi_event_loop_maybe_unsubscribe(loop, context->input_semaphore);
|
|
furi_semaphore_free(context->input_semaphore);
|
|
furi_string_free(context->header);
|
|
free(context->buffer);
|
|
free(context);
|
|
}
|
|
|
|
static const JsViewDescriptor view_descriptor = {
|
|
.alloc = (JsViewAlloc)text_input_alloc,
|
|
.free = (JsViewFree)text_input_free,
|
|
.get_view = (JsViewGetView)text_input_get_view,
|
|
.custom_make = (JsViewCustomMake)ctx_make,
|
|
.custom_destroy = (JsViewCustomDestroy)ctx_destroy,
|
|
.prop_cnt = 5,
|
|
.props = {
|
|
(JsViewPropDescriptor){
|
|
.name = "header",
|
|
.type = JsViewPropTypeString,
|
|
.assign = (JsViewPropAssign)header_assign},
|
|
(JsViewPropDescriptor){
|
|
.name = "minLength",
|
|
.type = JsViewPropTypeNumber,
|
|
.assign = (JsViewPropAssign)min_len_assign},
|
|
(JsViewPropDescriptor){
|
|
.name = "maxLength",
|
|
.type = JsViewPropTypeNumber,
|
|
.assign = (JsViewPropAssign)max_len_assign},
|
|
(JsViewPropDescriptor){
|
|
.name = "defaultText",
|
|
.type = JsViewPropTypeString,
|
|
.assign = (JsViewPropAssign)default_text_assign},
|
|
(JsViewPropDescriptor){
|
|
.name = "defaultTextClear",
|
|
.type = JsViewPropTypeBool,
|
|
.assign = (JsViewPropAssign)default_text_clear_assign},
|
|
}};
|
|
|
|
JS_GUI_VIEW_DEF(text_input, &view_descriptor);
|