Parent directory

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}