(working; mostly on the eeprom-macro implementation)
parent
d73b58e23d
commit
f630e3c6f5
|
@ -90,7 +90,8 @@
|
|||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define OPT__EEPROM_MACRO__EEPROM_SIZE 1024
|
||||
#define OPT__EEPROM__EEPROM_MACRO__START 0
|
||||
#define OPT__EEPROM__EEPROM_MACRO__END 1023
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
|
@ -9,6 +9,15 @@
|
|||
*
|
||||
* Prefix: `eeprom__`
|
||||
*
|
||||
* Conventions:
|
||||
* - Because the EEPROM must be shared between different parts of the entire
|
||||
* program, there needs to be a way to define which blocks are to be used for
|
||||
* which purposes. To that end, all blocks of the EEPROM that are being used
|
||||
* must have their start and end addresses `#define`ed to two globally
|
||||
* visible macros, of the form `OPT__EEPROM__[prefix]__START` and
|
||||
* `OPT__EEPROM__[prefix]__END`, where "[prefix]" is the prefix usually given
|
||||
* to public functions, etc., in that section of the code.
|
||||
*
|
||||
* Notes:
|
||||
* - This is meant to be a replacement for the read, write, and update
|
||||
* functions provided by `<avr/eeprom.h>`, and should be preferred for those
|
||||
|
|
|
@ -34,6 +34,15 @@
|
|||
* in the EEPROM, and contain a key action who's original behavior we wish
|
||||
* to mask, and a list of key actions that should be sequentially performed
|
||||
* instead.
|
||||
* - A "keystroke" is a full press then release of a key. Keystrokes may
|
||||
* overlap each other.
|
||||
* - To "remap" a key action is to assign a macro to it (masking, not
|
||||
* replacing, what the key action originally did).
|
||||
* - The "EEPROM" is an "Electronically Erasable Programmable Read Only
|
||||
* Memory". It is where this library stores persistent data.
|
||||
* - "EEMEM" is "EEprom MEMory" (i.e. another way of referring to the memory of
|
||||
* the EEPROM, instead of the EEPROM itself) following the convention of
|
||||
* avr-gcc.
|
||||
*
|
||||
* Usage notes:
|
||||
* - When macros are recorded, the key they are assigned to does not loose its
|
||||
|
@ -75,16 +84,28 @@
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** macros/OPT__EEPROM__EEPROM_MACRO__START/description
|
||||
* The first EEMEM address in the block assigned to this section of the code
|
||||
*/
|
||||
/** macros/OPT__EEPROM__EEPROM_MACRO__END/description
|
||||
* The last EEMEM address in the block assigned to this section of the code
|
||||
*/
|
||||
#if OPT__EEPROM__EEPROM_MACRO__START > OPT__EEPROM__EEPROM_MACRO__END
|
||||
#error "OPT__EEPROM__EEPROM_MACRO__START must be smaller than ...END"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define ARGS bool pressed, uint8_t layer, uint8_t row, uint8_t column
|
||||
uint8_t eeprom_macro__init (void);
|
||||
uint8_t eeprom_macro__record_init ( ARGS );
|
||||
uint8_t eeprom_macro__record_action ( ARGS );
|
||||
uint8_t eeprom_macro__record_finalize (void);
|
||||
uint8_t eeprom_macro__exists ( ARGS );
|
||||
uint8_t eeprom_macro__play ( ARGS );
|
||||
bool eeprom_macro__exists ( ARGS );
|
||||
void eeprom_macro__clear ( ARGS );
|
||||
void eeprom_macro__clear_all (void);
|
||||
#undef arguments
|
||||
#undef ARGS
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -113,8 +134,10 @@ void eeprom_macro__clear_all (void);
|
|||
* Meant to be called exactly once by `kb__init()`
|
||||
*
|
||||
* Notes:
|
||||
* - This function should initialize the EEPROM to the current format if the
|
||||
* version of the data stored is different than what we expect.
|
||||
* - This function should initialize our portion of the EEPROM to the current
|
||||
* format if we don't know how to read it; i.e. if the version of the data
|
||||
* stored is different than what we expect, or if the data has been
|
||||
* corrupted.
|
||||
*/
|
||||
|
||||
// === eeprom_macro__record_init() ===
|
||||
|
@ -122,12 +145,12 @@ void eeprom_macro__clear_all (void);
|
|||
* Prepare to record a new macro
|
||||
*
|
||||
* Arguments:
|
||||
* - (group) They key to remap
|
||||
* - `pressed`: Whether the key action to remap is a press (`true`) or a
|
||||
* release (`false`)
|
||||
* - `layer`: The layer of the key action to remap
|
||||
* - `row`: The row of the key action to remap
|
||||
* - `column`: The column of the key action to remap
|
||||
* - (group) The key action to remap
|
||||
* - `pressed`: Whether the key action is a press (`true`) or a release
|
||||
* (`false`)
|
||||
* - `layer`: The layer of the key action
|
||||
* - `row`: The row of the key action
|
||||
* - `column`: The column of the key action
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
|
@ -145,11 +168,12 @@ void eeprom_macro__clear_all (void);
|
|||
* Record the next key action of the current macro
|
||||
*
|
||||
* Arguments:
|
||||
* - `pressed`: Whether the key action being recorded is a press (`true`) or a
|
||||
* release (`false`)
|
||||
* - `layer`: The layer of the key action being recorded
|
||||
* - `row`: The row of the key action being recorded
|
||||
* - `column`: The column of the key action being recorded
|
||||
* - (group) The key action to record
|
||||
* - `pressed`: Whether the key action is a press (`true`) or a release
|
||||
* (`false`)
|
||||
* - `layer`: The layer of the key action
|
||||
* - `row`: The row of the key action
|
||||
* - `column`: The column of the key action
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
|
@ -166,64 +190,65 @@ void eeprom_macro__clear_all (void);
|
|||
*
|
||||
* Notes:
|
||||
* - Before this function is called, the macro (even though parts of it may be
|
||||
* written) should not be readable, or referenced anywhere in the EEPROM
|
||||
*/
|
||||
|
||||
// === eeprom_macro__exists() ===
|
||||
/** functions/eeprom_macro__exists/description
|
||||
* Predicate indicating whether the specified key action was remapped
|
||||
*
|
||||
* Arguments:
|
||||
* - `pressed`: Whether the key action that was remapped was a press (`true`)
|
||||
* or a release (`false`)
|
||||
* - `layer`: The layer of the key action that was remapped
|
||||
* - `row`: The row of the key action that was remapped
|
||||
* - `column`: The column of the key action that was remapped
|
||||
*
|
||||
* Returns:
|
||||
* - [nonzero]: if a macro with the given UID exists
|
||||
* - `0`: if a macro with the given UID does not exist
|
||||
* written) should not be readable, or referenced anywhere in the EEPROM.
|
||||
*/
|
||||
|
||||
// === eeprom_macro__play() ===
|
||||
/** functions/eeprom_macro__play/description
|
||||
* Play back recorded key actions for the macro with UID `index`
|
||||
* Play back recorded key actions for the macro assigned to the specified key
|
||||
* action
|
||||
*
|
||||
* Arguments:
|
||||
* - `pressed`: Whether the key action that was remapped was a press (`true`)
|
||||
* or a release (`false`)
|
||||
* - `layer`: The layer of the key action that was remapped
|
||||
* - `row`: The row of the key action that was remapped
|
||||
* - `column`: The column of the key action that was remapped
|
||||
* - (group) The key action to search for
|
||||
* - `pressed`: Whether the key action is a press (`true`) or a release
|
||||
* (`false`)
|
||||
* - `layer`: The layer of the key action
|
||||
* - `row`: The row of the key action
|
||||
* - `column`: The column of the key action
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0` (macro successfully played)
|
||||
* - failure: [other] (macro does not exist)
|
||||
*/
|
||||
|
||||
// === eeprom_macro__exists() ===
|
||||
/** functions/eeprom_macro__exists/description
|
||||
* Predicate indicating whether the specified key action has been remapped
|
||||
*
|
||||
* Notes:
|
||||
* - Keystrokes will be played back as if the same sequence of keys were being
|
||||
* pressed by the user (regardless of whether the current state of the
|
||||
* keyboard is the same), except as fast as possible (since timing is not
|
||||
* recorded).
|
||||
* Arguments:
|
||||
* - (group) The key action to search for
|
||||
* - `pressed`: Whether the key action is a press (`true`) or a release
|
||||
* (`false`)
|
||||
* - `layer`: The layer of the key action
|
||||
* - `row`: The row of the key action
|
||||
* - `column`: The column of the key action
|
||||
*
|
||||
* Returns:
|
||||
* - `true`: if a macro with the given UID exists
|
||||
* - `false`: if a macro with the given UID does not exist
|
||||
*/
|
||||
|
||||
// === eeprom_macro__clear() ===
|
||||
/** functions/eeprom_macro__clear/description
|
||||
* Clear the macro with UID `index`
|
||||
* Clear (delete) the macro assigned to the given key action
|
||||
*
|
||||
* Arguments:
|
||||
* - `index`: The UID of the macro to clear
|
||||
* - (group) The key action to un-remap
|
||||
* - `pressed`: Whether the key action is a press (`true`) or a release
|
||||
* (`false`)
|
||||
* - `layer`: The layer of the key action
|
||||
* - `row`: The row of the key action
|
||||
* - `column`: The column of the key action
|
||||
*/
|
||||
|
||||
// === eeprom_macro__clear_all() ===
|
||||
/** functions/eeprom_macro__clear_all/description
|
||||
* Clear all macros in the EEPROM
|
||||
* Clear (delete) all macros in the EEPROM
|
||||
*
|
||||
* Notes:
|
||||
* - For the purposes of this function, "clearing" the EEPROM means to put it
|
||||
* in such a state that none of the functions declared here will be able to
|
||||
* find a macro for any `index`. This does not necessarily imply that the
|
||||
* find a macro for any key action. This does not necessarily imply that the
|
||||
* EEPROM is in a fully known state.
|
||||
*/
|
||||
|
||||
|
||||
|
|
|
@ -9,17 +9,6 @@
|
|||
* the ATMega32U4
|
||||
*
|
||||
*
|
||||
* Implementation warnings:
|
||||
*
|
||||
* - One cannot trust the binary layout of bit-fields: the order of the fields
|
||||
* (among other things) is implementation defined, and [can change]
|
||||
* (http://avr.2057.n7.nabble.com/Bit-field-packing-order-changed-between-avrgcc-implementations-td19193.html),
|
||||
* even between different versions of the same compiler. The risk is
|
||||
* probably low when the compiler and architecture are both so specific, so I
|
||||
* depend on their binary layout anyway, but it's definitely something to be
|
||||
* aware of.
|
||||
*
|
||||
*
|
||||
* Implementation notes:
|
||||
*
|
||||
* - The default state (the "erased" state) of this EEPROM is all `1`s, which
|
||||
|
@ -27,44 +16,45 @@
|
|||
* it. This is reflected in some of our choices for default values, and
|
||||
* such.
|
||||
*
|
||||
* - In avr-gcc, multi-byte data types are allocated with the least significant
|
||||
* bit occupying the lowest address.
|
||||
* - GCC and AVR processors (and Intel processors, for that matter) are
|
||||
* primarily little endian: in avr-gcc, multi-byte data types are allocated
|
||||
* with the least significant byte occupying the lowest address. Protocols,
|
||||
* data formats (including UTF-8), and such are primarily big endian.
|
||||
* Because it feels more consistent, and makes a little more sense to me,
|
||||
* this code organizes bytes in a little endian manner whenever it has a
|
||||
* choice between the two.
|
||||
*
|
||||
* - For a long time, I was going to try to make this library robust in the
|
||||
* event of power loss, but in the end I decided not to. This feature is
|
||||
* meant to be used for *temporary* macros - permanent macros really should
|
||||
* be assigned to a key in the layout code directly, instead of using this
|
||||
* library's functionality after the firmware has already been loaded - so,
|
||||
* with the risk of power loss being fairly low, and the consequence of
|
||||
* (detected) eeprom-macro corruption hopefully more of an annoyance than
|
||||
* anything else, I decided the effort (and extra EEMEM usage) wasn't worth
|
||||
* it.
|
||||
* meant to be used for *temporary* macros - so, with the risk of power loss
|
||||
* during a critical time being fairly low, and the consequence of (detected)
|
||||
* data corruption hopefully more of an annoyance than anything else, I
|
||||
* decided the effort (and extra EEMEM usage) wasn't worth it.
|
||||
*/
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <avr/eeprom.h>
|
||||
#include "../../../../firmware/keyboard.h"
|
||||
#include "../../../../firmware/lib/eeprom.h"
|
||||
#include "../eeprom-macro.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// checks ---------------------------------------------------------------------
|
||||
|
||||
/** macros/OPT__EEPROM_MACRO__EEPROM_SIZE/description
|
||||
/** macros/OPT__EEPROM__EEPROM_MACRO__END/description
|
||||
* Implementation notes:
|
||||
* - The ATMega32U4 has 1024 bytes of internal EEPROM total
|
||||
* - The ATMega32U4 only has 1024 bytes of EEPROM (beginning with byte 0)
|
||||
*/
|
||||
#if OPT__EEPROM_MACRO__EEPROM_SIZE > 1024
|
||||
#error "OPT__EEPROM_MACRO__EEPROM_SIZE must be <= 1024"
|
||||
#if OPT__EEPROM__EEPROM_MACRO__END > 1023
|
||||
#error "OPT__EEPROM__EEPROM_MACRO__END must not be greater than 1023"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// macros ---------------------------------------------------------------------
|
||||
|
||||
/** macros/VERSION/description
|
||||
* The version number of `struct eeprom`
|
||||
* The version number of the EEMEM layout
|
||||
*
|
||||
* Assignments:
|
||||
* - 0x00: Reserved: EEPROM not yet initialized, or in inconsistent state
|
||||
|
@ -74,61 +64,93 @@
|
|||
*/
|
||||
#define VERSION 0x01
|
||||
|
||||
/** macros/MACROS_LENGTH
|
||||
* The number of items in `eeprom.macros.data`, stored persistently in
|
||||
* `eeprom.macros.length`
|
||||
*/
|
||||
#define MACROS_LENGTH ( OPT__EEPROM_MACRO__EEPROM_SIZE \
|
||||
- sizeof(uint16_t) /* for `length` */ \
|
||||
- sizeof(struct meta) \
|
||||
- sizeof(struct table) ) \
|
||||
/ sizeof(uint8_t)
|
||||
|
||||
/** macros/(enum) header_type/description
|
||||
* Valid values for `header_t.type`, describing what kind of data follows
|
||||
/** macros/(group) EEMEM layout/description
|
||||
* To define the layout of our section of the EEPROM
|
||||
*
|
||||
* Members:
|
||||
* - `HEADER_TYPE_DELETED = 0x00`: This macro has been deleted (only `length`
|
||||
* is valid)
|
||||
* - `HEADER_TYPE_VALID`: This macro is valid
|
||||
* - `HEADER_TYPE_END = 0xFF`: This header marks the end of the list (nothing
|
||||
* follows)
|
||||
* - `EEMEM_START`: The address of the first byte of our block of EEMEM
|
||||
* - `EEMEM_START_ADDRESS_START`
|
||||
* - `EEMEM_START_ADDRESS_END`
|
||||
* - `EEMEM_VERSION_START`
|
||||
* - `EEMEM_VERSION_END`
|
||||
* - `EEMEM_ROWS_START`
|
||||
* - `EEMEM_ROWS_END`
|
||||
* - `EEMEM_COLUMNS_START`
|
||||
* - `EEMEM_COLUMNS_END`
|
||||
* - `EEMEM_TABLE_START`
|
||||
* - `EEMEM_TABLE_END`
|
||||
* - `EEMEM_MACROS_START`
|
||||
* - `EEMEM_MACROS_END`
|
||||
* - `EEMEM_END_ADDRESS_START`
|
||||
* - `EEMEM_END_ADDRESS_END`
|
||||
* - `EEMEM_END`: The address of the last byte of our block of EEMEM
|
||||
*
|
||||
* EEMEM sections:
|
||||
* - START_ADDRESS:
|
||||
* - byte 0: LSB of `EEMEM_START`
|
||||
* - byte 1: MSB of `EEMEM_START`
|
||||
* - VERSION:
|
||||
* - byte 0..15: Each byte is a copy of the value of `VERSION`. We keep
|
||||
* more than 1 copy for write balancing.
|
||||
* - These bytes will all be set to `VERSION` as the last step of
|
||||
* initializing our portion of the EEPROM.
|
||||
* - Before sensitive writes (sequences of writes which will corrupt
|
||||
* our data if they are interrupted), a random one of these values
|
||||
* will be erased (set to `0xFF`). After the write is completed, the
|
||||
* changed value will be restored to `VERSION`.
|
||||
* - Upon initialization, if any of these values is not equal to the
|
||||
* current `VERSION`, our portion of the EEPROM should be
|
||||
* reinitialized.
|
||||
* - ROWS:
|
||||
* - byte 0: The number of rows in TABLE (`OPT__KB__ROWS`)
|
||||
* - COLUMNS:
|
||||
* - byte 0: The number of columns in TABLE (`OPT__KB__COLUMNS`)
|
||||
* - TABLE:
|
||||
* - byte 0..`(OPT__KB__ROWS * OPT__KB__COLUMNS)`: TODO
|
||||
* - MACROS:
|
||||
* - byte 0..`(EEMEM_END_ADDRESS - EEMEM_MACROS)`: TODO
|
||||
* - END_ADDRESS:
|
||||
* - byte 0: LSB of `EEMEM_END`
|
||||
* - byte 1: MSB of `EEMEM_END`
|
||||
*
|
||||
* Notes:
|
||||
* - `START_ADDRESS`, `ROWS`, `COLUMNS`, and `END_ADDRESS` are all written as
|
||||
* part of our effort to make sure that the assumptions in place when writing
|
||||
* the data don't shift (undetected) by the time it gets read. Any of these
|
||||
* values could change, legitimately, without `VERSION` being incremented,
|
||||
* but it's important that any two builds of the firmware that deal with this
|
||||
* section of the EEPROM have the same values for each.
|
||||
*/
|
||||
enum type {
|
||||
TYPE_DELETED = 0x00,
|
||||
TYPE_VALID_MACRO,
|
||||
TYPE_END = 0xFF,
|
||||
};
|
||||
#define EEMEM_START OPT__EEPROM__EEPROM_MACRO__START
|
||||
#define EEMEM_END OPT__EEPROM__EEPROM_MACRO__END
|
||||
// TODO: the rest of the definitions
|
||||
|
||||
/** macros/(group) type/description
|
||||
* Aliases for valid values of the "type" field in `MACROS`
|
||||
*
|
||||
* Members:
|
||||
* - `TYPE_DELETED`
|
||||
* - `TYPE_VALID_MACRO`
|
||||
* - `TYPE_END`
|
||||
*/
|
||||
#define TYPE_DELETED 0x00
|
||||
#define TYPE_VALID_MACRO 0x01
|
||||
#define TYPE_END 0xFF
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// types ----------------------------------------------------------------------
|
||||
|
||||
/** types/header_t/description
|
||||
* To describe the data that follows (most likely a sequence of `action_t`s,
|
||||
* making this the beginning of a macro)
|
||||
*
|
||||
* Struct members:
|
||||
* - type: (see description for `header_type` enum)
|
||||
* - `length`: the number of `action_t`s that follow
|
||||
* - `uid`: a Unique IDentifier for the macro
|
||||
/** types/key_action_t/description
|
||||
* TODO
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
eeprom_macro__uid_t uid;
|
||||
} header_t;
|
||||
bool pressed;
|
||||
uint8_t layer;
|
||||
uint8_t row;
|
||||
uint8_t column;
|
||||
} key_action_t;
|
||||
|
||||
/** types/action_t/description
|
||||
* To describe the "press" or "release" of a key when recording or playing back
|
||||
* a macro
|
||||
*
|
||||
* Notes:
|
||||
* - We reuse the `...uid_t` type for convenience and consistency. Only the
|
||||
* `pressed`, `row`, and `column` fields are relevant, since these are what
|
||||
* will be passed to `kb__layout__exec_key()` when playing back the macro.
|
||||
* `layer` will be ignored.
|
||||
*/
|
||||
typedef eeprom_macro__uid_t action_t;
|
||||
// TODO: rewriting (yet again) - stopped here
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// variables in EEMEM ---------------------------------------------------------
|
||||
|
@ -157,9 +179,22 @@ typedef eeprom_macro__uid_t action_t;
|
|||
* bits for `false`
|
||||
* - `macros`: To hold a block of memory for storing macros
|
||||
* - `length`: The number of bytes allocated to `macros.data`
|
||||
* - `data`: A list of "macro"s, where a "macro" is a `header_t`
|
||||
* followed by zero or more `action_t`s. The list contains no padding.
|
||||
* It is terminated by a macro with `header_t.type == HEADER_TYPE_END`.
|
||||
* - `data`: A (non-padded) list of macros, where a macro (in EEMEM) is
|
||||
* - 1 byte: `type`
|
||||
* - as defined in `enum type`
|
||||
* - 1 byte: `length`
|
||||
* - the number of bytes in the entire macro (i.e. the number of
|
||||
* bytes to skip over, if one had a pointer to the `type` byte of
|
||||
* this macro, in order to reach the `type` byte of the next
|
||||
* macro)
|
||||
* - (variable length): `key_action`
|
||||
* - as defined in `read_key_action()`
|
||||
* - this is the key action that is being remapped
|
||||
* - (list of 0 or more)
|
||||
* - (variable length): `key_action`
|
||||
* - as defined in `read_key_action()`
|
||||
* - these are the key actions to be performed instead of the
|
||||
* one being remapped
|
||||
*
|
||||
*
|
||||
* Notes:
|
||||
|
@ -195,127 +230,35 @@ struct eeprom {
|
|||
// ----------------------------------------------------------------------------
|
||||
// variables in SRAM ----------------------------------------------------------
|
||||
|
||||
// TODO: names for the pointers to where to write the next new macro header,
|
||||
// and next recorded action
|
||||
// - macros_next_free_header, macros_next_free_action
|
||||
static void * macros_free_begin; // TODO: needs a better name?
|
||||
static void * current_macro;
|
||||
uint8_t current_macro_length;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TODO:
|
||||
//
|
||||
// - should we define macros as
|
||||
// - length
|
||||
// - 0x00 => end
|
||||
// - uid
|
||||
// - 0xFF => deleted
|
||||
// - actions(s)
|
||||
// - 1 bit : indicate extended action (read next action, and combine,
|
||||
// to reach higher layers, rows, and columns)
|
||||
// - 1 bit : pressed
|
||||
// - 3 bits : row
|
||||
// - 3 bits : column
|
||||
// - maybe we could define UIDs similar to the actions described above?
|
||||
// multi-byte encodings ftw! lol. like utf-8...
|
||||
// - the calling function need not ignore layer shift keys, or any other keys.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// - use little endian order for the multi-byte encoding. explain why (since
|
||||
// utf-8 is big endian)
|
||||
//
|
||||
// - actually, yes, we could store UIDs as
|
||||
// - 1 bit : indicate whether the next byte extends this one (i.e. for
|
||||
// every field in the next byte, take it, shift it up, and concatenate it
|
||||
// with the corresponding field in this byte to obtain the final value.
|
||||
// if the next byte has its extended bit set too, then the byte after
|
||||
// that extends each field further, etc.)
|
||||
// - 1 bit : pressed (this field is ignored in extended bytes)
|
||||
// - 2 bits : layer
|
||||
// - 2 bits : row
|
||||
// - 2 bits : column
|
||||
// - 255 bytes (so, on average, about 100 keystrokes = 200 key actions) should
|
||||
// be enough for a macro, i think. `length` can be 1 byte, and count the
|
||||
// total number of bytes (including `type` and `length`, and anything else)
|
||||
//
|
||||
// - then we could use the same format for actions (and use the same read
|
||||
// function for both)
|
||||
// - need to write a function to read, and another to write, multi-byte key
|
||||
// actions
|
||||
//
|
||||
// - then we would probably want to make a function
|
||||
// `kb__layout__exec_key_layer()` or some such, to execute a key on a
|
||||
// specific layer. `kb__layout__exec_key()` could look up the top layer in
|
||||
// the stack, and call the more specific function.
|
||||
// - so for now, we have
|
||||
// - macro = `type` `length` uid key_action*
|
||||
// - uid = key_action
|
||||
// - key_action =
|
||||
// - 1 bit : are the fields in this byte extended by the next?
|
||||
// - 1 bit : `pressed`
|
||||
// - 2 bits : `layer`
|
||||
// - 2 bits : `row`
|
||||
// - 2 bits : `column`
|
||||
//
|
||||
// - recording the layer, and specifying it explicitly when playing back macros
|
||||
// might eliminate some problems with defining a macro for a key on a
|
||||
// different layer than the ones it presses, or on a different layer than the
|
||||
// key indicating to start recording. this way, we could just ignore all
|
||||
// layer shift keys when recording (or... the exec_key function could, since
|
||||
// i think that's where functions from this library will be called).
|
||||
// otherwise, if we just record (pressed, row, column) tuples, the logic will
|
||||
// have to be more complicated... how will we know which key to assign the
|
||||
// macro to? will we not allow macros to be assigned to layer keys?
|
||||
// (actually... that last question might be a problem anyway... but that's
|
||||
// for the logic later on)
|
||||
//
|
||||
// - still need to consider though: do we really want to give up the `type`
|
||||
// byte? it would much more easily allow for extensions in the future if we
|
||||
// kept it, and it would eliminate the need to steal values from `length`.
|
||||
//
|
||||
// - actually, if we multi-byte encode the uid, we do need to keep `type`,
|
||||
// because otherwise, how will we indicate a deleted macro (where the length
|
||||
// still needs to be valid)?
|
||||
//
|
||||
// - we'll need to write a read and a write function for (pressed, layer, row,
|
||||
// column) tuples
|
||||
//
|
||||
// - also, under this scheme, `length` will have to be the number of bytes,
|
||||
// since there will be no quick way to know how many bytes there are (to skip
|
||||
// over to get to the next macro) if we specify the number of actions.
|
||||
//
|
||||
// - this also means we'll need to encode the uid (and may as well write it)
|
||||
// before beginning to record
|
||||
//
|
||||
// - maybe the variables we want to keep track of in memory are
|
||||
// - pointer to beginning of macro currently being recorded
|
||||
// - length, in bytes, of the entire thing (minus the 2 bytes for `type`
|
||||
// and `length`)
|
||||
//
|
||||
// - will we want macros more than 255 bytes long? if so, should we use 2 bytes
|
||||
// for `length`s, or should we align macros on the 2 or 4 byte boundary? (and
|
||||
// if we align on a boundary, how should we pad the ends of the macros?)
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// - 255 bytes (so, on average, say 100 keystrokes) should be enough for a
|
||||
// macro, i think. no need to make `length` 2 bytes, or multi-byte encode it.
|
||||
//
|
||||
// - should `eeprom_macro__uid_t` still stick around? we won't need to use its
|
||||
// binary layout, if we handle things this way, and it's restrictions aren't
|
||||
// prohibitave, i think... and it does keep uid's in SRAM down to a constant
|
||||
// 2 bytes. and make the function signatures smaller, lol.
|
||||
//
|
||||
// - i just looked back at some other code, and utf-8 is big-endian. should
|
||||
// this multi-byte format be big endian too? or small, as described above?
|
||||
//
|
||||
// - even if storing `layer` in the `action`s, there's no real need for the
|
||||
// calling function to ignore layer key presses and releases. maybe the user
|
||||
// wants to use a macro to change the state of their keyboard. i am thinking
|
||||
// we should keep `layer` though, instead of just having (pressed, row,
|
||||
// column) tuples... it feels like it might be easier that way to do what the
|
||||
// user expects (i.e. repeat the *actions* that they performed, not just the
|
||||
// keystrokes, which given layers and such could, theoretically, have some
|
||||
// interesting consequences... lol)
|
||||
//
|
||||
// - should we allow chaining of macros? i kind of think so. actually,
|
||||
// nevermind... i kind of think not. the benefits would be that it would do
|
||||
// what the user expects if they have a longstanding macro that they're used
|
||||
// to, and they try to include it in another without thinking. or if they
|
||||
// want to chain macros to get one longer than 255 bytes. either of those
|
||||
// cases would probably be better served by having them do it in code.
|
||||
// having macros unchainable, however, would allow people to, e.g., remap the
|
||||
// letter keys one to another on their home layer, for experimentation
|
||||
// purposes. which seems like it'd be a useful thing to allow, and a
|
||||
// capability that users would expect (especially coming from the kinesis).
|
||||
// it would also make macros safe from being inadvertently redefined by new
|
||||
// macro definitions. it would also save time a little in key lookup when
|
||||
// playing macros back... lol (insignificant though). it would take an extra
|
||||
// flag or something somewhere though, i think, so that the exec_key function
|
||||
// would know to not look in the EEPROM for aliases of keys when macros were
|
||||
// being played back. hmm... it would also prevent circular (= never ending)
|
||||
// macros. ya, we should probably disallow chaining.
|
||||
// - need to write `kb__layout__exec_key_layer()` (or something)
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
#ifndef OPT__TWI__FREQUENCY
|
||||
#error "OPT__TWI__FREQUENCY not defined"
|
||||
#endif
|
||||
/** macros/OPT__TWI__FREQUENCY/description
|
||||
* The frequency (in Hz) at which TWI will run
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue