mirror of
				https://github.com/mfulz/qmk_firmware.git
				synced 2025-11-03 14:52:38 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			461 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			461 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "doogle999.h"
 | 
						|
 | 
						|
static unsigned char inputLocation = 0; // Current index in text input
 | 
						|
 | 
						|
static double calc(const char input[CALC_BUFFER_SIZE +1]) // Finds value of input char array, relatively small and fast I think
 | 
						|
{
 | 
						|
  char inputToken[CALC_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[CALC_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 < CALC_BUFFER_SIZE + 1)
 | 
						|
  {
 | 
						|
    char digit = input[inputLocation];
 | 
						|
 | 
						|
    if(inputLocation == 0 && input[inputLocation] == CALC_CHAR_SUB && input[inputLocation + 1] == CALC_CHAR_BEG)
 | 
						|
    {
 | 
						|
      tokens[tokenCount].raw.num = 0;
 | 
						|
      tokens[tokenCount].isNum = true;
 | 
						|
 | 
						|
      tokenCount++;
 | 
						|
      dashAsMinus = true;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((digit >= '0' && digit <= '9') || /* valid digit */
 | 
						|
        (inputTokenLocation != 0 && input[inputLocation] == CALC_CHAR_DEC) || /* valid floating point */
 | 
						|
        (!dashAsMinus && inputTokenLocation == 0 && input[inputLocation] == CALC_CHAR_SUB)) /* - is negative sign */
 | 
						|
    {
 | 
						|
      inputToken[inputTokenLocation] = input[inputLocation];
 | 
						|
      inputTokenLocation++;
 | 
						|
      inputLocation++;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    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;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    /* inputTokenLocation == 0 */
 | 
						|
    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 CALC_CHAR_BEG:
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_END:
 | 
						|
        dashAsMinus = true;
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_ADD:
 | 
						|
        tokens[tokenCount].raw.op.priority = CALC_PRIO_ADD;
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_SUB:
 | 
						|
        tokens[tokenCount].raw.op.priority = CALC_PRIO_SUB;
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_MUL:
 | 
						|
        tokens[tokenCount].raw.op.priority = CALC_PRIO_MUL;
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_DIV:
 | 
						|
        tokens[tokenCount].raw.op.priority = CALC_PRIO_DIV;
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_EXP:
 | 
						|
        tokens[tokenCount].raw.op.priority = CALC_PRIO_EXP;
 | 
						|
        tokens[tokenCount].raw.op.ltr = false;
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_SIN:
 | 
						|
      case CALC_CHAR_COS:
 | 
						|
      case CALC_CHAR_TAN:
 | 
						|
      case CALC_CHAR_ASN:
 | 
						|
      case CALC_CHAR_ACS:
 | 
						|
      case CALC_CHAR_ATN:
 | 
						|
      case CALC_CHAR_LGE:
 | 
						|
      case CALC_CHAR_LOG:
 | 
						|
      case CALC_CHAR_SQT:
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_EUL:
 | 
						|
        tokens[tokenCount].isNum = true;
 | 
						|
        tokens[tokenCount].raw.num = CALC_VALU_EUL;
 | 
						|
        dashAsMinus = true;
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_PI:
 | 
						|
        tokens[tokenCount].isNum = true;
 | 
						|
        tokens[tokenCount].raw.num = CALC_VALU_PI;
 | 
						|
        dashAsMinus = true;
 | 
						|
        break;
 | 
						|
      case '\0':
 | 
						|
        tokenCount--;
 | 
						|
        inputLocation = CALC_BUFFER_SIZE;
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        tokenCount--;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    tokenCount++;
 | 
						|
    inputLocation++;
 | 
						|
  }
 | 
						|
 | 
						|
  struct Token output[CALC_BUFFER_SIZE + 1]; // Final output tokens before evaluation
 | 
						|
  struct Token opstack[CALC_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 == CALC_CHAR_BEG)
 | 
						|
    {
 | 
						|
      opstack[opstackLocation] = tokens[i];
 | 
						|
      opstackLocation++;
 | 
						|
    }
 | 
						|
    else if(tokens[i].raw.op.c == CALC_CHAR_END)
 | 
						|
    {
 | 
						|
      while(opstack[opstackLocation - 1].raw.op.c != CALC_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 != CALC_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[CALC_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++;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if(output[i].raw.op.priority == 0)
 | 
						|
    {
 | 
						|
      if (answerLocation < 1) { /* not handled here -- ERROR? */ } else
 | 
						|
      if(answerLocation >= 1)
 | 
						|
      {
 | 
						|
        double (*op)(double);
 | 
						|
        switch(output[i].raw.op.c)
 | 
						|
        {
 | 
						|
        case CALC_CHAR_SIN:
 | 
						|
          op = sin;
 | 
						|
          break;
 | 
						|
        case CALC_CHAR_COS:
 | 
						|
          op = cos;
 | 
						|
          break;
 | 
						|
        case CALC_CHAR_TAN:
 | 
						|
          op = tan;
 | 
						|
          break;
 | 
						|
        case CALC_CHAR_ASN:
 | 
						|
          op = asin;
 | 
						|
          break;
 | 
						|
        case CALC_CHAR_ACS:
 | 
						|
          op = acos;
 | 
						|
          break;
 | 
						|
        case CALC_CHAR_ATN:
 | 
						|
          op = atan;
 | 
						|
          break;
 | 
						|
        case CALC_CHAR_LGE:
 | 
						|
          op = log;
 | 
						|
          break;
 | 
						|
        case CALC_CHAR_LOG:
 | 
						|
          op = log10;
 | 
						|
          break;
 | 
						|
        case CALC_CHAR_SQT:
 | 
						|
          op = sqrt;
 | 
						|
          break;
 | 
						|
        default:
 | 
						|
          continue; /* invalid input */
 | 
						|
        }
 | 
						|
        answer[answerLocation - 1] = op(answer[answerLocation - 1]);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    /* priority != 0 */
 | 
						|
    else if(answerLocation >= 2)
 | 
						|
    {
 | 
						|
      switch(output[i].raw.op.c)
 | 
						|
      {
 | 
						|
      case CALC_CHAR_ADD:
 | 
						|
        answer[answerLocation - 2] += answer[answerLocation - 1];
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_SUB:
 | 
						|
        answer[answerLocation - 2] -= answer[answerLocation - 1];
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_MUL:
 | 
						|
        answer[answerLocation - 2] *= answer[answerLocation - 1];
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_DIV:
 | 
						|
        answer[answerLocation - 2] /= answer[answerLocation - 1];
 | 
						|
        break;
 | 
						|
      case CALC_CHAR_EXP:
 | 
						|
        answer[answerLocation - 2] = pow(answer[answerLocation - 2], answer[answerLocation - 1]);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      answerLocation--;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return answer[0];
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * @returns  0 when nothing should happen and QMK should work as usual
 | 
						|
 * @returns -1 when invalid input was given, QMK should ignore it
 | 
						|
 * @returns -2 when BSP should be done
 | 
						|
 * @returns -3 when CALC should be done
 | 
						|
 * @returns -4 when ENDCALC should be done
 | 
						|
 * @returns positive value of CALC_* when normal input was processed
 | 
						|
 */
 | 
						|
static int process_input(const uint16_t keycode, const uint8_t mods, const keyevent_t event)
 | 
						|
{
 | 
						|
  /* handle even when no key was pressed */
 | 
						|
  if(!event.pressed)
 | 
						|
  {
 | 
						|
    switch(keycode)
 | 
						|
    {
 | 
						|
      /* QMK should handle those */
 | 
						|
      case KC_RSFT:
 | 
						|
      case KC_LSFT:
 | 
						|
        return 0;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    /* ??? ignore */
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  /* when shift key is pressed handle characters differently */
 | 
						|
  char characterPressed;
 | 
						|
  if((get_mods() & MODS_SHIFT_MASK))
 | 
						|
  {
 | 
						|
    switch(keycode)
 | 
						|
    {
 | 
						|
      case KC_9:
 | 
						|
        characterPressed = CALC_CHAR_BEG;
 | 
						|
        break;
 | 
						|
      case KC_0:
 | 
						|
        characterPressed = CALC_CHAR_END;
 | 
						|
        break;
 | 
						|
      case KC_EQUAL:
 | 
						|
        characterPressed = CALC_CHAR_ADD;
 | 
						|
        break;
 | 
						|
      case KC_KP_PLUS:
 | 
						|
        characterPressed = CALC_CHAR_ADD;
 | 
						|
        break;
 | 
						|
      case KC_6:
 | 
						|
        characterPressed = CALC_CHAR_EXP;
 | 
						|
        break;
 | 
						|
      case KC_8:
 | 
						|
        characterPressed = CALC_CHAR_MUL;
 | 
						|
        break;
 | 
						|
      case KC_KP_ASTERISK:
 | 
						|
        characterPressed = CALC_CHAR_MUL;
 | 
						|
        break;
 | 
						|
      case KC_S:
 | 
						|
        characterPressed = CALC_CHAR_ASN;
 | 
						|
        break;
 | 
						|
      case KC_C:
 | 
						|
        characterPressed = CALC_CHAR_ACS;
 | 
						|
        break;
 | 
						|
      case KC_T:
 | 
						|
        characterPressed = CALC_CHAR_ATN;
 | 
						|
        break;
 | 
						|
      case KC_L:
 | 
						|
        characterPressed = CALC_CHAR_LOG;
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        return -1;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    return characterPressed;
 | 
						|
  }
 | 
						|
 | 
						|
  /* normal key handling:  shift not pressed */
 | 
						|
 | 
						|
  /* digits */
 | 
						|
  if (keycode == KC_KP_0 || keycode == KC_0) {
 | 
						|
    return '0';
 | 
						|
  } else if (keycode >= KC_KP_1 && keycode <= KC_KP_9) {
 | 
						|
    return keycode - KC_KP_1 +1 + '0';
 | 
						|
  } else if (keycode >= KC_1 && keycode <= KC_9) {
 | 
						|
    return keycode - KC_1 +1 + '0';
 | 
						|
  }
 | 
						|
 | 
						|
  /* other tokens */
 | 
						|
  switch (keycode) {
 | 
						|
    case KC_MINUS:
 | 
						|
    case KC_KP_MINUS:
 | 
						|
      return characterPressed = CALC_CHAR_SUB;
 | 
						|
    case KC_SLASH:
 | 
						|
    case KC_KP_SLASH:
 | 
						|
      return characterPressed = CALC_CHAR_DIV;
 | 
						|
    case KC_S:
 | 
						|
      return characterPressed = CALC_CHAR_SIN;
 | 
						|
    case KC_C:
 | 
						|
      return characterPressed = CALC_CHAR_COS;
 | 
						|
    case KC_T:
 | 
						|
      return characterPressed = CALC_CHAR_TAN;
 | 
						|
    case KC_Q:
 | 
						|
      return characterPressed = CALC_CHAR_SQT;
 | 
						|
    case KC_L:
 | 
						|
      return characterPressed = CALC_CHAR_LGE;
 | 
						|
    case KC_DOT:
 | 
						|
    case KC_KP_DOT:
 | 
						|
      return characterPressed = CALC_CHAR_DEC;
 | 
						|
    case KC_P:
 | 
						|
      return characterPressed = CALC_CHAR_PI;
 | 
						|
    case KC_E:
 | 
						|
      return characterPressed = CALC_CHAR_EUL;
 | 
						|
    case KC_BSPC:
 | 
						|
      return -2;
 | 
						|
    case KC_RSFT:
 | 
						|
      return 0;
 | 
						|
    case KC_LSFT:
 | 
						|
      return 0;
 | 
						|
    case CALC:
 | 
						|
      return -3;
 | 
						|
    case ENDCALC:
 | 
						|
      return -4;
 | 
						|
    default:
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool process_record_user(uint16_t keycode, keyrecord_t* record)
 | 
						|
{
 | 
						|
	static char text[CALC_BUFFER_SIZE + 1]; // Used to store input and then output when ready to print
 | 
						|
	static char backspaceText[CALC_BUFFER_SIZE + 1]; // Pretty dumb waste of memory because only backspace characters, used with send_string to backspace and remove input
 | 
						|
 | 
						|
	if((get_highest_layer(layer_state) == CALC_LAYER && CALC_FORCE_NUM_LOCK_INSIDE_CALC) || (get_highest_layer(layer_state) != CALC_LAYER && CALC_FORCE_NUM_LOCK_OUTSIDE_CALC))
 | 
						|
	{
 | 
						|
		bool numpadKeyPressed = record->event.pressed &&
 | 
						|
			!(get_mods() & MODS_SHIFT_MASK) &&
 | 
						|
			/* KC_KP_1, KC_KP_2, ..., KC_KP_0, KC_KP_DOT */
 | 
						|
			(keycode >= KC_KP_1 && keycode <= KC_KP_DOT);
 | 
						|
 | 
						|
		if(numpadKeyPressed && !(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)))
 | 
						|
		{
 | 
						|
			add_key(KC_NUM_LOCK);
 | 
						|
			send_keyboard_report();
 | 
						|
			del_key(KC_NUM_LOCK);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if(get_highest_layer(layer_state) != CALC_LAYER) { return true; }
 | 
						|
 | 
						|
	int action = process_input(keycode, get_mods(), record->event);
 | 
						|
	switch(action)
 | 
						|
	{
 | 
						|
	case 0:
 | 
						|
		return true;
 | 
						|
	case -1:
 | 
						|
		return false;
 | 
						|
	case -2:
 | 
						|
		if(inputLocation > 0)
 | 
						|
		{
 | 
						|
			inputLocation--;
 | 
						|
			text[inputLocation] = '\0';
 | 
						|
			backspaceText[0] = (char)8;
 | 
						|
			backspaceText[1] = '\0';
 | 
						|
			send_string(backspaceText);
 | 
						|
		}
 | 
						|
		return false;
 | 
						|
	case -3:
 | 
						|
		for(int i = 0; i < inputLocation; i++)
 | 
						|
		{
 | 
						|
			backspaceText[i] = (char)8;
 | 
						|
		}
 | 
						|
		send_string(backspaceText);
 | 
						|
		dtostrf(calc(text), CALC_PRINT_SIZE, CALC_PRINT_SIZE, text);
 | 
						|
		send_string(text);
 | 
						|
		for(unsigned char i = 0; i < CALC_BUFFER_SIZE; i++)
 | 
						|
		{
 | 
						|
			text[i] = '\0';
 | 
						|
			backspaceText[i] = '\0';
 | 
						|
		}
 | 
						|
		inputLocation = 0;
 | 
						|
		return false;
 | 
						|
	case -4:
 | 
						|
		for(unsigned char i = 0; i < CALC_BUFFER_SIZE; i++)
 | 
						|
		{
 | 
						|
			text[i] = '\0';
 | 
						|
			backspaceText[i] = '\0';
 | 
						|
		}
 | 
						|
		inputLocation = 0;
 | 
						|
		layer_off(CALC_LAYER);
 | 
						|
		return false;
 | 
						|
	default:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	char characterPressed = (char)action;
 | 
						|
 | 
						|
	if(inputLocation < CALC_BUFFER_SIZE)
 | 
						|
	{
 | 
						|
		text[inputLocation] = characterPressed;
 | 
						|
		inputLocation++;
 | 
						|
 | 
						|
		char characterToSend[2];
 | 
						|
		characterToSend[0] = characterPressed;
 | 
						|
		characterToSend[1] = '\0';
 | 
						|
 | 
						|
		send_string(characterToSend);
 | 
						|
	}
 | 
						|
	return false;
 | 
						|
}
 |