logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

calc.c (8462B)


  1. /*
  2. This is the modified version of [calculator by MWWorks](https://github.com/MWWorks/mw_calc_numpad/blob/master/calc.c). Below is the quote from [MWWorks](https://github.com/MWWorks).
  3. Calculator for QMK-based keyboard by MWWorks, https://mwworks.uk
  4. This is free, usual disclaimers, don't use it to calculate megaton yields, surgery plans, etc
  5. I did not plan to reinvent the wheel for this - I figured surely somebody somewhere has working calculator code?
  6. Found lots but none that actually work like you expect a calculator to, hence DIYing it
  7. As such, this is probably a bit janky, especially as I am a bit of a hack at C
  8. Seems to be working well, with occasional glitchs, solved by clearing it
  9. And some occasional floating-point issues - eg get a long decimal rather than the whole number you were expecting
  10. Feel free to fix it! I think it needs to detect the precision of the two operands and then figure out what the precision of the result should be
  11. */
  12. #include <math.h>
  13. #include "rubi.h"
  14. static uint8_t calc_current_operand = 0;
  15. static char calc_operand_0[CALC_DIGITS+1] = "";
  16. static char calc_operand_1[CALC_DIGITS+1] = "";
  17. char calc_result[CALC_DIGITS+1] = "";
  18. static char calc_status[CALC_DIGITS+1] = "";
  19. static char calc_operator = ' ';
  20. static bool calc_reset = false;
  21. void calcBegin(void){
  22. }
  23. //update display
  24. void calcUpdate(void){
  25. if (calc_display_lines == 2) {
  26. if((calc_current_operand == 1) || (calc_reset)){
  27. strcpy(calc_status, calc_operand_0);
  28. if((strlen(calc_operand_0)>0) || (strlen(calc_operand_1)>0)){
  29. uint8_t len = strlen(calc_status);
  30. if (!(calc_operator == 's' || calc_operator == 'r' || calc_operator == 'n')) {
  31. calc_status[len] = calc_operator;
  32. }
  33. calc_status[len+1] = 0;
  34. if(calc_reset
  35. && !(calc_operator == 's' || calc_operator == 'r' || calc_operator == 'n')){
  36. strncat(calc_status, calc_operand_1, CALC_DIGITS-strlen(calc_status));
  37. calc_operator = ' ';
  38. }
  39. }
  40. strcpy(calc_status_display, calc_status);
  41. }
  42. } else if (calc_display_lines == 1) {
  43. if(calc_reset
  44. && !(calc_operator == 's' || calc_operator == 'r' || calc_operator == 'n')){
  45. calc_operator = ' ';
  46. }
  47. }
  48. calc_operator_display = calc_operator;
  49. strcpy(calc_result_display, calc_result);
  50. }
  51. //perform calculation on the 2 operands
  52. void calcOperands(void){
  53. float result = 0;
  54. switch (calc_operator){
  55. //standard operators
  56. case '+':
  57. result = strtod(calc_operand_0, NULL) + strtod(calc_operand_1, NULL);
  58. break;
  59. case '-':
  60. result = strtod(calc_operand_0, NULL) - strtod(calc_operand_1, NULL);
  61. break;
  62. case '/':
  63. result = strtod(calc_operand_0, NULL) / strtod(calc_operand_1, NULL);
  64. break;
  65. case '*':
  66. result = strtod(calc_operand_0, NULL) * strtod(calc_operand_1, NULL);
  67. break;
  68. //single operand operators - these are all in 2
  69. case 's':
  70. result = sqrt(strtod(calc_operand_0, NULL));
  71. break;
  72. case 'r':
  73. result = 1/(strtod(calc_operand_0, NULL));
  74. break;
  75. }
  76. //now convert the float result into a string
  77. //we know the total string size but we need to find the size of the integer component to know how much we have for decimals
  78. uint8_t magnitude = ceil(log10(result));
  79. uint8_t max_decimals = CALC_DIGITS-magnitude-1;
  80. //but max it at 7 because that seems the useful limit of our floats
  81. if(max_decimals>7){
  82. max_decimals = 7;
  83. }
  84. dtostrf(result, CALC_DIGITS, max_decimals, calc_result);
  85. //now to clean up the result - we need it clean as it may be the input of next calculation
  86. //this seems a lot of code to format this string :| note that this c doesn't support float in sprintf
  87. uint8_t i;
  88. //first find if theres a dot
  89. uint8_t dotpos = CALC_DIGITS+1;
  90. for(i=0; i<strlen(calc_result); i++){
  91. if(calc_result[i] == '.'){
  92. dotpos = i;
  93. break;
  94. }
  95. }
  96. //if there is, work back to it and remove trailing 0 or .
  97. if(dotpos>=0){
  98. for(i=strlen(calc_result)-1; i>=dotpos; i--){
  99. if((calc_result[i] == '0') || (calc_result[i] == '.')){
  100. calc_result[i] = 0;
  101. }else{
  102. break;
  103. }
  104. }
  105. }
  106. //now find how many leading spaces
  107. uint8_t spaces = 0;
  108. for(i=0; i<strlen(calc_result); i++){
  109. if(calc_result[i] == ' '){
  110. spaces++;
  111. }else{
  112. break;
  113. }
  114. }
  115. //and shift the string
  116. for(i=0; i<strlen(calc_result)-spaces; i++){
  117. calc_result[i] = calc_result[i+spaces];
  118. }
  119. calc_result[strlen(calc_result)-spaces] = 0;
  120. calcUpdate();
  121. //the result is available as the first operand for another calculation
  122. strcpy(calc_operand_0, calc_result);
  123. calc_operand_1[0] = 0;
  124. }
  125. void calcInput(char input){
  126. char *operand = calc_operand_0;
  127. if(calc_current_operand == 1){
  128. operand = calc_operand_1;
  129. }
  130. uint8_t len = strlen(operand);
  131. if(
  132. ((input >= 48) && (input <= 57)) ||
  133. (input == '.')
  134. ){
  135. //if this is following an equals, then we start from scratch as if new calculation
  136. if(calc_reset == true){
  137. calc_reset = false;
  138. calc_current_operand = 0;
  139. calc_operand_0[0] = 0;
  140. calc_operand_1[0] = 0;
  141. operand = calc_operand_0;
  142. len = 0;
  143. }
  144. if(len<CALC_DIGITS){
  145. operand[len] = input;
  146. operand[len+1] = 0;
  147. strcpy(calc_result, operand);
  148. calcUpdate();
  149. }
  150. //special input to backspace
  151. }else if(input == 'x'){
  152. operand[len-1] = 0;
  153. strcpy(calc_result, operand);
  154. calcUpdate();
  155. //clear
  156. }else if(input == 'c'){
  157. operand[0] = 0;
  158. calc_operand_0[0] = 0;
  159. calc_operand_1[0] = 0;
  160. calc_operator = ' ';
  161. calc_reset = true;
  162. strcpy(calc_result, operand);
  163. calcUpdate();
  164. //special input switch neg/pos
  165. }else if((input == 'n') && (len>0)){
  166. uint8_t i;
  167. if(operand[0] == '-'){
  168. for(i=1; i<=len; i++){
  169. operand[i-1] = operand[i];
  170. }
  171. }else if(len<CALC_DIGITS){
  172. for(i=0; i<=len; i++){
  173. operand[len-i+1] = operand[len-i];
  174. }
  175. operand[0] = '-';
  176. }
  177. calc_operator = input;
  178. strcpy(calc_result, operand);
  179. calcUpdate();
  180. //standard 2 operand operators
  181. }else if((input == '+') || (input == '-') || (input == '*') || (input == '/')){
  182. //get ready for second operand
  183. if(calc_current_operand == 0){
  184. calc_operator = input;
  185. calc_current_operand = 1;
  186. calcUpdate();
  187. //we pressed = we now expect a new second operand
  188. }else if(calc_reset){
  189. calc_operator = input;
  190. calc_reset = false;
  191. calc_operand_1[0] = 0;
  192. calcUpdate();
  193. }else {
  194. //if we use this on the second operand, calculate first, then ready for a second operand again
  195. if (strlen(calc_operand_1)>0){
  196. calcOperands();
  197. }
  198. calc_operand_1[0] = 0;
  199. calc_operator = input;
  200. calcUpdate();
  201. }
  202. }else if(input == '='){
  203. //only accept = if we are on the second operand
  204. if(calc_current_operand == 1){
  205. //keep the second operand for a subsequent press of =; but flag to reset if start entry of new operand
  206. calc_reset = true;
  207. calcOperands();
  208. }
  209. //single operands - square root and reciprocal - needs to operate on 0 so it works after a previous = result
  210. }else if((input == 's') || (input == 'r')){
  211. //but maybe we started entering 1
  212. if(calc_current_operand == 1 && !calc_reset){
  213. strcpy(calc_operand_0, calc_operand_1);
  214. }
  215. calc_current_operand = 1;
  216. calc_operand_1[0] = 0;
  217. calc_operator = input;
  218. calc_reset = true; //simulate another =
  219. calcOperands();
  220. }
  221. }