Skip to content

Commit

Permalink
other updates and fix nkro issue
Browse files Browse the repository at this point in the history
  • Loading branch information
Dialgatrainer02 committed Sep 13, 2024
1 parent b106220 commit df3c055
Show file tree
Hide file tree
Showing 8 changed files with 765 additions and 51 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,10 +1,2 @@
//optimisations
#define NO_MUSIC_MODE
#undef LOCKING_SUPPORT_ENABLE
#undef LOCKING_RESYNC_ENABLE
#define LAYER_STATE_8BIT

#define DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD
/*TODO
add snap tap and configire once implementation is ready
*/

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022-2023 Google LLC
// Copyright 2022-2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -62,14 +62,14 @@ static uint8_t sentence_state = STATE_INIT;

// Sets the current state to `new_state`.
static void set_sentence_state(uint8_t new_state) {
#ifndef NO_DEBUG
#if !defined(NO_DEBUG) && defined(SENTENCE_CASE_DEBUG)
if (debug_enable && sentence_state != new_state) {
static const char* state_names[] = {
"INIT", "WORD", "ABBREV", "ENDING", "PRIMED", "DISABLED",
};
dprintf("Sentence case: %s\n", state_names[new_state]);
}
#endif // NO_DEBUG
#endif // !NO_DEBUG && SENTENCE_CASE_DEBUG

const bool primed = (new_state == STATE_PRIMED);
if (primed != (sentence_state == STATE_PRIMED)) {
Expand Down Expand Up @@ -144,6 +144,20 @@ bool process_sentence_case(uint16_t keycode, keyrecord_t* record) {
#endif // SENTENCE_CASE_TIMEOUT > 0

switch (keycode) {
case KC_LCTL ... KC_RGUI: // Ignore mod keys.
case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: // Ignore one-shot mod.
// Ignore MO, TO, TG, TT, OSL, TL layer switch keys.
case QK_MOMENTARY ... QK_MOMENTARY_MAX:
case QK_TO ... QK_TO_MAX:
case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: // Ignore one-shot layer.
#ifdef TRI_LAYER_ENABLE // Ignore Tri Layer keys.
case QK_TRI_LAYER_LOWER:
case QK_TRI_LAYER_UPPER:
#endif // TRI_LAYER_ENABLE
return true;

#ifndef NO_ACTION_TAPPING
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
if (record->tap.count == 0) {
Expand All @@ -153,12 +167,12 @@ bool process_sentence_case(uint16_t keycode, keyrecord_t* record) {
break;
#ifndef NO_ACTION_LAYER
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
#endif // NO_ACTION_LAYER
if (record->tap.count == 0) {
return true;
}
keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);
break;
#endif // NO_ACTION_LAYER
#endif // NO_ACTION_TAPPING

#ifdef SWAP_HANDS_ENABLE
Expand Down Expand Up @@ -200,7 +214,9 @@ bool process_sentence_case(uint16_t keycode, keyrecord_t* record) {
// ENDING | ABBREV INIT PRIMED ENDING
// PRIMED | match! INIT PRIMED PRIMED
char code = sentence_case_press_user(keycode, record, mods);
#if defined SENTENCE_CASE_DEBUG
dprintf("Sentence Case: code = '%c' (%d)\n", code, (int)code);
#endif // SENTENCE_CASE_DEBUG
switch (code) {
case '\0': // Current key should be ignored.
return true;
Expand Down Expand Up @@ -269,7 +285,9 @@ bool process_sentence_case(uint16_t keycode, keyrecord_t* record) {
#if SENTENCE_CASE_BUFFER_SIZE > 1
key_buffer[SENTENCE_CASE_BUFFER_SIZE - 1] = keycode;
if (new_state == STATE_ENDING && !sentence_case_check_ending(key_buffer)) {
#if defined SENTENCE_CASE_DEBUG
dprintf("Not a real ending.\n");
#endif // SENTENCE_CASE_DEBUG
new_state = STATE_INIT;
}
#endif // SENTENCE_CASE_BUFFER_SIZE > 1
Expand Down Expand Up @@ -311,9 +329,6 @@ __attribute__((weak)) char sentence_case_press_user(uint16_t keycode,
if ((mods & ~(MOD_MASK_SHIFT | MOD_BIT(KC_RALT))) == 0) {
const bool shifted = mods & MOD_MASK_SHIFT;
switch (keycode) {
case KC_LCTL ... KC_RGUI: // Mod keys.
return '\0'; // These keys are ignored.

case KC_A ... KC_Z:
return 'a'; // Letter key.

Expand All @@ -322,8 +337,13 @@ __attribute__((weak)) char sentence_case_press_user(uint16_t keycode,
case KC_1:
case KC_SLSH:
return shifted ? '.' : '#';
case KC_EXLM:
case KC_QUES:
return '.';
case KC_2 ... KC_0: // 2 3 4 5 6 7 8 9 0
case KC_MINS ... KC_SCLN: // - = [ ] ; backslash
case KC_AT ... KC_RPRN: // @ # $ % ^ & * ( )
case KC_MINS ... KC_SCLN: // - = [ ] backslash ;
case KC_UNDS ... KC_COLN: // _ + { } | :
case KC_GRV:
case KC_COMM:
return '#'; // Symbol key.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022 Google LLC
// Copyright 2022, 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -157,7 +157,7 @@ bool sentence_case_just_typed_P(const uint16_t* buffer, const uint16_t* pattern,
* '.' Key is sentence-ending punctuation. Default: . ? !
*
* '#' Key types a backspaceable character that isn't part of a word.
* Default: - = [ ] ; ' ` , < > / digits backslash
* Default includes - = [ ] ; ' , < > / _ + @ # $ % ^ & * ( ) { } digits
*
* ' ' Key is a space. Default: KC_SPC
*
Expand All @@ -177,9 +177,6 @@ bool sentence_case_just_typed_P(const uint16_t* buffer, const uint16_t* pattern,
* if ((mods & ~(MOD_MASK_SHIFT | MOD_BIT(KC_RALT))) == 0) {
* const bool shifted = mods & MOD_MASK_SHIFT;
* switch (keycode) {
* case KC_LCTL ... KC_RGUI: // Mod keys.
* return '\0'; // These keys are ignored.
*
* case KC_A ... KC_Z:
* return 'a'; // Letter key.
*
Expand All @@ -188,8 +185,13 @@ bool sentence_case_just_typed_P(const uint16_t* buffer, const uint16_t* pattern,
* case KC_1:
* case KC_SLSH:
* return shifted ? '.' : '#';
* case KC_EXLM:
* case KC_QUES:
* return '.';
* case KC_2 ... KC_0: // 2 3 4 5 6 7 8 9 0
* case KC_MINS ... KC_SCLN: // - = [ ] ; backslash
* case KC_AT ... KC_RPRN: // @ # $ % ^ & * ( )
* case KC_MINS ... KC_SCLN: // - = [ ] backslash ;
* case KC_UNDS ... KC_COLN: // _ + { } | :
* case KC_GRV:
* case KC_COMM:
* return '#'; // Symbol key.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* @file socd_cleaner.c
* @brief SOCD Cleaner implementation
*
* For full documentation, see
* <https://getreuer.info/posts/keyboards/socd-cleaner>
*/

#include "socd_cleaner.h"

#ifdef __cplusplus
extern "C" {
#endif

bool socd_cleaner_enabled = true;

static void update_key(uint8_t keycode, bool press) {
if (press) {
add_key(keycode);
} else {
del_key(keycode);
}
}

bool process_socd_cleaner(uint16_t keycode, keyrecord_t* record,
socd_cleaner_t* state) {
if (!socd_cleaner_enabled || !state->resolution ||
(keycode != state->keys[0] && keycode != state->keys[1])) {
return true; // Quick return when disabled or on unrelated events.
}
// The current event corresponds to index `i`, 0 or 1, in the SOCD key pair.
const uint8_t i = (keycode == state->keys[1]);
const uint8_t opposing = i ^ 1; // Index of the opposing key.

// Track which keys are physically held (vs. keys in the report).
state->held[i] = record->event.pressed;

// Perform SOCD resolution for events where the opposing key is held.
if (state->held[opposing]) {
switch (state->resolution) {
case SOCD_CLEANER_LAST: // Last input priority with reactivation.
// If the current event is a press, then release the opposing key.
// Otherwise if this is a release, then press the opposing key.
update_key(state->keys[opposing], !state->held[i]);
break;

case SOCD_CLEANER_NEUTRAL: // Neutral resolution.
// Same logic as SOCD_CLEANER_LAST, but skip default handling so that
// the current key has no effect while the opposing key is held.
update_key(state->keys[opposing], !state->held[i]);
// Send updated report (normally, default handling would do this).
send_keyboard_report();
return false; // Skip default handling.

case SOCD_CLEANER_0_WINS: // Key 0 wins.
case SOCD_CLEANER_1_WINS: // Key 1 wins.
if (opposing == (state->resolution - SOCD_CLEANER_0_WINS)) {
// The opposing key is the winner. The current key has no effect.
return false; // Skip default handling.
} else {
// The current key is the winner. Update logic is same as above.
update_key(state->keys[opposing], !state->held[i]);
}
break;
}
}
return true; // Continue default handling to press/release current key.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* @file socd_cleaner.h
* @brief SOCD Cleaner - enhance WASD for fast inputs for gaming
*
* Overview
* --------
*
* SOCD Cleaner is a QMK library for Simultaneous Opposing Cardinal Directions
* (SOCD) filtering, which is popular for fast inputs on the WASD keys in
* gaming. When two keys of opposing direction are physically held at the same
* time, a rule is applied to decide which key is sent to the computer.
*
*
* Add it to your keymap
* ---------------------
*
* In rules.mk, add `SRC += features/socd_cleaner.c`. Then in keymap.c, add
*
* #include "features/socd_cleaner.h"
*
* socd_cleaner_t socd_v = {{KC_W, KC_S}, SOCD_CLEANER_LAST};
* socd_cleaner_t socd_h = {{KC_A, KC_D}, SOCD_CLEANER_LAST};
*
* bool process_record_user(uint16_t keycode, keyrecord_t* record) {
* if (!process_socd_cleaner(keycode, record, &socd_v)) { return false; }
* if (!process_socd_cleaner(keycode, record, &socd_h)) { return false; }
* // Your macros...
* return true;
* }
*
* Each `socd_cleaner_t` instance defines a pair of opposing keys and an SOCD
* resolution strategy (explained below). In `process_record_user()`, handler
* `process_socd_cleaner()` is called with each `socd_cleaner_t` instance.
*
* NOTE: The keys don't have to be WASD. But they must be basic keycodes
* (https://docs.qmk.fm/keycodes_basic).
*
*
* Enabling / disabling
* --------------------
*
* SOCD filtering is enabled/disabled globally by assigning to variable
* `socd_cleaner_enabled`. For instance, to enable only on a GAME layer:
*
* layer_state_t layer_state_set_user(layer_state_t state) {
* socd_cleaner_enabled = IS_LAYER_ON_STATE(state, GAME);
* return state;
* }
*
* Or filtering can be disabled per `socd_cleaner_t` instance by setting its
* resolution to SOCD_CLEANER_OFF.
*
*
* Resolution strategies
* ---------------------
*
* As controls vary across games, there are multiple possible SOCD resolution
* strategies. SOCD Cleaner implements the following resolutions:
*
* - SOCD_CLEANER_LAST: (Recommended) Last input priority with reactivation.
* The last key pressed wins. Rapid alternating inputs can be made.
* Repeatedly tapping the D key while A is held sends "ADADADAD."
*
* - SOCD_CLEANER_NEUTRAL: Neutral resolution. When both keys are pressed, they
* cancel and neither is sent.
*
* - SOCD_CLEANER_0_WINS: Key 0 always wins, the first key listed in defining
* the `socd_cleaner_t`. For example, the W key always wins in
*
* socd_cleaner_t socd_ud = {{KC_W, KC_S}, SOCD_CLEANER_0_WINS};
*
* - SOCD_CLEANER_1_WINS: Key 1 always wins, the second key listed.
*
* If you don't know what to pick, SOCD_CLEANER_LAST is recommended. The
* resolution strategy on a `socd_cleaner_t` may be changed at run time by
* assigning to `.resolution`.
*
*
* For full documentation, see
* <https://getreuer.info/posts/keyboards/socd-cleaner>
*/

#pragma once

#include "quantum.h"

#ifdef __cplusplus
extern "C" {
#endif

enum socd_cleaner_resolution {
// Disable SOCD filtering for this key pair.
SOCD_CLEANER_OFF,
// Last input priority with reactivation.
SOCD_CLEANER_LAST,
// Neutral resolution. When both keys are pressed, they cancel.
SOCD_CLEANER_NEUTRAL,
// Key 0 always wins.
SOCD_CLEANER_0_WINS,
// Key 1 always wins.
SOCD_CLEANER_1_WINS,
// Sentinel to count the number of resolution strategies.
SOCD_CLEANER_NUM_RESOLUTIONS,
};

typedef struct {
uint8_t keys[2]; // Basic keycodes for the two opposing keys.
uint8_t resolution; // Resolution strategy.
bool held[2]; // Tracks which keys are physically held.
} socd_cleaner_t;

/**
* Handler function for SOCD cleaner.
*
* This function should be called from process_record_user(). The function may
* be called multiple times with different socd_cleaner_t instances to filter
* more than one SOCD key pair.
*/
bool process_socd_cleaner(uint16_t keycode, keyrecord_t* record,
socd_cleaner_t* state);

/** Determines globally whether SOCD cleaner is enabled. */
extern bool socd_cleaner_enabled;

#ifdef __cplusplus
}
#endif
Loading

0 comments on commit df3c055

Please sign in to comment.