diff --git a/smol_gkos.ino b/smol_gkos.ino index 262d64b..6fd3864 100644 --- a/smol_gkos.ino +++ b/smol_gkos.ino @@ -23,17 +23,7 @@ const byte PIN_BALL_SDA = 18; const byte PIN_BALL_SCL = 19; const byte PIN_BALL_INT = 11; -const byte BUTTON_A = 0; -const byte BUTTON_B = 1; -const byte BUTTON_C = 2; -const byte BUTTON_D = 3; -const byte BUTTON_E = 4; -const byte BUTTON_F = 5; -const byte BUTTON_MOUSE_UL = 6; -const byte BUTTON_MOUSE_UR = 7; -const byte BUTTON_MOUSE_DL = 8; -const byte BUTTON_MOUSE_DR = 9; - +enum Button { A, B, C, D, E, F, MouseUL, MouseUR, MouseDL, MouseDR }; const int button_pins[10] = { PIN_A, PIN_B, PIN_C, PIN_D, PIN_E, PIN_F, PIN_MOUSE_UL, PIN_MOUSE_UR, PIN_MOUSE_DL, PIN_MOUSE_DR @@ -41,12 +31,76 @@ const int button_pins[10] = { const int debounce_time = 400; int button_timers[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -// TODO: differentiate between GKOS modifiers and keyboard modifiers somehow +// different actions that the keyboard can take +// - print a single character +// - press (and release) a key +// - toggle a modifier +enum ActionType { PrintCharacter, PressKey, ToggleModifier }; + +const char button_characters[6] = { 'a', 'b', 'c', 'd', 'e', 'f' }; +const char button_characters_shifted[6] = { 'A', 'B', 'C', 'D', 'E', 'F' }; +const char button_characters_symbol[6] = { '1', '2', '3', '4', '5', '6' }; + /* backspace, space (keep those two first since they overlap with the rest ) g, k, o, s, w, native */ -const byte modifiers[8] = { 7, 56, 24, 48, 3, 6, 40, 5 }; +const byte chords[8] = { 7, 56, 24, 48, 3, 6, 40, 5 }; +// buttons that can be combined with a chord for a keypress +const byte chord_buttons[24] = { + Button::D, Button::E, Button::F, + Button::A, Button::B, Button::C, + Button::A, Button::B, Button::C, + Button::A, Button::B, Button::C, + Button::D, Button::E, Button::F, + Button::D, Button::E, Button::F, + Button::A, Button::B, Button::C, + Button::D, Button::E, Button::F, +}; +// keypresses for chords in sets of 4 +// index 0 is the chord on its own, 1-3 correspond to chord_buttons +const int chord_targets[32] = { + KEY_BACKSPACE, KEY_LEFT, KEY_LEFT, KEY_HOME, + KEY_SPACE, KEY_RIGHT, KEY_RIGHT, KEY_END, + 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', + 0, 0, 0, 0 +}; +const int chord_targets_shifted[32] = { + KEY_BACKSPACE, KEY_LEFT, KEY_LEFT, KEY_HOME, + KEY_SPACE, KEY_RIGHT, KEY_RIGHT, KEY_END, + 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', + 0, 0, 0, 0 +}; +// TODO: check that all the symbols work +// TODO: match symbols to keypresses in the keyboard layout +const int chord_targets_symbol[32] = { + KEY_BACKSPACE, KEY_LEFT, KEY_LEFT, KEY_HOME, + KEY_SPACE, KEY_RIGHT, KEY_RIGHT, KEY_END, + '0', '7', '8', '9', + '#', '@', '½', '&', + '+', '%', '=', '^', + '*', '$', '€', '£', + '(', '[', '<', '{', + ')', ']', '>', '}' +}; +const byte chord_target_types[32] = { + 1, 1, 1, 1, + 1, 1, 1, 1, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; /* dash, backslash, slash, apostrophe, comma, exclamation point, question mark, @@ -57,10 +111,6 @@ const byte specials[22] = { 17, 51, 30, 10, 20, 12, 33, 34, 9, 36, 27, 54, 18, 45, 63, 31, 47, 55, 62, 43, 61, 59 }; -enum SpecialType { PrintCharacter, PressKey, ToggleModifier }; -const byte special_action_types[22] = { - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 1, 1, 1, 1 -}; enum Modifier { Shift, SymbolShift, Keyset, Control, Alt }; const int special_action_targets[22] = { '-', '\\', '/', '\'', ',', '!', '?', '/', KEY_UP, KEY_DOWN, KEY_PAGE_UP, @@ -68,11 +118,28 @@ const int special_action_targets[22] = { KEY_ESC, Modifier::Control, Modifier::Alt, KEY_DELETE, KEY_INSERT, KEY_TAB, KEY_ENTER }; +const int special_action_targets_symbol[22] = { + '_', '\`', 0, '\"', ';', '|', '~', ':', 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0 +}; +const byte special_action_target_types[22] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 1, 1, 1, 1 +}; byte key_pressed = 0; byte key_pressed_total = 0; -bool modifier_used = false; -bool modified_pressed = false; +bool chord_used = false; +bool chorded_pressed = false; + +bool mod_shift = false; +bool mod_shift_lock = false; +bool mod_symbol = false; +bool mod_symbol_lock = false; +// TODO: control and alt modifiers +bool mod_control = false; +bool mod_alt = false; void setup() { pinMode(PIN_A, INPUT_PULLUP); @@ -117,12 +184,15 @@ void loop() { if (b < 6) { key_pressed &= ~(1 << b); // gkos key was released, check if it completed a chord - key_released(b); + // clear temporary modifiers if a keypress was completed + if (key_released(b)) + mod_shift = mod_symbol = mod_control = mod_alt = false; + update_leds(); if (key_pressed == 0) { key_pressed_total = 0; // clear flags - modifier_used = false; - modified_pressed = false; + chord_used = false; + chorded_pressed = false; } } } @@ -131,7 +201,10 @@ void loop() { } } -void key_released(byte key) { +// returns true if a keypress was completed +bool key_released(byte key) { + int target = 0; + /* SPECIALS */ @@ -145,229 +218,133 @@ void key_released(byte key) { } } - if (is_special && !modifier_used) { + if (is_special && !chord_used) { // only register a special keypress when all keys have been released if (key_pressed != 0) - return; + return false; - switch (special_action_types[special]) { - case SpecialType::PrintCharacter: - Keyboard.print((char)special_action_targets[special]); - return; - case SpecialType::PressKey: - Keyboard.press(special_action_targets[special]); - Keyboard.release(special_action_targets[special]); - return; - case SpecialType::ToggleModifier: - // TODO - return; + target = special_action_targets[special]; + if (mod_shift || mod_shift_lock || mod_symbol || mod_symbol_lock) { + if (special_action_targets_symbol[special] != 0) + target = special_action_targets_symbol[special]; } + + switch (special_action_target_types[special]) { + case ActionType::PrintCharacter: + Keyboard.print((char)target); + return true; + case ActionType::PressKey: + Keyboard.press(target); + Keyboard.release(target); + return true; + case ActionType::ToggleModifier: + switch (target) { + case Modifier::Shift: + mod_symbol = mod_symbol_lock = false; + if (mod_shift_lock) { + mod_shift_lock = false; + } else if (mod_shift) { + mod_shift = false; + mod_shift_lock = true; + } else { + mod_shift = true; + } + return false; + case Modifier::SymbolShift: + mod_shift = mod_shift_lock = mod_symbol_lock = false; + mod_symbol = !mod_symbol; + return false; + case Modifier::Keyset: + mod_shift = mod_shift_lock = mod_symbol = false; + mod_symbol_lock = !mod_symbol_lock; + return false; + case Modifier::Control: + mod_control = !mod_control; + return false; + case Modifier::Alt: + mod_alt = !mod_alt; + return false; + } + } + return false; } /* - MODIFIERS + CHORDS */ - bool is_modifier = false; - byte modifier = 0; + bool is_chord = false; + byte chord = 0; for (byte b = 0; b < 8; b++) { - if ((key_pressed_total & modifiers[b]) == modifiers[b]) { - is_modifier = true; - modifier = b; - modifier_used = true; + if ((key_pressed_total & chords[b]) == chords[b]) { + is_chord = true; + chord = b; + chord_used = true; break; } } - if (is_modifier) { - switch (modifier) { - case 0: // backspace - if (!modified_pressed && key_pressed == 0 && - key_pressed_total == modifiers[modifier]) { - Keyboard.press(KEY_BACKSPACE); - Keyboard.release(KEY_BACKSPACE); - return; - } - if (key == BUTTON_D || key == BUTTON_E) { - Keyboard.press(KEY_LEFT); - Keyboard.release(KEY_LEFT); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_F) { - Keyboard.press(KEY_HOME); - Keyboard.release(KEY_HOME); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } - return; - case 1: // space - if (!modified_pressed && key_pressed == 0 && - key_pressed_total == modifiers[modifier]) { - Keyboard.press(KEY_SPACE); - Keyboard.release(KEY_SPACE); - return; - } - if (key == BUTTON_A || key == BUTTON_B) { - Keyboard.press(KEY_RIGHT); - Keyboard.release(KEY_RIGHT); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_C) { - Keyboard.press(KEY_END); - Keyboard.release(KEY_END); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } - return; - case 2: // G - if (!modified_pressed && key_pressed == 0 && - key_pressed_total == modifiers[modifier]) { - Keyboard.print('g'); - return; - } - if (key == BUTTON_A) { - Keyboard.print('h'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_B) { - Keyboard.print('i'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_C) { - Keyboard.print('j'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } - return; - case 3: // K - if (!modified_pressed && key_pressed == 0 && - key_pressed_total == modifiers[modifier]) { - Keyboard.print('k'); - return; - } - if (key == BUTTON_A) { - Keyboard.print('l'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_B) { - Keyboard.print('m'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_C) { - Keyboard.print('n'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } - return; - case 4: // O - if (!modified_pressed && key_pressed == 0 && - key_pressed_total == modifiers[modifier]) { - Keyboard.print('o'); - return; - } - if (key == BUTTON_D) { - Keyboard.print('p'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_E) { - Keyboard.print('q'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_F) { - Keyboard.print('r'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } - return; - case 5: // S - if (!modified_pressed && key_pressed == 0 && - key_pressed_total == modifiers[modifier]) { - Keyboard.print('s'); - return; - } - if (key == BUTTON_D) { - Keyboard.print('t'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_E) { - Keyboard.print('u'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_F) { - Keyboard.print('v'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } - return; - case 6: // W - if (!modified_pressed && key_pressed == 0 && - key_pressed_total == modifiers[modifier]) { - Keyboard.print('w'); - return; - } - if (key == BUTTON_A) { - Keyboard.print('x'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_B) { - Keyboard.print('y'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } else if (key == BUTTON_C) { - Keyboard.print('z'); - key_pressed_total &= ~(1 << key); - modified_pressed = true; - return; - } - return; - case 7: // native - // TODO - return; + if (is_chord) { + // chord on its own + if (!chorded_pressed && key_pressed == 0 && + key_pressed_total == chords[chord]) { + target = chord_targets[chord * 4]; + if (mod_shift || mod_shift_lock) + target = chord_targets_shifted[chord * 4]; + else if (mod_symbol || mod_symbol_lock) + target = chord_targets_symbol[chord * 4]; + + switch (chord_target_types[chord * 4]) { + case ActionType::PrintCharacter: + Keyboard.print((char)target); + return true; + case ActionType::PressKey: + Keyboard.press(target); + Keyboard.release(target); + return true; + } + return false; } + // keys that can be chorded with + for (byte b = 0; b < 3; b++) { + if (key == chord_buttons[chord * 3 + b]) { + target = chord_targets[chord * 4 + 1 + b]; + if (mod_shift || mod_shift_lock) + target = chord_targets_shifted[chord * 4 + 1 + b]; + else if (mod_symbol || mod_symbol_lock) + target = chord_targets_symbol[chord * 4 + 1 + b]; + + // erase key from total so you can hold the chord down for + // multiple chorded keypresses + key_pressed_total &= ~(1 << key); + chorded_pressed = true; + switch (chord_target_types[chord * 4 + 1 + b]) { + case ActionType::PrintCharacter: + Keyboard.print((char)target); + return true; + case ActionType::PressKey: + Keyboard.press(target); + Keyboard.release(target); + return true; + } + } + } + return false; } /* REGULAR KEYS */ - switch (key) { - case BUTTON_A: - Keyboard.print('a'); - return; - case BUTTON_B: - Keyboard.print('b'); - return; - case BUTTON_C: - Keyboard.print('c'); - return; - case BUTTON_D: - Keyboard.print('d'); - return; - case BUTTON_E: - Keyboard.print('e'); - return; - case BUTTON_F: - Keyboard.print('f'); - return; - default: - Serial.print("unknown key "); - Serial.println(key); - return; - } + if (mod_shift || mod_shift_lock) + Keyboard.print(button_characters_shifted[key]); + else if (mod_symbol || mod_symbol_lock) + Keyboard.print(button_characters_symbol[key]); + else + Keyboard.print(button_characters[key]); + return true; +} + +void update_leds() { + digitalWrite(PIN_LED1, mod_shift || mod_shift_lock); + digitalWrite(PIN_LED2, mod_symbol || mod_symbol_lock); + digitalWrite(PIN_LED3, mod_control || mod_alt); }