bf_dev_env.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. #include "bf_dev_env.h"
  2. #include <gui/elements.h>
  3. typedef struct BFDevEnv {
  4. View* view;
  5. DevEnvOkCallback callback;
  6. void* context;
  7. BFApp* appDev;
  8. } BFDevEnv;
  9. typedef struct {
  10. uint32_t row;
  11. uint32_t col;
  12. } BFDevEnvModel;
  13. typedef struct {
  14. int up;
  15. int down;
  16. int left;
  17. int right;
  18. } bMapping;
  19. #ifdef FW_ORIGIN_Official
  20. #define FONT_NAME FontSecondary
  21. #else
  22. #define FONT_NAME FontBatteryPercent
  23. #endif
  24. static bool bf_dev_process_up(BFDevEnv* devEnv);
  25. static bool bf_dev_process_down(BFDevEnv* devEnv);
  26. static bool bf_dev_process_left(BFDevEnv* devEnv);
  27. static bool bf_dev_process_right(BFDevEnv* devEnv);
  28. static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event);
  29. BFApp* appDev;
  30. FuriThread* workerThread;
  31. char bfChars[9] = {'<', '>', '[', ']', '+', '-', '.', ',', 0x00};
  32. int selectedButton = 0;
  33. int saveNotifyCountdown = 0;
  34. int execCountdown = 0;
  35. char dspLine0[25] = {0x00};
  36. char dspLine1[25] = {0x00};
  37. char dspLine2[25] = {0x00};
  38. static bMapping buttonMappings[12] = {
  39. {8, 8, 7, 1}, //0
  40. {8, 8, 0, 2}, //1
  41. {9, 9, 1, 3}, //2
  42. {9, 9, 2, 4}, //3
  43. {10, 10, 3, 5}, //4
  44. {10, 10, 4, 6}, //5
  45. {11, 11, 5, 7}, //6
  46. {11, 11, 6, 0}, //7
  47. {0, 0, 11, 9}, //8
  48. {3, 3, 8, 10}, //9
  49. {5, 5, 9, 11}, //10
  50. {6, 6, 10, 8} //11
  51. };
  52. #define BT_X 14
  53. #define BT_Y 14
  54. static void bf_dev_draw_button(Canvas* canvas, int x, int y, bool selected, const char* lbl) {
  55. UNUSED(lbl);
  56. if(selected) {
  57. canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3);
  58. canvas_invert_color(canvas);
  59. canvas_set_font(canvas, FONT_NAME);
  60. canvas_draw_str_aligned(
  61. canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl);
  62. canvas_invert_color(canvas);
  63. } else {
  64. canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3);
  65. canvas_invert_color(canvas);
  66. canvas_draw_rbox(canvas, x + 2, y - 1, BT_X - 2, BT_Y - 1, 3);
  67. canvas_invert_color(canvas);
  68. canvas_draw_rframe(canvas, x, y, BT_X, BT_Y, 3);
  69. canvas_set_font(canvas, FONT_NAME);
  70. canvas_draw_str_aligned(
  71. canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl);
  72. }
  73. }
  74. void bf_save_changes() {
  75. //remove old file
  76. Storage* storage = furi_record_open(RECORD_STORAGE);
  77. storage_simply_remove(storage, furi_string_get_cstr(appDev->BF_file_path));
  78. //save new file
  79. Stream* stream = buffered_file_stream_alloc(storage);
  80. buffered_file_stream_open(
  81. stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS);
  82. stream_write(stream, (const uint8_t*)appDev->dataBuffer, appDev->dataSize);
  83. buffered_file_stream_close(stream);
  84. }
  85. static void bf_dev_draw_callback(Canvas* canvas, void* _model) {
  86. UNUSED(_model);
  87. if(saveNotifyCountdown > 0) {
  88. canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "SAVED");
  89. saveNotifyCountdown--;
  90. return;
  91. }
  92. bf_dev_draw_button(canvas, 1, 36, (selectedButton == 0), "+"); //T 0
  93. bf_dev_draw_button(canvas, 17, 36, (selectedButton == 1), "-"); //T 1
  94. bf_dev_draw_button(canvas, 33, 36, (selectedButton == 2), "<"); //T 2
  95. bf_dev_draw_button(canvas, 49, 36, (selectedButton == 3), ">"); //T 3
  96. bf_dev_draw_button(canvas, 65, 36, (selectedButton == 4), "["); //B 0
  97. bf_dev_draw_button(canvas, 81, 36, (selectedButton == 5), "]"); //B 1
  98. bf_dev_draw_button(canvas, 97, 36, (selectedButton == 6), "."); //B 2
  99. bf_dev_draw_button(canvas, 113, 36, (selectedButton == 7), ","); //B 3
  100. //backspace, input, run, save
  101. canvas_draw_icon(
  102. canvas,
  103. 1,
  104. 52,
  105. (selectedButton == 8) ? &I_KeyBackspaceSelected_24x11 : &I_KeyBackspace_24x11);
  106. canvas_draw_icon(
  107. canvas, 45, 52, (selectedButton == 9) ? &I_KeyInputSelected_30x11 : &I_KeyInput_30x11);
  108. canvas_draw_icon(
  109. canvas, 77, 52, (selectedButton == 10) ? &I_KeyRunSelected_24x11 : &I_KeyRun_24x11);
  110. canvas_draw_icon(
  111. canvas, 103, 52, (selectedButton == 11) ? &I_KeySaveSelected_24x11 : &I_KeySave_24x11);
  112. if(saveNotifyCountdown > 0) {
  113. canvas_draw_icon(canvas, 98, 54, &I_ButtonRightSmall_3x5);
  114. saveNotifyCountdown--;
  115. }
  116. //textbox
  117. //grossly overcomplicated. not fixing it.
  118. canvas_draw_rframe(canvas, 1, 1, 126, 33, 2);
  119. canvas_set_font(canvas, FONT_NAME);
  120. int dbOffset = 0;
  121. if(appDev->dataSize > 72) {
  122. dbOffset = (appDev->dataSize - 72);
  123. }
  124. memset(dspLine0, 0x00, 25);
  125. memset(dspLine1, 0x00, 25);
  126. memset(dspLine2, 0x00, 25);
  127. int tpM = 0;
  128. int tp0 = 0;
  129. int tp1 = 0;
  130. int tp2 = 0;
  131. for(int p = dbOffset; p < appDev->dataSize; p++) {
  132. if(tpM < 24 * 1) {
  133. dspLine0[tp0] = appDev->dataBuffer[p];
  134. tp0++;
  135. } else if(tpM < 24 * 2) {
  136. dspLine1[tp1] = appDev->dataBuffer[p];
  137. tp1++;
  138. } else if(tpM < 24 * 3) {
  139. dspLine2[tp2] = appDev->dataBuffer[p];
  140. tp2++;
  141. }
  142. tpM++;
  143. }
  144. canvas_draw_str_aligned(canvas, 3, 8, AlignLeft, AlignCenter, dspLine0);
  145. canvas_draw_str_aligned(canvas, 3, 17, AlignLeft, AlignCenter, dspLine1);
  146. canvas_draw_str_aligned(canvas, 3, 26, AlignLeft, AlignCenter, dspLine2);
  147. }
  148. static bool bf_dev_input_callback(InputEvent* event, void* context) {
  149. furi_assert(context);
  150. BFDevEnv* devEnv = context;
  151. bool consumed = false;
  152. if(event->type == InputTypeShort) {
  153. if(event->key == InputKeyRight) {
  154. consumed = bf_dev_process_right(devEnv);
  155. } else if(event->key == InputKeyLeft) {
  156. consumed = bf_dev_process_left(devEnv);
  157. } else if(event->key == InputKeyUp) {
  158. consumed = bf_dev_process_up(devEnv);
  159. } else if(event->key == InputKeyDown) {
  160. consumed = bf_dev_process_down(devEnv);
  161. }
  162. } else if(event->key == InputKeyOk) {
  163. consumed = bf_dev_process_ok(devEnv, event);
  164. }
  165. return consumed;
  166. }
  167. static bool bf_dev_process_up(BFDevEnv* devEnv) {
  168. UNUSED(devEnv);
  169. selectedButton = buttonMappings[selectedButton].up;
  170. return true;
  171. }
  172. static bool bf_dev_process_down(BFDevEnv* devEnv) {
  173. UNUSED(devEnv);
  174. selectedButton = buttonMappings[selectedButton].down;
  175. return true;
  176. }
  177. static bool bf_dev_process_left(BFDevEnv* devEnv) {
  178. UNUSED(devEnv);
  179. selectedButton = buttonMappings[selectedButton].left;
  180. return true;
  181. }
  182. static bool bf_dev_process_right(BFDevEnv* devEnv) {
  183. UNUSED(devEnv);
  184. selectedButton = buttonMappings[selectedButton].right;
  185. return true;
  186. }
  187. static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event) {
  188. UNUSED(devEnv);
  189. UNUSED(event);
  190. if(event->type != InputTypePress) {
  191. return false;
  192. }
  193. switch(selectedButton) {
  194. case 0: {
  195. if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
  196. appDev->dataBuffer[appDev->dataSize] = '+';
  197. appDev->dataSize++;
  198. }
  199. break;
  200. }
  201. case 1: {
  202. if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
  203. appDev->dataBuffer[appDev->dataSize] = '-';
  204. appDev->dataSize++;
  205. }
  206. break;
  207. }
  208. case 2: {
  209. if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
  210. appDev->dataBuffer[appDev->dataSize] = '<';
  211. appDev->dataSize++;
  212. }
  213. break;
  214. }
  215. case 3: {
  216. if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
  217. appDev->dataBuffer[appDev->dataSize] = '>';
  218. appDev->dataSize++;
  219. }
  220. break;
  221. }
  222. case 4: {
  223. if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
  224. appDev->dataBuffer[appDev->dataSize] = '[';
  225. appDev->dataSize++;
  226. }
  227. break;
  228. }
  229. case 5: {
  230. if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
  231. appDev->dataBuffer[appDev->dataSize] = ']';
  232. appDev->dataSize++;
  233. }
  234. break;
  235. }
  236. case 6: {
  237. if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
  238. appDev->dataBuffer[appDev->dataSize] = '.';
  239. appDev->dataSize++;
  240. }
  241. break;
  242. }
  243. case 7: {
  244. if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
  245. appDev->dataBuffer[appDev->dataSize] = ',';
  246. appDev->dataSize++;
  247. }
  248. break;
  249. }
  250. case 8: {
  251. if(appDev->dataSize > 0) {
  252. appDev->dataSize--;
  253. appDev->dataBuffer[appDev->dataSize] = (uint32_t)0x00;
  254. }
  255. break;
  256. }
  257. case 9: {
  258. scene_manager_next_scene(appDev->scene_manager, brainfuckSceneSetInput);
  259. break;
  260. }
  261. case 10: {
  262. if(getStatus() != 0) {
  263. killThread();
  264. furi_thread_join(workerThread);
  265. }
  266. bf_save_changes();
  267. initWorker(appDev);
  268. text_box_set_focus(appDev->text_box, TextBoxFocusEnd);
  269. text_box_set_text(appDev->text_box, workerGetOutput());
  270. workerThread = furi_thread_alloc_ex("Worker", 2048, (void*)beginWorker, NULL);
  271. furi_thread_start(workerThread);
  272. scene_manager_next_scene(appDev->scene_manager, brainfuckSceneExecEnv);
  273. break;
  274. }
  275. case 11: {
  276. bf_save_changes();
  277. saveNotifyCountdown = 3;
  278. break;
  279. }
  280. }
  281. bool consumed = false;
  282. return consumed;
  283. }
  284. static void bf_dev_enter_callback(void* context) {
  285. furi_assert(context);
  286. BFDevEnv* devEnv = context;
  287. with_view_model(
  288. devEnv->view,
  289. BFDevEnvModel * model,
  290. {
  291. model->col = 0;
  292. model->row = 0;
  293. },
  294. true);
  295. appDev = devEnv->appDev;
  296. selectedButton = 0;
  297. //exit the running thread if required
  298. if(getStatus() != 0) {
  299. killThread();
  300. furi_thread_join(workerThread);
  301. }
  302. //clear the bf instruction buffer
  303. memset(appDev->dataBuffer, 0x00, BF_INST_BUFFER_SIZE * sizeof(char));
  304. //open the file
  305. Storage* storage = furi_record_open(RECORD_STORAGE);
  306. Stream* stream = buffered_file_stream_alloc(storage);
  307. buffered_file_stream_open(
  308. stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_READ, FSOM_OPEN_EXISTING);
  309. //read into the buffer
  310. appDev->dataSize = stream_size(stream);
  311. if(appDev->dataSize > 2000) {
  312. return; //BF file is too large
  313. }
  314. stream_read(stream, (uint8_t*)appDev->dataBuffer, appDev->dataSize);
  315. buffered_file_stream_close(stream);
  316. //replaces any invalid characters with an underscore. strips out newlines, comments, etc
  317. for(int i = 0; i < appDev->dataSize; i++) {
  318. if(!strchr(bfChars, appDev->dataBuffer[i])) {
  319. appDev->dataBuffer[i] = '_';
  320. }
  321. }
  322. //find the end of the file to begin editing
  323. int tptr = 0;
  324. while(appDev->dataBuffer[tptr] != 0x00) {
  325. tptr++;
  326. }
  327. appDev->dataSize = tptr;
  328. }
  329. BFDevEnv* bf_dev_env_alloc(BFApp* appDev) {
  330. BFDevEnv* devEnv = malloc(sizeof(BFDevEnv));
  331. devEnv->view = view_alloc();
  332. devEnv->appDev = appDev;
  333. view_allocate_model(devEnv->view, ViewModelTypeLocking, sizeof(BFDevEnvModel));
  334. with_view_model(
  335. devEnv->view,
  336. BFDevEnvModel * model,
  337. {
  338. model->col = 0;
  339. model->row = 0;
  340. },
  341. true);
  342. view_set_context(devEnv->view, devEnv);
  343. view_set_draw_callback(devEnv->view, bf_dev_draw_callback);
  344. view_set_input_callback(devEnv->view, bf_dev_input_callback);
  345. view_set_enter_callback(devEnv->view, bf_dev_enter_callback);
  346. return devEnv;
  347. }
  348. void bf_dev_env_free(BFDevEnv* devEnv) {
  349. if(getStatus() != 0) {
  350. killThread();
  351. furi_thread_join(workerThread);
  352. }
  353. furi_assert(devEnv);
  354. view_free(devEnv->view);
  355. free(devEnv);
  356. }
  357. View* bf_dev_env_get_view(BFDevEnv* devEnv) {
  358. furi_assert(devEnv);
  359. return devEnv->view;
  360. }