calculator.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <gui/gui.h>
  4. #include <input/input.h>
  5. #include <notification/notification.h>
  6. #include <notification/notification_messages.h>
  7. #include <stdbool.h> // Header-file for boolean data-type.
  8. #include <string.h> // Header-file for string functions.
  9. #include "tinyexpr.h" // Header-file for the TinyExpr library.
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. const short MAX_TEXT_LENGTH = 20;
  13. typedef struct {
  14. short x;
  15. short y;
  16. } selectedPosition;
  17. typedef struct {
  18. FuriMutex* mutex;
  19. selectedPosition position;
  20. //string with the inputted calculator text
  21. char text[20];
  22. short textLength;
  23. char log[20];
  24. } Calculator;
  25. char getKeyAtPosition(short x, short y) {
  26. if(x == 0 && y == 0) {
  27. return 'C';
  28. }
  29. if(x == 1 && y == 0) {
  30. return '<';
  31. }
  32. if(x == 2 && y == 0) {
  33. return '%';
  34. }
  35. if(x == 3 && y == 0) {
  36. return '/';
  37. }
  38. if(x == 0 && y == 1) {
  39. return '1';
  40. }
  41. if(x == 1 && y == 1) {
  42. return '2';
  43. }
  44. if(x == 2 && y == 1) {
  45. return '3';
  46. }
  47. if(x == 3 && y == 1) {
  48. return '*';
  49. }
  50. if(x == 0 && y == 2) {
  51. return '4';
  52. }
  53. if(x == 1 && y == 2) {
  54. return '5';
  55. }
  56. if(x == 2 && y == 2) {
  57. return '6';
  58. }
  59. if(x == 3 && y == 2) {
  60. return '-';
  61. }
  62. if(x == 0 && y == 3) {
  63. return '7';
  64. }
  65. if(x == 1 && y == 3) {
  66. return '8';
  67. }
  68. if(x == 2 && y == 3) {
  69. return '9';
  70. }
  71. if(x == 3 && y == 3) {
  72. return '+';
  73. }
  74. if(x == 0 && y == 4) {
  75. return '(';
  76. }
  77. if(x == 1 && y == 4) {
  78. return '0';
  79. }
  80. if(x == 2 && y == 4) {
  81. return '.';
  82. }
  83. if(x == 3 && y == 4) {
  84. return '=';
  85. }
  86. return ' ';
  87. }
  88. short calculateStringWidth(const char* str, short lenght) {
  89. /* widths:
  90. 1 = 2
  91. 2, 3, 4, 5, 6, 7, 8, 9, 0, X, -, +, . = = 5
  92. %, / = 7
  93. S = 5
  94. (, ) = 3
  95. */
  96. short width = 0;
  97. for(short i = 0; i < lenght; i++) {
  98. switch(str[i]) {
  99. case '1':
  100. width += 2;
  101. break;
  102. case '2':
  103. case '3':
  104. case '4':
  105. case '5':
  106. case '6':
  107. case '7':
  108. case '8':
  109. case '9':
  110. case '0':
  111. case '*':
  112. case '-':
  113. case '+':
  114. case '.':
  115. width += 5;
  116. break;
  117. case '%':
  118. case '/':
  119. width += 7;
  120. break;
  121. case 'S':
  122. width += 5;
  123. break;
  124. case '(':
  125. case ')':
  126. width += 3;
  127. break;
  128. default:
  129. break;
  130. }
  131. width += 1;
  132. }
  133. return width;
  134. }
  135. void generate_calculator_layout(Canvas* canvas) {
  136. //draw dotted lines
  137. for(int i = 0; i <= 64; i++) {
  138. if(i % 2 == 0) {
  139. canvas_draw_dot(canvas, i, 14);
  140. canvas_draw_dot(canvas, i, 33);
  141. }
  142. if(i % 2 == 1) {
  143. canvas_draw_dot(canvas, i, 15);
  144. canvas_draw_dot(canvas, i, 34);
  145. }
  146. }
  147. //draw horizontal lines
  148. canvas_draw_box(canvas, 0, 41, 64, 2);
  149. canvas_draw_box(canvas, 0, 57, 64, 2);
  150. canvas_draw_box(canvas, 0, 73, 64, 2);
  151. canvas_draw_box(canvas, 0, 89, 64, 2);
  152. canvas_draw_box(canvas, 0, 105, 64, 2);
  153. canvas_draw_box(canvas, 0, 121, 64, 2);
  154. //draw vertical lines
  155. canvas_draw_box(canvas, 0, 43, 1, 80);
  156. canvas_draw_box(canvas, 15, 43, 2, 80);
  157. canvas_draw_box(canvas, 31, 43, 2, 80);
  158. canvas_draw_box(canvas, 47, 43, 2, 80);
  159. canvas_draw_box(canvas, 63, 43, 1, 80);
  160. //draw buttons
  161. //row 1 (C, ;, %, ÷)
  162. canvas_draw_str(canvas, 5, 54, "C");
  163. canvas_draw_str(canvas, 19, 54, " <-");
  164. canvas_draw_str(canvas, 35, 54, " %");
  165. canvas_draw_str(canvas, 51, 54, " /");
  166. //row 2 (1, 2, 3, X)
  167. canvas_draw_str(canvas, 5, 70, " 1");
  168. canvas_draw_str(canvas, 19, 70, " 2");
  169. canvas_draw_str(canvas, 35, 70, " 3");
  170. canvas_draw_str(canvas, 51, 70, " X");
  171. //row 3 (4, 5, 6, -)
  172. canvas_draw_str(canvas, 5, 86, " 4");
  173. canvas_draw_str(canvas, 19, 86, " 5");
  174. canvas_draw_str(canvas, 35, 86, " 6");
  175. canvas_draw_str(canvas, 51, 86, " -");
  176. //row 4 (7, 8, 9, +)
  177. canvas_draw_str(canvas, 5, 102, " 7");
  178. canvas_draw_str(canvas, 19, 102, " 8");
  179. canvas_draw_str(canvas, 35, 102, " 9");
  180. canvas_draw_str(canvas, 51, 102, " +");
  181. //row 5 (+/-, 0, ., =)
  182. canvas_draw_str(canvas, 3, 118, "( )");
  183. canvas_draw_str(canvas, 19, 118, " 0");
  184. canvas_draw_str(canvas, 35, 118, " .");
  185. canvas_draw_str(canvas, 51, 118, " =");
  186. }
  187. void calculator_draw_callback(Canvas* canvas, void* ctx) {
  188. furi_assert(ctx);
  189. const Calculator* calculator_state = ctx;
  190. furi_mutex_acquire(calculator_state->mutex, FuriWaitForever);
  191. canvas_clear(canvas);
  192. //show selected button
  193. short startX = 1;
  194. short startY = 43;
  195. canvas_set_color(canvas, ColorBlack);
  196. canvas_draw_box(
  197. canvas,
  198. startX + (calculator_state->position.x) * 16,
  199. (startY) + (calculator_state->position.y) * 16,
  200. 16,
  201. 16);
  202. canvas_set_color(canvas, ColorWhite);
  203. canvas_draw_box(
  204. canvas,
  205. startX + (calculator_state->position.x) * 16 + 2,
  206. (startY) + (calculator_state->position.y) * 16 + 2,
  207. 10,
  208. 10);
  209. canvas_set_color(canvas, ColorBlack);
  210. generate_calculator_layout(canvas);
  211. //draw text
  212. short stringWidth = calculateStringWidth(calculator_state->text, calculator_state->textLength);
  213. short startingPosition = 5;
  214. if(stringWidth > 60) {
  215. startingPosition += 60 - (stringWidth + 5);
  216. }
  217. canvas_set_color(canvas, ColorBlack);
  218. canvas_draw_str(canvas, startingPosition, 28, calculator_state->text);
  219. //canvas_draw_str(canvas, 10, 10, calculator_state->log);
  220. //draw cursor
  221. canvas_draw_box(canvas, stringWidth + 5, 29, 5, 1);
  222. furi_mutex_release(calculator_state->mutex);
  223. }
  224. void calculator_input_callback(InputEvent* input_event, void* ctx) {
  225. furi_assert(ctx);
  226. FuriMessageQueue* event_queue = ctx;
  227. furi_message_queue_put(event_queue, input_event, FuriWaitForever);
  228. }
  229. void calculate(Calculator* calculator_state) {
  230. double result;
  231. result = te_interp(calculator_state->text, 0);
  232. calculator_state->textLength = 0;
  233. calculator_state->text[0] = '\0';
  234. // sprintf(calculator_state->text, "%f", result);
  235. //invert sign if negative
  236. if(result < 0) {
  237. calculator_state->text[calculator_state->textLength++] = '-';
  238. result = -result;
  239. }
  240. //get numbers before and after decimal
  241. int beforeDecimal = result;
  242. int afterDecimal = (result - beforeDecimal) * 100;
  243. char beforeDecimalString[10];
  244. char afterDecimalString[10];
  245. int i = 0;
  246. //parse to a string
  247. while(beforeDecimal > 0) {
  248. beforeDecimalString[i++] = beforeDecimal % 10 + '0';
  249. beforeDecimal /= 10;
  250. }
  251. // invert string
  252. for(int j = 0; j < i / 2; j++) {
  253. char temp = beforeDecimalString[j];
  254. beforeDecimalString[j] = beforeDecimalString[i - j - 1];
  255. beforeDecimalString[i - j - 1] = temp;
  256. }
  257. //add it to the answer
  258. for(int j = 0; j < i; j++) {
  259. calculator_state->text[calculator_state->textLength++] = beforeDecimalString[j];
  260. }
  261. i = 0;
  262. if(afterDecimal > 0) {
  263. while(afterDecimal > 0) {
  264. afterDecimalString[i++] = afterDecimal % 10 + '0';
  265. afterDecimal /= 10;
  266. }
  267. // invert string
  268. for(int j = 0; j < i / 2; j++) {
  269. char temp = afterDecimalString[j];
  270. afterDecimalString[j] = afterDecimalString[i - j - 1];
  271. afterDecimalString[i - j - 1] = temp;
  272. }
  273. //add decimal point
  274. calculator_state->text[calculator_state->textLength++] = '.';
  275. //add numbers after decimal
  276. for(int j = 0; j < i; j++) {
  277. calculator_state->text[calculator_state->textLength++] = afterDecimalString[j];
  278. }
  279. }
  280. calculator_state->text[calculator_state->textLength] = '\0';
  281. }
  282. int32_t calculator_app(void* p) {
  283. UNUSED(p);
  284. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  285. Calculator* calculator_state = malloc(sizeof(Calculator));
  286. calculator_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  287. if(!calculator_state->mutex) {
  288. //FURI_LOG_E("calculator", "cannot create mutex\r\n");
  289. free(calculator_state);
  290. return -1;
  291. }
  292. // Configure view port
  293. ViewPort* view_port = view_port_alloc();
  294. view_port_draw_callback_set(view_port, calculator_draw_callback, calculator_state);
  295. view_port_input_callback_set(view_port, calculator_input_callback, event_queue);
  296. view_port_set_orientation(view_port, ViewPortOrientationVertical);
  297. // Register view port in GUI
  298. Gui* gui = furi_record_open(RECORD_GUI);
  299. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  300. //NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  301. InputEvent event;
  302. while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
  303. //break out of the loop if the back key is pressed
  304. if(event.type == InputTypeShort && event.key == InputKeyBack) {
  305. break;
  306. }
  307. if(event.type == InputTypeShort) {
  308. switch(event.key) {
  309. case InputKeyUp:
  310. if(calculator_state->position.y > 0) {
  311. calculator_state->position.y--;
  312. }
  313. break;
  314. case InputKeyDown:
  315. if(calculator_state->position.y < 4) {
  316. calculator_state->position.y++;
  317. }
  318. break;
  319. case InputKeyLeft:
  320. if(calculator_state->position.x > 0) {
  321. calculator_state->position.x--;
  322. }
  323. break;
  324. case InputKeyRight:
  325. if(calculator_state->position.x < 3) {
  326. calculator_state->position.x++;
  327. }
  328. break;
  329. case InputKeyOk: {
  330. //add the selected button to the text
  331. //char* text = calculator_state->text;
  332. // short* textLength = &calculator_state->textLength;
  333. char key =
  334. getKeyAtPosition(calculator_state->position.x, calculator_state->position.y);
  335. switch(key) {
  336. case 'C':
  337. while(calculator_state->textLength > 0) {
  338. calculator_state->text[calculator_state->textLength--] = '\0';
  339. }
  340. calculator_state->text[0] = '\0';
  341. calculator_state->log[2] = key;
  342. break;
  343. case '<':
  344. calculator_state->log[2] = key;
  345. if(calculator_state->textLength > 0) {
  346. calculator_state->text[--calculator_state->textLength] = '\0';
  347. } else {
  348. calculator_state->text[0] = '\0';
  349. }
  350. break;
  351. case '=':
  352. calculator_state->log[2] = key;
  353. calculate(calculator_state);
  354. break;
  355. case '%':
  356. case '/':
  357. case '*':
  358. case '-':
  359. case '+':
  360. case '.':
  361. case '(':
  362. case '1':
  363. case '2':
  364. case '3':
  365. case '4':
  366. case '5':
  367. case '6':
  368. case '7':
  369. case '8':
  370. case '9':
  371. case '0':
  372. if(calculator_state->textLength < MAX_TEXT_LENGTH) {
  373. calculator_state->text[calculator_state->textLength++] = key;
  374. calculator_state->text[calculator_state->textLength] = '\0';
  375. }
  376. //calculator_state->log[1] = calculator_state->text[*textLength];
  377. break;
  378. default:
  379. break;
  380. }
  381. }
  382. default:
  383. break;
  384. }
  385. view_port_update(view_port);
  386. }
  387. if(event.type == InputTypeLong) {
  388. switch(event.key) {
  389. case InputKeyOk:
  390. if(calculator_state->position.x == 0 && calculator_state->position.y == 4) {
  391. if(calculator_state->textLength < MAX_TEXT_LENGTH) {
  392. calculator_state->text[calculator_state->textLength++] = ')';
  393. calculator_state->text[calculator_state->textLength] = '\0';
  394. }
  395. view_port_update(view_port);
  396. }
  397. break;
  398. default:
  399. break;
  400. }
  401. }
  402. }
  403. gui_remove_view_port(gui, view_port);
  404. view_port_free(view_port);
  405. furi_mutex_free(calculator_state->mutex);
  406. furi_message_queue_free(event_queue);
  407. furi_record_close(RECORD_NOTIFICATION);
  408. furi_record_close(RECORD_GUI);
  409. free(calculator_state);
  410. return 0;
  411. }