diff options
Diffstat (limited to 'users/drashna/readme')
-rw-r--r-- | users/drashna/readme/handlers.md | 97 | ||||
-rw-r--r-- | users/drashna/readme/keycodes.md | 10 | ||||
-rw-r--r-- | users/drashna/readme/rgb.md | 43 | ||||
-rw-r--r-- | users/drashna/readme/secrets.md | 123 | ||||
-rw-r--r-- | users/drashna/readme/tap_dance.md | 119 | ||||
-rw-r--r-- | users/drashna/readme/wrappers.md | 11 |
6 files changed, 403 insertions, 0 deletions
diff --git a/users/drashna/readme/handlers.md b/users/drashna/readme/handlers.md new file mode 100644 index 0000000000..4abaf51473 --- /dev/null +++ b/users/drashna/readme/handlers.md @@ -0,0 +1,97 @@ +# Custom Userspace Function handlers + +Specifically QMK works by using customized handlers for everything. This allows for multiple levels of customization. + +`matrix_scan` calls `matrix_scan_quantum`, which calls `matrix_scan_kb`, which calls `matrix_scan_user`. +`process_record` calls a bunch of stuff, but eventually calls `process_record_kb` which calls `process_record_user` +The same goes for `matrix_init`, `layer_state_set`, `led_set`, and a few other functions. + +All (most) `_user` functions are handled here, in the userspace instead. To allow keyboard specific configuration, I've created `_keymap` functions that can be called by the keymap.c files instead. + +This allows for keyboard specific configuration while maintaining the ability to customize the board. + +My [Ergodox EZ Keymap](https://github.com/qmk/qmk_firmware/blob/master/layouts/community/ergodox/drashna/keymap.c) is a good example of this, as it uses the LEDs as modifier indicators. + +But for a list: + +```c +__attribute__ ((weak)) +void matrix_init_keymap(void) {} + +void matrix_init_user(void) { + matrix_init_keymap(); +} + +__attribute__((weak)) +void keyboard_post_init_keymap(void){ } + +void keyboard_post_init_user(void){ + keyboard_post_init_keymap(); +} + +__attribute__ ((weak)) +void matrix_scan_keymap(void) {} + +void matrix_scan_user(void) { + matrix_scan_keymap(); +} + + +__attribute__ ((weak)) +bool process_record_keymap(uint16_t keycode, keyrecord_t *record) { + return true; +} + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + return process_record_keymap(keycode, record); +} + + +__attribute__ ((weak)) +layer_state_t layer_state_set_keymap (layer_state_t state) { + return state; +} + +layer_state_t layer_state_set_user (layer_state_t state) { + return layer_state_set_keymap (state); +} + + +__attribute__ ((weak)) +void led_set_keymap(uint8_t usb_led) {} + +void led_set_user(uint8_t usb_led) { + led_set_keymap(usb_led); +} + + +__attribute__ ((weak)) +void suspend_power_down_keymap(void) {} + +void suspend_power_down_user(void) { + suspend_power_down_keymap(); +} + + +__attribute__ ((weak)) +void suspend_wakeup_init_keymap(void) {} + +void suspend_wakeup_init_user(void) { + suspend_wakeup_init_keymap(); +} + + +__attribute__ ((weak)) +void shutdown_keymap(void) {} + +void shutdown_user (void) { + shutdown_keymap(); +} + +__attribute__ ((weak)) +void eeconfig_init_keymap(void) {} + +void eeconfig_init_user(void) { + eeconfig_init_keymap(); +} +``` diff --git a/users/drashna/readme/keycodes.md b/users/drashna/readme/keycodes.md new file mode 100644 index 0000000000..af4dd54bc0 --- /dev/null +++ b/users/drashna/readme/keycodes.md @@ -0,0 +1,10 @@ + +# Custom Keycodes + +Keycodes are defined in the drashna.h file and need to be included in the keymap.c files, so that they can be used there. + +A bunch of macros are present and are only included on boards that are not the Ergodox EZ or Orthodox, as they are not needed for those boards. + +Included is a custom macro for compiling my keyboards. This includes the bootloader target (`:teensy`, `:avrdude`, or `:dfu`), and keeps RGBLIGHT, AUDIO enabled, if it previously was (regardless of the rules file). + +This also includes a modified RESET keycode as well, that sets the underglow to red. diff --git a/users/drashna/readme/rgb.md b/users/drashna/readme/rgb.md new file mode 100644 index 0000000000..acf01b051e --- /dev/null +++ b/users/drashna/readme/rgb.md @@ -0,0 +1,43 @@ +# Layer Indication Code + +At least for RGB Light, the `layer_state_set` function is used to detect the current highest layer, and change the underglow based on that layer. + +This works for both the regular layers, and for the default layers, too. + +I use the sethsv variants of the commands, so that different modes can be used, as well. + +RGB Matrix uses a custom, per board implementation, at the moment. + +# RGB Light Startup Animation + +On startup, if enabled, the board will cycle through the entire hue wheel, starting and ending on the default layer color. + +```c +void keyboard_post_init_rgb(void) { +#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_STARTUP_ANIMATION) + if (userspace_config.rgb_layer_change) { rgblight_enable_noeeprom(); } + if (rgblight_config.enable) { + layer_state_set_user(layer_state); + uint16_t old_hue = rgblight_config.hue; + rgblight_mode_noeeprom(RGBLIGHT_MODE_STATIC_LIGHT); + for (uint16_t i = 255; i > 0; i--) { + rgblight_sethsv_noeeprom( ( i + old_hue) % 255, 255, 255); + matrix_scan(); + wait_ms(10); + } + } +#endif + layer_state_set_user(layer_state); +} +``` + +This could probably benefit from some cleanup and better handling. + + +# RGB Light Twinkling + +This enables random twinkling of the LEDs when typing. + +# RGB Light Mod Indicators + +Allows feedback of which mods (oneshot or otherwise) are enabled. diff --git a/users/drashna/readme/secrets.md b/users/drashna/readme/secrets.md new file mode 100644 index 0000000000..a9408dc2ef --- /dev/null +++ b/users/drashna/readme/secrets.md @@ -0,0 +1,123 @@ +# Secret Macros + +With help from gitter and Colinta, this adds the ability to add hidden macros from other users. + +First, I have several files that are hidden/excluded from Git/GitHub. These contain everything needed for the macros. To hide these files, open `.git/info/exclude` and add `secrets.c` and `secrets.h` to that file, below the comments. + +And this requires `KC_SECRET_1` through `KC_SECRET_5` to be added in your keycode enum (usually in your `<name>.h` file) the keycodes for the new macros. + +## Git Exclusion + +To prevent `git` from seeing, or committing the secret files, you can exclude them. What's the point of having secrets if they're posted on GitHub for everyone to see!?! + +You can do this with the `.git/info/exclude` file, so that it's only ignored locally. Unfortunately, that means it's not consistently handled on each system. + +However, if you create a `.gitignore` file in the same folder, you keep things consistent between every system that the code is checked out on. + +```c +secrets.c +secrets.h +``` + +## secrets.c + +Here is the magic. This handles including the "secrets", and adding the custom macros to send them. + +```c +#include "drashna.h" // replace with your keymap's "h" file, or whatever file stores the keycodes + +#if (__has_include("secrets.h") && !defined(NO_SECRETS)) +#include "secrets.h" +#else +// `PROGMEM const char secret[][x]` may work better, but it takes up more space in the firmware +// And I'm not familiar enough to know which is better or why... +static const char * const secret[] = { + "test1", + "test2", + "test3", + "test4", + "test5" +}; +#endif + +bool process_record_secrets(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case KC_SECRET_1 ... KC_SECRET_5: // Secrets! Externally defined strings, not stored in repo + if (!record->event.pressed) { + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); + send_string_with_delay(secret[keycode - KC_SECRET_1], MACRO_TIMER); + } + return false; + break; + } + return true; +} +``` + +## secrets.h + +Now, for the actual secrets! The file needs to look like + +```c +static const char * secrets[] = { + "secret1", + "secret2", + "secret3", + "secret4", + "secret5" +}; +``` + +Replacing the strings with the codes that you need. + +## Process Record + +In whichever file you have your `process_record_*` function in, you will want to add this to the top: + +```c +__attribute__ ((weak)) +bool process_record_secrets(uint16_t keycode, keyrecord_t *record) { + return true; +} +``` + +This is so that the function can be called here, and replaced in the `secrets.c` file, and so it won't error out if it doesn't exist. + +And then, in the `process_record_user` function, assuming you have `return process_record_keymap(keycode, record)` here, you'll want to replace the "final" return with the following. Otherwise, you want to replace the `return true;` with `return process_record_secrets(keycode, record);` + +```c +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + // your existing macro code here. + return process_record_keymap(keycode, record) && process_record_secrets(keycode, record); +} +``` + +## rules.mk + +Here, you want your `/users/<name>/rules.mk` file to "detect" the existence of the `secrets.c` file, and only add it if the file exists. + +Additionally, to ensure that it's not added or processed in any way, it checks to see if `NO_SECRETS` is set. This way, if you run `make keyboard:name NO_SECRETS=yes`, it will remove the feature altogether. + +```make +ifneq ($(strip $(NO_SECRETS)), yes) + ifneq ("$(wildcard $(USER_PATH)/secrets.c)","") + SRC += secrets.c + endif +endif +``` + +Alternately, if you want to make sure that you can disable the function without messing with the file, you need to add this to your `/users/<name>/rules.mk`, so that it catches the flag: + +```make +ifneq ("$(wildcard $(USER_PATH)/secrets.c)","") + SRC += secrets.c +endif + +ifeq ($(strip $(NO_SECRETS)), yes) + OPT_DEFS += -DNO_SECRETS +endif +``` + +## Extras + +Additionally, because this file isn't present in the repo at all, you could add additional functionality that nobody else will see. diff --git a/users/drashna/readme/tap_dance.md b/users/drashna/readme/tap_dance.md new file mode 100644 index 0000000000..a61dd1f2b0 --- /dev/null +++ b/users/drashna/readme/tap_dance.md @@ -0,0 +1,119 @@ +# Diablo Tap Dances + +My [Tap Dance](https://github.com/qmk/qmk_firmware/blob/master/users/drashna/tap_dances.c) file includes the tap dance declarations, and everything needed for them. + +This is used for making Diablo 3 much easier to plan, especially at high rift levels. + +This works by using Tap Dances. The taps don't actually "do anything". Instead, it sets up the interval for how often to send specific keypresses. As you can tell, this makes automating things very easy. + +For critics that think this is cheating, just search "[diablo 3 num lock auto cast](http://lmgtfy.com/?q=diablo+3+numlock+autocast)". This is just a simpler method, that doesn't require a numpad. + + +## Custom Tap Dance Type +The real fun here is that the tap dances use a custom defined Tap Dance type: + +```c +#define ACTION_TAP_DANCE_DIABLO(index, keycode) { \ + .fn = { NULL, (void *)diablo_tapdance_master, NULL }, \ + .user_data = (void *)&((diable_keys_t) { index, keycode }), \ + } +``` +This lets me set an index and keycode for the tap dance. This isn't the cool part yet, but this allows for the really cool stuff. + +The Index is needed because I don't know how to handle it otherwise. + +## The Actual Dances + +These are the custom defined dances that I'm using. It sets up everything for later, using the above custom dance type. + +```c +//Tap Dance Definitions, sets the index and the keycode. +qk_tap_dance_action_t tap_dance_actions[] = { + // tap once to disable, and more to enable timed micros + [TD_D3_1] = ACTION_TAP_DANCE_DIABLO(0, KC_1), + [TD_D3_2] = ACTION_TAP_DANCE_DIABLO(1, KC_2), + [TD_D3_3] = ACTION_TAP_DANCE_DIABLO(2, KC_3), + [TD_D3_4] = ACTION_TAP_DANCE_DIABLO(3, KC_4), +}; +``` + +## Custom Data Structures + +First, to get this all working, there are a couple of things that need to be set up. In a header file (or you could put it into the keymap), you need to create a couple of custom structures: + +```c +typedef struct { + uint16_t timer; + uint8_t key_interval; + uint8_t keycode; +} diablo_timer_t; + +typedef struct { + uint8_t index; + uint8_t keycode; +} diable_keys_t; +``` + +The first structure is for tracking each key that is being used. The second is to pass data from the Tap Dance action array to the actual function that we will need. + + +## Custom Arrays + +To facilitate things, you will need a couple of arrays in your `c` file. + +```c +//define diablo macro timer variables +diablo_timer_t diablo_timer[4]; + +// Set the default intervals. Always start with 0 so that it will disable on first hit. +// Otherwise, you will need to hit a bunch of times, or hit the "clear" command +uint8_t diablo_times[] = { 0, 1, 3, 5, 10, 30 }; +``` + +The first one (`diablo_timer`) is what keeps track of the timer used for the keys, the interval that it uses, and the actual keycode. This makes managing it a lot easier. + +The second array is a list of predefined intervals, in seconds. You can add more here, or remove entries. It doesn't matter how long the array is, as this is computed automatically. + +## The Magic - Part 1: Master function + +The first part of the magic here is the `diablo_tapdance_master` function. The Tap Dance feature calls this function, directly, and passes some data to the function. Namely, it passes the array of the index and the keycode (`diablo_keys_t` from above). This sets the keycode and the interval for the specific index of `diabolo_timer` based on the number of taps. If you hit it more than the number of items in the array, then it zeroes out the interval, disabling it. + +```c +// Cycle through the times for the macro, starting at 0, for disabled. +void diablo_tapdance_master(qk_tap_dance_state_t *state, void *user_data) { + diable_keys_t *diablo_keys = (diable_keys_t *)user_data; + // Sets the keycode based on the index + diablo_timer[diablo_keys->index].keycode = diablo_keys->keycode; + + // if the tapdance is hit more than the number of elemints in the array, reset + if (state->count >= (sizeof(diablo_times) / sizeof(uint8_t) ) ) { + diablo_timer[diablo_keys->index].key_interval = 0; + reset_tap_dance(state); + } else { // else set the interval (tapdance count starts at 1, array starts at 0, so offset by one) + diablo_timer[diablo_keys->index].key_interval = diablo_times[state->count - 1]; + } +} +``` + +## The Magic - Part 2: The Coup de Grace + +The real core here is the `run_diablo_macro_check()` function. You need to call this from `matrix_scan_user`, as this handles the timer check. + +Specifically, it runs a check for each index of the timer. It checks to see if it's enabled, and if enough time has passed. If enough time has passed, it resets the timer, and will tap the keycode that you set for that index, but only if the Diablo layer is enabled. + +```c +// Checks each of the 4 timers/keys to see if enough time has elapsed +void run_diablo_macro_check(void) { + for (uint8_t index = 0; index < NUM_OF_DIABLO_KEYS; index++) { + // if key_interval is 0, it's disabled, so only run if it's set. If it's set, check the timer. + if ( diablo_timer[index].key_interval && timer_elapsed( diablo_timer[index].timer ) > ( diablo_timer[index].key_interval * 1000 ) ) { + // reset the timer, since enough time has passed + diablo_timer[index].timer = timer_read(); + // send keycode ONLY if we're on the diablo layer. + if (IS_LAYER_ON(_DIABLO)) { + tap_code(diablo_timer[index].keycode); + } + } + } +} +``` diff --git a/users/drashna/readme/wrappers.md b/users/drashna/readme/wrappers.md new file mode 100644 index 0000000000..fd62ff1609 --- /dev/null +++ b/users/drashna/readme/wrappers.md @@ -0,0 +1,11 @@ +## Keyboard Layout Templates + +This borrows from @jola5's "Not quite neo" code. This allows me to maintain blocks of keymaps in the userspace, so that I can modify the userspace, and this is reflected in all of the keyboards that use it, at once. + +This makes adding tap/hold mods, or other special keycodes or functions to all keyboards super easy, as it's done to all of them at once. + +The caveat here is that the keymap needs a processor/wrapper, as it doesn't like the substitutions. However, this is as simple as just pushing it through a define. For instance: + +`#define LAYOUT_ergodox_wrapper(...) LAYOUT_ergodox(__VA_ARGS__)` + +Once that's been done and you've switched the keymaps to use the "wrapper", it will read the substitution blocks just fine. |