Skip to content

Commit cc62f60

Browse files
committed
init commit
0 parents  commit cc62f60

File tree

7 files changed

+1718
-0
lines changed

7 files changed

+1718
-0
lines changed

README.md

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Аккорды
2+
3+
Данный модуль позволяет вам задавать аккорды любой длины, причём в отличие от стандартной фичи [combos](https://docs.qmk.fm/#/feature_combo) в QMK, данный модуль способен посылать любые кейкоды. Причём данные аккорды способны работать не только в качестве "нажал-отпустил", а качестве зажимаемых клавиш, будь то переключение слоя или модификатор.
4+
5+
Аккорд срабатывает в следующих случаях:
6+
* Если вы зажимаете клавиши для аккорда более 100 миллисекунд, то считается что вы больше не нажмёте клавиш для следующего аккорда, и кейкод текущего аккорда нажимается. Например, можно задать на клавише `CMB_001` аккорд из одной этой клавиши, который при срабатанывании будет нажимать `KC_BSPC` (Backspace), таким образом если вы зажмёте клавишу `CMB_001`, то у вас сначала будет ожидание 100мс, затем нажмётся клавиша Backspace, и начнёт стираться текст. То есть клавиши для аккордов можно свободно использовать даже для клавиш, которые предполагается зажимать, а не только нажать и отпускать.
7+
* Если вы во время зажатия аккорда нажимаете другую клавишу, то считается что вы больше не нажмёте клавиш для следующего аккорда, и кейкод текущего аккорда нажимается.
8+
9+
Если вы хотите более подробно понять как работает обработка аккордов, или хотите модифицировать код, смотрите секцию [принцип работы](#принцип-работы).
10+
11+
Советую вам не бояться нажимать две клавиши одним пальцем, и помещать на такие нажатия аккорды.
12+
13+
# Как использовать?
14+
15+
Приготовьтесь, использовать довольно сложно, потому что данный модуль построен на костылях и хаках (если придумаете как сделать иначе - делайте PR!).
16+
17+
Последовательность действий будет описана так, чтобы вы модифицировали свой файл `keymap.c` сверху-вниз.
18+
19+
## Подключить другой модуль
20+
21+
Необходимо подключить и настроить модуль [arbitrary_keycode](../arbitrary_keycode/README.md). Подключение файла из этого модуля необходимо поместить раньше подключения текущего модуля.
22+
23+
## Задать характеристики
24+
25+
В `config.h` нужно прописать задание следующих переменных, модифицируя их под свои нужды:
26+
27+
* `#define COMBO_KEYS_COUNT 5` - количество используемых клавиш для аккордов, для данной опции у вас в итоге получатся клавиши `CMB_000`, `CMB_001`, ... , `CMB_004`.
28+
* `#define COMBO_MAX_SIZE 3` - максимальное количество одновременно зажимаемых клавиш для одного аккорда, больше этого размера аккорд задавать нельзя. При больших значениях потребляет больше памяти.
29+
* `#define COMBO_STACK_MAX_SIZE 3` - максимальное количество одновременно зажимаемых аккордов. То есть, например, у вас есть аккорд для получения шифта, вы его зажимаете, затем вы нажимаете другой аккорд один раз, это значит что максимально у вас было 2 одновременно зажатых аккорда. 3 должно хватить для всех целей.
30+
* `#define COMBO_WAIT_TIME 100` - время в миллисекундах в течении которого ждётся что все клавиши текущего аккорда будут нажаты. Если это время истекло, и текущую комбинацию нажатых клавиш можно трактовать как аккорд, то именно эта комбинация и пошлётся.
31+
32+
## Задание SAFE_RANGE
33+
34+
Для пользовательских кейкодов существует такое понятие как `SAFE_RANGE`, обычно он используется для того чтобы после него помещать свои кейкоды, которые делают особые действия:
35+
36+
```c
37+
enum custom_keycodes {
38+
MY_KEY1 = SAFE_RANGE,
39+
MY_KEY2,
40+
// ...
41+
}
42+
```
43+
44+
Таким образом гарантируется что пользовательские кейкоды не пересекутся с системными кейкодами. Обычно для этого используется `SAFE_RANGE`.
45+
46+
Но иногда случается так, что клавиатура добавляет своих кейкодов, и создаёт новую переменную SAFE_RANGE, которую называет по-другому, нужно за этим следить. Посмотрите, нету ли у вас такой особой переменной. Например, в Moonlander эта переменная называется `ML_SAFE_RANGE`.
47+
48+
После того как вы найдёте имя этой переменной, нужно написать:
49+
50+
```c
51+
#define CUSTOM_SAFE_RANGE <ваша переменная>
52+
```
53+
54+
Пример для Moonlander:
55+
```c
56+
#define CUSTOM_SAFE_RANGE ML_SAFE_RANGE
57+
```
58+
59+
Затем при использовании своих кейкодов необходимо указывать именно `CUSTOM_SAFE_RANGE`, потому что данный модуль использует необходимое количество кейкодов из пользовательских кейкодов и переопределяет эту переменную. Пример:
60+
61+
```c
62+
enum custom_keycodes {
63+
KEYCODES_START = CUSTOM_SAFE_RANGE,
64+
65+
// English specific keys
66+
EN_LTEQ, // <=
67+
// ...
68+
};
69+
```
70+
71+
## Подключение кода
72+
73+
В своём файле `keymap.c` в самом верху подключаем файл `combo/include.h`:
74+
```c
75+
#include "combo/include.h"
76+
```
77+
78+
## Записать аккорды
79+
80+
Далее надо записать аккорды и зажимаемые для них клавиши. Записывается при помощи макроса `CHORD`, где первым аргументом передаётся кейкод, который будет нажат (там можно задать даже кастомный кейкод, и переключение слоя), затем нужно указать клавиши `CMB_***` одновременное зажатие которых будет посылать зажатие данного аккорда.
81+
82+
```c
83+
const ComboWithKeycode combos[] PROGMEM = {
84+
// Left Index
85+
CHORD(MO(4), CMB_000),
86+
CHORD(MY_KEY, CMB_001),
87+
CHORD(LGUI(S(KC_A)), CMB_000, CMB_001),
88+
// ...
89+
};
90+
const uint8_t combos_size = sizeof(combos)/sizeof(ComboWithKeycode);
91+
```
92+
93+
Здесь при единичном зажатии клавиши `CMB_000` будет включаться 4 слой, а при нажатии этой клавиши одновременно с `CMB_001` будет нажиматься `Win+Shift+A`.
94+
95+
## Поместить аккорды на вашу раскладку
96+
97+
Используйте кейкоды `CMB_000`-`CMB_XXX` (зависит сколько вы задали их в начале файла).
98+
99+
```c
100+
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
101+
//---------------------------------------------------------------------------
102+
[0] = MY_layout(
103+
// ...
104+
XXXXXXX, XXXXXXX, XXXXXXX, CMB_003, CMB_004,
105+
CMB_002,
106+
CMB_000, CMB_001, KC_ENT,
107+
// ...
108+
),
109+
// ...
110+
```
111+
112+
## Вызов модуля для каждой клавиши
113+
114+
В функции `process_record_user` в самом начале, перед всеми проверками, добавляем код обработки аккорда:
115+
```c
116+
bool process_record_user(uint16_t key, keyrecord_t *record) {
117+
if (!combo_process_record(keycode, record))
118+
return false;
119+
120+
// ...
121+
}
122+
```
123+
124+
Затем нужно определить функцию `matrix_scan_user`, если она у вас ещё не определена, и вызывать функцию `user_timer`, а в этой функции вызывать `combo_user_timer();`:
125+
```c
126+
void user_timer(void) {
127+
combo_user_timer();
128+
}
129+
130+
void matrix_scan_user(void) {
131+
user_timer();
132+
}
133+
```
134+
135+
**Объяснение:** функция `matrix_scan_user` вызывается примерно каждые 2 миллисекунды, она сканирует матрицу. Значит её вполне можно использовать для отслеживания собственных таймеров. Поэтому мы вызываем из неё функцию `user_timer`, которая лучше говорит о наших намерениях, чем `matrx_scan_user`. А уже в функции `user_timer` мы вызываем обработку случая когда мы слишком долго держим аккорд.
136+
137+
Далее нужно определить функции для обработки ошибок:
138+
```c
139+
void combo_max_count_error(void) {
140+
// ...
141+
}
142+
143+
void combo_max_size_error(void) {
144+
// ...
145+
}
146+
147+
```
148+
149+
Эти функции будут вызываться в случае превышения статических размеров массивов, которые хранят текущие состояния аккордов. `combo_max_count_error` будет вызвано, когда у вас одновременно нажато больше аккордов, чем `COMBO_STACK_MAX_SIZE`. `combo_max_size_error` будет вызвано, когда у вас в одном аккорде нажато больше клавиш, чем задано в `COMBO_MAX_SIZE`. Рекомендуется на эти функции проигрывать особые звуки, либо мигать подсветкой/светодиодами, либо выводить принт через дебаг, либо посылать макрос на нажатие. Если какая-то из таких ошибок произошла, это означает, что соответствующую переменную максимального количества лучше увеличить.
150+
151+
# Принцип работы
152+
153+
![](dka.png)
154+
155+
Данная картинка нарисована через [draw.io](https://app.diagrams.net), хранится в файле `dka.drawio`, и чтобы получить данную картинку надо указать следующие настройки при экспорте как PNG: `Zoom: 150%`.
156+
157+
Здесь показано как работает обработка аккордов. Оранжевые прямоугольники показывают условиях перехода; белые показывают ждущие состояния; зелёные показывают действия. В белых прямоугольниках подписан номер состояния, которое хранится в `combo->state`, а в оранжевых подписана буква перехода, которую можно найти в коде по использованию макроса `TRANSITION_DEBUG()`.
158+
159+
Данный конечный автомат показывает обработку для одного аккорда, находящегося в стэке. Порядок обработки следующий:
160+
* Сначала обрабатываем все аккорды в стэке согласно этому ДКА, если хотя бы один сработал, завершаем всю обработку.
161+
* Если ни один не сработал, то обрабатываем текущую клавишу синим прямоугольником.
162+
163+
Таким образом мы можем добавлять новые аккорды в стэк если они не добавляют ничего к уже зажатым, и можем добавлять к зажатым ещё клавиш, если в итоге можно что-то нажать.
164+
165+
# Changelog
166+
167+
**20.01.2021**:
168+
* Теперь надо писать `PROGMEM` у массива `combos`.
169+
* Теперь надо определить функции для логирования ошибок: `combo_max_size_error`, `combo_max_count_error`.

0 commit comments

Comments
 (0)