dynamic_keymap.c
13183 bytes
1/* Copyright 2017 Jason Williams (Wilba)
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "dynamic_keymap.h"
18#include "keymap_introspection.h"
19#include "action.h"
20#include "eeprom.h"
21#include "progmem.h"
22#include "send_string.h"
23#include "keycodes.h"
24
25#ifdef VIA_ENABLE
26# include "via.h"
27# define DYNAMIC_KEYMAP_EEPROM_START (VIA_EEPROM_CONFIG_END)
28#else
29# include "eeconfig.h"
30# define DYNAMIC_KEYMAP_EEPROM_START (EECONFIG_SIZE)
31#endif
32
33#ifdef ENCODER_ENABLE
34# include "encoder.h"
35#else
36# define NUM_ENCODERS 0
37#endif
38
39#ifndef DYNAMIC_KEYMAP_LAYER_COUNT
40# define DYNAMIC_KEYMAP_LAYER_COUNT 4
41#endif
42
43#ifndef DYNAMIC_KEYMAP_MACRO_COUNT
44# define DYNAMIC_KEYMAP_MACRO_COUNT 16
45#endif
46
47#ifndef TOTAL_EEPROM_BYTE_COUNT
48# error Unknown total EEPROM size. Cannot derive maximum for dynamic keymaps.
49#endif
50
51#ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR
52# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR (TOTAL_EEPROM_BYTE_COUNT - 1)
53#endif
54
55#if DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > (TOTAL_EEPROM_BYTE_COUNT - 1)
56# pragma message STR(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) " > " STR((TOTAL_EEPROM_BYTE_COUNT - 1))
57# error DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is configured to use more space than what is available for the selected EEPROM driver
58#endif
59
60// Due to usage of uint16_t check for max 65535
61#if DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > 65535
62# pragma message STR(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) " > 65535"
63# error DYNAMIC_KEYMAP_EEPROM_MAX_ADDR must be less than 65536
64#endif
65
66// If DYNAMIC_KEYMAP_EEPROM_ADDR not explicitly defined in config.h,
67#ifndef DYNAMIC_KEYMAP_EEPROM_ADDR
68# define DYNAMIC_KEYMAP_EEPROM_ADDR DYNAMIC_KEYMAP_EEPROM_START
69#endif
70
71// Dynamic encoders starts after dynamic keymaps
72#ifndef DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR
73# define DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR (DYNAMIC_KEYMAP_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2))
74#endif
75
76// Dynamic macro starts after dynamic encoders, but only when using ENCODER_MAP
77#ifdef ENCODER_MAP_ENABLE
78# ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
79# define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * NUM_ENCODERS * 2 * 2))
80# endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
81#else // ENCODER_MAP_ENABLE
82# ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
83# define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR)
84# endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
85#endif // ENCODER_MAP_ENABLE
86
87// Sanity check that dynamic keymaps fit in available EEPROM
88// If there's not 100 bytes available for macros, then something is wrong.
89// The keyboard should override DYNAMIC_KEYMAP_LAYER_COUNT to reduce it,
90// or DYNAMIC_KEYMAP_EEPROM_MAX_ADDR to increase it, *only if* the microcontroller has
91// more than the default.
92_Static_assert((DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) - (DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR) >= 100, "Dynamic keymaps are configured to use more EEPROM than is available.");
93
94// Dynamic macros are stored after the keymaps and use what is available
95// up to and including DYNAMIC_KEYMAP_EEPROM_MAX_ADDR.
96#ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE
97# define DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR - DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + 1)
98#endif
99
100#ifndef DYNAMIC_KEYMAP_MACRO_DELAY
101# define DYNAMIC_KEYMAP_MACRO_DELAY TAP_CODE_DELAY
102#endif
103
104uint8_t dynamic_keymap_get_layer_count(void) {
105 return DYNAMIC_KEYMAP_LAYER_COUNT;
106}
107
108void *dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t column) {
109 // TODO: optimize this with some left shifts
110 return ((void *)DYNAMIC_KEYMAP_EEPROM_ADDR) + (layer * MATRIX_ROWS * MATRIX_COLS * 2) + (row * MATRIX_COLS * 2) + (column * 2);
111}
112
113uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column) {
114 if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return KC_NO;
115 void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
116 // Big endian, so we can read/write EEPROM directly from host if we want
117 uint16_t keycode = eeprom_read_byte(address) << 8;
118 keycode |= eeprom_read_byte(address + 1);
119 return keycode;
120}
121
122void dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode) {
123 if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return;
124 void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
125 // Big endian, so we can read/write EEPROM directly from host if we want
126 eeprom_update_byte(address, (uint8_t)(keycode >> 8));
127 eeprom_update_byte(address + 1, (uint8_t)(keycode & 0xFF));
128}
129
130#ifdef ENCODER_MAP_ENABLE
131void *dynamic_keymap_encoder_to_eeprom_address(uint8_t layer, uint8_t encoder_id) {
132 return ((void *)DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR) + (layer * NUM_ENCODERS * 2 * 2) + (encoder_id * 2 * 2);
133}
134
135uint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise) {
136 if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return KC_NO;
137 void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);
138 // Big endian, so we can read/write EEPROM directly from host if we want
139 uint16_t keycode = ((uint16_t)eeprom_read_byte(address + (clockwise ? 0 : 2))) << 8;
140 keycode |= eeprom_read_byte(address + (clockwise ? 0 : 2) + 1);
141 return keycode;
142}
143
144void dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode) {
145 if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return;
146 void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);
147 // Big endian, so we can read/write EEPROM directly from host if we want
148 eeprom_update_byte(address + (clockwise ? 0 : 2), (uint8_t)(keycode >> 8));
149 eeprom_update_byte(address + (clockwise ? 0 : 2) + 1, (uint8_t)(keycode & 0xFF));
150}
151#endif // ENCODER_MAP_ENABLE
152
153void dynamic_keymap_reset(void) {
154 // Reset the keymaps in EEPROM to what is in flash.
155 for (int layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
156 for (int row = 0; row < MATRIX_ROWS; row++) {
157 for (int column = 0; column < MATRIX_COLS; column++) {
158 dynamic_keymap_set_keycode(layer, row, column, keycode_at_keymap_location_raw(layer, row, column));
159 }
160 }
161#ifdef ENCODER_MAP_ENABLE
162 for (int encoder = 0; encoder < NUM_ENCODERS; encoder++) {
163 dynamic_keymap_set_encoder(layer, encoder, true, keycode_at_encodermap_location_raw(layer, encoder, true));
164 dynamic_keymap_set_encoder(layer, encoder, false, keycode_at_encodermap_location_raw(layer, encoder, false));
165 }
166#endif // ENCODER_MAP_ENABLE
167 }
168}
169
170void dynamic_keymap_get_buffer(uint16_t offset, uint16_t size, uint8_t *data) {
171 uint16_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;
172 void * source = (void *)(DYNAMIC_KEYMAP_EEPROM_ADDR + offset);
173 uint8_t *target = data;
174 for (uint16_t i = 0; i < size; i++) {
175 if (offset + i < dynamic_keymap_eeprom_size) {
176 *target = eeprom_read_byte(source);
177 } else {
178 *target = 0x00;
179 }
180 source++;
181 target++;
182 }
183}
184
185void dynamic_keymap_set_buffer(uint16_t offset, uint16_t size, uint8_t *data) {
186 uint16_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;
187 void * target = (void *)(DYNAMIC_KEYMAP_EEPROM_ADDR + offset);
188 uint8_t *source = data;
189 for (uint16_t i = 0; i < size; i++) {
190 if (offset + i < dynamic_keymap_eeprom_size) {
191 eeprom_update_byte(target, *source);
192 }
193 source++;
194 target++;
195 }
196}
197
198uint16_t keycode_at_keymap_location(uint8_t layer_num, uint8_t row, uint8_t column) {
199 if (layer_num < DYNAMIC_KEYMAP_LAYER_COUNT && row < MATRIX_ROWS && column < MATRIX_COLS) {
200 return dynamic_keymap_get_keycode(layer_num, row, column);
201 }
202 return KC_NO;
203}
204
205#ifdef ENCODER_MAP_ENABLE
206uint16_t keycode_at_encodermap_location(uint8_t layer_num, uint8_t encoder_idx, bool clockwise) {
207 if (layer_num < DYNAMIC_KEYMAP_LAYER_COUNT && encoder_idx < NUM_ENCODERS) {
208 return dynamic_keymap_get_encoder(layer_num, encoder_idx, clockwise);
209 }
210 return KC_NO;
211}
212#endif // ENCODER_MAP_ENABLE
213
214uint8_t dynamic_keymap_macro_get_count(void) {
215 return DYNAMIC_KEYMAP_MACRO_COUNT;
216}
217
218uint16_t dynamic_keymap_macro_get_buffer_size(void) {
219 return DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE;
220}
221
222void dynamic_keymap_macro_get_buffer(uint16_t offset, uint16_t size, uint8_t *data) {
223 void * source = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + offset);
224 uint8_t *target = data;
225 for (uint16_t i = 0; i < size; i++) {
226 if (offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) {
227 *target = eeprom_read_byte(source);
228 } else {
229 *target = 0x00;
230 }
231 source++;
232 target++;
233 }
234}
235
236void dynamic_keymap_macro_set_buffer(uint16_t offset, uint16_t size, uint8_t *data) {
237 void * target = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + offset);
238 uint8_t *source = data;
239 for (uint16_t i = 0; i < size; i++) {
240 if (offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) {
241 eeprom_update_byte(target, *source);
242 }
243 source++;
244 target++;
245 }
246}
247
248void dynamic_keymap_macro_reset(void) {
249 void *p = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR);
250 void *end = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE);
251 while (p != end) {
252 eeprom_update_byte(p, 0);
253 ++p;
254 }
255}
256
257void dynamic_keymap_macro_send(uint8_t id) {
258 if (id >= DYNAMIC_KEYMAP_MACRO_COUNT) {
259 return;
260 }
261
262 // Check the last byte of the buffer.
263 // If it's not zero, then we are in the middle
264 // of buffer writing, possibly an aborted buffer
265 // write. So do nothing.
266 void *p = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE - 1);
267 if (eeprom_read_byte(p) != 0) {
268 return;
269 }
270
271 // Skip N null characters
272 // p will then point to the Nth macro
273 p = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR);
274 void *end = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE);
275 while (id > 0) {
276 // If we are past the end of the buffer, then there is
277 // no Nth macro in the buffer.
278 if (p == end) {
279 return;
280 }
281 if (eeprom_read_byte(p) == 0) {
282 --id;
283 }
284 ++p;
285 }
286
287 // Send the macro string by making a temporary string.
288 char data[8] = {0};
289 // We already checked there was a null at the end of
290 // the buffer, so this cannot go past the end
291 while (1) {
292 data[0] = eeprom_read_byte(p++);
293 data[1] = 0;
294 // Stop at the null terminator of this macro string
295 if (data[0] == 0) {
296 break;
297 }
298 if (data[0] == SS_QMK_PREFIX) {
299 // Get the code
300 data[1] = eeprom_read_byte(p++);
301 // Unexpected null, abort.
302 if (data[1] == 0) {
303 return;
304 }
305 if (data[1] == SS_TAP_CODE || data[1] == SS_DOWN_CODE || data[1] == SS_UP_CODE) {
306 // Get the keycode
307 data[2] = eeprom_read_byte(p++);
308 // Unexpected null, abort.
309 if (data[2] == 0) {
310 return;
311 }
312 // Null terminate
313 data[3] = 0;
314 } else if (data[1] == SS_DELAY_CODE) {
315 // Get the number and '|'
316 // At most this is 4 digits plus '|'
317 uint8_t i = 2;
318 while (1) {
319 data[i] = eeprom_read_byte(p++);
320 // Unexpected null, abort
321 if (data[i] == 0) {
322 return;
323 }
324 // Found '|', send it
325 if (data[i] == '|') {
326 data[i + 1] = 0;
327 break;
328 }
329 // If haven't found '|' by i==6 then
330 // number too big, abort
331 if (i == 6) {
332 return;
333 }
334 ++i;
335 }
336 }
337 }
338 send_string_with_delay(data, DYNAMIC_KEYMAP_MACRO_DELAY);
339 }
340}