diff options
Diffstat (limited to 'quantum/process_keycode')
-rw-r--r-- | quantum/process_keycode/process_auto_shift.c | 213 | ||||
-rw-r--r-- | quantum/process_keycode/process_auto_shift.h | 11 | ||||
-rw-r--r-- | quantum/process_keycode/process_joystick.c | 8 | ||||
-rw-r--r-- | quantum/process_keycode/process_midi.c | 25 | ||||
-rw-r--r-- | quantum/process_keycode/process_midi.h | 2 | ||||
-rw-r--r-- | quantum/process_keycode/process_sequencer.c | 62 | ||||
-rw-r--r-- | quantum/process_keycode/process_sequencer.h | 21 |
7 files changed, 265 insertions, 77 deletions
diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c index 76530959c9..41e60496b9 100644 --- a/quantum/process_keycode/process_auto_shift.c +++ b/quantum/process_keycode/process_auto_shift.c @@ -16,49 +16,149 @@ #ifdef AUTO_SHIFT_ENABLE +# include <stdbool.h> # include <stdio.h> # include "process_auto_shift.h" -static bool autoshift_enabled = true; -static uint16_t autoshift_time = 0; +static uint16_t autoshift_time = 0; static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT; static uint16_t autoshift_lastkey = KC_NO; +static struct { + // Whether autoshift is enabled. + bool enabled : 1; + // Whether the last auto-shifted key was released after the timeout. This + // is used to replicate the last key for a tap-then-hold. + bool lastshifted : 1; + // Whether an auto-shiftable key has been pressed but not processed. + bool in_progress : 1; + // Whether the auto-shifted keypress has been registered. + bool holding_shift : 1; +} autoshift_flags = {true, false, false, false}; + +/** \brief Record the press of an autoshiftable key + * + * \return Whether the record should be further processed. + */ +static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record) { + if (!autoshift_flags.enabled) { + return true; + } -void autoshift_flush(void) { - if (autoshift_lastkey != KC_NO) { - uint16_t elapsed = timer_elapsed(autoshift_time); +# ifndef AUTO_SHIFT_MODIFIERS + if (get_mods() & (~MOD_BIT(KC_LSFT))) { + return true; + } +# endif +# ifdef AUTO_SHIFT_REPEAT + const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); +# ifndef AUTO_SHIFT_NO_AUTO_REPEAT + if (!autoshift_flags.lastshifted) { +# endif + if (elapsed < TAPPING_TERM && keycode == autoshift_lastkey) { + // Allow a tap-then-hold for keyrepeat. + if (!autoshift_flags.lastshifted) { + register_code(autoshift_lastkey); + } else { + // Simulate pressing the shift key. + add_weak_mods(MOD_BIT(KC_LSFT)); + register_code(autoshift_lastkey); + } + return false; + } +# ifndef AUTO_SHIFT_NO_AUTO_REPEAT + } +# endif +# endif + + // Record the keycode so we can simulate it later. + autoshift_lastkey = keycode; + autoshift_time = now; + autoshift_flags.in_progress = true; - if (elapsed > autoshift_timeout) { - tap_code16(LSFT(autoshift_lastkey)); +# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); +# endif + return false; +} + +/** \brief Registers an autoshiftable key under the right conditions + * + * If the autoshift delay has elapsed, register a shift and the key. + * + * If the autoshift key is released before the delay has elapsed, register the + * key without a shift. + */ +static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) { + // Called on key down with KC_NO, auto-shifted key up, and timeout. + if (autoshift_flags.in_progress) { + // Process the auto-shiftable key. + autoshift_flags.in_progress = false; + + // Time since the initial press was recorded. + const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); + if (elapsed < autoshift_timeout) { + register_code(autoshift_lastkey); + autoshift_flags.lastshifted = false; } else { - tap_code(autoshift_lastkey); + // Simulate pressing the shift key. + add_weak_mods(MOD_BIT(KC_LSFT)); + register_code(autoshift_lastkey); + autoshift_flags.lastshifted = true; +# if defined(AUTO_SHIFT_REPEAT) && !defined(AUTO_SHIFT_NO_AUTO_REPEAT) + if (matrix_trigger) { + // Prevents release. + return; + } +# endif } - autoshift_time = 0; - autoshift_lastkey = KC_NO; +# if TAP_CODE_DELAY > 0 + wait_ms(TAP_CODE_DELAY); +# endif + unregister_code(autoshift_lastkey); + del_weak_mods(MOD_BIT(KC_LSFT)); + } else { + // Release after keyrepeat. + unregister_code(keycode); + if (keycode == autoshift_lastkey) { + // This will only fire when the key was the last auto-shiftable + // pressed. That prevents aaaaBBBB then releasing a from unshifting + // later Bs (if B wasn't auto-shiftable). + del_weak_mods(MOD_BIT(KC_LSFT)); + } } + send_keyboard_report(); // del_weak_mods doesn't send one. + // Roll the autoshift_time forward for detecting tap-and-hold. + autoshift_time = now; } - -void autoshift_on(uint16_t keycode) { - autoshift_time = timer_read(); - autoshift_lastkey = keycode; +/** \brief Simulates auto-shifted key releases when timeout is hit + * + * Can be called from \c matrix_scan_user so that auto-shifted keys are sent + * immediately after the timeout has expired, rather than waiting for the key + * to be released. + */ +void autoshift_matrix_scan(void) { + if (autoshift_flags.in_progress) { + const uint16_t now = timer_read(); + const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); + if (elapsed >= autoshift_timeout) { + autoshift_end(autoshift_lastkey, now, true); + } + } } void autoshift_toggle(void) { - if (autoshift_enabled) { - autoshift_enabled = false; - autoshift_flush(); - } else { - autoshift_enabled = true; - } + autoshift_flags.enabled = !autoshift_flags.enabled; + del_weak_mods(MOD_BIT(KC_LSFT)); } -void autoshift_enable(void) { autoshift_enabled = true; } +void autoshift_enable(void) { autoshift_flags.enabled = true; } + void autoshift_disable(void) { - autoshift_enabled = false; - autoshift_flush(); + autoshift_flags.enabled = false; + del_weak_mods(MOD_BIT(KC_LSFT)); } # ifndef AUTO_SHIFT_NO_SETUP @@ -71,24 +171,30 @@ void autoshift_timer_report(void) { } # endif -bool get_autoshift_state(void) { return autoshift_enabled; } +bool get_autoshift_state(void) { return autoshift_flags.enabled; } -uint16_t get_autoshift_timeout(void) { - return autoshift_timeout; -} +uint16_t get_autoshift_timeout(void) { return autoshift_timeout; } -void set_autoshift_timeout(uint16_t timeout) { - autoshift_timeout = timeout; -} +void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; } bool process_auto_shift(uint16_t keycode, keyrecord_t *record) { + // Note that record->event.time isn't reliable, see: + // https://github.com/qmk/qmk_firmware/pull/9826#issuecomment-733559550 + const uint16_t now = timer_read(); if (record->event.pressed) { + if (autoshift_flags.in_progress) { + // Evaluate previous key if there is one. Doing this elsewhere is + // more complicated and easier to break. + autoshift_end(KC_NO, now, false); + } + // For pressing another key while keyrepeating shifted autoshift. + del_weak_mods(MOD_BIT(KC_LSFT)); + switch (keycode) { case KC_ASTG: autoshift_toggle(); return true; - case KC_ASON: autoshift_enable(); return true; @@ -108,43 +214,30 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) { autoshift_timer_report(); return true; # endif + } + } + + switch (keycode) { # ifndef NO_AUTO_SHIFT_ALPHA - case KC_A ... KC_Z: + case KC_A ... KC_Z: # endif # ifndef NO_AUTO_SHIFT_NUMERIC - case KC_1 ... KC_0: + case KC_1 ... KC_0: # endif # ifndef NO_AUTO_SHIFT_SPECIAL -# ifndef NO_AUTO_SHIFT_TAB - case KC_TAB: -# endif - case KC_MINUS ... KC_SLASH: - case KC_NONUS_BSLASH: -# endif - autoshift_flush(); - if (!autoshift_enabled) return true; - -# ifndef AUTO_SHIFT_MODIFIERS - if (get_mods()) { - return true; - } -# endif - autoshift_on(keycode); - - // We need some extra handling here for OSL edge cases -# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) - clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); +# ifndef NO_AUTO_SHIFT_TAB + case KC_TAB: +# endif + case KC_MINUS ... KC_SLASH: + case KC_NONUS_BSLASH: # endif + if (record->event.pressed) { + return autoshift_press(keycode, now, record); + } else { + autoshift_end(keycode, now, false); return false; - - default: - autoshift_flush(); - return true; - } - } else { - autoshift_flush(); + } } - return true; } diff --git a/quantum/process_keycode/process_auto_shift.h b/quantum/process_keycode/process_auto_shift.h index b909d28eef..5b2718f11c 100644 --- a/quantum/process_keycode/process_auto_shift.h +++ b/quantum/process_keycode/process_auto_shift.h @@ -24,9 +24,10 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record); -void autoshift_enable(void); -void autoshift_disable(void); -void autoshift_toggle(void); -bool get_autoshift_state(void); +void autoshift_enable(void); +void autoshift_disable(void); +void autoshift_toggle(void); +bool get_autoshift_state(void); uint16_t get_autoshift_timeout(void); -void set_autoshift_timeout(uint16_t timeout); +void set_autoshift_timeout(uint16_t timeout); +void autoshift_matrix_scan(void); diff --git a/quantum/process_keycode/process_joystick.c b/quantum/process_keycode/process_joystick.c index 5778a7434c..3ffaf42bf8 100644 --- a/quantum/process_keycode/process_joystick.c +++ b/quantum/process_keycode/process_joystick.c @@ -129,17 +129,17 @@ bool process_joystick_analogread_quantum() { // test the converted value against the lower range int32_t ref = joystick_axes[axis_index].mid_digit; int32_t range = joystick_axes[axis_index].min_digit; - int32_t ranged_val = ((axis_val - ref) * -127) / (range - ref); + int32_t ranged_val = ((axis_val - ref) * -JOYSTICK_RESOLUTION) / (range - ref); if (ranged_val > 0) { // the value is in the higher range range = joystick_axes[axis_index].max_digit; - ranged_val = ((axis_val - ref) * 127) / (range - ref); + ranged_val = ((axis_val - ref) * JOYSTICK_RESOLUTION) / (range - ref); } // clamp the result in the valid range - ranged_val = ranged_val < -127 ? -127 : ranged_val; - ranged_val = ranged_val > 127 ? 127 : ranged_val; + ranged_val = ranged_val < -JOYSTICK_RESOLUTION ? -JOYSTICK_RESOLUTION : ranged_val; + ranged_val = ranged_val > JOYSTICK_RESOLUTION ? JOYSTICK_RESOLUTION : ranged_val; if (ranged_val != joystick_status.axes[axis_index]) { joystick_status.axes[axis_index] = ranged_val; diff --git a/quantum/process_keycode/process_midi.c b/quantum/process_keycode/process_midi.c index e525770144..8e2fb955e7 100644 --- a/quantum/process_keycode/process_midi.c +++ b/quantum/process_keycode/process_midi.c @@ -41,12 +41,12 @@ static int8_t midi_modulation_step; static uint16_t midi_modulation_timer; midi_config_t midi_config; -inline uint8_t compute_velocity(uint8_t setting) { return (setting + 1) * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN + 1)); } +inline uint8_t compute_velocity(uint8_t setting) { return setting * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN)); } void midi_init(void) { midi_config.octave = MI_OCT_2 - MIDI_OCTAVE_MIN; midi_config.transpose = 0; - midi_config.velocity = (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN); + midi_config.velocity = 127; midi_config.channel = 0; midi_config.modulation_interval = 8; @@ -66,7 +66,7 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) { case MIDI_TONE_MIN ... MIDI_TONE_MAX: { uint8_t channel = midi_config.channel; uint8_t tone = keycode - MIDI_TONE_MIN; - uint8_t velocity = compute_velocity(midi_config.velocity); + uint8_t velocity = midi_config.velocity; if (record->event.pressed) { if (tone_status[tone] == MIDI_INVALID_NOTE) { uint8_t note = midi_compute_note(keycode); @@ -124,19 +124,30 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) { return false; case MIDI_VELOCITY_MIN ... MIDI_VELOCITY_MAX: if (record->event.pressed) { - midi_config.velocity = keycode - MIDI_VELOCITY_MIN; + midi_config.velocity = compute_velocity(keycode - MIDI_VELOCITY_MIN); dprintf("midi velocity %d\n", midi_config.velocity); } return false; case MI_VELD: if (record->event.pressed && midi_config.velocity > 0) { - midi_config.velocity--; + if (midi_config.velocity == 127) { + midi_config.velocity -= 10; + } else if (midi_config.velocity > 12) { + midi_config.velocity -= 13; + } else { + midi_config.velocity = 0; + } + dprintf("midi velocity %d\n", midi_config.velocity); } return false; case MI_VELU: - if (record->event.pressed) { - midi_config.velocity++; + if (record->event.pressed && midi_config.velocity < 127) { + if (midi_config.velocity < 115) { + midi_config.velocity += 13; + } else { + midi_config.velocity = 127; + } dprintf("midi velocity %d\n", midi_config.velocity); } return false; diff --git a/quantum/process_keycode/process_midi.h b/quantum/process_keycode/process_midi.h index 0007b3ed25..ef5661dd4d 100644 --- a/quantum/process_keycode/process_midi.h +++ b/quantum/process_keycode/process_midi.h @@ -35,7 +35,7 @@ typedef union { struct { uint8_t octave : 4; int8_t transpose : 4; - uint8_t velocity : 4; + uint8_t velocity : 7; uint8_t channel : 4; uint8_t modulation_interval : 4; }; diff --git a/quantum/process_keycode/process_sequencer.c b/quantum/process_keycode/process_sequencer.c new file mode 100644 index 0000000000..334b4c0092 --- /dev/null +++ b/quantum/process_keycode/process_sequencer.c @@ -0,0 +1,62 @@ +/* Copyright 2020 Rodolphe Belouin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "process_sequencer.h" + +bool process_sequencer(uint16_t keycode, keyrecord_t *record) { + if (record->event.pressed) { + switch (keycode) { + case SQ_ON: + sequencer_on(); + return false; + case SQ_OFF: + sequencer_off(); + return false; + case SQ_TOG: + sequencer_toggle(); + return false; + case SQ_TMPD: + sequencer_decrease_tempo(); + return false; + case SQ_TMPU: + sequencer_increase_tempo(); + return false; + case SEQUENCER_RESOLUTION_MIN ... SEQUENCER_RESOLUTION_MAX: + sequencer_set_resolution(keycode - SEQUENCER_RESOLUTION_MIN); + return false; + case SQ_RESD: + sequencer_decrease_resolution(); + return false; + case SQ_RESU: + sequencer_increase_resolution(); + return false; + case SQ_SALL: + sequencer_set_all_steps_on(); + return false; + case SQ_SCLR: + sequencer_set_all_steps_off(); + return false; + case SEQUENCER_STEP_MIN ... SEQUENCER_STEP_MAX: + sequencer_toggle_step(keycode - SEQUENCER_STEP_MIN); + return false; + case SEQUENCER_TRACK_MIN ... SEQUENCER_TRACK_MAX: + sequencer_toggle_single_active_track(keycode - SEQUENCER_TRACK_MIN); + return false; + } + } + + return true; +} diff --git a/quantum/process_keycode/process_sequencer.h b/quantum/process_keycode/process_sequencer.h new file mode 100644 index 0000000000..2b85f24299 --- /dev/null +++ b/quantum/process_keycode/process_sequencer.h @@ -0,0 +1,21 @@ +/* Copyright 2020 Rodolphe Belouin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "quantum.h" + +bool process_sequencer(uint16_t keycode, keyrecord_t *record); |