From a75589a0997e152357d8e53612c670ca82bb2d31 Mon Sep 17 00:00:00 2001 From: Ajax Date: Mon, 30 Apr 2018 01:40:46 -0400 Subject: [PATCH] Modularized and added a userfile so that this code can be used on various keyboards, found in doogle999 --- .../keymaps/{60_calc => doogle999}/keymap.c | 83 +--- users/doogle999/doogle999.c | 446 ++++++++++++++++++ users/doogle999/doogle999.h | 87 ++++ users/doogle999/readme.md | 18 + users/doogle999/rules.mk | 12 + 5 files changed, 566 insertions(+), 80 deletions(-) rename keyboards/dz60/keymaps/{60_calc => doogle999}/keymap.c (92%) create mode 100644 users/doogle999/doogle999.c create mode 100644 users/doogle999/doogle999.h create mode 100644 users/doogle999/readme.md create mode 100644 users/doogle999/rules.mk diff --git a/keyboards/dz60/keymaps/60_calc/keymap.c b/keyboards/dz60/keymaps/doogle999/keymap.c similarity index 92% rename from keyboards/dz60/keymaps/60_calc/keymap.c rename to keyboards/dz60/keymaps/doogle999/keymap.c index d7bde26ff9..955812db56 100644 --- a/keyboards/dz60/keymaps/60_calc/keymap.c +++ b/keyboards/dz60/keymaps/doogle999/keymap.c @@ -1,83 +1,8 @@ #include "dz60.h" -#include "math.h" - -#define NO_ACTION_ONESHOT -#define NO_ACTION_MACRO - -#define MODS_SHIFT_MASK (MOD_BIT(KC_LSHIFT)|MOD_BIT(KC_RSHIFT)) +#include "users/doogle999/doogle999.h" #define ______ KC_NO -#define BUFFER_SIZE 32 - -/*----- - Special ------*/ - -#define CHAR_BEG '(' -#define CHAR_END ')' - -/*----- - Operators ------*/ - -#define CHAR_ADD '+' -#define PRIO_ADD 1 - -#define CHAR_SUB '-' -#define PRIO_SUB 1 - -#define CHAR_MUL '*' -#define PRIO_MUL 2 - -#define CHAR_DIV '/' -#define PRIO_DIV 2 - -#define CHAR_EXP '^' -#define PRIO_EXP 3 - -/*----- - FUNCTIONS ------*/ - -#define CHAR_SIN 's' -#define CHAR_COS 'c' -#define CHAR_TAN 't' - -#define CHAR_ASN 'S' -#define CHAR_ACS 'C' -#define CHAR_ATN 'T' - -#define CHAR_LGE 'l' -#define CHAR_LOG 'L' - -#define CHAR_SQT 'q' - -struct OP // Operator/function -{ - char c; - unsigned char priority; - bool ltr; -}; - -union TokenRaw // A token after the input has been processed, can either be a number or an operator/function -{ - double num; - struct OP op; -}; - -struct Token // Encapsulator -{ - bool isNum; - union TokenRaw raw; -}; - -enum CalcFunctions // Hardware calculator key functionality -{ - CALC = SAFE_RANGE, - ENDCALC -}; - const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { /* Base layer @@ -146,14 +71,11 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { ______, ______, ______, ______, ______, ______, KC_SLSH, ______, ______, ______, ______ ), }; - +/* static char backspaceText[BUFFER_SIZE + 1]; // Pretty dumb waste of memory because only backspace characters, used with send_string to backspace and remove input static char text[BUFFER_SIZE + 1]; // Used to store input and then output when ready to print static unsigned char inputLocation = 0; // Current index in text input -/*----- - Known Problem: A negative sign before an open parenthesis as the first character in the input such as "-(4+3)" will not be parsed correctly, a very hacky solution would be to force the first character of the input to be a 0 ------*/ double calc(char input[]) // Finds value of input char array, relatively small and fast I think { char inputToken[BUFFER_SIZE + 1]; // Input buffer, used when a single token (generally a number) takes up more @@ -594,3 +516,4 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) return true; } } +*/ \ No newline at end of file diff --git a/users/doogle999/doogle999.c b/users/doogle999/doogle999.c new file mode 100644 index 0000000000..238acd9a9e --- /dev/null +++ b/users/doogle999/doogle999.c @@ -0,0 +1,446 @@ +#include "doogle999.h" + +char backspaceText[BUFFER_SIZE + 1]; // Pretty dumb waste of memory because only backspace characters, used with send_string to backspace and remove input +char text[BUFFER_SIZE + 1]; // Used to store input and then output when ready to print +unsigned char inputLocation = 0; // Current index in text input + +double calc(char input[]) // Finds value of input char array, relatively small and fast I think +{ + char inputToken[BUFFER_SIZE + 1]; // Input buffer, used when a single token (generally a number) takes up more + unsigned char inputTokenLocation = 0, inputLocation = 0; // Keep track of indices + + struct Token tokens[BUFFER_SIZE + 1]; // Input, converted to tokens, one extra large to accomodate for possible negative sign then open parenthesis as first character + unsigned char tokenCount = 0; // Keep track of index + + bool dashAsMinus = false; // Kind of a hacky solution to determining whether to treat a dash as a minus sign or a negative sign + + while(inputLocation < BUFFER_SIZE) + { + short number = input[inputLocation] - '0'; // Using a short here because both signed char and unsigned char would overflow, potentially + if(inputLocation == 0 && input[inputLocation] == CHAR_SUB && input[inputLocation + 1] == CHAR_BEG) + { + tokens[tokenCount].raw.num = 0; + tokens[tokenCount].isNum = true; + + tokenCount++; + dashAsMinus = true; + } + if((number < 10 && number >= 0) || (inputTokenLocation != 0 && input[inputLocation] == '.') || (!dashAsMinus && inputTokenLocation == 0 && input[inputLocation] == '-')) + { + inputToken[inputTokenLocation] = input[inputLocation]; + inputTokenLocation++; + inputLocation++; + } + else + { + if(inputTokenLocation != 0) + { + // sscanf(inputToken, "%lf", &tokens[tokenCount].raw.num); // I would like to use sscanf here, but the small version of stdio.h on the chip doesn't allow sscanf or its sister functions to be used to process floats + tokens[tokenCount].raw.num = atof(inputToken); + tokens[tokenCount].isNum = true; + for(unsigned char i = 0; i < inputTokenLocation + 1; i++) + { + inputToken[i] = '\0'; + } + inputTokenLocation = 0; + tokenCount++; + dashAsMinus = true; + } + tokens[tokenCount].isNum = false; + tokens[tokenCount].raw.op.c = input[inputLocation]; + tokens[tokenCount].raw.op.priority = 0; + tokens[tokenCount].raw.op.ltr = true; + dashAsMinus = false; + + switch(input[inputLocation]) + { + case CHAR_BEG: + break; + case CHAR_END: + dashAsMinus = true; + break; + case CHAR_ADD: + tokens[tokenCount].raw.op.priority = PRIO_ADD; + break; + case CHAR_SUB: + tokens[tokenCount].raw.op.priority = PRIO_SUB; + break; + case CHAR_MUL: + tokens[tokenCount].raw.op.priority = PRIO_MUL; + break; + case CHAR_DIV: + tokens[tokenCount].raw.op.priority = PRIO_DIV; + break; + case CHAR_EXP: + tokens[tokenCount].raw.op.priority = PRIO_EXP; + tokens[tokenCount].raw.op.ltr = false; + break; + case CHAR_SIN: + break; + case CHAR_COS: + break; + case CHAR_TAN: + break; + case CHAR_ASN: + break; + case CHAR_ACS: + break; + case CHAR_ATN: + break; + case CHAR_LGE: + break; + case CHAR_LOG: + break; + case CHAR_SQT: + break; + case '\0': + tokenCount--; + inputLocation = BUFFER_SIZE; + break; + default: + tokenCount--; + break; + } + tokenCount++; + inputLocation++; + } + } + + struct Token output[BUFFER_SIZE + 1]; // Final output tokens before evaluation + struct Token opstack[BUFFER_SIZE + 1]; // Stack of operators + unsigned char outputLocation = 0, opstackLocation = 0; // Keep track of indices + + unsigned char numBrackets = 0; // The number of parenthesis + + for(unsigned char i = 0; i < tokenCount; i++) + { + if(tokens[i].isNum) + { + output[outputLocation] = tokens[i]; + outputLocation++; + } + else if(tokens[i].raw.op.c == CHAR_BEG) + { + opstack[opstackLocation] = tokens[i]; + opstackLocation++; + } + else if(tokens[i].raw.op.c == CHAR_END) + { + while(opstack[opstackLocation - 1].raw.op.c != CHAR_BEG) + { + output[outputLocation] = opstack[opstackLocation - 1]; + outputLocation++; + opstackLocation--; + } + opstackLocation--; + + numBrackets += 2; + } + else if(tokens[i].raw.op.priority == 0) + { + opstack[opstackLocation] = tokens[i]; + opstackLocation++; + } + else + { + while(opstackLocation != 0 + && (opstack[opstackLocation - 1].raw.op.priority == 0 + || tokens[i].raw.op.priority < opstack[opstackLocation - 1].raw.op.priority + || (tokens[i].raw.op.priority == opstack[opstackLocation - 1].raw.op.priority && opstack[opstackLocation - 1].raw.op.ltr)) + && opstack[opstackLocation - 1].raw.op.c != CHAR_BEG) + { + output[outputLocation] = opstack[opstackLocation - 1]; + outputLocation++; + opstackLocation--; + } + opstack[opstackLocation] = tokens[i]; + opstackLocation++; + } + } + + tokenCount -= numBrackets; + + for(signed char i = opstackLocation - 1; i >= 0; i--) + { + output[outputLocation] = opstack[i]; + outputLocation++; + opstackLocation--; + } + + double answer[BUFFER_SIZE]; + unsigned char answerLocation = 0; + + for(unsigned char i = 0; i < tokenCount; i++) + { + if(output[i].isNum) + { + answer[answerLocation] = output[i].raw.num; + answerLocation++; + } + else + { + if(output[i].raw.op.priority == 0) + { + if(answerLocation >= 1) + { + switch(output[i].raw.op.c) + { + case CHAR_SIN: + answer[answerLocation - 1] = sin(answer[answerLocation - 1]); + break; + case CHAR_COS: + answer[answerLocation - 1] = cos(answer[answerLocation - 1]); + break; + case CHAR_TAN: + answer[answerLocation - 1] = tan(answer[answerLocation - 1]); + break; + case CHAR_ASN: + answer[answerLocation - 1] = asin(answer[answerLocation - 1]); + break; + case CHAR_ACS: + answer[answerLocation - 1] = acos(answer[answerLocation - 1]); + break; + case CHAR_ATN: + answer[answerLocation - 1] = atan(answer[answerLocation - 1]); + break; + case CHAR_LGE: + answer[answerLocation - 1] = log(answer[answerLocation - 1]); + break; + case CHAR_LOG: + answer[answerLocation - 1] = log10(answer[answerLocation - 1]); + break; + case CHAR_SQT: + answer[answerLocation - 1] = sqrt(answer[answerLocation - 1]); + break; + } + } + } + else if(answerLocation >= 2) + { + switch(output[i].raw.op.c) + { + case CHAR_ADD: + answer[answerLocation - 2] += answer[answerLocation - 1]; + break; + case CHAR_SUB: + answer[answerLocation - 2] -= answer[answerLocation - 1]; + break; + case CHAR_MUL: + answer[answerLocation - 2] *= answer[answerLocation - 1]; + break; + case CHAR_DIV: + answer[answerLocation - 2] /= answer[answerLocation - 1]; + break; + case CHAR_EXP: + answer[answerLocation - 2] = pow(answer[answerLocation - 2], answer[answerLocation - 1]); + break; + } + + answerLocation--; + } + } + } + + return answer[0]; +} + +bool process_record_user(uint16_t keycode, keyrecord_t *record) +{ + bool numpadKeyPressed = false; + + if(record->event.pressed) + { + if(!(get_mods() & MODS_SHIFT_MASK)) + { + switch(keycode) + { + case KC_KP_0: + numpadKeyPressed = true; + break; + case KC_KP_1: + numpadKeyPressed = true; + break; + case KC_KP_2: + numpadKeyPressed = true; + break; + case KC_KP_3: + numpadKeyPressed = true; + break; + case KC_KP_4: + numpadKeyPressed = true; + break; + case KC_KP_5: + numpadKeyPressed = true; + break; + case KC_KP_6: + numpadKeyPressed = true; + break; + case KC_KP_7: + numpadKeyPressed = true; + break; + case KC_KP_8: + numpadKeyPressed = true; + break; + case KC_KP_9: + numpadKeyPressed = true; + break; + } + } + } + if(numpadKeyPressed && !(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK))) + { + add_key(KC_NLCK); + send_keyboard_report(); + } + + if(biton32(layer_state) == 2) + { + char characterPressed = '\0'; + + bool forceReturnTrue = false; + + if(record->event.pressed) + { + if(!(get_mods() & MODS_SHIFT_MASK)) + { + switch(keycode) + { + case KC_0: + characterPressed = '0'; + break; + case KC_1: + characterPressed = '1'; + break; + case KC_2: + characterPressed = '2'; + break; + case KC_3: + characterPressed = '3'; + break; + case KC_4: + characterPressed = '4'; + break; + case KC_5: + characterPressed = '5'; + break; + case KC_6: + characterPressed = '6'; + break; + case KC_7: + characterPressed = '7'; + break; + case KC_8: + characterPressed = '8'; + break; + case KC_9: + characterPressed = '9'; + break; + case KC_MINUS: + characterPressed = CHAR_SUB; + break; + case KC_SLASH: + characterPressed = CHAR_DIV; + break; + case KC_S: + characterPressed = CHAR_SIN; + break; + case KC_C: + characterPressed = CHAR_COS; + break; + case KC_T: + characterPressed = CHAR_TAN; + break; + case KC_Q: + characterPressed = CHAR_SQT; + break; + case KC_L: + characterPressed = CHAR_LGE; + break; + case KC_DOT: + characterPressed = '.'; + break; + case KC_BSPC: + if(inputLocation > 0) + { + inputLocation--; + } + forceReturnTrue = true; + break; + case KC_RSFT: + forceReturnTrue = true; + break; + case KC_LSFT: + forceReturnTrue = true; + break; + case CALC: + for(int i = 0; i < inputLocation; i++) + { + backspaceText[i] = (char)8; + } + send_string(backspaceText); + dtostrf(calc(text), 6, 6, text); + send_string(text); + for(unsigned char i = 0; i < BUFFER_SIZE; i++) + { + text[i] = '\0'; + backspaceText[i] = '\0'; + } + inputLocation = 0; + break; + case ENDCALC: + layer_state = 0; + break; + default: + break; + } + } + else + { + switch(keycode) + { + case KC_9: + characterPressed = CHAR_BEG; + break; + case KC_0: + characterPressed = CHAR_END; + break; + case KC_EQUAL: + characterPressed = CHAR_ADD; + break; + case KC_6: + characterPressed = CHAR_EXP; + break; + case KC_8: + characterPressed = CHAR_MUL; + break; + case KC_S: + characterPressed = CHAR_ASN; + break; + case KC_C: + characterPressed = CHAR_ACS; + break; + case KC_T: + characterPressed = CHAR_ATN; + break; + case KC_L: + characterPressed = CHAR_LOG; + break; + default: + break; + } + } + } + + if(inputLocation < BUFFER_SIZE && characterPressed != '\0') + { + text[inputLocation] = characterPressed; + inputLocation++; + } + + return (!record->event.pressed || (record->event.pressed && (characterPressed != '\0' || forceReturnTrue))); + } + else + { + return true; + } +} \ No newline at end of file diff --git a/users/doogle999/doogle999.h b/users/doogle999/doogle999.h new file mode 100644 index 0000000000..901861fd99 --- /dev/null +++ b/users/doogle999/doogle999.h @@ -0,0 +1,87 @@ +#ifndef USERSPACE +#define USERSPACE + +#include "quantum.h" + +#define NO_ACTION_ONESHOT +#define NO_ACTION_MACRO + +#define MODS_SHIFT_MASK (MOD_BIT(KC_LSHIFT)|MOD_BIT(KC_RSHIFT)) + +#define BUFFER_SIZE 32 + +/*----- + Special +-----*/ + +#define CHAR_BEG '(' +#define CHAR_END ')' + +/*----- + Operators +-----*/ + +#define CHAR_ADD '+' +#define PRIO_ADD 1 + +#define CHAR_SUB '-' +#define PRIO_SUB 1 + +#define CHAR_MUL '*' +#define PRIO_MUL 2 + +#define CHAR_DIV '/' +#define PRIO_DIV 2 + +#define CHAR_EXP '^' +#define PRIO_EXP 3 + +/*----- + Functions +-----*/ + +#define CHAR_SIN 's' +#define CHAR_COS 'c' +#define CHAR_TAN 't' + +#define CHAR_ASN 'S' +#define CHAR_ACS 'C' +#define CHAR_ATN 'T' + +#define CHAR_LGE 'l' +#define CHAR_LOG 'L' + +#define CHAR_SQT 'q' + +struct OP // Operator/function +{ + char c; + unsigned char priority; + bool ltr; +}; + +union TokenRaw // A token after the input has been processed, can either be a number or an operator/function +{ + double num; + struct OP op; +}; + +struct Token // Encapsulator +{ + bool isNum; + union TokenRaw raw; +}; + +enum CalcFunctions // Hardware calculator key functionality +{ + CALC = SAFE_RANGE, + ENDCALC +}; + +extern char backspaceText[BUFFER_SIZE + 1]; // Pretty dumb waste of memory because only backspace characters, used with send_string to backspace and remove input +extern char text[BUFFER_SIZE + 1]; // Used to store input and then output when ready to print +extern unsigned char inputLocation; // Current index in text input + +double calc(char input[]); + +#endif \ No newline at end of file diff --git a/users/doogle999/readme.md b/users/doogle999/readme.md new file mode 100644 index 0000000000..4e460d1815 --- /dev/null +++ b/users/doogle999/readme.md @@ -0,0 +1,18 @@ +Copyright 2018 @doogle999 + + +This folder is just for some calculator code for my keyboards. + + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . \ No newline at end of file diff --git a/users/doogle999/rules.mk b/users/doogle999/rules.mk new file mode 100644 index 0000000000..969b41cab0 --- /dev/null +++ b/users/doogle999/rules.mk @@ -0,0 +1,12 @@ +SRC += doogle999.c + +BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000) +MOUSEKEY_ENABLE = no # Mouse keys(+4700) +EXTRAKEY_ENABLE = yes # Audio control and System control(+450) +CONSOLE_ENABLE = no # Console for debug(+400) +COMMAND_ENABLE = no # Commands for debug and configuration +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend +NKRO_ENABLE = yes # USB Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work +BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality +AUDIO_ENABLE = no +RGBLIGHT_ENABLE = yes \ No newline at end of file