X-Git-Url: https://git.friedersdorff.com/?a=blobdiff_plain;f=common%2Fkeyboard.c;h=c7ea2b840c4124ce437b027adf84c2f9ad9d7686;hb=e451c059296a4c9af7a476577fee64afb9965bca;hp=25f32eb02f52a615adf4c44e016afb03a488ff4f;hpb=fca518f90dfc7f72132ff7950b000cfbd5709807;p=max%2Ftmk_keyboard.git
diff --git a/common/keyboard.c b/common/keyboard.c
index 25f32eb0..c7ea2b84 100644
--- a/common/keyboard.c
+++ b/common/keyboard.c
@@ -15,179 +15,605 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "keyboard.h"
-#include "host.h"
-#include "layer.h"
#include "matrix.h"
+#include "keymap.h"
+#include "host.h"
#include "led.h"
-#include "usb_keycodes.h"
+#include "keycode.h"
#include "timer.h"
#include "print.h"
#include "debug.h"
#include "command.h"
+#include "util.h"
#ifdef MOUSEKEY_ENABLE
#include "mousekey.h"
#endif
-#ifdef EXTRAKEY_ENABLE
-#include
+
+
+#define LAYER_DELAY 250
+
+typedef enum keykind {
+ NONE,
+ FN_DOWN, FN_UP,
+ FNK_DOWN, FNK_UP,
+ KEY_DOWN, KEY_UP,
+ MOD_DOWN, MOD_UP,
+} keykind_t;
+
+typedef enum { IDLE, DELAYING, WAITING, PRESSING } kbdstate_t;
+
+
+#ifdef KEYMAP_DEFAULT_LAYER
+uint8_t default_layer = KEYMAP_DEFAULT_LAYER;
+uint8_t current_layer = KEYMAP_DEFAULT_LAYER;
+#else
+uint8_t default_layer = 0;
+uint8_t current_layer = 0;
#endif
+/* keyboard internal states */
+static kbdstate_t kbdstate = IDLE;
+static uint8_t fn_state_bits = 0;
+static keyrecord_t delayed_fn;
+static keyrecord_t waiting_key;
-static uint8_t last_leds = 0;
+static const char *state_str(kbdstate_t state)
+{
+ if (state == IDLE) return PSTR("IDLE");
+ if (state == DELAYING) return PSTR("DELAYING");
+ if (state == WAITING) return PSTR("WAITING");
+ if (state == PRESSING) return PSTR("PRESSING");
+ return PSTR("UNKNOWN");
+}
-void keyboard_init(void)
+static inline keykind_t get_keykind(uint8_t code, bool pressed)
{
- timer_init();
- matrix_init();
-#ifdef PS2_MOUSE_ENABLE
- ps2_mouse_init();
-#endif
+ if IS_KEY(code) return (pressed ? KEY_DOWN : KEY_UP);
+ if IS_MOD(code) return (pressed ? MOD_DOWN : MOD_UP);
+ if IS_FN(code) {
+ if (keymap_fn_keycode(FN_INDEX(code)))
+ return (pressed ? FNK_DOWN : FNK_UP);
+ else
+ return (pressed ? FN_DOWN : FN_UP);
+ }
+ if IS_MOUSEKEY(code) return (pressed ? KEY_DOWN : KEY_UP);
+ if IS_SYSTEM(code) return (pressed ? KEY_DOWN : KEY_UP);
+ if IS_CONSUMER(code) return (pressed ? KEY_DOWN : KEY_UP);
+ return NONE;
}
-void keyboard_proc(void)
+static void clear_keyboard(void)
{
- uint8_t fn_bits = 0;
-#ifdef EXTRAKEY_ENABLE
- uint16_t consumer_code = 0;
- uint16_t system_code = 0;
+ host_clear_keys();
+ host_clear_mods();
+ host_send_keyboard_report();
+
+ host_system_send(0);
+ host_consumer_send(0);
+
+#ifdef MOUSEKEY_ENABLE
+ mousekey_clear();
+ mousekey_send();
#endif
+}
- matrix_scan();
+static void clear_keyboard_but_mods(void)
+{
+ host_clear_keys();
+ host_send_keyboard_report();
- if (matrix_is_modified()) {
- if (debug_matrix) matrix_print();
-#ifdef DEBUG_LED
- // LED flash for debug
- DEBUG_LED_CONFIG;
- DEBUG_LED_ON;
+ host_system_send(0);
+ host_consumer_send(0);
+
+#ifdef MOUSEKEY_ENABLE
+ mousekey_clear();
+ mousekey_send();
#endif
+}
+
+static bool anykey_sent_to_host(void)
+{
+ return (host_has_anykey() || host_mouse_in_use() ||
+ host_last_sysytem_report() || host_last_consumer_report());
+}
+
+static void layer_switch_on(uint8_t code)
+{
+ if (!IS_FN(code)) return;
+ fn_state_bits |= FN_BIT(code);
+ uint8_t new_layer = (fn_state_bits ? keymap_fn_layer(biton(fn_state_bits)) : default_layer);
+ if (current_layer != new_layer) {
+ debug("Layer Switch(on): "); debug_hex(current_layer);
+ debug(" -> "); debug_hex(new_layer); debug("\n");
+
+ clear_keyboard_but_mods();
+ current_layer = new_layer;
+ }
+}
+
+static bool layer_switch_off(uint8_t code)
+{
+ if (!IS_FN(code)) return false;
+ fn_state_bits &= ~FN_BIT(code);
+ uint8_t new_layer = (fn_state_bits ? keymap_fn_layer(biton(fn_state_bits)) : default_layer);
+ if (current_layer != new_layer) {
+ debug("Layer Switch(off): "); debug_hex(current_layer);
+ debug(" -> "); debug_hex(new_layer); debug("\n");
+
+ clear_keyboard_but_mods();
+ current_layer = new_layer;
+ return true;
}
+ return false;
+}
- if (matrix_has_ghost()) {
- // should send error?
- debug("matrix has ghost!!\n");
- return;
+static void register_code(uint8_t code)
+{
+ if IS_KEY(code) {
+ if (command_proc(code)) {
+ //clear_keyboard();
+ } else {
+ host_add_key(code);
+ host_send_keyboard_report();
+ }
+ }
+ else if IS_MOD(code) {
+ host_add_mod_bit(MOD_BIT(code));
+ host_send_keyboard_report();
+ }
+ else if IS_FN(code) {
+ host_add_key(keymap_fn_keycode(FN_INDEX(code)));
+ host_send_keyboard_report();
+ }
+ else if IS_MOUSEKEY(code) {
+#ifdef MOUSEKEY_ENABLE
+ mousekey_on(code);
+ mousekey_send();
+#endif
+ }
+ else if IS_CONSUMER(code) {
+ uint16_t usage = 0;
+ switch (code) {
+ case KC_AUDIO_MUTE:
+ usage = AUDIO_MUTE;
+ break;
+ case KC_AUDIO_VOL_UP:
+ usage = AUDIO_VOL_UP;
+ break;
+ case KC_AUDIO_VOL_DOWN:
+ usage = AUDIO_VOL_DOWN;
+ break;
+ case KC_MEDIA_NEXT_TRACK:
+ usage = TRANSPORT_NEXT_TRACK;
+ break;
+ case KC_MEDIA_PREV_TRACK:
+ usage = TRANSPORT_PREV_TRACK;
+ break;
+ case KC_MEDIA_STOP:
+ usage = TRANSPORT_STOP;
+ break;
+ case KC_MEDIA_PLAY_PAUSE:
+ usage = TRANSPORT_PLAY_PAUSE;
+ break;
+ case KC_MEDIA_SELECT:
+ usage = AL_CC_CONFIG;
+ break;
+ case KC_MAIL:
+ usage = AL_EMAIL;
+ break;
+ case KC_CALCULATOR:
+ usage = AL_CALCULATOR;
+ break;
+ case KC_MY_COMPUTER:
+ usage = AL_LOCAL_BROWSER;
+ break;
+ case KC_WWW_SEARCH:
+ usage = AC_SEARCH;
+ break;
+ case KC_WWW_HOME:
+ usage = AC_HOME;
+ break;
+ case KC_WWW_BACK:
+ usage = AC_BACK;
+ break;
+ case KC_WWW_FORWARD:
+ usage = AC_FORWARD;
+ break;
+ case KC_WWW_STOP:
+ usage = AC_STOP;
+ break;
+ case KC_WWW_REFRESH:
+ usage = AC_REFRESH;
+ break;
+ case KC_WWW_FAVORITES:
+ usage = AC_BOOKMARKS;
+ break;
+ }
+ host_consumer_send(usage);
+ }
+ else if IS_SYSTEM(code) {
+ uint16_t usage = 0;
+ switch (code) {
+ case KC_SYSTEM_POWER:
+ usage = SYSTEM_POWER_DOWN;
+ break;
+ case KC_SYSTEM_SLEEP:
+ usage = SYSTEM_SLEEP;
+ break;
+ case KC_SYSTEM_WAKE:
+ usage = SYSTEM_WAKE_UP;
+ break;
+ }
+ host_system_send(usage);
}
- host_swap_keyboard_report();
- host_clear_keyboard_report();
- for (int row = 0; row < matrix_rows(); row++) {
- for (int col = 0; col < matrix_cols(); col++) {
- if (!matrix_is_on(row, col)) continue;
+}
- uint8_t code = layer_get_keycode(row, col);
- if (code == KB_NO) {
- // do nothing
- } else if (IS_MOD(code)) {
- host_add_mod_bit(MOD_BIT(code));
- } else if (IS_FN(code)) {
- fn_bits |= FN_BIT(code);
- }
-// TODO: use table or something
-#ifdef EXTRAKEY_ENABLE
- // System Control
- else if (code == KB_SYSTEM_POWER) {
-#ifdef HOST_PJRC
- if (suspend && remote_wakeup) {
- usb_remote_wakeup();
- }
+static void unregister_code(uint8_t code)
+{
+ if IS_KEY(code) {
+ host_del_key(code);
+ host_send_keyboard_report();
+ }
+ else if IS_MOD(code) {
+ host_del_mod_bit(MOD_BIT(code));
+ host_send_keyboard_report();
+ }
+ else if IS_FN(code) {
+ host_del_key(keymap_fn_keycode(FN_INDEX(code)));
+ host_send_keyboard_report();
+ }
+ else if IS_MOUSEKEY(code) {
+#ifdef MOUSEKEY_ENABLE
+ mousekey_off(code);
+ mousekey_send();
#endif
- system_code = SYSTEM_POWER_DOWN;
- } else if (code == KB_SYSTEM_SLEEP) {
- system_code = SYSTEM_SLEEP;
- } else if (code == KB_SYSTEM_WAKE) {
- system_code = SYSTEM_WAKE_UP;
- }
- // Consumer Page
- else if (code == KB_AUDIO_MUTE) {
- consumer_code = AUDIO_MUTE;
- } else if (code == KB_AUDIO_VOL_UP) {
- consumer_code = AUDIO_VOL_UP;
- } else if (code == KB_AUDIO_VOL_DOWN) {
- consumer_code = AUDIO_VOL_DOWN;
- }
- else if (code == KB_MEDIA_NEXT_TRACK) {
- consumer_code = TRANSPORT_NEXT_TRACK;
- } else if (code == KB_MEDIA_PREV_TRACK) {
- consumer_code = TRANSPORT_PREV_TRACK;
- } else if (code == KB_MEDIA_STOP) {
- consumer_code = TRANSPORT_STOP;
- } else if (code == KB_MEDIA_PLAY_PAUSE) {
- consumer_code = TRANSPORT_PLAY_PAUSE;
- } else if (code == KB_MEDIA_SELECT) {
- consumer_code = AL_CC_CONFIG;
- }
- else if (code == KB_MAIL) {
- consumer_code = AL_EMAIL;
- } else if (code == KB_CALCULATOR) {
- consumer_code = AL_CALCULATOR;
- } else if (code == KB_MY_COMPUTER) {
- consumer_code = AL_LOCAL_BROWSER;
- }
- else if (code == KB_WWW_SEARCH) {
- consumer_code = AC_SEARCH;
- } else if (code == KB_WWW_HOME) {
- consumer_code = AC_HOME;
- } else if (code == KB_WWW_BACK) {
- consumer_code = AC_BACK;
- } else if (code == KB_WWW_FORWARD) {
- consumer_code = AC_FORWARD;
- } else if (code == KB_WWW_STOP) {
- consumer_code = AC_STOP;
- } else if (code == KB_WWW_REFRESH) {
- consumer_code = AC_REFRESH;
- } else if (code == KB_WWW_FAVORITES) {
- consumer_code = AC_BOOKMARKS;
+ }
+ else if IS_CONSUMER(code) {
+ host_consumer_send(0x0000);
+ }
+ else if IS_SYSTEM(code) {
+ host_system_send(0x0000);
+ }
+}
+
+/*
+ *
+ * Event/State|IDLE PRESSING DELAYING[f] WAITING[f,k]
+ * -----------+------------------------------------------------------------------
+ * Fn Down |(L+) -*1 WAITING(Sk) IDLE(Rf,Ps)*7
+ * Up |(L-) IDLE(L-)*8 IDLE(L-)*8 IDLE(L-)*8
+ * Fnk Down |DELAYING(Sf)* (Rf) WAITING(Sk) IDLE(Rf,Ps,Rf)
+ * Up |(L-) IDLE(L-/Uf)*8 IDLE(Rf,Uf/L-)*3 IDLE(Rf,Ps,Uf/L-)*3
+ * Key Down |PRESSING(Rk) (Rk) WAITING(Sk) IDLE(Rf,Ps,Rk)
+ * Up |(Uk) IDLE(Uk)*4 (Uk) IDLE(L+,Ps,Pk)/(Uk)*a
+ * |
+ * Delay |- - IDLE(L+) IDLE(L+,Ps)
+ * Magic Key |COMMAND*5
+ *
+ * *1: ignore Fn if other key is down.
+ * *2: register Fnk if any key is pressing
+ * *3: register/unregister delayed Fnk and move to IDLE if code == delayed Fnk, else *8
+ * *4: if no keys registered to host
+ * *5: unregister all keys
+ * *6: only if no keys down
+ * *7: ignore Fn because Fnk key and stored key are down.
+ * *8: move to IDLE if layer switch(off) occurs, else stay at current state
+ * *9: repeat key if pressing Fnk twice quickly(move to PRESSING)
+ * *a: layer switch and process waiting key and code if code == wainting key, else unregister key
+ *
+ * States:
+ * IDLE: No key is down except modifiers
+ * DELAYING: delay layer switch after pressing Fn with alt keycode
+ * WAITING: key is pressed during DELAYING
+ *
+ * Events:
+ * Fn: Fn key without alternative keycode
+ * Fnk: Fn key with alternative keycode
+ * -: ignore
+ * Delay: layer switch delay term is elapsed
+ *
+ * Actions:
+ * Rk: register key
+ * Uk: unregister key
+ * Rf: register Fn(alt keycode)
+ * Uf: unregister Fn(alt keycode)
+ * Rs: register stored key
+ * Us: unregister stored key
+ * Sk: Store key(waiting Key)
+ * Sf: Store Fn(delayed Fn)
+ * Ps: Process stored key
+ * Ps: Process key
+ * Is: Interpret stored keys in current layer
+ * L+: Switch to new layer(*unregister* all keys but modifiers)
+ * L-: Switch back to last layer(*unregister* all keys but modifiers)
+ * Ld: Switch back to default layer(*unregister* all keys but modifiers)
+ */
+#define NEXT(state) do { \
+ debug("NEXT: "); debug_P(state_str(kbdstate)); \
+ kbdstate = state; \
+ debug(" -> "); debug_P(state_str(kbdstate)); debug("\n"); \
+} while (0)
+
+static inline void process_key(keyevent_t event)
+{
+ uint8_t code = keymap_get_keycode(current_layer, event.key.row, event.key.col);
+ keykind_t kind = get_keykind(code, event.pressed);
+
+ uint8_t tmp_mods;
+
+ debug("state: "); debug_P(state_str(kbdstate));
+ debug(" kind: "); debug_hex(kind);
+ debug(" code: "); debug_hex(code);
+ if (event.pressed) { debug("d"); } else { debug("u"); }
+ debug("\n");
+
+ switch (kbdstate) {
+ case IDLE:
+ switch (kind) {
+ case FN_DOWN:
+ layer_switch_on(code);
+ break;
+ case FN_UP:
+ layer_switch_off(code);
+ break;
+ case FNK_DOWN:
+ // repeat Fn alt key when press Fn key down, up then down again quickly
+ if (KEYEQ(delayed_fn.event.key, event.key) &&
+ timer_elapsed(delayed_fn.time) < LAYER_DELAY) {
+ register_code(code);
+ NEXT(PRESSING);
+ } else {
+ delayed_fn = (keyrecord_t) {
+ .event = event,
+ .code = code,
+ .mods = keyboard_report->mods,
+ .time = timer_read()
+ };
+ NEXT(DELAYING);
+ }
+ break;
+ case FNK_UP:
+ layer_switch_off(code);
+ break;
+ case KEY_DOWN:
+ register_code(code);
+ NEXT(PRESSING);
+ break;
+ case MOD_DOWN:
+ register_code(code);
+ break;
+ case KEY_UP:
+ case MOD_UP:
+ unregister_code(code);
+ break;
+ default:
+ break;
}
-#endif
- else if (IS_KEY(code)) {
- host_add_key(code);
+ break;
+ case PRESSING:
+ switch (kind) {
+ case FN_DOWN:
+ // ignored when any key is pressed
+ break;
+ case FN_UP:
+ if (layer_switch_off(code))
+ NEXT(IDLE);
+ break;
+ case FNK_DOWN:
+ register_code(code);
+ break;
+ case FNK_UP:
+ if (layer_switch_off(code)) {
+ NEXT(IDLE);
+ } else {
+ unregister_code(code);
+ if (!anykey_sent_to_host())
+ NEXT(IDLE);
+ }
+ break;
+ case KEY_DOWN:
+ case MOD_DOWN:
+ register_code(code);
+ break;
+ case KEY_UP:
+ case MOD_UP:
+ unregister_code(code);
+ if (!anykey_sent_to_host())
+ NEXT(IDLE);
+ break;
+ default:
+ break;
}
-#ifdef MOUSEKEY_ENABLE
- else if (IS_MOUSEKEY(code)) {
- mousekey_decode(code);
+ break;
+ case DELAYING:
+ switch (kind) {
+ case FN_DOWN:
+ case FNK_DOWN:
+ case KEY_DOWN:
+ waiting_key = (keyrecord_t) {
+ .event = event,
+ .code = code,
+ .mods = keyboard_report->mods,
+ .time = timer_read()
+ };
+ NEXT(WAITING);
+ break;
+ case MOD_DOWN:
+ register_code(code);
+ break;
+ case FN_UP:
+ if (layer_switch_off(code))
+ NEXT(IDLE);
+ break;
+ case FNK_UP:
+ if (code == delayed_fn.code) {
+ // type Fn with alt keycode
+ // restore the mod status at the time of pressing Fn key
+ tmp_mods = keyboard_report->mods;
+ host_set_mods(delayed_fn.mods);
+ register_code(delayed_fn.code);
+ unregister_code(delayed_fn.code);
+ host_set_mods(tmp_mods);
+ NEXT(IDLE);
+ } else {
+ if (layer_switch_off(code))
+ NEXT(IDLE);
+ }
+ break;
+ case KEY_UP:
+ case MOD_UP:
+ unregister_code(code);
+ break;
+ default:
+ break;
}
-#endif
- else {
- debug("ignore keycode: "); debug_hex(code); debug("\n");
+ break;
+ case WAITING:
+ switch (kind) {
+ case FN_DOWN:
+ case FNK_DOWN:
+ case KEY_DOWN:
+ tmp_mods = keyboard_report->mods;
+ host_set_mods(delayed_fn.mods);
+ register_code(delayed_fn.code);
+ host_set_mods(waiting_key.mods);
+ register_code(waiting_key.code);
+ host_set_mods(tmp_mods);
+ if (kind == FN_DOWN) {
+ // ignore Fn
+ } else if (kind == FNK_DOWN) {
+ register_code(code);
+ } else if (kind == KEY_DOWN) {
+ register_code(code);
+ }
+ NEXT(IDLE);
+ break;
+ case MOD_DOWN:
+ register_code(code);
+ break;
+ case FN_UP:
+ if (layer_switch_off(code))
+ NEXT(IDLE);
+ break;
+ case FNK_UP:
+ if (code == delayed_fn.code) {
+ // alt down, key down, alt up
+ tmp_mods = keyboard_report->mods;
+ host_set_mods(delayed_fn.mods);
+ register_code(delayed_fn.code);
+ host_set_mods(waiting_key.mods);
+ register_code(waiting_key.code);
+ unregister_code(delayed_fn.code);
+ host_set_mods(tmp_mods);
+ NEXT(IDLE);
+ } else {
+ if (layer_switch_off(code))
+ NEXT(IDLE);
+ }
+ break;
+ case KEY_UP:
+ if (code == waiting_key.code) {
+ layer_switch_on(delayed_fn.code);
+ NEXT(IDLE);
+ // process waiting_key
+ tmp_mods = keyboard_report->mods;
+ host_set_mods(waiting_key.mods);
+ process_key(waiting_key.event);
+ host_set_mods(tmp_mods);
+ process_key(event);
+ } else {
+ unregister_code(code);
+ }
+ break;
+ case MOD_UP:
+ unregister_code(code);
+ break;
+ default:
+ break;
}
- }
+ break;
}
+}
+
+void keyboard_init(void)
+{
+ debug_keyboard = true;
+
+ timer_init();
+ matrix_init();
+#ifdef PS2_MOUSE_ENABLE
+ ps2_mouse_init();
+#endif
+}
- layer_switching(fn_bits);
+void keyboard_task(void)
+{
+ static matrix_row_t matrix_prev[MATRIX_ROWS];
+ matrix_row_t matrix_row = 0;
+ matrix_row_t matrix_change = 0;
- if (command_proc()) {
- return;
+ matrix_scan();
+ for (int r = 0; r < MATRIX_ROWS; r++) {
+ matrix_row = matrix_get_row(r);
+ matrix_change = matrix_row ^ matrix_prev[r];
+ if (matrix_change) {
+ matrix_debug();
+
+ for (int c = 0; c < MATRIX_COLS; c++) {
+ if (matrix_change & (1< LAYER_DELAY) {
+ if (kbdstate == DELAYING) {
+ layer_switch_on(delayed_fn.code);
+ NEXT(IDLE);
+ }
+ if (kbdstate == WAITING) {
+ layer_switch_on(delayed_fn.code);
+ NEXT(IDLE);
+ uint8_t tmp_mods = keyboard_report->mods;
+ host_set_mods(waiting_key.mods);
+ process_key(waiting_key.event);
+ host_set_mods(tmp_mods);
+ }
+ }
}
#ifdef MOUSEKEY_ENABLE
- mousekey_send();
-#endif
-
-#ifdef PS2_MOUSE_ENABLE
- // TODO: should comform new API
- if (ps2_mouse_read() == 0)
- ps2_mouse_usb_send();
+ // mousekey repeat & acceleration
+ mousekey_task();
#endif
- if (last_leds != host_keyboard_leds()) {
- keyboard_set_leds(host_keyboard_leds());
- last_leds = host_keyboard_leds();
+ // FAIL SAFE: clear all key if no key down
+ if (matrix_change) {
+ matrix_row_t is_matrix_on = 0;
+ for (int r = 0; r < MATRIX_ROWS; r++) {
+ is_matrix_on |= matrix_get_row(r);
+ }
+ if (!is_matrix_on) {
+ debug("FAIL SAFE: clear all keys(default layer).\n");
+ clear_keyboard();
+ current_layer = default_layer;
+ }
}
+
+ return;
}
void keyboard_set_leds(uint8_t leds)