This commit is contained in:
parent
4dc5811f00
commit
ee0a3c3be0
61 changed files with 5743 additions and 0 deletions
79
modules/getreuer/socd_cleaner/README.md
Normal file
79
modules/getreuer/socd_cleaner/README.md
Normal file
|
@ -0,0 +1,79 @@
|
|||
# SOCD Cleaner
|
||||
|
||||
<table>
|
||||
<tr><td><b>Module</b></td><td><tt>getreuer/socd_cleaner</tt></td></tr>
|
||||
<tr><td><b>Version</b></td><td>2025-03-07</td></tr>
|
||||
<tr><td><b>Maintainer</b></td><td>Pascal Getreuer (@getreuer)</td></tr>
|
||||
<tr><td><b>License</b></td><td><a href="../LICENSE.txt">Apache 2.0</a></td></tr>
|
||||
<tr><td><b>Documentation</b></td><td>
|
||||
<a href="https://getreuer.info/posts/keyboards/socd-cleaner">https://getreuer.info/posts/keyboards/socd-cleaner</a>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
This is a community module adaptation of [SOCD Cleaner
|
||||
Case](https://getreuer.info/posts/keyboards/socd-cleaner) for Simultaneous
|
||||
Opposing Cardinal Directions (SOCD) filtering. What this mouthful of a name
|
||||
means is that when two keys of opposing direction are held at the same time, a
|
||||
rule is applied to decide which key is sent to the computer. Such filtering is
|
||||
popular for fast inputs on the WASD keys in gaming.
|
||||
|
||||
**Caution: Check game rules before using.** Notably, [Counter-Strike does not
|
||||
allow SOCD
|
||||
filtering](https://store.steampowered.com/news/app/730/view/6500469346429600836).
|
||||
It is your responsibility to disable SOCD Cleaner where it is prohibited.
|
||||
|
||||
## Add SOCD to your keymap
|
||||
|
||||
Add the following to your `keymap.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"modules": ["getreuer/socd_cleaner"]
|
||||
}
|
||||
```
|
||||
|
||||
Then in your `keymap.c`, add:
|
||||
|
||||
```c
|
||||
socd_cleaner_t socd_opposing_pairs[] = {
|
||||
{{KC_W, KC_S}, SOCD_CLEANER_LAST},
|
||||
{{KC_A, KC_D}, SOCD_CLEANER_LAST},
|
||||
};
|
||||
```
|
||||
|
||||
These lines specify that SOCD filtering is to be performed on the WASD keys
|
||||
(referred to by keycodes `KC_W`, `KC_A`, `KC_S`, `KC_D`) with last input
|
||||
priority resolution (`SOCD_CLEANER_LAST`). If you want to do something else,
|
||||
this is where to change that.
|
||||
|
||||
Resolution strategies:
|
||||
|
||||
* `SOCD_CLEANER_LAST`: (Recommended) Last input priority with reactivation. The
|
||||
last key pressed wins. If the last key is released while the opposing key is
|
||||
still held, the opposing key is reactivated. 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
|
||||
opposing pair.
|
||||
|
||||
* `SOCD_CLEANER_1_WINS`: Key 1 always wins, the second key listed.
|
||||
|
||||
* `SOCD_CLEANER_OFF`: SOCD filtering is disabled for this key pair.
|
||||
|
||||
SOCD Cleaner is enabled by default. Optionally, use these keycodes to enable and
|
||||
disable SOCD Cleaner globally for all opposing pairs:
|
||||
|
||||
| Keycode | Description |
|
||||
|-----------|---------------------------|
|
||||
| `SOCDON` | Turn SOCD Cleaner on. |
|
||||
| `SOCDOFF` | Turn SOCD Cleaner off. |
|
||||
| `SOCDTOG` | Toggle SOCD Cleaner. |
|
||||
|
||||
|
||||
See the [SOCD Cleaner
|
||||
documentation](https://getreuer.info/posts/keyboards/socd-cleaner) for further
|
||||
explanation and details.
|
||||
|
36
modules/getreuer/socd_cleaner/introspection.c
Normal file
36
modules/getreuer/socd_cleaner/introspection.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2025 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.
|
||||
|
||||
#ifdef COMMUNITY_MODULE_SOCD_CLEANER_ENABLE
|
||||
|
||||
uint16_t socd_opposing_pairs_count_raw(void) {
|
||||
return ARRAY_SIZE(socd_opposing_pairs);
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint16_t socd_opposing_pairs_count(void) {
|
||||
return socd_opposing_pairs_count_raw();
|
||||
}
|
||||
|
||||
socd_cleaner_t* socd_opposing_pairs_get_raw(uint16_t index) {
|
||||
if (index >= socd_opposing_pairs_count_raw()) {
|
||||
return NULL;
|
||||
}
|
||||
return &socd_opposing_pairs[index];
|
||||
}
|
||||
|
||||
__attribute__((weak)) socd_cleaner_t* socd_opposing_pairs_get(uint16_t index) {
|
||||
return socd_opposing_pairs_get_raw(index);
|
||||
}
|
||||
|
||||
#endif // COMMUNITY_MODULE_SOCD_CLEANER_ENABLE
|
17
modules/getreuer/socd_cleaner/introspection.h
Normal file
17
modules/getreuer/socd_cleaner/introspection.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2025 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.
|
||||
|
||||
#pragma once
|
||||
#include "socd_cleaner.h"
|
||||
|
9
modules/getreuer/socd_cleaner/qmk_module.json
Normal file
9
modules/getreuer/socd_cleaner/qmk_module.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"module_name": "SOCD Cleaner",
|
||||
"maintainer": "getreuer",
|
||||
"keycodes": [
|
||||
{"key": "SOCDON"},
|
||||
{"key": "SOCDOFF"},
|
||||
{"key": "SOCDTOG"}
|
||||
]
|
||||
}
|
116
modules/getreuer/socd_cleaner/socd_cleaner.c
Normal file
116
modules/getreuer/socd_cleaner/socd_cleaner.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2024-2025 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 community module implementation
|
||||
*
|
||||
* For full documentation, see
|
||||
* <https://getreuer.info/posts/keyboards/socd-cleaner>
|
||||
*/
|
||||
|
||||
#include "socd_cleaner.h"
|
||||
|
||||
ASSERT_COMMUNITY_MODULES_MIN_API_VERSION(1, 0, 0);
|
||||
|
||||
// Defined in introspection.c.
|
||||
uint16_t socd_opposing_pairs_count(void);
|
||||
socd_cleaner_t* socd_opposing_pairs_get(uint16_t index);
|
||||
|
||||
bool socd_cleaner_enabled = true;
|
||||
|
||||
static void update_key(uint8_t keycode, bool press) {
|
||||
if (press) {
|
||||
add_key(keycode);
|
||||
} else {
|
||||
del_key(keycode);
|
||||
}
|
||||
}
|
||||
|
||||
static bool process_opposing_pair(
|
||||
uint16_t keycode, keyrecord_t* record, socd_cleaner_t* state) {
|
||||
if (!state || !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.
|
||||
}
|
||||
|
||||
bool process_record_socd_cleaner(uint16_t keycode, keyrecord_t* record) {
|
||||
switch (keycode) {
|
||||
case SOCDON: // Turn SOCD Cleaner on.
|
||||
if (record->event.pressed) {
|
||||
socd_cleaner_enabled = true;
|
||||
}
|
||||
return false;
|
||||
case SOCDOFF: // Turn SOCD Cleaner off.
|
||||
if (record->event.pressed) {
|
||||
socd_cleaner_enabled = false;
|
||||
}
|
||||
return false;
|
||||
case SOCDTOG: // Toggle SOCD Cleaner.
|
||||
if (record->event.pressed) {
|
||||
socd_cleaner_enabled = !socd_cleaner_enabled;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (socd_cleaner_enabled) {
|
||||
for (int i = 0; i < (int)socd_opposing_pairs_count(); ++i) {
|
||||
socd_cleaner_t* state = socd_opposing_pairs_get(i);
|
||||
if (!process_opposing_pair(keycode, record, state)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
83
modules/getreuer/socd_cleaner/socd_cleaner.h
Normal file
83
modules/getreuer/socd_cleaner/socd_cleaner.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2024-2025 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 community module: enhance WASD for fast inputs for gaming
|
||||
*
|
||||
*
|
||||
* SOCD Cleaner is a QMK module 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.
|
||||
*
|
||||
* 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`.
|
||||
*
|
||||
* - 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;
|
||||
|
||||
/** Determines globally whether SOCD cleaner is enabled. */
|
||||
extern bool socd_cleaner_enabled;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue