qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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