summaryrefslogtreecommitdiffstats
path: root/users
diff options
context:
space:
mode:
authorDrashna Jaelre <drashna@live.com>2023-05-31 11:44:06 -0700
committerGitHub <noreply@github.com>2023-05-31 11:44:06 -0700
commit3a3e5abac992712a8bb4e9b61430f5fc62dc6043 (patch)
tree832d14c0ca63e13ca1b97e2cbfe94d75d6bb89c7 /users
parent1411c79aefc989f5fae138b795f53f3b10863ec9 (diff)
[Keymap] Drashna Keymap updates for 0.21.0 (#21073)
Diffstat (limited to 'users')
-rw-r--r--users/drashna/bootmagic_better.c2
-rw-r--r--users/drashna/callbacks.c107
-rw-r--r--users/drashna/config.h21
-rw-r--r--users/drashna/drashna.c114
-rw-r--r--users/drashna/drashna.h28
-rw-r--r--users/drashna/eeconfig_users.c53
-rw-r--r--users/drashna/eeconfig_users.h12
-rw-r--r--users/drashna/keyrecords/dynamic_macros.c283
-rw-r--r--users/drashna/keyrecords/dynamic_macros.h50
-rw-r--r--users/drashna/keyrecords/process_records.c68
-rw-r--r--users/drashna/keyrecords/process_records.h88
-rw-r--r--users/drashna/keyrecords/tapping.c5
-rw-r--r--users/drashna/keyrecords/unicode.c10
-rw-r--r--users/drashna/keyrecords/unicode.h1
-rw-r--r--users/drashna/keyrecords/wrappers.h2
-rw-r--r--users/drashna/oled/drashna_font.h394
-rw-r--r--users/drashna/oled/oled_assets.h207
-rw-r--r--users/drashna/oled/oled_config.h33
-rw-r--r--users/drashna/oled/oled_stuff.c421
-rw-r--r--users/drashna/oled/oled_stuff.h7
-rw-r--r--users/drashna/oled/rules.mk12
-rw-r--r--users/drashna/oled/sh110x.c860
-rw-r--r--users/drashna/pointing/pointing.c33
-rw-r--r--users/drashna/pointing/pointing.h1
-rw-r--r--users/drashna/post_config.h4
-rw-r--r--users/drashna/rgb/rgb_matrix_config.h2
-rw-r--r--users/drashna/rgb/rgb_matrix_stuff.c44
-rw-r--r--users/drashna/rgb/rgb_matrix_stuff.h2
-rw-r--r--users/drashna/rgb/rgb_stuff.c42
-rw-r--r--users/drashna/rgb/rgb_stuff.h3
-rw-r--r--users/drashna/rules.mk65
-rw-r--r--users/drashna/split/split_config.h8
-rw-r--r--users/drashna/split/transport_sync.c28
-rw-r--r--users/drashna/split/transport_sync.h1
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