diff options
Diffstat (limited to 'keyboards/rubi/lib/calc.c')
-rw-r--r-- | keyboards/rubi/lib/calc.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/keyboards/rubi/lib/calc.c b/keyboards/rubi/lib/calc.c new file mode 100644 index 0000000000..7796a9be45 --- /dev/null +++ b/keyboards/rubi/lib/calc.c @@ -0,0 +1,261 @@ +/* +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). + + Calculator for QMK-based keyboard by MWWorks, https://mwworks.uk + This is free, usual disclaimers, don't use it to calculate megaton yields, surgery plans, etc + + I did not plan to reinvent the wheel for this - I figured surely somebody somewhere has working calculator code? + Found lots but none that actually work like you expect a calculator to, hence DIYing it + + As such, this is probably a bit janky, especially as I am a bit of a hack at C + Seems to be working well, with occasional glitchs, solved by clearing it + And some occasional floating-point issues - eg get a long decimal rather than the whole number you were expecting + 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 + +*/ +#include "rubi.h" + +static uint8_t calc_current_operand = 0; +static char calc_operand_0[CALC_DIGITS+1] = ""; +static char calc_operand_1[CALC_DIGITS+1] = ""; +char calc_result[CALC_DIGITS+1] = ""; +static char calc_status[CALC_DIGITS+1] = ""; +static char calc_operator = ' '; +static bool calc_reset = false; + + +void calcBegin(void){ +} + +//update display +void calcUpdate(void){ + if (calc_display_lines == 2) { + if((calc_current_operand == 1) || (calc_reset)){ + strcpy(calc_status, calc_operand_0); + if((strlen(calc_operand_0)>0) || (strlen(calc_operand_1)>0)){ + uint8_t len = strlen(calc_status); + if (!(calc_operator == 's' || calc_operator == 'r' || calc_operator == 'n')) { + calc_status[len] = calc_operator; + } + calc_status[len+1] = 0; + if(calc_reset + && !(calc_operator == 's' || calc_operator == 'r' || calc_operator == 'n')){ + strncat(calc_status, calc_operand_1, CALC_DIGITS-strlen(calc_status)); + calc_operator = ' '; + } + } + strcpy(calc_status_display, calc_status); + } + } else if (calc_display_lines == 1) { + if(calc_reset + && !(calc_operator == 's' || calc_operator == 'r' || calc_operator == 'n')){ + calc_operator = ' '; + } + } + calc_operator_display = calc_operator; + strcpy(calc_result_display, calc_result); +} + +//perform calculation on the 2 operands +void calcOperands(void){ + float result = 0; + switch (calc_operator){ + + //standard operators + case '+': + result = strtod(calc_operand_0, NULL) + strtod(calc_operand_1, NULL); + break; + + case '-': + result = strtod(calc_operand_0, NULL) - strtod(calc_operand_1, NULL); + break; + + case '/': + result = strtod(calc_operand_0, NULL) / strtod(calc_operand_1, NULL); + break; + + case '*': + result = strtod(calc_operand_0, NULL) * strtod(calc_operand_1, NULL); + break; + + //single operand operators - these are all in 2 + case 's': + result = sqrt(strtod(calc_operand_0, NULL)); + break; + + case 'r': + result = 1/(strtod(calc_operand_0, NULL)); + break; + + } + + //now convert the float result into a string + //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 + uint8_t magnitude = ceil(log10(result)); + uint8_t max_decimals = CALC_DIGITS-magnitude-1; + //but max it at 7 because that seems the useful limit of our floats + if(max_decimals>7){ + max_decimals = 7; + } + dtostrf(result, CALC_DIGITS, max_decimals, calc_result); + + //now to clean up the result - we need it clean as it may be the input of next calculation + //this seems a lot of code to format this string :| note that this c doesn't support float in sprintf + uint8_t i; + + //first find if theres a dot + uint8_t dotpos = CALC_DIGITS+1; + for(i=0; i<strlen(calc_result); i++){ + if(calc_result[i] == '.'){ + dotpos = i; + break; + } + } + + //if there is, work back to it and remove trailing 0 or . + if(dotpos>=0){ + for(i=strlen(calc_result)-1; i>=dotpos; i--){ + if((calc_result[i] == '0') || (calc_result[i] == '.')){ + calc_result[i] = 0; + }else{ + break; + } + } + } + + //now find how many leading spaces + uint8_t spaces = 0; + for(i=0; i<strlen(calc_result); i++){ + if(calc_result[i] == ' '){ + spaces++; + }else{ + break; + } + } + + //and shift the string + for(i=0; i<strlen(calc_result)-spaces; i++){ + calc_result[i] = calc_result[i+spaces]; + } + calc_result[strlen(calc_result)-spaces] = 0; + + calcUpdate(); + //the result is available as the first operand for another calculation + strcpy(calc_operand_0, calc_result); + calc_operand_1[0] = 0; + +} + +void calcInput(char input){ + char *operand = calc_operand_0; + if(calc_current_operand == 1){ + operand = calc_operand_1; + } + uint8_t len = strlen(operand); + + if( + ((input >= 48) && (input <= 57)) || + (input == '.') + ){ + //if this is following an equals, then we start from scratch as if new calculation + if(calc_reset == true){ + calc_reset = false; + calc_current_operand = 0; + calc_operand_0[0] = 0; + calc_operand_1[0] = 0; + operand = calc_operand_0; + len = 0; + } + + if(len<CALC_DIGITS){ + operand[len] = input; + operand[len+1] = 0; + strcpy(calc_result, operand); + calcUpdate(); + } + + //special input to backspace + }else if(input == 'x'){ + operand[len-1] = 0; + strcpy(calc_result, operand); + calcUpdate(); + + //clear + }else if(input == 'c'){ + operand[0] = 0; + calc_operand_0[0] = 0; + calc_operand_1[0] = 0; + calc_operator = ' '; + calc_reset = true; + strcpy(calc_result, operand); + calcUpdate(); + + //special input switch neg/pos + }else if((input == 'n') && (len>0)){ + uint8_t i; + + if(operand[0] == '-'){ + for(i=1; i<=len; i++){ + operand[i-1] = operand[i]; + } + }else if(len<CALC_DIGITS){ + for(i=0; i<=len; i++){ + operand[len-i+1] = operand[len-i]; + } + operand[0] = '-'; + } + calc_operator = input; + strcpy(calc_result, operand); + calcUpdate(); + + + //standard 2 operand operators + }else if((input == '+') || (input == '-') || (input == '*') || (input == '/')){ + + //get ready for second operand + if(calc_current_operand == 0){ + calc_operator = input; + calc_current_operand = 1; + calcUpdate(); + + //we pressed = we now expect a new second operand + }else if(calc_reset){ + calc_operator = input; + calc_reset = false; + calc_operand_1[0] = 0; + calcUpdate(); + + }else { + //if we use this on the second operand, calculate first, then ready for a second operand again + if (strlen(calc_operand_1)>0){ + calcOperands(); + } + calc_operand_1[0] = 0; + calc_operator = input; + calcUpdate(); + } + + + }else if(input == '='){ + //only accept = if we are on the second operand + if(calc_current_operand == 1){ + //keep the second operand for a subsequent press of =; but flag to reset if start entry of new operand + calc_reset = true; + calcOperands(); + } + + //single operands - square root and reciprocal - needs to operate on 0 so it works after a previous = result + }else if((input == 's') || (input == 'r')){ + //but maybe we started entering 1 + if(calc_current_operand == 1 && !calc_reset){ + strcpy(calc_operand_0, calc_operand_1); + } + calc_current_operand = 1; + calc_operand_1[0] = 0; + calc_operator = input; + calc_reset = true; //simulate another = + calcOperands(); + + } + +} |