1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
|
// Copyright 2022 Era James(@Era1112)
// SPDX - License - Identifier: GPL - 2.0 - or -later
#include QMK_KEYBOARD_H // Default statement
#define HSV_RETRO_CONSOLE 36, 150, 255 //HSV_YELLOW = 43, 255, 255
//----------- RGB Default Settings -----------//
//--------------------------------------------//
#ifdef RGBLIGHT_ENABLE
void keyboard_post_init_user(void) {
rgblight_enable_noeeprom(); // Enables RGB, without saving settings
rgblight_sethsv_noeeprom(HSV_RETRO_CONSOLE);
rgblight_mode_noeeprom(RGBLIGHT_MODE_STATIC_LIGHT);
}
#endif // RGBLIGHT_ENABLE
//----------- Layer names -----------//
//-----------------------------------//
enum preonic_layers {
_QWERTY,
_LOWER,
_RAISE,
_ADJUST,
};
//----------- Sounds -----------//
//------------------------------//
#ifdef AUDIO_ENABLE
float capslockToggleSound[][2] = SONG(STARTUP_SOUND);
float dynamicBufferRecordSound[][2] = SONG(STARTUP_SOUND);
float dynamicBufferFullSound[][2] = SONG(GOODBYE_SOUND);
#endif // AUDIO_ENABLE
//----------- Called when dynamic buffer full -----------//
//-------------------------------------------------------//
void backlight_toggle(void) {
#ifdef AUDIO_ENABLE
PLAY_SONG(dynamicBufferFullSound);
#endif // AUDIO_ENABLE
}
//----------- Tapdance prelims -----------//
//----------------------------------------//
typedef enum {
TD_NONE,
TD_UNKNOWN,
TD_1_TAP,
TD_1_HOLD,
TD_2_TAP,
TD_2_HOLD,
} td_state_t;
typedef struct {
bool is_press_action;
td_state_t state;
} td_tap_t;
td_state_t cur_dance(qk_tap_dance_state_t* state);
/* Return an integer that corresponds to what kind of tap dance should be executed.
*
* How to figure out tap dance state: interrupted and pressed.
*
* Interrupted: If the state of a dance dance is "interrupted", that means that another key has been hit
* under the tapping term. This is typically indicitive that you are trying to "tap" the key.
*
* Pressed: Whether or not the key is still being pressed. If this value is true, that means the tapping term
* has ended, but the key is still being pressed down. This generally means the key is being "held".
*
* One thing that is currenlty not possible with qmk software in regards to tap dance is to mimic the "permissive hold"
* feature. In general, advanced tap dances do not work well if they are used with commonly typed letters.
* For example "A". Tap dances are best used on non-letter keys that are not hit while typing letters.
*
* Good places to put an advanced tap dance:
* z,q,x,j,k,v,b, any function key, home/end, comma, semi-colon
*
* Criteria for "good placement" of a tap dance key:
* Not a key that is hit frequently in a sentence
* Not a key that is used frequently to double tap, for example 'tab' is often double tapped in a terminal, or
* in a web form. So 'tab' would be a poor choice for a tap dance.
* Letters used in common words as a double. For example 'p' in 'pepper'. If a tap dance function existed on the
* letter 'p', the word 'pepper' would be quite frustating to type.
*
* For the third point, there does exist the 'TD_DOUBLE_SINGLE_TAP', however this is not fully tested
*
*/
td_state_t cur_dance(qk_tap_dance_state_t* state) {
if (state->count == 1) {
if (state->interrupted || !state->pressed) {
return TD_1_TAP;
// Key has not been interrupted, but the key is still held. Means you want to send a 'HOLD'.
} else {
return TD_1_HOLD;
}
} else if (state->count == 2) {
// TD_DOUBLE_SINGLE_TAP is to distinguish between typing "pepper", and actually wanting a double tap
// action when hitting 'pp'. Suggested use case for this return value is when you want to send two
// keystrokes of the key, and not the 'double tap' action/macro.
if (state->pressed) return TD_2_HOLD;
else return TD_2_TAP;
} else {
return TD_UNKNOWN;
}
}
//----------- 2tap capslock --------------//
//----------------------------------------//
void twoCapsLock_finished(qk_tap_dance_state_t* state, void* user_data);
void twoCapsLock_reset(qk_tap_dance_state_t* state, void* user_data);
static td_tap_t twoCapsLock_tap_state = {
.is_press_action = true,
.state = TD_NONE
};
void twoCapsLock_finished(qk_tap_dance_state_t* state, void* user_data) {
twoCapsLock_tap_state.state = cur_dance(state);
switch (twoCapsLock_tap_state.state) {
case TD_NONE: register_code(KC_LSFT); break;
case TD_UNKNOWN: register_code(KC_LSFT); break;
case TD_1_TAP: register_code(KC_LSFT); break;
case TD_1_HOLD: register_code(KC_LSFT); break;
case TD_2_TAP:
register_code(KC_CAPS);
#ifdef AUDIO_ENABLE
PLAY_SONG(capslockToggleSound);
#endif // AUDIO_ENABLE
break;
case TD_2_HOLD: register_code(KC_LSFT); break;
}
}
void twoCapsLock_reset(qk_tap_dance_state_t* state, void* user_data) {
switch (twoCapsLock_tap_state.state) {
case TD_UNKNOWN: unregister_code(KC_LSFT); break;
case TD_NONE: unregister_code(KC_LSFT); break;
case TD_1_TAP: unregister_code(KC_LSFT); break;
case TD_1_HOLD: unregister_code(KC_LSFT); break;
case TD_2_TAP:
unregister_code(KC_CAPS);
#ifdef AUDIO_ENABLE
PLAY_SONG(capslockToggleSound);
#endif // AUDIO_ENABLE
break;
case TD_2_HOLD: unregister_code(KC_LSFT); break;
}
twoCapsLock_tap_state.state = TD_NONE;
}
//----------- Rotary Encoder --------------//
//----------------------------------------//
bool encoder_update_user(uint8_t index, bool clockwise) {
if (layer_state_is(_QWERTY)) {
if (clockwise) {
tap_code(KC_WH_U);
} else {
tap_code(KC_WH_D);
}
}
else if (layer_state_is(_LOWER)) {
if (clockwise) {
tap_code16(S(KC_F3));
} else {
tap_code(KC_F3);
}
} else if (layer_state_is(_RAISE)) {
if (clockwise) {
tap_code16(C(KC_Z));
} else {
tap_code16(C(KC_Y));
}
}
return false;
}
//----------- Custom keycodes ------------//
//----------------------------------------//
enum {
TD_2_CAPSLOCK
};
enum custom_keycodes {
CU_BLNKON = SAFE_RANGE,
CU_BLNKOFF,
CU_RGBON,
CU_RGBOFF,
ENC_MODE
};
static bool blinky = false; // Track blinky behavior on/off for keycode
qk_tap_dance_action_t tap_dance_actions[] = {
[TD_2_CAPSLOCK] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, twoCapsLock_finished, twoCapsLock_reset)
};
//----------- Intercepts and overrides ------------//
//-------------------------------------=-----------//
bool process_record_user(uint16_t keycode, keyrecord_t* record) {
switch (keycode) {
// Turn RGB LEDs off
case CU_RGBOFF:
// If pressed
if (record->event.pressed) {
rgblight_sethsv_noeeprom(HSV_OFF);
return true;
// If released
} else {
return true;
}
// Turn RGB LEDs on
case CU_RGBON:
// If pressed
if (record->event.pressed) {
rgblight_sethsv_noeeprom(HSV_RETRO_CONSOLE);
return true;
// If released
} else {
return true;
}
// Turn blinky LEDs off
case CU_BLNKOFF:
// If pressed
if (record->event.pressed) {
blinky = false;
return true;
// If released
} else {
return true;
}
// Turn blinky LEDs on
case CU_BLNKON:
// If pressed
if (record->event.pressed) {
blinky = true;
return true;
// If released
} else {
return true;
}
// Sound when Dynamic recording started
case QK_DYNAMIC_MACRO_RECORD_START_1:
// If pressed
if (record->event.pressed) {
#ifdef AUDIO_ENABLE
PLAY_SONG(dynamicBufferRecordSound);
#endif // AUDIO_ENABLE
return true; // Let QMK send the press/release events
// If released
} else {
return true; // Let QMK send the press/release events
}
// Sound when Dynamic recording stopped
case QK_DYNAMIC_MACRO_RECORD_STOP:
// If pressed
if (record->event.pressed) {
#ifdef AUDIO_ENABLE
PLAY_SONG(dynamicBufferFullSound);
#endif // AUDIO_ENABLE
return true; // Let QMK send the enter press/release events
// If released
} else {
return true; // Let QMK send the press/release events
}
// Encoder Click
case ENC_MODE:
if (record->event.pressed) {
if (layer_state_is(_QWERTY)) {
tap_code(KC_BTN1);
return false;
} else if (layer_state_is(_LOWER)) {
return false;
} else if (layer_state_is(_RAISE)) {
return false;
}
}
// Adds blinks if blinky is on
default:
if (blinky == true) {
rgblight_toggle();
}
return true; // Process all other keycodes normally
}
}
//----------- Keymap ------------//
//-------------------------------//
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
// main layer
[_QWERTY] = LAYOUT_ortho_5x12 (
// (Non-disabled top row), uncomment and replace if you want preonic-style instead of planck-style
// KC_MINS, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_EQL,
KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC,
KC_ESC, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_ENT,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, TD(TD_2_CAPSLOCK),
ENC_MODE, KC_LCTL, KC_LGUI, KC_LALT, MO(_LOWER), KC_SPC, KC_SPC, MO(_RAISE), KC_LEFT, KC_DOWN, KC_UP, KC_RGHT
),
// lower key
[_LOWER] = LAYOUT_ortho_5x12 (
DM_PLY1, DM_REC1, DM_RSTP, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_DEL,
KC_BSPC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_QUOT, KC_GRV, KC_LCBR, KC_RCBR, KC_TRNS,
KC_TRNS, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_MINS, KC_EQL, KC_TRNS, KC_BSLS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, MO(_ADJUST), KC_HOME, KC_PGDN, KC_PGUP, KC_END
),
// raise key
[_RAISE] = LAYOUT_ortho_5x12 (
DM_PLY1, DM_REC1, DM_RSTP, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_DEL,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_DQUO, KC_TILD, KC_LBRC, KC_RBRC, KC_TRNS,
KC_TRNS, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_UNDS, KC_PLUS , KC_TRNS, KC_PIPE, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, MO(_ADJUST), KC_TRNS, KC_TRNS, KC_TRNS, KC_MUTE, KC_VOLD, KC_VOLU, KC_F24
),
// hardware adjust layer, raise+lower
[_ADJUST] = LAYOUT_ortho_5x12 (
AU_ON, AU_OFF, CK_ON, CK_OFF, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
CU_RGBON, CU_RGBOFF, CU_BLNKON, CU_BLNKOFF, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO
)
};
|