diff options
author | Drashna Jaelre <drashna@live.com> | 2023-05-31 11:44:06 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-31 11:44:06 -0700 |
commit | 3a3e5abac992712a8bb4e9b61430f5fc62dc6043 (patch) | |
tree | 832d14c0ca63e13ca1b97e2cbfe94d75d6bb89c7 /users | |
parent | 1411c79aefc989f5fae138b795f53f3b10863ec9 (diff) |
[Keymap] Drashna Keymap updates for 0.21.0 (#21073)
Diffstat (limited to 'users')
34 files changed, 1253 insertions, 1758 deletions
diff --git a/users/drashna/bootmagic_better.c b/users/drashna/bootmagic_better.c index ffd2e609ae..fa1078e37d 100644 --- a/users/drashna/bootmagic_better.c +++ b/users/drashna/bootmagic_better.c @@ -28,7 +28,7 @@ void bootmagic_lite(void) { if (!is_keyboard_left()) { row = BOOTMAGIC_LITE_ROW_RIGHT; col = BOOTMAGIC_LITE_COLUMN_RIGHT; -#if defined(BOOTMAGIC_LITE_EEPROM_ROW) && defined(BOOTMAGIC_LITE_EEPROM_COLUMN) && defined(BOOTMAGIC_LITE_EEPROM_ROW_RIGHT) && defined(BOOTMAGIC_LITE_EEPROM_COLUMN_RIGHT) +# if defined(BOOTMAGIC_LITE_EEPROM_ROW) && defined(BOOTMAGIC_LITE_EEPROM_COLUMN) && defined(BOOTMAGIC_LITE_EEPROM_ROW_RIGHT) && defined(BOOTMAGIC_LITE_EEPROM_COLUMN_RIGHT) row_e = BOOTMAGIC_LITE_EEPROM_ROW_RIGHT; col_e = BOOTMAGIC_LITE_EEPROM_COLUMN_RIGHT; # endif diff --git a/users/drashna/callbacks.c b/users/drashna/callbacks.c index 568f56c8d1..cab7e5278f 100644 --- a/users/drashna/callbacks.c +++ b/users/drashna/callbacks.c @@ -3,6 +3,9 @@ #include "drashna.h" +#ifdef CUSTOM_DYNAMIC_MACROS_ENABLE +# include "keyrecords/dynamic_macros.h" +#endif #ifdef I2C_SCANNER_ENABLE void housekeeping_task_i2c_scanner(void); void keyboard_post_init_i2c(void); @@ -10,7 +13,10 @@ void keyboard_post_init_i2c(void); __attribute__((weak)) void keyboard_pre_init_keymap(void) {} void keyboard_pre_init_user(void) { - userspace_config.raw = eeconfig_read_user(); + eeconfig_read_user_config(&userspace_config.raw); + if (!userspace_config.check) { + eeconfig_init_user(); + } keyboard_pre_init_keymap(); } // Add reconfigurable functions here, for keymap customization @@ -24,58 +30,8 @@ void keyboard_pre_init_user(void) { void keyboard_post_init_qp(void); #endif -#ifdef OS_DETECTION_ENABLE -os_variant_t os_type; - -uint32_t startup_exec(uint32_t trigger_time, void *cb_arg) { - /* do something */ - - if (is_keyboard_master()) { - os_type = detected_host_os(); - if (os_type) { - bool is_mac = (os_type == OS_MACOS) || (os_type == OS_IOS); - keymap_config.swap_lctl_lgui = keymap_config.swap_rctl_rgui = is_mac; -# ifdef UNICODE_COMMON_ENABLE - uint8_t mode = is_mac ? UNICODE_MODE_MACOS : UNICODE_MODE_WINCOMPOSE; - if (mode != get_unicode_input_mode()) { - set_unicode_input_mode(mode); - } -# endif - switch (os_type) { - case OS_UNSURE: - xprintf("unknown OS Detected\n"); - break; - case OS_LINUX: - xprintf("Linux Detected\n"); - break; - case OS_WINDOWS: - xprintf("Windows Detected\n"); - break; -# if 0 - case OS_WINDOWS_UNSURE: - xprintf("Windows? Detected\n"); - break; -# endif - case OS_MACOS: - xprintf("MacOS Detected\n"); - break; - case OS_IOS: - xprintf("iOS Detected\n"); - break; -# if 0 - case OS_PS5: - xprintf("PlayStation 5 Detected\n"); - break; - case OS_HANDHELD: - xprintf("Nintend Switch/Quest 2 Detected\n"); - break; -# endif - } - } - } - - return os_type ? 0 : 500; -} +#if defined(OS_DETECTION_ENABLE) && defined(DEFERRED_EXEC_ENABLE) +uint32_t startup_exec(uint32_t trigger_time, void *cb_arg); #endif __attribute__((weak)) void keyboard_post_init_keymap(void) {} @@ -103,8 +59,10 @@ void keyboard_post_init_user(void) { DDRB &= ~(1 << 0); PORTB &= ~(1 << 0); #endif - -#ifdef OS_DETECTION_ENABLE +#ifdef CUSTOM_DYNAMIC_MACROS_ENABLE + dynamic_macro_init(); +#endif +#if defined(OS_DETECTION_ENABLE) && defined(DEFERRED_EXEC_ENABLE) defer_exec(100, startup_exec, NULL); #endif @@ -153,9 +111,6 @@ void suspend_power_down_user(void) { __attribute__((weak)) void suspend_wakeup_init_keymap(void) {} void suspend_wakeup_init_user(void) { -#ifdef OLED_ENABLE - oled_timer_reset(); -#endif suspend_wakeup_init_keymap(); } @@ -217,6 +172,11 @@ layer_state_t layer_state_set_user(layer_state_t state) { __attribute__((weak)) layer_state_t default_layer_state_set_keymap(layer_state_t state) { return state; } + +#if defined(AUDIO_ENABLE) && defined(DEFAULT_LAYER_SONGS) +static float default_layer_songs[][MAX_LAYER][2] = DEFAULT_LAYER_SONGS; +#endif + layer_state_t default_layer_state_set_user(layer_state_t state) { if (!is_keyboard_master()) { return state; @@ -226,6 +186,21 @@ layer_state_t default_layer_state_set_user(layer_state_t state) { #if defined(CUSTOM_RGBLIGHT) state = default_layer_state_set_rgb_light(state); #endif + + static bool has_init_been_ran = false; + // We don't want to run this the first time it's called, since it's read from eeeprom and called + // as part of the startup process. But after that, it's okay. + if (has_init_been_ran) { +#if defined(AUDIO_ENABLE) && defined(DEFAULT_LAYER_SONGS) + if (get_highest_layer(state) < MAX_LAYER) { + PLAY_SONG(default_layer_songs[get_highest_layer(state)]); + } +#endif + eeconfig_update_default_layer(state); + } else { + has_init_been_ran = true; + } + return state; } @@ -238,11 +213,23 @@ __attribute__((weak)) void eeconfig_init_keymap(void) {} void eeconfig_init_user(void) { userspace_config.raw = 0; userspace_config.rgb_layer_change = true; - userspace_config.autocorrection = true; - eeconfig_update_user(userspace_config.raw); + userspace_config.check = true; +#if defined(OLED_ENABLE) + userspace_config.oled_brightness = OLED_BRIGHTNESS; +#else + userspace_config.oled_brightness = 255; +#endif + eeconfig_update_user_config(&userspace_config.raw); eeconfig_init_keymap(); } +void eeconfig_init_user_datablock(void) { +#if (EECONFIG_USER_DATA_SIZE) > 4 + uint8_t eeconfig_empty_temp[(EECONFIG_USER_DATA_SIZE)-4] = {0}; + eeconfig_update_user_data(eeconfig_empty_temp); +#endif +} + #ifdef SPLIT_KEYBOARD __attribute__((weak)) void matrix_slave_scan_keymap(void) {} void matrix_slave_scan_user(void) { diff --git a/users/drashna/config.h b/users/drashna/config.h index b4aa1283eb..ec9bbf1afb 100644 --- a/users/drashna/config.h +++ b/users/drashna/config.h @@ -3,9 +3,6 @@ #pragma once -// Use custom magic number so that when switching branches, EEPROM always gets reset -#define EECONFIG_MAGIC_NUMBER (uint16_t)0x1339 - #ifdef IS_COMMAND # undef IS_COMMAND #endif @@ -43,7 +40,6 @@ # define WPM_ESTIMATED_WORD_SIZE 5 #endif - #define UNICODE_SELECTED_MODES UNICODE_MODE_WINCOMPOSE, UNICODE_MODE_MACOS #ifndef ONESHOT_TAP_TOGGLE @@ -98,7 +94,22 @@ # define C15 PAL_LINE(GPIOC, 15) #endif - #define ENABLE_COMPILE_KEYCODE #define BOTH_SHIFTS_TURNS_ON_CAPS_WORD + +/* --- PRINTF_BYTE_TO_BINARY macro's --- */ +#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c" +#define PRINTF_BYTE_TO_BINARY_INT8(i) (((i)&0x80ll) ? '1' : '0'), (((i)&0x40ll) ? '1' : '0'), (((i)&0x20ll) ? '1' : '0'), (((i)&0x10ll) ? '1' : '0'), (((i)&0x08ll) ? '1' : '0'), (((i)&0x04ll) ? '1' : '0'), (((i)&0x02ll) ? '1' : '0'), (((i)&0x01ll) ? '1' : '0') + +#define PRINTF_BINARY_PATTERN_INT16 PRINTF_BINARY_PATTERN_INT8 " " PRINTF_BINARY_PATTERN_INT8 +#define PRINTF_BYTE_TO_BINARY_INT16(i) PRINTF_BYTE_TO_BINARY_INT8((i) >> 8), PRINTF_BYTE_TO_BINARY_INT8(i) +#define PRINTF_BINARY_PATTERN_INT32 PRINTF_BINARY_PATTERN_INT16 " " PRINTF_BINARY_PATTERN_INT16 +#define PRINTF_BYTE_TO_BINARY_INT32(i) PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i) +#define PRINTF_BINARY_PATTERN_INT64 PRINTF_BINARY_PATTERN_INT32 " " PRINTF_BINARY_PATTERN_INT32 +#define PRINTF_BYTE_TO_BINARY_INT64(i) PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i) +/* --- end macros --- */ + +#ifndef EECONFIG_USER_DATA_SIZE +# define EECONFIG_USER_DATA_SIZE 8 +#endif diff --git a/users/drashna/drashna.c b/users/drashna/drashna.c index 259810c70f..cad6db8f3d 100644 --- a/users/drashna/drashna.c +++ b/users/drashna/drashna.c @@ -2,6 +2,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "drashna.h" +#include <stdio.h> +#include <ctype.h> userspace_config_t userspace_config; @@ -139,7 +141,7 @@ float autocorrect_song[][2] = SONG(PLOVER_GOODBYE_SOUND); # endif # endif -bool apply_autocorrect(uint8_t backspaces, const char* str) { +bool apply_autocorrect(uint8_t backspaces, const char *str) { if (layer_state_is(_GAMEPAD)) { return false; } @@ -188,7 +190,7 @@ void oneshot_locked_mods_changed_user(uint8_t mods) { # endif #endif -void format_layer_bitmap_string(char* buffer, layer_state_t state, layer_state_t default_state) { +void format_layer_bitmap_string(char *buffer, layer_state_t state, layer_state_t default_state) { for (int i = 0; i < 16; i++) { if (i == 0 || i == 4 || i == 8 || i == 12) { *buffer = ' '; @@ -207,3 +209,111 @@ void format_layer_bitmap_string(char* buffer, layer_state_t state, layer_state_t } *buffer = 0; } + +#if defined(OS_DETECTION_ENABLE) && defined(DEFERRED_EXEC_ENABLE) +os_variant_t os_type; + +uint32_t startup_exec(uint32_t trigger_time, void *cb_arg) { + if (is_keyboard_master()) { + os_type = detected_host_os(); + if (os_type) { + bool is_mac = (os_type == OS_MACOS) || (os_type == OS_IOS); + if (keymap_config.swap_lctl_lgui != is_mac) { + keymap_config.swap_lctl_lgui = keymap_config.swap_rctl_rgui = is_mac; + eeconfig_update_keymap(keymap_config.raw); + } +# ifdef UNICODE_COMMON_ENABLE + set_unicode_input_mode_soft(keymap_config.swap_lctl_lgui ? UNICODE_MODE_MACOS : UNICODE_MODE_WINCOMPOSE); +# endif + switch (os_type) { + case OS_UNSURE: + xprintf("unknown OS Detected\n"); + break; + case OS_LINUX: + xprintf("Linux Detected\n"); + break; + case OS_WINDOWS: + xprintf("Windows Detected\n"); + break; +# if 0 + case OS_WINDOWS_UNSURE: + xprintf("Windows? Detected\n"); + break; +# endif + case OS_MACOS: + xprintf("MacOS Detected\n"); + break; + case OS_IOS: + xprintf("iOS Detected\n"); + break; +# if 0 + case OS_PS5: + xprintf("PlayStation 5 Detected\n"); + break; + case OS_HANDHELD: + xprintf("Nintend Switch/Quest 2 Detected\n"); + break; +# endif + } + } + } + + return os_type ? 0 : 500; +} +#endif + +static host_driver_t *host_driver = 0; +static bool host_driver_disabled = false; + +void set_keyboard_lock(bool status) { + if (!status && !host_get_driver()) { + host_set_driver(host_driver); + } else if (status && host_get_driver()) { + host_driver = host_get_driver(); + clear_keyboard(); + host_set_driver(0); + } else if (status) { + clear_keyboard(); + } + + host_driver_disabled = status; +} + +void toggle_keyboard_lock(void) { + set_keyboard_lock(!host_driver_disabled); +} + +bool get_keyboard_lock(void) { + return host_driver_disabled; +} + +const char *get_layer_name_string(layer_state_t state, bool alt_name) { + switch (get_highest_layer(state)) { + case _QWERTY: + return alt_name ? "Num Pad" : "QWERTY"; + case _COLEMAK: + return "Colemak"; + case _COLEMAK_DH: + return "Colemak-DH"; + case _DVORAK: + return "Dvorak"; + case _GAMEPAD: + return "Gamepad"; + case _DIABLO: + return "Diablo"; + case _DIABLOII: + return "Diablo II"; + case _MOUSE: + return alt_name ? "Macros" : "Mouse"; + case _MEDIA: + return "Media"; + case _LOWER: + return "Lower"; + case _RAISE: + return "Raise"; + case _ADJUST: + return "Adjust"; + default: + return "Unknown"; + } +} diff --git a/users/drashna/drashna.h b/users/drashna/drashna.h index 4e2a4d5acb..49cdf6ca21 100644 --- a/users/drashna/drashna.h +++ b/users/drashna/drashna.h @@ -4,7 +4,7 @@ #pragma once #include QMK_KEYBOARD_H -#include "eeprom.h" +#include "eeconfig_users.h" #include "keyrecords/wrappers.h" #include "keyrecords/process_records.h" #include "callbacks.h" @@ -30,6 +30,9 @@ #ifdef OS_DETECTION_ENABLE # include "os_detection.h" #endif +#ifdef UNICODE_COMMON_ENABLE +# include "keyrecords/unicode.h" +#endif /* Define layer names */ enum userspace_layers { @@ -88,14 +91,25 @@ void format_layer_bitmap_string(char* buffer, layer_state_t state, layer_state_t typedef union { uint32_t raw; struct { - bool rgb_layer_change :1; - bool is_overwatch :1; - bool nuke_switch :1; - bool swapped_numbers :1; - bool rgb_matrix_idle_anim :1; - bool autocorrection :1; + bool rgb_layer_change :1; + bool is_overwatch :1; + bool nuke_switch :1; + bool swapped_numbers :1; + bool rgb_matrix_idle_anim :1; + bool mouse_jiggler :1; + uint8_t align_reserved :2; + uint8_t oled_brightness :8; + uint32_t reserved :15; + bool check :1; }; } userspace_config_t; // clang-format on +_Static_assert(sizeof(userspace_config_t) == sizeof(uint32_t), "Userspace EECONFIG out of spec."); + extern userspace_config_t userspace_config; + +void set_keyboard_lock(bool enable); +bool get_keyboard_lock(void); +void toggle_keyboard_lock(void); +const char* get_layer_name_string(layer_state_t state, bool alt_name); diff --git a/users/drashna/eeconfig_users.c b/users/drashna/eeconfig_users.c new file mode 100644 index 0000000000..8e0f1f10b6 --- /dev/null +++ b/users/drashna/eeconfig_users.c @@ -0,0 +1,53 @@ +// Copyright 2023 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "eeconfig_users.h" +#include "eeprom.h" +#include "eeconfig.h" +#include <string.h> + +#if (TOTAL_EEPROM_BYTE_COUNT - 1) < EECONFIG_SIZE && !defined(KEYBOARD_input_club_ergodox_infinity) +# error "More eeprom configured than is available." +#endif +#if (EECONFIG_USER_DATA_SIZE) != 0 && (EECONFIG_USER_DATA_SIZE) < 4 +# error "Not enough EEPROM configured for user config." +#endif + +#if (EECONFIG_USER_DATA_SIZE) == 0 +# define EECONFIG_USER_TEMP EECONFIG_USER +#else +# define EECONFIG_USER_TEMP (uint32_t *)(EECONFIG_USER_DATABLOCK) +#endif + +void eeconfig_read_user_config(uint32_t *data) { +#if (EECONFIG_USER_DATA_SIZE) > 0 + if (!eeconfig_is_user_datablock_valid()) { + memset(data, 0, 4); + } else +#endif + eeprom_read_block(data, EECONFIG_USER_TEMP, 4); +} + +void eeconfig_update_user_config(const uint32_t *data) { + eeprom_update_block(data, EECONFIG_USER_TEMP, 4); +#if (EECONFIG_USER_DATA_SIZE) > 0 + eeprom_update_dword(EECONFIG_USER, (EECONFIG_USER_DATA_VERSION)); +#endif +} + +void eeconfig_read_user_data(void *data) { +#if (EECONFIG_USER_DATA_SIZE) > 4 + if (eeconfig_is_user_datablock_valid()) { + eeprom_read_block(data, EECONFIG_USER_DATABLOCK + 4, (EECONFIG_USER_DATA_SIZE)-4); + } else { + memset(data, 0, (EECONFIG_USER_DATA_SIZE)); + } +#endif +} + +void eeconfig_update_user_data(const void *data) { +#if (EECONFIG_USER_DATA_SIZE) > 4 + eeprom_update_dword(EECONFIG_USER, (EECONFIG_USER_DATA_VERSION)); + eeprom_update_block(data, EECONFIG_USER_DATABLOCK + 4, (EECONFIG_USER_DATA_SIZE)-4); +#endif +} diff --git a/users/drashna/eeconfig_users.h b/users/drashna/eeconfig_users.h new file mode 100644 index 0000000000..c9b230df9c --- /dev/null +++ b/users/drashna/eeconfig_users.h @@ -0,0 +1,12 @@ +// Copyright 2023 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <stdint.h> + +void eeconfig_read_user_config(uint32_t *data); +void eeconfig_update_user_config(const uint32_t *data); + +void eeconfig_read_user_data(void *data); +void eeconfig_update_user_data(const void *data); diff --git a/users/drashna/keyrecords/dynamic_macros.c b/users/drashna/keyrecords/dynamic_macros.c new file mode 100644 index 0000000000..43c2336cb6 --- /dev/null +++ b/users/drashna/keyrecords/dynamic_macros.c @@ -0,0 +1,283 @@ +// Copyright 2016 Jack Humbert +// Copyright 2019 Wojciech Siewierski < wojciech dot siewierski at onet dot pl > +// Copyright 2023 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "keyrecords/dynamic_macros.h" +#include "keyrecords/process_records.h" +#include "wait.h" +#include "debug.h" +#include "eeprom.h" +#include "eeconfig.h" +#include <string.h> + +static uint8_t macro_id = 255; +static uint8_t recording_state = STATE_NOT_RECORDING; + +#if EECONFIG_USER_DATA_SIZE < 4 +# error "EECONFIG_USER_DATA_SIZE not set. Don't step on others eeprom." +#endif +#ifndef DYNAMIC_MACRO_EEPROM_BLOCK0_ADDR +# define DYNAMIC_MACRO_EEPROM_BLOCK0_ADDR (uint8_t*)(EECONFIG_USER_DATABLOCK + 4) +#endif + +dynamic_macro_t dynamic_macros[DYNAMIC_MACRO_COUNT]; +_Static_assert((sizeof(dynamic_macros)) <= (EECONFIG_USER_DATA_SIZE - 4), "User Data Size must be large enough to host all macros"); + +__attribute__((weak)) void dynamic_macro_record_start_user(void) {} + +__attribute__((weak)) void dynamic_macro_play_user(uint8_t macro_id) {} + +__attribute__((weak)) void dynamic_macro_record_key_user(uint8_t macro_id, keyrecord_t* record) {} + +__attribute__((weak)) void dynamic_macro_record_end_user(uint8_t macro_id) {} + +/** + * @brief Gets the current macro ID + * + * @return uint8_t + */ +uint8_t dynamic_macro_get_current_id(void) { + return macro_id; +} + +/** + * @brief Gets the current recording state + * + * @return uint8_t + */ +uint8_t dynamic_macro_get_recording_state(void) { + return recording_state; +} + +/** + * Start recording of the dynamic macro. + * + * @param macro_id[in] The id of macro to be recorded + */ +bool dynamic_macro_record_start(uint8_t macro_id) { + if (macro_id >= (uint8_t)(DYNAMIC_MACRO_COUNT)) { + return false; + } + dprintf("dynamic macro recording: started for slot %d\n", macro_id); + + dynamic_macro_record_start_user(); + + clear_keyboard(); + layer_clear(); + + dynamic_macros[macro_id].length = 0; + return true; +} + +/** + * Play the dynamic macro. + * + * @param macro_id[in] The id of macro to be played + */ +void dynamic_macro_play(uint8_t macro_id) { + if (macro_id >= (uint8_t)(DYNAMIC_MACRO_COUNT)) { + return; + } + + dprintf("dynamic macro: slot %d playback, length %d\n", macro_id, dynamic_macros[macro_id].length); + + layer_state_t saved_layer_state = layer_state; + + clear_keyboard(); + layer_clear(); + + for (uint8_t i = 0; i < dynamic_macros[macro_id].length; ++i) { + process_record(&dynamic_macros[macro_id].events[i]); + } + + clear_keyboard(); + + layer_state_set(saved_layer_state); + + dynamic_macro_play_user(macro_id); +} + +/** + * Record a single key in a dynamic macro. + * + * @param macro_id[in] The start of the used macro buffer. + * @param record[in] The current keypress. + */ +void dynamic_macro_record_key(uint8_t macro_id, keyrecord_t* record) { + dynamic_macro_t* macro = &dynamic_macros[macro_id]; + uint8_t length = macro->length; + + /* If we've just started recording, ignore all the key releases. */ + if (!record->event.pressed && length == 0) { + dprintln("dynamic macro: ignoring a leading key-up event"); + return; + } + + if (length < DYNAMIC_MACRO_SIZE) { + macro->events[length] = *record; + macro->length = ++length; + } else { + dynamic_macro_record_key_user(macro_id, record); + } + + dprintf("dynamic macro: slot %d length: %d/%d\n", macro_id, length, DYNAMIC_MACRO_SIZE); +} + +/** + * End recording of the dynamic macro. Essentially just update the + * pointer to the end of the macro. + */ +void dynamic_macro_record_end(uint8_t macro_id) { + if (macro_id >= (uint8_t)(DYNAMIC_MACRO_COUNT)) { + return; + } + dynamic_macro_record_end_user(macro_id); + + dynamic_macro_t* macro = &dynamic_macros[macro_id]; + uint8_t length = macro->length; + + keyrecord_t* events_begin = &(macro->events[0]); + keyrecord_t* events_pointer = &(macro->events[length - 1]); + + dprintf("dynamic_macro: macro length before trimming: %d\n", macro->length); + while (events_pointer != events_begin && (events_pointer)->event.pressed) { + dprintln("dynamic macro: trimming a trailing key-down event"); + --(macro->length); + --events_pointer; + } + + macro->checksum = dynamic_macro_calc_crc(macro); + dynamic_macro_save_eeprom(macro_id); + + dprintf("dynamic macro: slot %d saved, length: %d\n", macro_id, length); +} + +bool process_record_dynamic_macro(uint16_t keycode, keyrecord_t* record) { + if (STATE_NOT_RECORDING == recording_state) { + /* Program key pressed to request programming mode */ + if (keycode == DYN_MACRO_PROG && record->event.pressed) { + // dynamic_macro_led_blink(); + + recording_state = STATE_RECORD_KEY_PRESSED; + dprintf("dynamic macro: programming key pressed, waiting for macro slot selection. %d\n", recording_state); + + return false; + } + /* Macro key pressed to request macro playback */ + if (IS_DYN_KEYCODE(keycode) && record->event.pressed) { + dynamic_macro_play(keycode - DYN_MACRO_KEY00); + + return false; + } + + /* Non-dynamic macro key, process it elsewhere. */ + return true; + } else if (STATE_RECORD_KEY_PRESSED == recording_state) { + /* Program key pressed again before a macro selector key, cancel macro recording. + Blink leds to indicate cancelation. */ + if (keycode == DYN_MACRO_PROG && record->event.pressed) { + // dynamic_macro_led_blink(); + + recording_state = STATE_NOT_RECORDING; + dprintf("dynamic macro: programming key pressed, programming mode canceled. %d\n", recording_state); + + return false; + } else if (IS_DYN_KEYCODE(keycode) && record->event.pressed) { + macro_id = keycode - DYN_MACRO_KEY00; + + if (dynamic_macro_record_start(macro_id)) { + /* Macro slot selected, enter recording state. */ + recording_state = STATE_CURRENTLY_RECORDING; + } else { + recording_state = STATE_NOT_RECORDING; + } + + return false; + } + /* Ignore any non-macro key press while in RECORD_KEY_PRESSED state. */ + return false; + } else if (STATE_CURRENTLY_RECORDING == recording_state) { + /* Program key pressed to request end of macro recording. */ + if (keycode == DYN_MACRO_PROG && record->event.pressed) { + dynamic_macro_record_end(macro_id); + recording_state = STATE_NOT_RECORDING; + + return false; + } + /* Don't record other macro key presses. */ + else if (IS_DYN_KEYCODE(keycode) && record->event.pressed) { + dprintln("dynamic macro: playback key ignored in programming mode."); + return false; + } + /* Non-macro keypress that should be recorded */ + else { + dynamic_macro_record_key(macro_id, record); + + /* Don't output recorded keypress. */ + return false; + } + } + + return true; +} + +static inline uint16_t crc16_update(uint16_t crc, uint8_t a) { + crc ^= a; + for (uint8_t i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + return crc; +} + +uint16_t dynamic_macro_calc_crc(dynamic_macro_t* macro) { + uint16_t crc = 0; + uint8_t* data = (uint8_t*)macro; + + for (uint16_t i = 0; i < DYNAMIC_MACRO_CRC_LENGTH; ++i) { + crc = crc16_update(crc, *(data++)); + } + return crc; +} + +inline void* dynamic_macro_eeprom_macro_addr(uint8_t macro_id) { + return DYNAMIC_MACRO_EEPROM_BLOCK0_ADDR + sizeof(dynamic_macro_t) * macro_id; +} + +void dynamic_macro_load_eeprom_all(void) { + for (uint8_t i = 0; i < DYNAMIC_MACRO_COUNT; ++i) { + dynamic_macro_load_eeprom(i); + } +} + +void dynamic_macro_load_eeprom(uint8_t macro_id) { + dynamic_macro_t* dst = &dynamic_macros[macro_id]; + + eeprom_read_block(dst, dynamic_macro_eeprom_macro_addr(macro_id), sizeof(dynamic_macro_t)); + + /* Validate checksum, ifchecksum is NOT valid for macro, set its length to 0 to prevent its use. */ + if (dynamic_macro_calc_crc(dst) != dst->checksum) { + dprintf("dynamic macro: slot %d not loaded, checksum mismatch\n", macro_id); + dst->length = 0; + + return; + } + + dprintf("dynamic macro: slot %d loaded from eeprom, checksum okay\n", macro_id); +} + +void dynamic_macro_save_eeprom(uint8_t macro_id) { + dynamic_macro_t* src = &dynamic_macros[macro_id]; + + eeprom_update_block(src, dynamic_macro_eeprom_macro_addr(macro_id), sizeof(dynamic_macro_t)); + dprintf("dynamic macro: slot %d saved to eeprom\n", macro_id); +} + +void dynamic_macro_init(void) { + /* zero out macro blocks */ + memset(&dynamic_macros, 0, DYNAMIC_MACRO_COUNT * sizeof(dynamic_macro_t)); + dynamic_macro_load_eeprom_all(); +} d |