js.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. // main.c
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <assert.h>
  5. #include <furi.h>
  6. #include <gui/gui.h>
  7. #include <gui/view_dispatcher.h>
  8. #include <gui/view.h>
  9. #include <gui/modules/text_box.h>
  10. #include <gui/modules/dialog_ex.h>
  11. #include <storage/storage.h>
  12. #include "microvium.h"
  13. #define JS_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX
  14. #define TAG "microvium"
  15. static int32_t js_run(void* context);
  16. // A function in the host (this file) for the VM to call
  17. #define IMPORT_FLIPPER_FURI_DELAY_MS 1
  18. #define IMPORT_FLIPPER_CANVAS_STOP 2
  19. #define IMPORT_FLIPPER_CANVAS_SET_FONT 3
  20. #define IMPORT_FLIPPER_CANVAS_DRAW_STR 4
  21. #define IMPORT_FLIPPER_CANVAS_DRAW_STR_ALIGNED 5
  22. #define IMPORT_CONSOLE_CLEAR 6
  23. #define IMPORT_CONSOLE_LOG 7
  24. #define IMPORT_CONSOLE_WARN 8
  25. // A function exported by VM to for the host to call
  26. const mvm_VMExportID MAIN = 1;
  27. /* Use when needed
  28. const mvm_VMExportID INIT = 2;
  29. */
  30. mvm_TeError resolveImport(mvm_HostFunctionID id, void*, mvm_TfHostFunction* out);
  31. mvm_TeError flipper_furi_delay_ms(
  32. mvm_VM* vm,
  33. mvm_HostFunctionID funcID,
  34. mvm_Value* result,
  35. mvm_Value* args,
  36. uint8_t argCount);
  37. mvm_TeError flipper_canvas_stop(
  38. mvm_VM* vm,
  39. mvm_HostFunctionID funcID,
  40. mvm_Value* result,
  41. mvm_Value* args,
  42. uint8_t argCount);
  43. mvm_TeError flipper_canvas_set_font(
  44. mvm_VM* vm,
  45. mvm_HostFunctionID funcID,
  46. mvm_Value* result,
  47. mvm_Value* args,
  48. uint8_t argCount);
  49. mvm_TeError flipper_canvas_draw_str(
  50. mvm_VM* vm,
  51. mvm_HostFunctionID funcID,
  52. mvm_Value* result,
  53. mvm_Value* args,
  54. uint8_t argCount);
  55. mvm_TeError flipper_canvas_draw_str_aligned(
  56. mvm_VM* vm,
  57. mvm_HostFunctionID funcID,
  58. mvm_Value* result,
  59. mvm_Value* args,
  60. uint8_t argCount);
  61. mvm_TeError console_clear(
  62. mvm_VM* vm,
  63. mvm_HostFunctionID funcID,
  64. mvm_Value* result,
  65. mvm_Value* args,
  66. uint8_t argCount);
  67. mvm_TeError console_log(
  68. mvm_VM* vm,
  69. mvm_HostFunctionID funcID,
  70. mvm_Value* result,
  71. mvm_Value* args,
  72. uint8_t argCount);
  73. mvm_TeError console_warn(
  74. mvm_VM* vm,
  75. mvm_HostFunctionID funcID,
  76. mvm_Value* result,
  77. mvm_Value* args,
  78. uint8_t argCount);
  79. typedef enum {
  80. MyEventTypeKey,
  81. MyEventTypeDone,
  82. } MyEventType;
  83. typedef struct {
  84. MyEventType type; // The reason for this event.
  85. InputEvent input; // This data is specific to keypress data.
  86. } MyEvent;
  87. typedef enum {
  88. JSDisplay,
  89. JSConsole,
  90. // JSConfirm,
  91. } ViewId;
  92. FuriMessageQueue* queue;
  93. ViewId current_view;
  94. ViewDispatcher* view_dispatcher;
  95. TextBox* text_box;
  96. /*
  97. bool confirmGot = false;
  98. bool confirmRes = false;
  99. */
  100. typedef struct {
  101. FuriThread* thread;
  102. } JSRtThread;
  103. typedef struct {
  104. FuriString* conLog;
  105. } Console;
  106. Console* console;
  107. typedef enum {
  108. CNone,
  109. CDrawStr,
  110. CDrawStrAli,
  111. } CDrawEvent;
  112. typedef struct {
  113. CDrawEvent cEvent;
  114. Font font;
  115. const char* str;
  116. int x;
  117. int y;
  118. Align horizontal;
  119. Align vertical;
  120. } Display;
  121. Display* display;
  122. size_t fileSize;
  123. uint8_t* fileBuff;
  124. static void draw_callback(Canvas* canvas, void* context) {
  125. UNUSED(context);
  126. canvas_set_font(canvas, display->font);
  127. if(display->cEvent == CDrawStr) {
  128. canvas_draw_str(canvas, display->x, display->y, display->str);
  129. } else if(display->cEvent == CDrawStrAli) {
  130. canvas_draw_str_aligned(
  131. canvas, display->x, display->y, display->horizontal, display->vertical, display->str);
  132. }
  133. }
  134. static bool input_callback(InputEvent* input_event, void* context) {
  135. UNUSED(context);
  136. bool handled = false;
  137. // we set our callback context to be the view_dispatcher.
  138. if(input_event->type == InputTypeShort) {
  139. if(input_event->key == InputKeyBack) {
  140. // Default back handler.
  141. handled = false;
  142. } else if(input_event->key == InputKeyOk) {
  143. // switch the view!
  144. view_dispatcher_send_custom_event(view_dispatcher, 42);
  145. handled = true;
  146. }
  147. }
  148. return handled;
  149. }
  150. bool navigation_event_callback(void* context) {
  151. UNUSED(context);
  152. // We did not handle the event, so return false.
  153. return false;
  154. }
  155. bool custom_event_callback(void* context, uint32_t event) {
  156. UNUSED(context);
  157. bool handled = false;
  158. if(event == 42) {
  159. if(current_view == JSDisplay) {
  160. current_view = JSConsole;
  161. }
  162. view_dispatcher_switch_to_view(view_dispatcher, current_view);
  163. handled = true;
  164. }
  165. // NOTE: The return value is not currently used by the ViewDispatcher.
  166. return handled;
  167. }
  168. static uint32_t exit_console_callback(void* context) {
  169. UNUSED(context);
  170. return JSDisplay;
  171. }
  172. static int32_t js_run(void* context) {
  173. UNUSED(context);
  174. mvm_TeError err;
  175. mvm_VM* vm;
  176. mvm_Value main;
  177. mvm_Value result;
  178. // Restore the VM from the snapshot
  179. err = mvm_restore(&vm, fileBuff, fileSize, NULL, resolveImport);
  180. if(err != MVM_E_SUCCESS) {
  181. FURI_LOG_E(TAG, "Error with restore: %d", err);
  182. return err;
  183. }
  184. // Find the "sayHello" function exported by the VM
  185. err = mvm_resolveExports(vm, &MAIN, &main, 1);
  186. if(err != MVM_E_SUCCESS) {
  187. FURI_LOG_E(TAG, "Error with exports: %d", err);
  188. return err;
  189. }
  190. // Call "main"
  191. err = mvm_call(vm, main, &result, NULL, 0);
  192. if(err != MVM_E_SUCCESS) {
  193. FURI_LOG_E(TAG, "Error with call: %d", err);
  194. return err;
  195. }
  196. // Clean up
  197. mvm_runGC(vm, true);
  198. return 0;
  199. }
  200. int32_t js_app() {
  201. JSRtThread* jsThread = malloc(sizeof(JSRtThread));
  202. Storage* storage = furi_record_open(RECORD_STORAGE);
  203. File* bytecode = storage_file_alloc(storage);
  204. storage_file_open(bytecode, APP_DATA_PATH("script.mvm-bc"), FSAM_READ, FSOM_OPEN_EXISTING);
  205. fileSize = storage_file_size(bytecode);
  206. FURI_LOG_D("microvium", "File Size: %d", fileSize);
  207. fileBuff = malloc(fileSize);
  208. storage_file_read(bytecode, fileBuff, fileSize);
  209. storage_file_close(bytecode);
  210. storage_file_free(bytecode);
  211. furi_record_close(RECORD_STORAGE);
  212. jsThread->thread = furi_thread_alloc_ex("microium", 1024, js_run, jsThread);
  213. view_dispatcher = view_dispatcher_alloc();
  214. // For this demo, we just use view_dispatcher as our application context.
  215. void* context = view_dispatcher;
  216. View* view1 = view_alloc();
  217. view_set_context(view1, context);
  218. view_set_draw_callback(view1, draw_callback);
  219. view_set_input_callback(view1, input_callback);
  220. view_set_orientation(view1, ViewOrientationHorizontal);
  221. text_box = text_box_alloc();
  222. text_box_set_font(text_box, TextBoxFontText);
  223. view_set_previous_callback(text_box_get_view(text_box), exit_console_callback);
  224. /*
  225. DialogEx* dialog_ex = dialog_ex_alloc();
  226. dialog_ex_set_left_button_text(dialog_ex, "No");
  227. dialog_ex_set_right_button_text(dialog_ex, "Yes");
  228. dialog_ex_set_context(dialog_ex, context);
  229. dialog_ex_set_result_callback(dialog_ex, confirm_callback);
  230. */
  231. // set param 1 of custom event callback (impacts tick and navigation too).
  232. view_dispatcher_set_event_callback_context(view_dispatcher, context);
  233. view_dispatcher_set_navigation_event_callback(view_dispatcher, navigation_event_callback);
  234. view_dispatcher_set_custom_event_callback(view_dispatcher, custom_event_callback);
  235. view_dispatcher_enable_queue(view_dispatcher);
  236. view_dispatcher_add_view(view_dispatcher, JSDisplay, view1);
  237. view_dispatcher_add_view(view_dispatcher, JSConsole, text_box_get_view(text_box));
  238. //view_dispatcher_add_view(view_dispatcher, JSConfirm, dialog_ex_get_view(dialog_ex));
  239. Gui* gui = furi_record_open(RECORD_GUI);
  240. view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
  241. current_view = JSDisplay;
  242. view_dispatcher_switch_to_view(view_dispatcher, current_view);
  243. // console init
  244. console = malloc(sizeof(Console));
  245. console->conLog = furi_string_alloc();
  246. // display init and set defaults
  247. display = malloc(sizeof(Display));
  248. display->cEvent = CNone;
  249. display->font = FontSecondary;
  250. furi_thread_start(jsThread->thread);
  251. view_dispatcher_run(view_dispatcher);
  252. furi_thread_join(jsThread->thread);
  253. furi_thread_free(jsThread->thread);
  254. free(jsThread);
  255. furi_string_free(console->conLog);
  256. free(console);
  257. free(display);
  258. view_dispatcher_remove_view(view_dispatcher, JSDisplay);
  259. view_dispatcher_remove_view(view_dispatcher, JSConsole);
  260. furi_record_close(RECORD_GUI);
  261. view_dispatcher_free(view_dispatcher);
  262. return 0;
  263. }
  264. void fatalError(void* vm, int e) {
  265. UNUSED(vm);
  266. FURI_LOG_E(TAG, "Error: %d\n", e);
  267. furi_crash("Microvium fatal error");
  268. }
  269. /*
  270. * This function is called by `mvm_restore` to search for host functions
  271. * imported by the VM based on their ID. Given an ID, it needs to pass back
  272. * a pointer to the corresponding C function to be used by the VM.
  273. */
  274. mvm_TeError resolveImport(mvm_HostFunctionID funcID, void* context, mvm_TfHostFunction* out) {
  275. UNUSED(context);
  276. if(funcID == IMPORT_FLIPPER_FURI_DELAY_MS) {
  277. *out = flipper_furi_delay_ms;
  278. } else if(funcID == IMPORT_FLIPPER_CANVAS_SET_FONT) {
  279. *out = flipper_canvas_set_font;
  280. return MVM_E_SUCCESS;
  281. } else if(funcID == IMPORT_FLIPPER_CANVAS_DRAW_STR) {
  282. *out = flipper_canvas_draw_str;
  283. return MVM_E_SUCCESS;
  284. } else if(funcID == IMPORT_FLIPPER_CANVAS_DRAW_STR_ALIGNED) {
  285. *out = flipper_canvas_draw_str_aligned;
  286. } else if(funcID == IMPORT_CONSOLE_LOG) {
  287. *out = console_log;
  288. return MVM_E_SUCCESS;
  289. } else if(funcID == IMPORT_CONSOLE_CLEAR) {
  290. *out = console_clear;
  291. return MVM_E_SUCCESS;
  292. } else if(funcID == IMPORT_CONSOLE_WARN) {
  293. *out = console_warn;
  294. return MVM_E_SUCCESS;
  295. }
  296. return MVM_E_UNRESOLVED_IMPORT;
  297. }
  298. mvm_TeError flipper_furi_delay_ms(
  299. mvm_VM* vm,
  300. mvm_HostFunctionID funcID,
  301. mvm_Value* result,
  302. mvm_Value* args,
  303. uint8_t argCount) {
  304. UNUSED(funcID);
  305. UNUSED(result);
  306. furi_assert(argCount == 1);
  307. FURI_LOG_D(TAG, "delay_ms()");
  308. furi_delay_ms((int32_t)mvm_toInt32(vm, args[0]));
  309. return MVM_E_SUCCESS;
  310. }
  311. mvm_TeError flipper_canvas_stop(
  312. mvm_VM* vm,
  313. mvm_HostFunctionID funcID,
  314. mvm_Value* result,
  315. mvm_Value* args,
  316. uint8_t argCount) {
  317. UNUSED(vm);
  318. UNUSED(funcID);
  319. UNUSED(result);
  320. UNUSED(args);
  321. furi_assert(argCount == 0);
  322. FURI_LOG_D(TAG, "canvas_stop()");
  323. display->cEvent = CNone;
  324. return MVM_E_SUCCESS;
  325. }
  326. mvm_TeError flipper_canvas_set_font(
  327. mvm_VM* vm,
  328. mvm_HostFunctionID funcID,
  329. mvm_Value* result,
  330. mvm_Value* args,
  331. uint8_t argCount) {
  332. UNUSED(funcID);
  333. UNUSED(result);
  334. furi_assert(argCount == 1);
  335. FURI_LOG_D(TAG, "canvas_set_font()");
  336. // display->cEvent = CSetFont;
  337. display->font = mvm_toInt32(vm, args[0]);
  338. // display->cEvent = CNone;
  339. return MVM_E_SUCCESS;
  340. }
  341. mvm_TeError flipper_canvas_draw_str(
  342. mvm_VM* vm,
  343. mvm_HostFunctionID funcID,
  344. mvm_Value* result,
  345. mvm_Value* args,
  346. uint8_t argCount) {
  347. UNUSED(funcID);
  348. UNUSED(result);
  349. furi_assert(argCount == 3);
  350. FURI_LOG_D(TAG, "canvas_draw_str()");
  351. display->x = (int32_t)mvm_toInt32(vm, args[0]);
  352. display->y = (int32_t)mvm_toInt32(vm, args[1]);
  353. display->str = (const char*)mvm_toStringUtf8(vm, args[2], NULL);
  354. display->cEvent = CDrawStr;
  355. return MVM_E_SUCCESS;
  356. }
  357. mvm_TeError flipper_canvas_draw_str_aligned(
  358. mvm_VM* vm,
  359. mvm_HostFunctionID funcID,
  360. mvm_Value* result,
  361. mvm_Value* args,
  362. uint8_t argCount) {
  363. UNUSED(funcID);
  364. UNUSED(result);
  365. furi_assert(argCount == 5);
  366. FURI_LOG_D(TAG, "canvas_draw_str_aligned()");
  367. display->x = (int32_t)mvm_toInt32(vm, args[0]);
  368. display->y = (int32_t)mvm_toInt32(vm, args[1]);
  369. display->horizontal = (int32_t)mvm_toInt32(vm, args[2]);
  370. display->vertical = (int32_t)mvm_toInt32(vm, args[3]);
  371. display->str = (const char*)mvm_toStringUtf8(vm, args[4], NULL);
  372. display->cEvent = CDrawStrAli;
  373. return MVM_E_SUCCESS;
  374. }
  375. mvm_TeError console_clear(
  376. mvm_VM* vm,
  377. mvm_HostFunctionID funcID,
  378. mvm_Value* result,
  379. mvm_Value* args,
  380. uint8_t argCount) {
  381. UNUSED(vm);
  382. UNUSED(funcID);
  383. UNUSED(result);
  384. UNUSED(args);
  385. furi_assert(argCount == 0);
  386. FURI_LOG_D(TAG, "console.clear()");
  387. furi_string_reset(console->conLog);
  388. text_box_set_text(text_box, furi_string_get_cstr(console->conLog));
  389. return MVM_E_SUCCESS;
  390. }
  391. mvm_TeError console_log(
  392. mvm_VM* vm,
  393. mvm_HostFunctionID funcID,
  394. mvm_Value* result,
  395. mvm_Value* args,
  396. uint8_t argCount) {
  397. UNUSED(funcID);
  398. UNUSED(result);
  399. FURI_LOG_D(TAG, "console.log()");
  400. for(int i = 0; i < argCount - 1; i++) {
  401. if(mvm_typeOf(vm, args[i]) == VM_T_NUMBER) {
  402. furi_string_cat_printf(console->conLog, "%d", (int)mvm_toInt32(vm, args[i]));
  403. } else if(mvm_typeOf(vm, args[i]) == VM_T_STRING) {
  404. furi_string_cat_printf(
  405. console->conLog, "%s", (const char*)mvm_toStringUtf8(vm, args[i], NULL));
  406. }
  407. }
  408. furi_string_cat_printf(console->conLog, "\n");
  409. text_box_set_text(text_box, furi_string_get_cstr(console->conLog));
  410. return MVM_E_SUCCESS;
  411. }
  412. mvm_TeError console_warn(
  413. mvm_VM* vm,
  414. mvm_HostFunctionID funcID,
  415. mvm_Value* result,
  416. mvm_Value* args,
  417. uint8_t argCount) {
  418. UNUSED(funcID);
  419. UNUSED(result);
  420. furi_assert(argCount == 1);
  421. FURI_LOG_D(TAG, "console.warn()");
  422. for(int i = 0; i < argCount - 1; i++) {
  423. if(mvm_typeOf(vm, args[i]) == VM_T_NUMBER) {
  424. FURI_LOG_W(TAG, "%d", (int)mvm_toInt32(vm, args[i]));
  425. } else if(mvm_typeOf(vm, args[i]) == VM_T_STRING) {
  426. FURI_LOG_W(TAG, "%s", (const char*)mvm_toStringUtf8(vm, args[i], NULL));
  427. }
  428. }
  429. FURI_LOG_W(TAG, "\n");
  430. return MVM_E_SUCCESS;
  431. }