Merge remote-tracking branch 'qmk/master' into merge-2022-07-11

This commit is contained in:
Ilya Zhuravlev 2022-07-11 18:29:50 -06:00
commit fde0c10bae
7121 changed files with 170388 additions and 28118 deletions

View file

@ -14,9 +14,18 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <limits.h>
#ifdef DEBUG_ACTION
# include "debug.h"
#else
# include "nodebug.h"
#endif
#include "host.h"
#include "keycode.h"
#include "keyboard.h"
#include "keymap.h"
#include "mousekey.h"
#include "programmable_button.h"
#include "command.h"
@ -33,12 +42,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "backlight.h"
#endif
#ifdef DEBUG_ACTION
# include "debug.h"
#else
# include "nodebug.h"
#endif
#ifdef POINTING_DEVICE_ENABLE
# include "pointing_device.h"
#endif
@ -90,6 +93,7 @@ void action_exec(keyevent_t event) {
}
#ifdef SWAP_HANDS_ENABLE
// Swap hands handles both keys and encoders, if ENCODER_MAP_ENABLE is defined.
if (!IS_NOEVENT(event)) {
process_hand_swap(&event);
}
@ -98,7 +102,7 @@ void action_exec(keyevent_t event) {
keyrecord_t record = {.event = event};
#ifndef NO_ACTION_ONESHOT
if (!keymap_config.oneshot_disable) {
if (keymap_config.oneshot_enable) {
if (QS_oneshot_timeout > 0) {
if (has_oneshot_layer_timed_out()) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
@ -137,27 +141,65 @@ if (QS_oneshot_timeout > 0) {
}
#ifdef SWAP_HANDS_ENABLE
extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS];
# ifdef ENCODER_MAP_ENABLE
extern const uint8_t PROGMEM encoder_hand_swap_config[NUM_ENCODERS];
# endif // ENCODER_MAP_ENABLE
bool swap_hands = false;
bool swap_held = false;
bool should_swap_hands(size_t index, uint8_t *swap_state, bool pressed) {
size_t array_index = index / (CHAR_BIT);
size_t bit_index = index % (CHAR_BIT);
uint8_t bit_val = 1 << bit_index;
bool do_swap = pressed ? swap_hands : swap_state[array_index] & bit_val;
return do_swap;
}
void set_swap_hands_state(size_t index, uint8_t *swap_state, bool on) {
size_t array_index = index / (CHAR_BIT);
size_t bit_index = index % (CHAR_BIT);
uint8_t bit_val = 1 << bit_index;
if (on) {
swap_state[array_index] |= bit_val;
} else {
swap_state[array_index] &= ~bit_val;
}
}
/** \brief Process Hand Swap
*
* FIXME: Needs documentation.
*/
void process_hand_swap(keyevent_t *event) {
static swap_state_row_t swap_state[MATRIX_ROWS];
keypos_t pos = event->key;
swap_state_row_t col_bit = (swap_state_row_t)1 << pos.col;
bool do_swap = event->pressed ? swap_hands : swap_state[pos.row] & (col_bit);
if (do_swap) {
event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row);
event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col);
swap_state[pos.row] |= col_bit;
} else {
swap_state[pos.row] &= ~(col_bit);
keypos_t pos = event->key;
if (pos.row < MATRIX_ROWS && pos.col < MATRIX_COLS) {
static uint8_t matrix_swap_state[((MATRIX_ROWS * MATRIX_COLS) + (CHAR_BIT)-1) / (CHAR_BIT)];
size_t index = (size_t)(pos.row * MATRIX_COLS) + pos.col;
bool do_swap = should_swap_hands(index, matrix_swap_state, event->pressed);
if (do_swap) {
event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row);
event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col);
set_swap_hands_state(index, matrix_swap_state, true);
} else {
set_swap_hands_state(index, matrix_swap_state, false);
}
}
# ifdef ENCODER_MAP_ENABLE
else if (pos.row == KEYLOC_ENCODER_CW || pos.row == KEYLOC_ENCODER_CCW) {
static uint8_t encoder_swap_state[((NUM_ENCODERS) + (CHAR_BIT)-1) / (CHAR_BIT)];
size_t index = pos.col;
bool do_swap = should_swap_hands(index, encoder_swap_state, event->pressed);
if (do_swap) {
event->key.row = pos.row;
event->key.col = pgm_read_byte(&encoder_hand_swap_config[pos.col]);
set_swap_hands_state(index, encoder_swap_state, true);
} else {
set_swap_hands_state(index, encoder_swap_state, false);
}
}
# endif // ENCODER_MAP_ENABLE
}
#endif
@ -217,7 +259,7 @@ void process_record(keyrecord_t *record) {
if (!process_record_quantum(record)) {
#ifndef NO_ACTION_ONESHOT
if (is_oneshot_layer_active() && record->event.pressed && !keymap_config.oneshot_disable) {
if (is_oneshot_layer_active() && record->event.pressed && keymap_config.oneshot_enable) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
}
#endif
@ -282,7 +324,7 @@ void process_action(keyrecord_t *record, action_t action) {
# ifdef SWAP_HANDS_ENABLE
&& !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)
# endif
&& !keymap_config.oneshot_disable) {
&& keymap_config.oneshot_enable) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
do_release_oneshot = !is_oneshot_layer_active();
}
@ -326,7 +368,7 @@ void process_action(keyrecord_t *record, action_t action) {
# ifndef NO_ACTION_ONESHOT
case MODS_ONESHOT:
// Oneshot modifier
if (keymap_config.oneshot_disable) {
if (!keymap_config.oneshot_enable) {
if (event.pressed) {
if (mods) {
if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
@ -362,7 +404,7 @@ void process_action(keyrecord_t *record, action_t action) {
} else if (QS_oneshot_tap_toggle > 1 && tap_count == QS_oneshot_tap_toggle) {
dprint("MODS_TAP: Toggling oneshot");
clear_oneshot_mods();
set_oneshot_locked_mods(mods);
set_oneshot_locked_mods(mods | get_oneshot_locked_mods());
register_mods(mods);
} else {
register_mods(mods | get_oneshot_mods());
@ -375,8 +417,8 @@ void process_action(keyrecord_t *record, action_t action) {
// Retain Oneshot mods
if (QS_oneshot_tap_toggle > 1) {
if (mods & get_mods()) {
clear_oneshot_locked_mods();
clear_oneshot_mods();
set_oneshot_locked_mods(~mods & get_oneshot_locked_mods());
unregister_mods(mods);
}
}
@ -570,7 +612,7 @@ if (QS_oneshot_tap_toggle > 1) {
# ifndef NO_ACTION_ONESHOT
case OP_ONESHOT:
// Oneshot modifier
if (keymap_config.oneshot_disable) {
if (!keymap_config.oneshot_enable) {
if (event.pressed) {
layer_on(action.layer_tap.val);
} else {
@ -580,7 +622,6 @@ if (QS_oneshot_tap_toggle > 1) {
if (QS_oneshot_tap_toggle > 1) {
do_release_oneshot = false;
if (event.pressed) {
del_mods(get_oneshot_locked_mods());
if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
reset_oneshot_layer();
layer_off(action.layer_tap.val);
@ -590,10 +631,8 @@ if (QS_oneshot_tap_toggle > 1) {
set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
}
} else {
add_mods(get_oneshot_locked_mods());
if (tap_count >= QS_oneshot_tap_toggle) {
reset_oneshot_layer();
clear_oneshot_locked_mods();
set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
} else {
clear_oneshot_layer_state(ONESHOT_PRESSED);

View file

@ -1,8 +1,5 @@
#include <limits.h>
#include <stdint.h>
#include "keyboard.h"
#include "action.h"
#include "util.h"
#include "action_layer.h"
#ifdef DEBUG_ACTION
# include "debug.h"
@ -10,6 +7,12 @@
# include "nodebug.h"
#endif
#include "keyboard.h"
#include "keymap.h"
#include "action.h"
#include "util.h"
#include "action_layer.h"
#ifdef VIAL_ENABLE
#include "vial.h"
#endif
@ -227,24 +230,58 @@ void layer_debug(void) {
/** \brief source layer cache
*/
uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}};
uint8_t source_layers_cache[((MATRIX_ROWS * MATRIX_COLS) + (CHAR_BIT)-1) / (CHAR_BIT)][MAX_LAYER_BITS] = {{0}};
# ifdef ENCODER_MAP_ENABLE
uint8_t encoder_source_layers_cache[(NUM_ENCODERS + (CHAR_BIT)-1) / (CHAR_BIT)][MAX_LAYER_BITS] = {{0}};
# endif // ENCODER_MAP_ENABLE
/** \brief update source layers cache
/** \brief update source layers cache impl
*
* Updates the cached keys when changing layers
* Updates the supplied cache when changing layers
*/
void update_source_layers_cache_impl(uint8_t layer, uint16_t entry_number, uint8_t cache[][MAX_LAYER_BITS]) {
const uint16_t storage_idx = entry_number / (CHAR_BIT);
const uint8_t storage_bit = entry_number % (CHAR_BIT);
for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
cache[storage_idx][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ cache[storage_idx][bit_number]) & (1U << storage_bit);
}
}
/** \brief read source layers cache
*
* reads the cached keys stored when the layer was changed
*/
uint8_t read_source_layers_cache_impl(uint16_t entry_number, uint8_t cache[][MAX_LAYER_BITS]) {
const uint16_t storage_idx = entry_number / (CHAR_BIT);
const uint8_t storage_bit = entry_number % (CHAR_BIT);
uint8_t layer = 0;
for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
layer |= ((cache[storage_idx][bit_number] & (1U << storage_bit)) != 0) << bit_number;
}
return layer;
}
/** \brief update encoder source layers cache
*
* Updates the cached encoders when changing layers
*/
void update_source_layers_cache(keypos_t key, uint8_t layer) {
#ifdef VIAL_ENABLE
if (key.row == VIAL_MATRIX_MAGIC) return;
#endif
const uint8_t key_number = key.col + (key.row * MATRIX_COLS);
const uint8_t storage_row = key_number / 8;
const uint8_t storage_bit = key_number % 8;
for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
source_layers_cache[storage_row][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ source_layers_cache[storage_row][bit_number]) & (1U << storage_bit);
if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) {
const uint16_t entry_number = (uint16_t)(key.row * MATRIX_COLS) + key.col;
update_source_layers_cache_impl(layer, entry_number, source_layers_cache);
}
# ifdef ENCODER_MAP_ENABLE
else if (key.row == KEYLOC_ENCODER_CW || key.row == KEYLOC_ENCODER_CCW) {
const uint16_t entry_number = key.col;
update_source_layers_cache_impl(layer, entry_number, encoder_source_layers_cache);
}
# endif // ENCODER_MAP_ENABLE
}
/** \brief read source layers cache
@ -256,16 +293,17 @@ uint8_t read_source_layers_cache(keypos_t key) {
if (key.row == VIAL_MATRIX_MAGIC) return 0;
#endif
const uint8_t key_number = key.col + (key.row * MATRIX_COLS);
const uint8_t storage_row = key_number / 8;
const uint8_t storage_bit = key_number % 8;
uint8_t layer = 0;
for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
layer |= ((source_layers_cache[storage_row][bit_number] & (1U << storage_bit)) != 0) << bit_number;
if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) {
const uint16_t entry_number = (uint16_t)(key.row * MATRIX_COLS) + key.col;
return read_source_layers_cache_impl(entry_number, source_layers_cache);
}
return layer;
# ifdef ENCODER_MAP_ENABLE
else if (key.row == KEYLOC_ENCODER_CW || key.row == KEYLOC_ENCODER_CCW) {
const uint16_t entry_number = key.col;
return read_source_layers_cache_impl(entry_number, encoder_source_layers_cache);
}
# endif // ENCODER_MAP_ENABLE
return 0;
}
#endif

View file

@ -1,10 +1,5 @@
#include <stdint.h>
#include <stdbool.h>
#include "action.h"
#include "action_layer.h"
#include "action_tapping.h"
#include "keycode.h"
#include "timer.h"
#ifdef DEBUG_ACTION
# include "debug.h"
@ -12,6 +7,12 @@
# include "nodebug.h"
#endif
#include "action.h"
#include "action_layer.h"
#include "action_tapping.h"
#include "keycode.h"
#include "timer.h"
#ifndef NO_ACTION_TAPPING
# define IS_TAPPING() !IS_NOEVENT(tapping_key.event)
@ -23,17 +24,20 @@
# else
# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)
# endif
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < GET_TAPPING_TERM(get_record_keycode(&tapping_key, false), &tapping_key))
# ifdef DYNAMIC_TAPPING_TERM_ENABLE
uint16_t g_tapping_term = TAPPING_TERM;
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {
return g_tapping_term;
}
# endif
# ifdef TAPPING_TERM_PER_KEY
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_record_keycode(&tapping_key, false), &tapping_key))
# else
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < g_tapping_term)
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {
# ifdef DYNAMIC_TAPPING_TERM_ENABLE
return g_tapping_term;
# else
return TAPPING_TERM;
# endif
}
# endif
# ifdef TAPPING_FORCE_HOLD_PER_KEY
@ -164,15 +168,7 @@ bool process_tapping(keyrecord_t *keyp) {
else if (
(
(
(
# ifdef TAPPING_TERM_PER_KEY
get_tapping_term(tapping_keycode, &tapping_key)
# else
g_tapping_term
# endif
>= 500
)
GET_TAPPING_TERM(tapping_keycode, &tapping_key) >= 500
# ifdef PERMISSIVE_HOLD_PER_KEY
|| get_permissive_hold(tapping_keycode, &tapping_key)
# elif defined(PERMISSIVE_HOLD)

View file

@ -44,3 +44,11 @@ bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
#ifdef DYNAMIC_TAPPING_TERM_ENABLE
extern uint16_t g_tapping_term;
#endif
#ifdef TAPPING_TERM_PER_KEY
# define GET_TAPPING_TERM(keycode, record) get_tapping_term(keycode, record)
#elif defined(DYNAMIC_TAPPING_TERM_ENABLE)
# define GET_TAPPING_TERM(keycode, record) g_tapping_term
#else
# define GET_TAPPING_TERM(keycode, record) (TAPPING_TERM)
#endif

View file

@ -144,7 +144,7 @@ void clear_oneshot_swaphands(void) {
* FIXME: needs doc
*/
void set_oneshot_layer(uint8_t layer, uint8_t state) {
if (!keymap_config.oneshot_disable) {
if (keymap_config.oneshot_enable) {
oneshot_layer_data = layer << 3 | state;
layer_on(layer);
oneshot_layer_time = timer_read();
@ -169,7 +169,7 @@ void reset_oneshot_layer(void) {
void clear_oneshot_layer_state(oneshot_fullfillment_t state) {
uint8_t start_state = oneshot_layer_data;
oneshot_layer_data &= ~state;
if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) && !keymap_config.oneshot_disable) {
if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) && keymap_config.oneshot_enable) {
layer_off(get_oneshot_layer());
reset_oneshot_layer();
}
@ -187,8 +187,8 @@ bool is_oneshot_layer_active(void) {
* FIXME: needs doc
*/
void oneshot_set(bool active) {
if (keymap_config.oneshot_disable != active) {
keymap_config.oneshot_disable = active;
if (keymap_config.oneshot_enable != active) {
keymap_config.oneshot_enable = active;
eeconfig_update_keymap(keymap_config.raw);
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
dprintf("Oneshot: active: %d\n", active);
@ -200,7 +200,7 @@ void oneshot_set(bool active) {
* FIXME: needs doc
*/
void oneshot_toggle(void) {
oneshot_set(!keymap_config.oneshot_disable);
oneshot_set(!keymap_config.oneshot_enable);
}
/** \brief enable oneshot
@ -220,7 +220,7 @@ void oneshot_disable(void) {
}
bool is_oneshot_enabled(void) {
return keymap_config.oneshot_disable;
return keymap_config.oneshot_enable;
}
#endif
@ -392,7 +392,7 @@ void del_oneshot_mods(uint8_t mods) {
* FIXME: needs doc
*/
void set_oneshot_mods(uint8_t mods) {
if (!keymap_config.oneshot_disable) {
if (keymap_config.oneshot_enable) {
if (oneshot_mods != mods) {
oneshot_time = timer_read();
oneshot_mods = mods;

View file

@ -405,14 +405,18 @@ ISR(TIMERx_OVF_vect)
uint16_t interval = (uint16_t)get_breathing_period() * breathing_ISR_frequency / BREATHING_STEPS;
// resetting after one period to prevent ugly reset at overflow.
breathing_counter = (breathing_counter + 1) % (get_breathing_period() * breathing_ISR_frequency);
uint8_t index = breathing_counter / interval % BREATHING_STEPS;
uint8_t index = breathing_counter / interval;
// limit index to max step value
if (index >= BREATHING_STEPS) {
index = BREATHING_STEPS - 1;
}
if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) || ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1))) {
breathing_interrupt_disable();
}
// Set PWM to a brightnessvalue scaled to the configured resolution
set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * ICRx / 255))));
set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint32_t)pgm_read_byte(&breathing_table[index]) * ICRx / 255))));
}
#endif // BACKLIGHT_BREATHING

80
quantum/caps_word.c Normal file
View file

@ -0,0 +1,80 @@
// Copyright 2021-2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "caps_word.h"
/** @brief True when Caps Word is active. */
static bool caps_word_active = false;
#if CAPS_WORD_IDLE_TIMEOUT > 0
// Constrain timeout to a sensible range. With 16-bit timers, the longest
// timeout possible is 32768 ms, rounded here to 30000 ms = half a minute.
# if CAPS_WORD_IDLE_TIMEOUT < 100 || CAPS_WORD_IDLE_TIMEOUT > 30000
# error "CAPS_WORD_IDLE_TIMEOUT must be between 100 and 30000 ms"
# endif
/** @brief Deadline for idle timeout. */
static uint16_t idle_timer = 0;
void caps_word_task(void) {
if (caps_word_active && timer_expired(timer_read(), idle_timer)) {
caps_word_off();
}
}
void caps_word_reset_idle_timer(void) {
idle_timer = timer_read() + CAPS_WORD_IDLE_TIMEOUT;
}
#endif // CAPS_WORD_IDLE_TIMEOUT > 0
void caps_word_on(void) {
if (caps_word_active) {
return;
}
clear_mods();
#ifndef NO_ACTION_ONESHOT
clear_oneshot_mods();
#endif // NO_ACTION_ONESHOT
#if CAPS_WORD_IDLE_TIMEOUT > 0
caps_word_reset_idle_timer();
#endif // CAPS_WORD_IDLE_TIMEOUT > 0
caps_word_active = true;
caps_word_set_user(true);
}
void caps_word_off(void) {
if (!caps_word_active) {
return;
}
unregister_weak_mods(MOD_MASK_SHIFT); // Make sure weak shift is off.
caps_word_active = false;
caps_word_set_user(false);
}
void caps_word_toggle(void) {
if (caps_word_active) {
caps_word_off();
} else {
caps_word_on();
}
}
bool is_caps_word_on(void) {
return caps_word_active;
}
__attribute__((weak)) void caps_word_set_user(bool active) {}

43
quantum/caps_word.h Normal file
View file

@ -0,0 +1,43 @@
// Copyright 2021-2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "quantum.h"
#ifndef CAPS_WORD_IDLE_TIMEOUT
# define CAPS_WORD_IDLE_TIMEOUT 5000 // Default timeout of 5 seconds.
#endif // CAPS_WORD_IDLE_TIMEOUT
#if CAPS_WORD_IDLE_TIMEOUT > 0
/** @brief Matrix scan task for Caps Word feature */
void caps_word_task(void);
/** @brief Resets timer for Caps Word idle timeout. */
void caps_word_reset_idle_timer(void);
#else
static inline void caps_word_task(void) {}
#endif // CAPS_WORD_IDLE_TIMEOUT > 0
void caps_word_on(void); /**< Activates Caps Word. */
void caps_word_off(void); /**< Deactivates Caps Word. */
void caps_word_toggle(void); /**< Toggles Caps Word. */
bool is_caps_word_on(void); /**< Gets whether currently active. */
/**
* @brief Caps Word set callback.
*
* @param active True if Caps Word is active, false otherwise
*/
void caps_word_set_user(bool active);

View file

@ -26,6 +26,16 @@
#include "vial.h"
#endif
#ifdef ENCODER_ENABLE
# include "encoder.h"
#else
# define NUM_ENCODERS 0
#endif
#ifndef DYNAMIC_KEYMAP_LAYER_COUNT
# define DYNAMIC_KEYMAP_LAYER_COUNT 4
#endif
#ifndef DYNAMIC_KEYMAP_MACRO_COUNT
# define DYNAMIC_KEYMAP_MACRO_COUNT 16
#endif
@ -116,6 +126,17 @@ static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
# define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (VIAL_KEY_OVERRIDE_EEPROM_ADDR + VIAL_KEY_OVERRIDE_SIZE)
#endif
// Dynamic macro starts after dynamic encoders, but only when using ENCODER_MAP
#ifdef ENCODER_MAP_ENABLE
# ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
# define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * NUM_ENCODERS * 2 * 2))
# endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
#else // ENCODER_MAP_ENABLE
# ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
# define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR)
# endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
#endif // ENCODER_MAP_ENABLE
// Sanity check that dynamic keymaps fit in available EEPROM
// If there's not 100 bytes available for macros, then something is wrong.
// The keyboard should override DYNAMIC_KEYMAP_LAYER_COUNT to reduce it,
@ -139,9 +160,7 @@ void *dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t c
}
uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column) {
if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS)
return KC_NO;
if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return KC_NO;
void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
// Big endian, so we can read/write EEPROM directly from host if we want
uint16_t keycode = eeprom_read_byte(address) << 8;
@ -150,44 +169,35 @@ uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column)
}
void dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode) {
if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS)
return;
#ifdef VIAL_ENABLE
if (keycode == RESET && !vial_unlocked)
return;
#endif
if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return;
void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
// Big endian, so we can read/write EEPROM directly from host if we want
eeprom_update_byte(address, (uint8_t)(keycode >> 8));
eeprom_update_byte(address + 1, (uint8_t)(keycode & 0xFF));
}
#ifdef VIAL_ENCODERS_ENABLE
static void *dynamic_keymap_encoder_to_eeprom_address(uint8_t layer, uint8_t idx, uint8_t dir) {
return ((void *)VIAL_ENCODERS_EEPROM_ADDR) + (layer * NUMBER_OF_ENCODERS * 2 * 2) + (idx * 2 * 2) + dir * 2;
#ifdef ENCODER_MAP_ENABLE
void *dynamic_keymap_encoder_to_eeprom_address(uint8_t layer, uint8_t encoder_id) {
return ((void *)DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR) + (layer * NUM_ENCODERS * 2 * 2) + (encoder_id * 2 * 2);
}
uint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t idx, uint8_t dir) {
if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || idx >= NUMBER_OF_ENCODERS || dir > 1)
return 0;
void *address = dynamic_keymap_encoder_to_eeprom_address(layer, idx, dir);
uint16_t keycode = eeprom_read_byte(address) << 8;
keycode |= eeprom_read_byte(address + 1);
uint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise) {
if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return KC_NO;
void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);
// Big endian, so we can read/write EEPROM directly from host if we want
uint16_t keycode = ((uint16_t)eeprom_read_byte(address + (clockwise ? 0 : 2))) << 8;
keycode |= eeprom_read_byte(address + (clockwise ? 0 : 2) + 1);
return keycode;
}
void dynamic_keymap_set_encoder(uint8_t layer, uint8_t idx, uint8_t dir, uint16_t keycode) {
if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || idx >= NUMBER_OF_ENCODERS || dir > 1)
return;
void *address = dynamic_keymap_encoder_to_eeprom_address(layer, idx, dir);
eeprom_update_byte(address, (uint8_t)(keycode >> 8));
eeprom_update_byte(address + 1, (uint8_t)(keycode & 0xFF));
void dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode) {
if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return;
void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);
// Big endian, so we can read/write EEPROM directly from host if we want
eeprom_update_byte(address + (clockwise ? 0 : 2), (uint8_t)(keycode >> 8));
eeprom_update_byte(address + (clockwise ? 0 : 2) + 1, (uint8_t)(keycode & 0xFF));
}
#endif
#endif // ENCODER_MAP_ENABLE
#ifdef QMK_SETTINGS
uint8_t dynamic_keymap_get_qmk_settings(uint16_t offset) {
@ -273,12 +283,6 @@ int dynamic_keymap_set_key_override(uint8_t index, const vial_key_override_entry
}
#endif
#if defined(VIAL_ENCODERS_ENABLE) && defined(VIAL_ENCODER_DEFAULT)
static const uint16_t PROGMEM vial_encoder_default[] = VIAL_ENCODER_DEFAULT;
_Static_assert(sizeof(vial_encoder_default)/sizeof(*vial_encoder_default) == 2 * DYNAMIC_KEYMAP_LAYER_COUNT * NUMBER_OF_ENCODERS,
"There should be DYNAMIC_KEYMAP_LAYER_COUNT * NUMBER_OF_ENCODERS * 2 entries in the VIAL_ENCODER_DEFAULT array.");
#endif
void dynamic_keymap_reset(void) {
#ifdef VIAL_ENABLE
/* temporarily unlock the keyboard so we can set hardcoded RESET keycode */
@ -295,18 +299,12 @@ void dynamic_keymap_reset(void) {
dynamic_keymap_set_keycode(layer, row, column, pgm_read_word(&keymaps[layer][row][column]));
}
}
#ifdef VIAL_ENCODERS_ENABLE
for (int idx = 0; idx < NUMBER_OF_ENCODERS; ++idx) {
#ifdef VIAL_ENCODER_DEFAULT
dynamic_keymap_set_encoder(layer, idx, 0, pgm_read_word(&vial_encoder_default[2 * (layer * NUMBER_OF_ENCODERS + idx)]));
dynamic_keymap_set_encoder(layer, idx, 1, pgm_read_word(&vial_encoder_default[2 * (layer * NUMBER_OF_ENCODERS + idx) + 1]));
#else
dynamic_keymap_set_encoder(layer, idx, 0, KC_TRNS);
dynamic_keymap_set_encoder(layer, idx, 1, KC_TRNS);
#endif
}
#endif
#ifdef ENCODER_MAP_ENABLE
for (int encoder = 0; encoder < NUM_ENCODERS; encoder++) {
dynamic_keymap_set_encoder(layer, encoder, true, pgm_read_word(&encoder_map[layer][encoder][0]));
dynamic_keymap_set_encoder(layer, encoder, false, pgm_read_word(&encoder_map[layer][encoder][1]));
}
#endif // ENCODER_MAP_ENABLE
}
#ifdef QMK_SETTINGS
@ -428,9 +426,15 @@ uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) {
if (layer < DYNAMIC_KEYMAP_LAYER_COUNT && key.row < MATRIX_ROWS && key.col < MATRIX_COLS) {
return dynamic_keymap_get_keycode(layer, key.row, key.col);
} else {
return KC_NO;
}
#ifdef ENCODER_MAP_ENABLE
else if (layer < DYNAMIC_KEYMAP_LAYER_COUNT && key.row == KEYLOC_ENCODER_CW && key.col < NUM_ENCODERS) {
return dynamic_keymap_get_encoder(layer, key.col, true);
} else if (layer < DYNAMIC_KEYMAP_LAYER_COUNT && key.row == KEYLOC_ENCODER_CCW && key.col < NUM_ENCODERS) {
return dynamic_keymap_get_encoder(layer, key.col, false);
}
#endif // ENCODER_MAP_ENABLE
return KC_NO;
}
uint8_t dynamic_keymap_macro_get_count(void) {

View file

@ -30,9 +30,9 @@ uint8_t dynamic_keymap_get_layer_count(void);
void * dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t column);
uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column);
void dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode);
#ifdef VIAL_ENCODERS_ENABLE
uint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t idx, uint8_t dir);
void dynamic_keymap_set_encoder(uint8_t layer, uint8_t idx, uint8_t dir, uint16_t keycode);
#ifdef ENCODER_MAP_ENABLE
uint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise);
void dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode);
#endif
#ifdef QMK_SETTINGS
uint8_t dynamic_keymap_get_qmk_settings(uint16_t offset);

View file

@ -46,7 +46,7 @@ void eeconfig_init_quantum(void) {
eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 0);
default_layer_state = 0;
eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, 0);
eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0);
eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0x4);
eeprom_update_byte(EECONFIG_MOUSEKEY_ACCEL, 0);
eeprom_update_byte(EECONFIG_BACKLIGHT, 0);
eeprom_update_byte(EECONFIG_AUDIO, 0xFF); // On by default

View file

@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <stdbool.h>
#ifndef EECONFIG_MAGIC_NUMBER
# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE9 // When changing, decrement this value to avoid future re-init issues
# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE8 // When changing, decrement this value to avoid future re-init issues
#endif
#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF

View file

@ -23,6 +23,10 @@
// for memcpy
#include <string.h>
#ifndef ENCODER_MAP_KEY_DELAY
# define ENCODER_MAP_KEY_DELAY 2
#endif
#if !defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTION)
# define ENCODER_RESOLUTION 4
#endif
@ -31,11 +35,13 @@
# error "No encoder pads defined by ENCODERS_PAD_A and ENCODERS_PAD_B"
#endif
#define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t))
static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
static pin_t encoders_pad_b[] = ENCODERS_PAD_B;
extern volatile bool isLeftHand;
static pin_t encoders_pad_a[NUM_ENCODERS_MAX_PER_SIDE] = ENCODERS_PAD_A;
static pin_t encoders_pad_b[NUM_ENCODERS_MAX_PER_SIDE] = ENCODERS_PAD_B;
#ifdef ENCODER_RESOLUTIONS
static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS;
static uint8_t encoder_resolutions[NUM_ENCODERS] = ENCODER_RESOLUTIONS;
#endif
#ifndef ENCODER_DIRECTION_FLIP
@ -47,18 +53,20 @@ static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS;
#endif
static int8_t encoder_LUT[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
static uint8_t encoder_state[NUMBER_OF_ENCODERS] = {0};
static int8_t encoder_pulses[NUMBER_OF_ENCODERS] = {0};
static uint8_t encoder_state[NUM_ENCODERS] = {0};
static int8_t encoder_pulses[NUM_ENCODERS] = {0};
// encoder counts
static uint8_t thisCount;
#ifdef SPLIT_KEYBOARD
// right half encoders come over as second set of encoders
static uint8_t encoder_value[NUMBER_OF_ENCODERS * 2] = {0};
// row offsets for each hand
// encoder offsets for each hand
static uint8_t thisHand, thatHand;
#else
static uint8_t encoder_value[NUMBER_OF_ENCODERS] = {0};
// encoder counts for each hand
static uint8_t thatCount;
#endif
static uint8_t encoder_value[NUM_ENCODERS] = {0};
__attribute__((weak)) void encoder_wait_pullup_charge(void) {
wait_us(100);
}
@ -77,46 +85,83 @@ __attribute__((weak)) bool encoder_update_kb(uint8_t index, bool clockwise) {
#endif
void encoder_init(void) {
#if defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT)
if (!isLeftHand) {
const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT;
const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT;
# if defined(ENCODER_RESOLUTIONS_RIGHT)
const uint8_t encoder_resolutions_right[] = ENCODER_RESOLUTIONS_RIGHT;
# endif
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
encoders_pad_a[i] = encoders_pad_a_right[i];
encoders_pad_b[i] = encoders_pad_b_right[i];
# if defined(ENCODER_RESOLUTIONS_RIGHT)
encoder_resolutions[i] = encoder_resolutions_right[i];
# endif
}
#ifdef SPLIT_KEYBOARD
thisHand = isLeftHand ? 0 : NUM_ENCODERS_LEFT;
thatHand = NUM_ENCODERS_LEFT - thisHand;
thisCount = isLeftHand ? NUM_ENCODERS_LEFT : NUM_ENCODERS_RIGHT;
thatCount = isLeftHand ? NUM_ENCODERS_RIGHT : NUM_ENCODERS_LEFT;
#else // SPLIT_KEYBOARD
thisCount = NUM_ENCODERS;
#endif
#ifdef ENCODER_TESTS
// Annoying that we have to clear out values during initialisation here, but
// because all the arrays are static locals, rerunning tests in the same
// executable doesn't reset any of these. Kinda crappy having test-only code
// here, but it's the simplest solution.
memset(encoder_value, 0, sizeof(encoder_value));
memset(encoder_state, 0, sizeof(encoder_state));
memset(encoder_pulses, 0, sizeof(encoder_pulses));
static const pin_t encoders_pad_a_left[] = ENCODERS_PAD_A;
static const pin_t encoders_pad_b_left[] = ENCODERS_PAD_B;
for (uint8_t i = 0; i < thisCount; i++) {
encoders_pad_a[i] = encoders_pad_a_left[i];
encoders_pad_b[i] = encoders_pad_b_left[i];
}
#endif
for (int i = 0; i < NUMBER_OF_ENCODERS; i++) {
#if defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT)
// Re-initialise the pads if it's the right-hand side
if (!isLeftHand) {
static const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT;
static const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT;
for (uint8_t i = 0; i < thisCount; i++) {
encoders_pad_a[i] = encoders_pad_a_right[i];
encoders_pad_b[i] = encoders_pad_b_right[i];
}
}
#endif // defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT)
// Encoder resolutions is handled purely master-side, so concatenate the two arrays
#if defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS)
# if defined(ENCODER_RESOLUTIONS_RIGHT)
static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS_RIGHT;
# else // defined(ENCODER_RESOLUTIONS_RIGHT)
static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS;
# endif // defined(ENCODER_RESOLUTIONS_RIGHT)
for (uint8_t i = 0; i < NUM_ENCODERS_RIGHT; i++) {
encoder_resolutions[NUM_ENCODERS_LEFT + i] = encoder_resolutions_right[i];
}
#endif // defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS)
for (uint8_t i = 0; i < thisCount; i++) {
setPinInputHigh(encoders_pad_a[i]);
setPinInputHigh(encoders_pad_b[i]);
}
encoder_wait_pullup_charge();
for (int i = 0; i < NUMBER_OF_ENCODERS; i++) {
for (uint8_t i = 0; i < thisCount; i++) {
encoder_state[i] = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
}
#ifdef SPLIT_KEYBOARD
thisHand = isLeftHand ? 0 : NUMBER_OF_ENCODERS;
thatHand = NUMBER_OF_ENCODERS - thisHand;
#endif
}
#ifdef ENCODER_MAP_ENABLE
static void encoder_exec_mapping(uint8_t index, bool clockwise) {
// The delays below cater for Windows and its wonderful requirements.
action_exec(clockwise ? ENCODER_CW_EVENT(index, true) : ENCODER_CCW_EVENT(index, true));
wait_ms(ENCODER_MAP_KEY_DELAY);
action_exec(clockwise ? ENCODER_CW_EVENT(index, false) : ENCODER_CCW_EVENT(index, false));
wait_ms(ENCODER_MAP_KEY_DELAY);
}
#endif // ENCODER_MAP_ENABLE
static bool encoder_update(uint8_t index, uint8_t state) {
bool changed = false;
uint8_t i = index;
#ifdef ENCODER_RESOLUTIONS
uint8_t resolution = encoder_resolutions[i];
const uint8_t resolution = encoder_resolutions[i];
#else
uint8_t resolution = ENCODER_RESOLUTION;
const uint8_t resolution = ENCODER_RESOLUTION;
#endif
#ifdef SPLIT_KEYBOARD
@ -126,12 +171,20 @@ static bool encoder_update(uint8_t index, uint8_t state) {
if (encoder_pulses[i] >= resolution) {
encoder_value[index]++;
changed = true;
#ifdef ENCODER_MAP_ENABLE
encoder_exec_mapping(index, ENCODER_COUNTER_CLOCKWISE);
#else // ENCODER_MAP_ENABLE
encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE);
#endif // ENCODER_MAP_ENABLE
}
if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise
encoder_value[index]--;
changed = true;
#ifdef ENCODER_MAP_ENABLE
encoder_exec_mapping(index, ENCODER_CLOCKWISE);
#else // ENCODER_MAP_ENABLE
encoder_update_kb(index, ENCODER_CLOCKWISE);
#endif // ENCODER_MAP_ENABLE
}
encoder_pulses[i] %= resolution;
#ifdef ENCODER_DEFAULT_POS
@ -144,10 +197,13 @@ static bool encoder_update(uint8_t index, uint8_t state) {
bool encoder_read(void) {
bool changed = false;
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
encoder_state[i] <<= 2;
encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
changed |= encoder_update(i, encoder_state[i]);
for (uint8_t i = 0; i < thisCount; i++) {
uint8_t new_status = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
if ((encoder_state[i] & 0x3) != new_status) {
encoder_state[i] <<= 2;
encoder_state[i] |= new_status;
changed |= encoder_update(i, encoder_state[i]);
}
}
return changed;
}
@ -155,26 +211,34 @@ bool encoder_read(void) {
#ifdef SPLIT_KEYBOARD
void last_encoder_activity_trigger(void);
void encoder_state_raw(uint8_t* slave_state) {
memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * NUMBER_OF_ENCODERS);
void encoder_state_raw(uint8_t *slave_state) {
memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * thisCount);
}
void encoder_update_raw(uint8_t* slave_state) {
void encoder_update_raw(uint8_t *slave_state) {
bool changed = false;
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
uint8_t index = i + thatHand;
int8_t delta = slave_state[i] - encoder_value[index];
for (uint8_t i = 0; i < thatCount; i++) { // Note inverted logic -- we want the opposite side
const uint8_t index = i + thatHand;
int8_t delta = slave_state[i] - encoder_value[index];
while (delta > 0) {
delta--;
encoder_value[index]++;
changed = true;
# ifdef ENCODER_MAP_ENABLE
encoder_exec_mapping(index, ENCODER_COUNTER_CLOCKWISE);
# else // ENCODER_MAP_ENABLE
encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE);
# endif // ENCODER_MAP_ENABLE
}
while (delta < 0) {
delta++;
encoder_value[index]--;
changed = true;
# ifdef ENCODER_MAP_ENABLE
encoder_exec_mapping(index, ENCODER_CLOCKWISE);
# else // ENCODER_MAP_ENABLE
encoder_update_kb(index, ENCODER_CLOCKWISE);
# endif // ENCODER_MAP_ENABLE
}
}

View file

@ -18,6 +18,7 @@
#pragma once
#include "quantum.h"
#include "util.h"
void encoder_init(void);
bool encoder_read(void);
@ -26,6 +27,37 @@ bool encoder_update_kb(uint8_t index, bool clockwise);
bool encoder_update_user(uint8_t index, bool clockwise);
#ifdef SPLIT_KEYBOARD
void encoder_state_raw(uint8_t* slave_state);
void encoder_update_raw(uint8_t* slave_state);
#endif
# if defined(ENCODERS_PAD_A_RIGHT)
# define NUM_ENCODERS_LEFT (sizeof(((pin_t[])ENCODERS_PAD_A)) / sizeof(pin_t))
# define NUM_ENCODERS_RIGHT (sizeof(((pin_t[])ENCODERS_PAD_A_RIGHT)) / sizeof(pin_t))
# else
# define NUM_ENCODERS_LEFT (sizeof(((pin_t[])ENCODERS_PAD_A)) / sizeof(pin_t))
# define NUM_ENCODERS_RIGHT NUM_ENCODERS_LEFT
# endif
# define NUM_ENCODERS (NUM_ENCODERS_LEFT + NUM_ENCODERS_RIGHT)
#else // SPLIT_KEYBOARD
# define NUM_ENCODERS (sizeof(((pin_t[])ENCODERS_PAD_A)) / sizeof(pin_t))
# define NUM_ENCODERS_LEFT NUM_ENCODERS
# define NUM_ENCODERS_RIGHT 0
#endif // SPLIT_KEYBOARD
#ifndef NUM_ENCODERS
# define NUM_ENCODERS 0
# define NUM_ENCODERS_LEFT 0
# define NUM_ENCODERS_RIGHT 0
#endif // NUM_ENCODERS
#define NUM_ENCODERS_MAX_PER_SIDE MAX(NUM_ENCODERS_LEFT, NUM_ENCODERS_RIGHT)
#ifdef ENCODER_MAP_ENABLE
# define ENCODER_CCW_CW(ccw, cw) \
{ (cw), (ccw) }
extern const uint16_t encoder_map[][NUM_ENCODERS][2];
#endif // ENCODER_MAP_ENABLE

View file

@ -0,0 +1,22 @@
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define MATRIX_ROWS 1
#define MATRIX_COLS 1
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0 }
#define ENCODERS_PAD_B \
{ 1 }
#ifdef __cplusplus
extern "C" {
#endif
#include "mock.h"
#ifdef __cplusplus
};
#endif

View file

@ -0,0 +1,26 @@
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define MATRIX_ROWS 1
#define MATRIX_COLS 1
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0, 2 }
#define ENCODERS_PAD_B \
{ 1, 3 }
#define ENCODERS_PAD_A_RIGHT \
{ 4, 6 }
#define ENCODERS_PAD_B_RIGHT \
{ 5, 7 }
#ifdef __cplusplus
extern "C" {
#endif
#include "mock_split.h"
#ifdef __cplusplus
};
#endif

View file

@ -0,0 +1,26 @@
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define MATRIX_ROWS 1
#define MATRIX_COLS 1
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0, 2, 4 }
#define ENCODERS_PAD_B \
{ 1, 3, 5 }
#define ENCODERS_PAD_A_RIGHT \
{ 6, 8 }
#define ENCODERS_PAD_B_RIGHT \
{ 7, 9 }
#ifdef __cplusplus
extern "C" {
#endif
#include "mock_split.h"
#ifdef __cplusplus
};
#endif

View file

@ -0,0 +1,26 @@
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define MATRIX_ROWS 1
#define MATRIX_COLS 1
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0, 2 }
#define ENCODERS_PAD_B \
{ 1, 3 }
#define ENCODERS_PAD_A_RIGHT \
{ 4, 6, 8 }
#define ENCODERS_PAD_B_RIGHT \
{ 5, 7, 9 }
#ifdef __cplusplus
extern "C" {
#endif
#include "mock_split.h"
#ifdef __cplusplus
};
#endif

View file

@ -0,0 +1,26 @@
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define MATRIX_ROWS 1
#define MATRIX_COLS 1
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{}
#define ENCODERS_PAD_B \
{}
#define ENCODERS_PAD_A_RIGHT \
{ 0, 2 }
#define ENCODERS_PAD_B_RIGHT \
{ 1, 3 }
#ifdef __cplusplus
extern "C" {
#endif
#include "mock_split.h"
#ifdef __cplusplus
};
#endif

View file

@ -0,0 +1,26 @@
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define MATRIX_ROWS 1
#define MATRIX_COLS 1
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0, 2 }
#define ENCODERS_PAD_B \
{ 1, 3 }
#define ENCODERS_PAD_A_RIGHT \
{}
#define ENCODERS_PAD_B_RIGHT \
{}
#ifdef __cplusplus
extern "C" {
#endif
#include "mock_split.h"
#ifdef __cplusplus
};
#endif

View file

@ -30,12 +30,12 @@ struct update {
bool clockwise;
};
uint8_t uidx = 0;
uint8_t updates_array_idx = 0;
update updates[32];
bool encoder_update_kb(uint8_t index, bool clockwise) {
updates[uidx % 32] = {index, clockwise};
uidx++;
updates[updates_array_idx % 32] = {index, clockwise};
updates_array_idx++;
return true;
}
@ -47,15 +47,15 @@ bool setAndRead(pin_t pin, bool val) {
class EncoderTest : public ::testing::Test {};
TEST_F(EncoderTest, TestInit) {
uidx = 0;
updates_array_idx = 0;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(uidx, 0);
EXPECT_EQ(updates_array_idx, 0);
}
TEST_F(EncoderTest, TestOneClockwise) {
uidx = 0;
updates_array_idx = 0;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(0, false);
@ -63,26 +63,26 @@ TEST_F(EncoderTest, TestOneClockwise) {
setAndRead(0, true);
setAndRead(1, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates_array_idx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderTest, TestOneCounterClockwise) {
uidx = 0;
updates_array_idx = 0;
encoder_init();
setAndRead(1, false);
setAndRead(0, false);
setAndRead(1, true);
setAndRead(0, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates_array_idx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, false);
}
TEST_F(EncoderTest, TestTwoClockwiseOneCC) {
uidx = 0;
updates_array_idx = 0;
encoder_init();
setAndRead(0, false);
setAndRead(1, false);
@ -97,7 +97,7 @@ TEST_F(EncoderTest, TestTwoClockwiseOneCC) {
setAndRead(1, true);
setAndRead(0, true);
EXPECT_EQ(uidx, 3);
EXPECT_EQ(updates_array_idx, 3);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
EXPECT_EQ(updates[1].index, 0);
@ -107,38 +107,38 @@ TEST_F(EncoderTest, TestTwoClockwiseOneCC) {
}
TEST_F(EncoderTest, TestNoEarly) {
uidx = 0;
updates_array_idx = 0;
encoder_init();
// send 3 pulses. with resolution 4, that's not enough for a step.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
EXPECT_EQ(uidx, 0);
EXPECT_EQ(updates_array_idx, 0);
// now send last pulse
setAndRead(1, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates_array_idx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderTest, TestHalfway) {
uidx = 0;
updates_array_idx = 0;
encoder_init();
// go halfway
setAndRead(0, false);
setAndRead(1, false);
EXPECT_EQ(uidx, 0);
EXPECT_EQ(updates_array_idx, 0);
// back off
setAndRead(1, true);
setAndRead(0, true);
EXPECT_EQ(uidx, 0);
EXPECT_EQ(updates_array_idx, 0);
// go all the way
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
// should result in 1 update
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates_array_idx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}

View file

@ -0,0 +1,135 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <vector>
#include <algorithm>
#include <stdio.h>
extern "C" {
#include "encoder.h"
#include "encoder/tests/mock_split.h"
}
struct update {
int8_t index;
bool clockwise;
};
uint8_t updates_array_idx = 0;
update updates[32];
bool isLeftHand;
bool encoder_update_kb(uint8_t index, bool clockwise) {
if (!isLeftHand) {
// this method has no effect on slave half
printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC");
return true;
}
updates[updates_array_idx % 32] = {index, clockwise};
updates_array_idx++;
return true;
}
bool setAndRead(pin_t pin, bool val) {
setPin(pin, val);
return encoder_read();
}
class EncoderSplitTestLeftEqRight : public ::testing::Test {
protected:
void SetUp() override {
updates_array_idx = 0;
for (int i = 0; i < 32; i++) {
pinIsInputHigh[i] = 0;
pins[i] = 0;
}
}
};
TEST_F(EncoderSplitTestLeftEqRight, TestInitLeft) {
isLeftHand = true;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(pinIsInputHigh[2], true);
EXPECT_EQ(pinIsInputHigh[3], true);
EXPECT_EQ(pinIsInputHigh[4], false);
EXPECT_EQ(pinIsInputHigh[5], false);
EXPECT_EQ(pinIsInputHigh[6], false);
EXPECT_EQ(pinIsInputHigh[7], false);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderSplitTestLeftEqRight, TestInitRight) {
isLeftHand = false;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], false);
EXPECT_EQ(pinIsInputHigh[1], false);
EXPECT_EQ(pinIsInputHigh[2], false);
EXPECT_EQ(pinIsInputHigh[3], false);
EXPECT_EQ(pinIsInputHigh[4], true);
EXPECT_EQ(pinIsInputHigh[5], true);
EXPECT_EQ(pinIsInputHigh[6], true);
EXPECT_EQ(pinIsInputHigh[7], true);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderSplitTestLeftEqRight, TestOneClockwiseLeft) {
isLeftHand = true;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
EXPECT_EQ(updates_array_idx, 1); // one update received
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderSplitTestLeftEqRight, TestOneClockwiseRightSent) {
isLeftHand = false;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(6, false);
setAndRead(7, false);
setAndRead(6, true);
setAndRead(7, true);
uint8_t slave_state[32] = {0};
encoder_state_raw(slave_state);
EXPECT_EQ(slave_state[0], 0);
EXPECT_EQ(slave_state[1], 0xFF);
}
TEST_F(EncoderSplitTestLeftEqRight, TestMultipleEncodersRightReceived) {
isLeftHand = true;
encoder_init();
uint8_t slave_state[32] = {1, 0xFF}; // First right encoder is CCW, Second right encoder CW
encoder_update_raw(slave_state);
EXPECT_EQ(updates_array_idx, 2); // two updates received, one for each changed item on the right side
EXPECT_EQ(updates[0].index, 2);
EXPECT_EQ(updates[0].clockwise, false);
EXPECT_EQ(updates[1].index, 3);
EXPECT_EQ(updates[1].clockwise, true);
}

View file

@ -0,0 +1,139 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <vector>
#include <algorithm>
#include <stdio.h>
extern "C" {
#include "encoder.h"
#include "encoder/tests/mock_split.h"
}
struct update {
int8_t index;
bool clockwise;
};
uint8_t updates_array_idx = 0;
update updates[32];
bool isLeftHand;
bool encoder_update_kb(uint8_t index, bool clockwise) {
if (!isLeftHand) {
// this method has no effect on slave half
printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC");
return true;
}
updates[updates_array_idx % 32] = {index, clockwise};
updates_array_idx++;
return true;
}
bool setAndRead(pin_t pin, bool val) {
setPin(pin, val);
return encoder_read();
}
class EncoderSplitTestLeftGreaterThanRight : public ::testing::Test {
protected:
void SetUp() override {
updates_array_idx = 0;
for (int i = 0; i < 32; i++) {
pinIsInputHigh[i] = 0;
pins[i] = 0;
}
}
};
TEST_F(EncoderSplitTestLeftGreaterThanRight, TestInitLeft) {
isLeftHand = true;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(pinIsInputHigh[2], true);
EXPECT_EQ(pinIsInputHigh[3], true);
EXPECT_EQ(pinIsInputHigh[4], true);
EXPECT_EQ(pinIsInputHigh[5], true);
EXPECT_EQ(pinIsInputHigh[6], false);
EXPECT_EQ(pinIsInputHigh[7], false);
EXPECT_EQ(pinIsInputHigh[8], false);
EXPECT_EQ(pinIsInputHigh[9], false);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderSplitTestLeftGreaterThanRight, TestInitRight) {
isLeftHand = false;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], false);
EXPECT_EQ(pinIsInputHigh[1], false);
EXPECT_EQ(pinIsInputHigh[2], false);
EXPECT_EQ(pinIsInputHigh[3], false);
EXPECT_EQ(pinIsInputHigh[4], false);
EXPECT_EQ(pinIsInputHigh[5], false);
EXPECT_EQ(pinIsInputHigh[6], true);
EXPECT_EQ(pinIsInputHigh[7], true);
EXPECT_EQ(pinIsInputHigh[8], true);
EXPECT_EQ(pinIsInputHigh[9], true);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderSplitTestLeftGreaterThanRight, TestOneClockwiseLeft) {
isLeftHand = true;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
EXPECT_EQ(updates_array_idx, 1); // one update received
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderSplitTestLeftGreaterThanRight, TestOneClockwiseRightSent) {
isLeftHand = false;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(6, false);
setAndRead(7, false);
setAndRead(6, true);
setAndRead(7, true);
uint8_t slave_state[32] = {0};
encoder_state_raw(slave_state);
EXPECT_EQ(slave_state[0], 0xFF);
EXPECT_EQ(slave_state[1], 0);
}
TEST_F(EncoderSplitTestLeftGreaterThanRight, TestMultipleEncodersRightReceived) {
isLeftHand = true;
encoder_init();
uint8_t slave_state[32] = {1, 0xFF}; // First right encoder is CCW, Second right encoder no change, third right encoder CW
encoder_update_raw(slave_state);
EXPECT_EQ(updates_array_idx, 2); // two updates received, one for each changed item on the right side
EXPECT_EQ(updates[0].index, 3);
EXPECT_EQ(updates[0].clockwise, false);
EXPECT_EQ(updates[1].index, 4);
EXPECT_EQ(updates[1].clockwise, true);
}

View file

@ -0,0 +1,139 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <vector>
#include <algorithm>
#include <stdio.h>
extern "C" {
#include "encoder.h"
#include "encoder/tests/mock_split.h"
}
struct update {
int8_t index;
bool clockwise;
};
uint8_t updates_array_idx = 0;
update updates[32];
bool isLeftHand;
bool encoder_update_kb(uint8_t index, bool clockwise) {
if (!isLeftHand) {
// this method has no effect on slave half
printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC");
return true;
}
updates[updates_array_idx % 32] = {index, clockwise};
updates_array_idx++;
return true;
}
bool setAndRead(pin_t pin, bool val) {
setPin(pin, val);
return encoder_read();
}
class EncoderSplitTestLeftLessThanRight : public ::testing::Test {
protected:
void SetUp() override {
updates_array_idx = 0;
for (int i = 0; i < 32; i++) {
pinIsInputHigh[i] = 0;
pins[i] = 0;
}
}
};
TEST_F(EncoderSplitTestLeftLessThanRight, TestInitLeft) {
isLeftHand = true;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(pinIsInputHigh[2], true);
EXPECT_EQ(pinIsInputHigh[3], true);
EXPECT_EQ(pinIsInputHigh[4], false);
EXPECT_EQ(pinIsInputHigh[5], false);
EXPECT_EQ(pinIsInputHigh[6], false);
EXPECT_EQ(pinIsInputHigh[7], false);
EXPECT_EQ(pinIsInputHigh[8], false);
EXPECT_EQ(pinIsInputHigh[9], false);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderSplitTestLeftLessThanRight, TestInitRight) {
isLeftHand = false;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], false);
EXPECT_EQ(pinIsInputHigh[1], false);
EXPECT_EQ(pinIsInputHigh[2], false);
EXPECT_EQ(pinIsInputHigh[3], false);
EXPECT_EQ(pinIsInputHigh[4], true);
EXPECT_EQ(pinIsInputHigh[5], true);
EXPECT_EQ(pinIsInputHigh[6], true);
EXPECT_EQ(pinIsInputHigh[7], true);
EXPECT_EQ(pinIsInputHigh[8], true);
EXPECT_EQ(pinIsInputHigh[9], true);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderSplitTestLeftLessThanRight, TestOneClockwiseLeft) {
isLeftHand = true;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
EXPECT_EQ(updates_array_idx, 1); // one update received
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderSplitTestLeftLessThanRight, TestOneClockwiseRightSent) {
isLeftHand = false;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(6, false);
setAndRead(7, false);
setAndRead(6, true);
setAndRead(7, true);
uint8_t slave_state[32] = {0};
encoder_state_raw(slave_state);
EXPECT_EQ(slave_state[0], 0);
EXPECT_EQ(slave_state[1], 0xFF);
}
TEST_F(EncoderSplitTestLeftLessThanRight, TestMultipleEncodersRightReceived) {
isLeftHand = true;
encoder_init();
uint8_t slave_state[32] = {1, 0, 0xFF}; // First right encoder is CCW, Second right encoder no change, third right encoder CW
encoder_update_raw(slave_state);
EXPECT_EQ(updates_array_idx, 2); // two updates received, one for each changed item on the right side
EXPECT_EQ(updates[0].index, 2);
EXPECT_EQ(updates[0].clockwise, false);
EXPECT_EQ(updates[1].index, 4);
EXPECT_EQ(updates[1].clockwise, true);
}

View file

@ -30,7 +30,7 @@ struct update {
bool clockwise;
};
uint8_t uidx = 0;
uint8_t updates_array_idx = 0;
update updates[32];
bool isLeftHand;
@ -41,8 +41,8 @@ bool encoder_update_kb(uint8_t index, bool clockwise) {
printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC");
return true;
}
updates[uidx % 32] = {index, clockwise};
uidx++;
updates[updates_array_idx % 32] = {index, clockwise};
updates_array_idx++;
return true;
}
@ -51,10 +51,10 @@ bool setAndRead(pin_t pin, bool val) {
return encoder_read();
}
class EncoderTest : public ::testing::Test {
class EncoderSplitTestNoLeft : public ::testing::Test {
protected:
void SetUp() override {
uidx = 0;
updates_array_idx = 0;
for (int i = 0; i < 32; i++) {
pinIsInputHigh[i] = 0;
pins[i] = 0;
@ -62,27 +62,27 @@ class EncoderTest : public ::testing::Test {
}
};
TEST_F(EncoderTest, TestInitLeft) {
TEST_F(EncoderSplitTestNoLeft, TestInitLeft) {
isLeftHand = true;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(pinIsInputHigh[2], false);
EXPECT_EQ(pinIsInputHigh[3], false);
EXPECT_EQ(uidx, 0);
}
TEST_F(EncoderTest, TestInitRight) {
isLeftHand = false;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], false);
EXPECT_EQ(pinIsInputHigh[1], false);
EXPECT_EQ(pinIsInputHigh[2], true);
EXPECT_EQ(pinIsInputHigh[3], true);
EXPECT_EQ(uidx, 0);
EXPECT_EQ(pinIsInputHigh[2], false);
EXPECT_EQ(pinIsInputHigh[3], false);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderTest, TestOneClockwiseLeft) {
TEST_F(EncoderSplitTestNoLeft, TestInitRight) {
isLeftHand = false;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(pinIsInputHigh[2], true);
EXPECT_EQ(pinIsInputHigh[3], true);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderSplitTestNoLeft, TestOneClockwiseLeft) {
isLeftHand = true;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
@ -91,12 +91,10 @@ TEST_F(EncoderTest, TestOneClockwiseLeft) {
setAndRead(0, true);
setAndRead(1, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderTest, TestOneClockwiseRightSent) {
TEST_F(EncoderSplitTestNoLeft, TestOneClockwiseRightSent) {
isLeftHand = false;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
@ -105,39 +103,23 @@ TEST_F(EncoderTest, TestOneClockwiseRightSent) {
setAndRead(2, true);
setAndRead(3, true);
uint8_t slave_state[2] = {0};
uint8_t slave_state[32] = {0};
encoder_state_raw(slave_state);
EXPECT_EQ((int8_t)slave_state[0], -1);
EXPECT_EQ(slave_state[0], 0);
EXPECT_EQ(slave_state[1], 0xFF);
}
/* this test will not work after the previous test.
* this is due to encoder_value[1] already being set to -1 when simulating the right half.
* When we now receive this update acting as the left half, there is no change.
* This is hard to mock, as the static values inside encoder.c normally exist twice, once on each half,
* but here, they only exist once.
*/
// TEST_F(EncoderTest, TestOneClockwiseRightReceived) {
// isLeftHand = true;
// encoder_init();
// uint8_t slave_state[2] = {255, 0};
// encoder_update_raw(slave_state);
// EXPECT_EQ(uidx, 1);
// EXPECT_EQ(updates[0].index, 1);
// EXPECT_EQ(updates[0].clockwise, true);
// }
TEST_F(EncoderTest, TestOneCounterClockwiseRightReceived) {
TEST_F(EncoderSplitTestNoLeft, TestMultipleEncodersRightReceived) {
isLeftHand = true;
encoder_init();
uint8_t slave_state[2] = {0, 0};
uint8_t slave_state[32] = {1, 0xFF}; // First right encoder is CCW, Second right encoder no change, third right encoder CW
encoder_update_raw(slave_state);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 1);
EXPECT_EQ(updates_array_idx, 2); // two updates received, one for each changed item on the right side
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, false);
EXPECT_EQ(updates[1].index, 1);
EXPECT_EQ(updates[1].clockwise, true);
}

View file

@ -0,0 +1,118 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <vector>
#include <algorithm>
#include <stdio.h>
extern "C" {
#include "encoder.h"
#include "encoder/tests/mock_split.h"
}
struct update {
int8_t index;
bool clockwise;
};
uint8_t updates_array_idx = 0;
update updates[32];
bool isLeftHand;
bool encoder_update_kb(uint8_t index, bool clockwise) {
if (!isLeftHand) {
// this method has no effect on slave half
printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC");
return true;
}
updates[updates_array_idx % 32] = {index, clockwise};
updates_array_idx++;
return true;
}
bool setAndRead(pin_t pin, bool val) {
setPin(pin, val);
return encoder_read();
}
class EncoderSplitTestNoRight : public ::testing::Test {
protected:
void SetUp() override {
updates_array_idx = 0;
for (int i = 0; i < 32; i++) {
pinIsInputHigh[i] = 0;
pins[i] = 0;
}
}
};
TEST_F(EncoderSplitTestNoRight, TestInitLeft) {
isLeftHand = true;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(pinIsInputHigh[2], true);
EXPECT_EQ(pinIsInputHigh[3], true);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderSplitTestNoRight, TestInitRight) {
isLeftHand = false;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], false);
EXPECT_EQ(pinIsInputHigh[1], false);
EXPECT_EQ(pinIsInputHigh[2], false);
EXPECT_EQ(pinIsInputHigh[3], false);
EXPECT_EQ(updates_array_idx, 0); // no updates received
}
TEST_F(EncoderSplitTestNoRight, TestOneClockwiseLeft) {
isLeftHand = true;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
EXPECT_EQ(updates_array_idx, 1); // one updates received
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderSplitTestNoRight, TestOneClockwiseRightSent) {
isLeftHand = false;
encoder_init();
uint8_t slave_state[32] = {0xAA, 0xAA};
encoder_state_raw(slave_state);
EXPECT_EQ(slave_state[0], 0xAA);
EXPECT_EQ(slave_state[1], 0xAA);
}
TEST_F(EncoderSplitTestNoRight, TestMultipleEncodersRightReceived) {
isLeftHand = true;
encoder_init();
uint8_t slave_state[32] = {1, 0xFF}; // These values would trigger updates if there were encoders on the other side
encoder_update_raw(slave_state);
EXPECT_EQ(updates_array_idx, 0); // no updates received -- no right-hand encoders
}

View file

@ -19,12 +19,6 @@
#include <stdint.h>
#include <stdbool.h>
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0 }
#define ENCODERS_PAD_B \
{ 1 }
typedef uint8_t pin_t;
extern bool pins[];

View file

@ -20,20 +20,10 @@
#include <stdbool.h>
#define SPLIT_KEYBOARD
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0 }
#define ENCODERS_PAD_B \
{ 1 }
#define ENCODERS_PAD_A_RIGHT \
{ 2 }
#define ENCODERS_PAD_B_RIGHT \
{ 3 }
typedef uint8_t pin_t;
extern bool isLeftHand;
void encoder_state_raw(uint8_t* slave_state);
void encoder_update_raw(uint8_t* slave_state);
void encoder_state_raw(uint8_t* slave_state);
void encoder_update_raw(uint8_t* slave_state);
extern bool pins[];
extern bool pinIsInputHigh[];

View file

@ -1,13 +1,58 @@
encoder_DEFS := -DENCODER_MOCK_SINGLE
encoder_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SINGLE
encoder_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock.h
encoder_SRC := \
platforms/test/timer.c \
$(QUANTUM_PATH)/encoder/tests/mock.c \
$(QUANTUM_PATH)/encoder/tests/encoder_tests.cpp \
$(QUANTUM_PATH)/encoder.c
encoder_split_DEFS := -DENCODER_MOCK_SPLIT
encoder_split_left_eq_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT
encoder_split_left_eq_right_INC := $(QUANTUM_PATH)/split_common
encoder_split_left_eq_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_left_eq_right.h
encoder_split_SRC := \
encoder_split_left_eq_right_SRC := \
platforms/test/timer.c \
$(QUANTUM_PATH)/encoder/tests/mock_split.c \
$(QUANTUM_PATH)/encoder/tests/encoder_tests_split.cpp \
$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_left_eq_right.cpp \
$(QUANTUM_PATH)/encoder.c
encoder_split_left_gt_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT
encoder_split_left_gt_right_INC := $(QUANTUM_PATH)/split_common
encoder_split_left_gt_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_left_gt_right.h
encoder_split_left_gt_right_SRC := \
platforms/test/timer.c \
$(QUANTUM_PATH)/encoder/tests/mock_split.c \
$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_left_gt_right.cpp \
$(QUANTUM_PATH)/encoder.c
encoder_split_left_lt_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT
encoder_split_left_lt_right_INC := $(QUANTUM_PATH)/split_common
encoder_split_left_lt_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_left_lt_right.h
encoder_split_left_lt_right_SRC := \
platforms/test/timer.c \
$(QUANTUM_PATH)/encoder/tests/mock_split.c \
$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_left_lt_right.cpp \
$(QUANTUM_PATH)/encoder.c
encoder_split_no_left_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT
encoder_split_no_left_INC := $(QUANTUM_PATH)/split_common
encoder_split_no_left_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_no_left.h
encoder_split_no_left_SRC := \
platforms/test/timer.c \
$(QUANTUM_PATH)/encoder/tests/mock_split.c \
$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_no_left.cpp \
$(QUANTUM_PATH)/encoder.c
encoder_split_no_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT
encoder_split_no_right_INC := $(QUANTUM_PATH)/split_common
encoder_split_no_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_no_right.h
encoder_split_no_right_SRC := \
platforms/test/timer.c \
$(QUANTUM_PATH)/encoder/tests/mock_split.c \
$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_no_right.cpp \
$(QUANTUM_PATH)/encoder.c

View file

@ -1,3 +1,7 @@
TEST_LIST += \
encoder \
encoder_split
encoder_split_left_eq_right \
encoder_split_left_gt_right \
encoder_split_left_lt_right \
encoder_split_no_left \
encoder_split_no_right

View file

@ -321,7 +321,7 @@ void haptic_play(void) {
DRV_pulse(play_eff);
#endif
#ifdef SOLENOID_ENABLE
solenoid_fire();
solenoid_fire_handler();
#endif
}

View file

@ -1,13 +1,38 @@
#include "joystick.h"
joystick_t joystick_status = {.buttons = {0},
.axes =
{
// clang-format off
joystick_t joystick_status = {
.buttons = {0},
.axes = {
#if JOYSTICK_AXES_COUNT > 0
0
0
#endif
},
.status = 0};
},
.status = 0
};
// clang-format on
// array defining the reading of analog values for each axis
__attribute__((weak)) joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = {};
// to be implemented in the hid protocol library
void send_joystick_packet(joystick_t *joystick);
void joystick_flush(void) {
if ((joystick_status.status & JS_UPDATED) > 0) {
send_joystick_packet(&joystick_status);
joystick_status.status &= ~JS_UPDATED;
}
}
void register_joystick_button(uint8_t button) {
joystick_status.buttons[button / 8] |= 1 << (button % 8);
joystick_status.status |= JS_UPDATED;
joystick_flush();
}
void unregister_joystick_button(uint8_t button) {
joystick_status.buttons[button / 8] &= ~(1 << (button % 8));
joystick_status.status |= JS_UPDATED;
joystick_flush();
}

View file

@ -1,15 +1,22 @@
#pragma once
#include "quantum.h"
#include <stdint.h>
#include "gpio.h"
#ifndef JOYSTICK_BUTTON_COUNT
# define JOYSTICK_BUTTON_COUNT 8
#elif JOYSTICK_BUTTON_COUNT > 32
# error Joystick feature only supports up to 32 buttons
#endif
#ifndef JOYSTICK_AXES_COUNT
# define JOYSTICK_AXES_COUNT 4
#elif JOYSTICK_AXES_COUNT > 6
# error Joystick feature only supports up to 6 axes
#endif
#if JOYSTICK_AXES_COUNT == 0 && JOYSTICK_BUTTON_COUNT == 0
# error Joystick feature requires at least one axis or button
#endif
#ifndef JOYSTICK_AXES_RESOLUTION
@ -58,5 +65,7 @@ typedef struct {
extern joystick_t joystick_status;
// to be implemented in the hid protocol library
void send_joystick_packet(joystick_t *joystick);
void joystick_flush(void);
void register_joystick_button(uint8_t button);
void unregister_joystick_button(uint8_t button);

View file

@ -114,6 +114,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef BLUETOOTH_ENABLE
# include "outputselect.h"
#endif
#ifdef CAPS_WORD_ENABLE
# include "caps_word.h"
#endif
static uint32_t last_input_modification_time = 0;
uint32_t last_input_activity_time(void) {
@ -155,7 +158,7 @@ void matrix_scan_perf_task(void) {
matrix_scan_count++;
uint32_t timer_now = timer_read32();
if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) {
if (TIMER_DIFF_32(timer_now, matrix_timer) >= 1000) {
# if defined(CONSOLE_ENABLE)
dprintf("matrix scan frequency: %lu\n", matrix_scan_count);
# endif
@ -217,17 +220,6 @@ static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) {
#endif
void disable_jtag(void) {
// To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles.
#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
MCUCR |= _BV(JTD);
MCUCR |= _BV(JTD);
#elif defined(__AVR_ATmega32A__)
MCUCSR |= _BV(JTD);
MCUCSR |= _BV(JTD);
#endif
}
/** \brief matrix_setup
*
* FIXME: needs doc
@ -269,9 +261,6 @@ __attribute__((weak)) void keyboard_post_init_kb(void) {
* FIXME: needs doc
*/
void keyboard_setup(void) {
#ifndef NO_JTAG_DISABLE
disable_jtag();
#endif
print_set_sendchar(sendchar);
#ifdef EEPROM_DRIVER
eeprom_driver_init();
@ -501,7 +490,7 @@ bool matrix_scan_task(void) {
// we can get here with some keys processed now.
if (!keys_processed)
#endif
action_exec(TICK);
action_exec(TICK_EVENT);
MATRIX_LOOP_END:
@ -574,6 +563,14 @@ void quantum_task(void) {
#ifdef AUTO_SHIFT_ENABLE
autoshift_matrix_scan();
#endif
#ifdef CAPS_WORD_ENABLE
caps_word_task();
#endif
#ifdef SECURE_ENABLE
secure_task();
#endif
}
/** \brief Keyboard task: Do keyboard routine jobs

View file

@ -40,25 +40,47 @@ typedef struct {
/* equivalent test of keypos_t */
#define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col)
/* special keypos_t entries */
#define KEYLOC_TICK 255
#define KEYLOC_COMBO 254
#define KEYLOC_ENCODER_CW 253
#define KEYLOC_ENCODER_CCW 252
/* Rules for No Event:
* 1) (time == 0) to handle (keyevent_t){} as empty event
* 2) Matrix(255, 255) to make TICK event available
*/
static inline bool IS_NOEVENT(keyevent_t event) {
return event.time == 0 || (event.key.row == 255 && event.key.col == 255);
return event.time == 0 || (event.key.row == KEYLOC_TICK && event.key.col == KEYLOC_TICK);
}
static inline bool IS_KEYEVENT(keyevent_t event) {
return event.key.row < MATRIX_ROWS && event.key.col < MATRIX_COLS;
}
static inline bool IS_COMBOEVENT(keyevent_t event) {
return event.key.row == KEYLOC_COMBO;
}
static inline bool IS_ENCODEREVENT(keyevent_t event) {
return event.key.row == KEYLOC_ENCODER_CW || event.key.row == KEYLOC_ENCODER_CCW;
}
static inline bool IS_PRESSED(keyevent_t event) {
return (!IS_NOEVENT(event) && event.pressed);
return !IS_NOEVENT(event) && event.pressed;
}
static inline bool IS_RELEASED(keyevent_t event) {
return (!IS_NOEVENT(event) && !event.pressed);
return !IS_NOEVENT(event) && !event.pressed;
}
/* Common keyevent object factory */
#define MAKE_KEYPOS(row_num, col_num) ((keypos_t){.row = (row_num), .col = (col_num)})
#define MAKE_KEYEVENT(row_num, col_num, press) ((keyevent_t){.key = MAKE_KEYPOS((row_num), (col_num)), .pressed = (press), .time = (timer_read() | 1)})
/* Tick event */
#define TICK \
(keyevent_t) { \
.key = (keypos_t){.row = 255, .col = 255}, .pressed = false, .time = (timer_read() | 1) \
}
#define TICK_EVENT MAKE_KEYEVENT(KEYLOC_TICK, KEYLOC_TICK, false)
#ifdef ENCODER_MAP_ENABLE
/* Encoder events */
# define ENCODER_CW_EVENT(enc_id, press) MAKE_KEYEVENT(KEYLOC_ENCODER_CW, (enc_id), (press))
# define ENCODER_CCW_EVENT(enc_id, press) MAKE_KEYEVENT(KEYLOC_ENCODER_CCW, (enc_id), (press))
#endif // ENCODER_MAP_ENABLE
/* it runs once at early stage of startup before keyboard_init. */
void keyboard_setup(void);

View file

@ -37,7 +37,7 @@ typedef union {
bool nkro : 1;
bool swap_lctl_lgui : 1;
bool swap_rctl_rgui : 1;
bool oneshot_disable : 1;
bool oneshot_enable : 1;
};
} keymap_config_t;

View file

@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// #include "print.h"
#include "debug.h"
#include "keycode_config.h"
#include "gpio.h" // for pin_t
// ChibiOS uses RESET in its FlagStatus enumeration
// Therefore define it as QK_BOOTLOADER here, to avoid name collision
@ -49,3 +50,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key);
extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
#ifdef ENCODER_MAP_ENABLE
// Ensure we have a forward declaration for the encoder map
# include "encoder.h"
#endif

View file

@ -148,6 +148,15 @@ action_t action_for_keycode(uint16_t keycode) {
// translates key to keycode
__attribute__((weak)) uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) {
// Read entire word (16bits)
return pgm_read_word(&keymaps[(layer)][(key.row)][(key.col)]);
if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) {
return pgm_read_word(&keymaps[layer][key.row][key.col]);
}
#ifdef ENCODER_MAP_ENABLE
else if (key.row == KEYLOC_ENCODER_CW && key.col < NUM_ENCODERS) {
return pgm_read_word(&encoder_map[layer][key.col][0]);
} else if (key.row == KEYLOC_ENCODER_CCW && key.col < NUM_ENCODERS) {
return pgm_read_word(&encoder_map[layer][key.col][1]);
}
#endif // ENCODER_MAP_ENABLE
return KC_NO;
}

View file

@ -87,8 +87,8 @@
#define BR_SCLN KC_SLSH // ;
#define BR_SLSH KC_INT1 // /
// Numpad
#define BR_PDOT KC_PCMM // .
#define BR_PCMM KC_PDOT // ,
#define BR_PDOT KC_PCMM // .
#define BR_PCMM KC_PDOT // ,
/* Shifted symbols
*

View file

@ -140,12 +140,12 @@
*
*/
// Row 2
#define IT_EURO ALGR(IT_E) // €
#define IT_LBRC ALGR(IT_EGRV) // [
#define IT_RBRC ALGR(IT_PLUS) // ]
#define IT_EURO ALGR(IT_E) // €
#define IT_LBRC ALGR(IT_EGRV) // [
#define IT_RBRC ALGR(IT_PLUS) // ]
// Row 3
#define IT_AT ALGR(IT_OGRV) // @
#define IT_HASH ALGR(IT_AGRV) // #
#define IT_AT ALGR(IT_OGRV) // @
#define IT_HASH ALGR(IT_AGRV) // #
/* Shift+AltGr symbols
*

View file

@ -0,0 +1,134 @@
/* Copyright 2022
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "keymap.h"
// clang-format off
/*
*
* ' 1 2 3 4 5 6 7 8 9 0 - =
*
* Й Ц У К Е Н Г Ш Щ З Х Ї \
*
* Ф І В А П Р О Л Д Ж Є
*
* Я Ч С М И Т Ь Б Ю .
*
*
*
*/
// Row 1
#define UA_QUOT KC_GRV // '
#define UA_1 KC_1 // 1
#define UA_2 KC_2 // 2
#define UA_3 KC_3 // 3
#define UA_4 KC_4 // 4
#define UA_5 KC_5 // 5
#define UA_6 KC_6 // 6
#define UA_7 KC_7 // 7
#define UA_8 KC_8 // 8
#define UA_9 KC_9 // 9
#define UA_0 KC_0 // 0
#define UA_MINS KC_MINS // -
#define UA_EQL KC_EQL // =
// Row 2
#define UA_YOT KC_Q // Й
#define UA_TSE KC_W // Ц
#define UA_U KC_E // У
#define UA_KA KC_R // К
#define UA_E KC_T // Е
#define UA_EN KC_Y // Н
#define UA_HE KC_U // Г
#define UA_SHA KC_I // Ш
#define UA_SHCH KC_O // Щ
#define UA_ZE KC_P // З
#define UA_KHA KC_LBRC // Х
#define UA_YI KC_RBRC // Ї
#define UA_BSLS KC_BSLS // (backslash)
// Row 3
#define UA_EF KC_A // Ф
#define UA_I KC_S // І
#define UA_VE KC_D // В
#define UA_A KC_F // А
#define UA_PE KC_G // П
#define UA_ER KC_H // Р
#define UA_O KC_J // О
#define UA_EL KC_K // Л
#define UA_DE KC_L // Д
#define UA_ZHE KC_SCLN // Ж
#define UA_YE KC_QUOT // Є
// Row 4
#define UA_YA KC_Z // Я
#define UA_CHE KC_X // Ч
#define UA_ES KC_C // С
#define UA_EM KC_V // М
#define UA_Y KC_B // И
#define UA_TE KC_N // Т
#define UA_SOFT KC_M // Ь
#define UA_BE KC_COMM // Б
#define UA_YU KC_DOT // Ю
#define UA_DOT KC_SLSH // .
/* Shifted symbols
*
* ! " │ № │ ; │ % │ : │ ? │ * │ ( │ ) │ _ │ + │ │
*
* /
*
*
*
* ,
*
*
*
*/
// Row 1
#define UA_HRYV S(UA_QUOT) // ₴
#define UA_EXLM S(UA_1) // !
#define UA_DQUO S(UA_2) // "
#define UA_NUM S(UA_3) // №
#define UA_SCLN S(UA_4) // ;
#define UA_PERC S(UA_5) // %
#define UA_COLN S(UA_6) // :
#define UA_QUES S(UA_7) // ?
#define UA_ASTR S(UA_8) // *
#define UA_LPRN S(UA_9) // (
#define UA_RPRN S(UA_0) // )
#define UA_UNDS S(UA_MINS) // _
#define UA_PLUS S(UA_EQL) // +
// Row 2
#define UA_SLSH S(UA_BSLS) // /
// Row 4
#define UA_COMM S(UA_DOT) // ,
/* AltGr symbols
*
*
*
* ґ
*
*
*
*
*
*
*
*/
// Row 2
#define UA_GE ALGR(UA_HE) // ґ

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -18,7 +18,7 @@
#pragma once
#include "keymap_br_abnt2.h"
#include "keymap_brazilian_abnt2.h"
#include "quantum.h"
// clang-format off
@ -43,6 +43,26 @@ const uint8_t ascii_to_shift_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0),
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -42,6 +42,25 @@ const uint8_t ascii_to_shift_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0),
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL

View file

@ -18,7 +18,7 @@
#pragma once
#include "keymap_dvp.h"
#include "keymap_dvorak_programmer.h"
#include "quantum.h"
// clang-format off

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -18,7 +18,7 @@
#pragma once
#include "keymap_french_osx.h"
#include "keymap_french_mac_iso.h"
#include "quantum.h"
// clang-format off
@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -18,7 +18,7 @@
#pragma once
#include "keymap_german_osx.h"
#include "keymap_german_mac_iso.h"
#include "quantum.h"
// clang-format off
@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -18,7 +18,7 @@
#pragma once
#include "keymap_italian_osx_ansi.h"
#include "keymap_italian_mac_ansi.h"
#include "quantum.h"
// clang-format off

View file

@ -18,7 +18,7 @@
#pragma once
#include "keymap_italian_osx_iso.h"
#include "keymap_italian_mac_iso.h"
#include "quantum.h"
// clang-format off

View file

@ -18,7 +18,7 @@
#pragma once
#include "keymap_jp.h"
#include "keymap_japanese.h"
#include "quantum.h"
// clang-format off

View file

@ -19,9 +19,30 @@
#pragma once
#include "keymap_latvian.h"
#include "quantum.h"
// clang-format off
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 1),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -18,7 +18,7 @@
#pragma once
#include "keymap_portuguese_osx_iso.h"
#include "keymap_portuguese_mac_iso.h"
#include "quantum.h"
// clang-format off
@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -18,7 +18,7 @@
#pragma once
#include "keymap_german_ch.h"
#include "keymap_swiss_de.h"
#include "quantum.h"
// clang-format off
@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -18,7 +18,7 @@
#pragma once
#include "keymap_fr_ch.h"
#include "keymap_swiss_fr.h"
#include "quantum.h"
// clang-format off
@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -63,6 +63,26 @@ const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
};
const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,

View file

@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Sendstring lookup tables for UK layouts
// Sendstring lookup tables for US International layouts
#pragma once

View file

@ -43,10 +43,6 @@ void protocol_task(void) {
protocol_post_task();
}
#ifdef DEFERRED_EXEC_ENABLE
void deferred_exec_task(void);
#endif // DEFERRED_EXEC_ENABLE
/** \brief Main
*
* FIXME: Needs doc
@ -63,8 +59,15 @@ int main(void) {
while (true) {
protocol_task();
#ifdef QUANTUM_PAINTER_ENABLE
// Run Quantum Painter animations
void qp_internal_animation_tick(void);
qp_internal_animation_tick();
#endif
#ifdef DEFERRED_EXEC_ENABLE
// Run deferred executions
void deferred_exec_task(void);
deferred_exec_task();
#endif // DEFERRED_EXEC_ENABLE

View file

@ -72,15 +72,12 @@ inline matrix_row_t matrix_get_row(uint8_t row) {
#if (MATRIX_COLS <= 8)
# define print_matrix_header() print("\nr/c 01234567\n")
# define print_matrix_row(row) print_bin_reverse8(matrix_get_row(row))
# define matrix_bitpop(row) bitpop(matrix_get_row(row))
#elif (MATRIX_COLS <= 16)
# define print_matrix_header() print("\nr/c 0123456789ABCDEF\n")
# define print_matrix_row(row) print_bin_reverse16(matrix_get_row(row))
# define matrix_bitpop(row) bitpop16(matrix_get_row(row))
#elif (MATRIX_COLS <= 32)
# define print_matrix_header() print("\nr/c 0123456789ABCDEF0123456789ABCDEF\n")
# define print_matrix_row(row) print_bin_reverse32(matrix_get_row(row))
# define matrix_bitpop(row) bitpop32(matrix_get_row(row))
#endif
void matrix_print(void) {
@ -94,14 +91,6 @@ void matrix_print(void) {
}
}
uint8_t matrix_key_count(void) {
uint8_t count = 0;
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
count += matrix_bitpop(i);
}
return count;
}
#ifdef SPLIT_KEYBOARD
bool matrix_post_scan(void) {
bool changed = false;

View file

@ -16,6 +16,7 @@
*/
#include <stdint.h>
#include <string.h>
#include "keycode.h"
#include "host.h"
#include "timer.h"
@ -210,7 +211,7 @@ static uint8_t wheel_unit(void) {
void mousekey_task(void) {
// report cursor and scroll movement independently
report_mouse_t const tmpmr = mouse_report;
report_mouse_t tmpmr = mouse_report;
mouse_report.x = 0;
mouse_report.y = 0;
@ -252,8 +253,10 @@ void mousekey_task(void) {
}
}
if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send();
mouse_report = tmpmr;
if (has_mouse_report_changed(&mouse_report, &tmpmr) || should_mousekey_report_send(&mouse_report)) {
mousekey_send();
}
memcpy(&mouse_report, &tmpmr, sizeof(tmpmr));
}
void mousekey_on(uint8_t code) {
@ -357,11 +360,11 @@ uint16_t w_intervals[mkspd_COUNT] = {MK_W_INTERVAL_UNMOD, MK_W_INTERVAL_0
void mousekey_task(void) {
// report cursor and scroll movement independently
report_mouse_t const tmpmr = mouse_report;
mouse_report.x = 0;
mouse_report.y = 0;
mouse_report.v = 0;
mouse_report.h = 0;
report_mouse_t tmpmr = mouse_report;
mouse_report.x = 0;
mouse_report.y = 0;
mouse_report.v = 0;
mouse_report.h = 0;
if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > c_intervals[mk_speed]) {
mouse_report.x = tmpmr.x;
@ -372,8 +375,10 @@ void mousekey_task(void) {
mouse_report.h = tmpmr.h;
}
if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send();
mouse_report = tmpmr;
if (has_mouse_report_changed(&mouse_report, &tmpmr) || should_mousekey_report_send(&mouse_report)) {
mousekey_send();
}
memcpy(&mouse_report, &tmpmr, sizeof(tmpmr));
}
void adjust_speed(void) {
@ -523,3 +528,7 @@ static void mousekey_debug(void) {
report_mouse_t mousekey_get_report(void) {
return mouse_report;
}
bool should_mousekey_report_send(report_mouse_t *mouse_report) {
return mouse_report->x || mouse_report->y || mouse_report->v || mouse_report->h;
}

View file

@ -176,6 +176,7 @@ void mousekey_off(uint8_t code);
void mousekey_clear(void);
void mousekey_send(void);
report_mouse_t mousekey_get_report(void);
bool should_mousekey_report_send(report_mouse_t *mouse_report);
#ifdef __cplusplus
}

137
quantum/painter/qff.c Normal file
View file

@ -0,0 +1,137 @@
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
// Quantum Font File "QFF" File Format.
// See https://docs.qmk.fm/#/quantum_painter_qff for more information.
#include "qff.h"
#include "qp_draw.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QFF API
bool qff_read_font_descriptor(qp_stream_t *stream, uint8_t *line_height, bool *has_ascii_table, uint16_t *num_unicode_glyphs, uint8_t *bpp, bool *has_palette, painter_compression_t *compression_scheme, uint32_t *total_bytes) {
// Seek to the start
qp_stream_setpos(stream, 0);
// Read and validate the font descriptor
qff_font_descriptor_v1_t font_descriptor;
if (qp_stream_read(&font_descriptor, sizeof(qff_font_descriptor_v1_t), 1, stream) != 1) {
qp_dprintf("Failed to read font_descriptor, expected length was not %d\n", (int)sizeof(qff_font_descriptor_v1_t));
return false;
}
// Make sure this block is valid
if (!qgf_validate_block_header(&font_descriptor.header, QFF_FONT_DESCRIPTOR_TYPEID, (sizeof(qff_font_descriptor_v1_t) - sizeof(qgf_block_header_v1_t)))) {
return false;
}
// Make sure the magic and version are correct
if (font_descriptor.magic != QFF_MAGIC || font_descriptor.qff_version != 0x01) {
qp_dprintf("Failed to validate font_descriptor, expected magic 0x%06X was 0x%06X, expected version = 0x%02X was 0x%02X\n", (int)QFF_MAGIC, (int)font_descriptor.magic, (int)0x01, (int)font_descriptor.qff_version);
return false;
}
// Make sure the file length is valid
if (font_descriptor.neg_total_file_size != ~font_descriptor.total_file_size) {
qp_dprintf("Failed to validate font_descriptor, expected negated length 0x%08X was 0x%08X\n", (int)(~font_descriptor.total_file_size), (int)font_descriptor.neg_total_file_size);
return false;
}
// Copy out the required info
if (line_height) {
*line_height = font_descriptor.line_height;
}
if (has_ascii_table) {
*has_ascii_table = font_descriptor.has_ascii_table;
}
if (num_unicode_glyphs) {
*num_unicode_glyphs = font_descriptor.num_unicode_glyphs;
}
if (bpp || has_palette) {
if (!qgf_parse_format(font_descriptor.format, bpp, has_palette)) {
return false;
}
}
if (compression_scheme) {
*compression_scheme = font_descriptor.compression_scheme;
}
if (total_bytes) {
*total_bytes = font_descriptor.total_file_size;
}
return true;
}
static bool qff_validate_ascii_descriptor(qp_stream_t *stream) {
// Read the raw descriptor
qff_ascii_glyph_table_v1_t ascii_descriptor;
if (qp_stream_read(&ascii_descriptor, sizeof(qff_ascii_glyph_table_v1_t), 1, stream) != 1) {
qp_dprintf("Failed to read ascii_descriptor, expected length was not %d\n", (int)sizeof(qff_ascii_glyph_table_v1_t));
return false;
}
// Make sure this block is valid
if (!qgf_validate_block_header(&ascii_descriptor.header, QFF_ASCII_GLYPH_DESCRIPTOR_TYPEID, (sizeof(qff_ascii_glyph_table_v1_t) - sizeof(qgf_block_header_v1_t)))) {
return false;
}
return true;
}
static bool qff_validate_unicode_descriptor(qp_stream_t *stream, uint16_t num_unicode_glyphs) {
// Read the raw descriptor
qff_unicode_glyph_table_v1_t unicode_descriptor;
if (qp_stream_read(&unicode_descriptor, sizeof(qff_unicode_glyph_table_v1_t), 1, stream) != 1) {
qp_dprintf("Failed to read unicode_descriptor, expected length was not %d\n", (int)sizeof(qff_unicode_glyph_table_v1_t));
return false;
}
// Make sure this block is valid
if (!qgf_validate_block_header(&unicode_descriptor.header, QFF_UNICODE_GLYPH_DESCRIPTOR_TYPEID, num_unicode_glyphs * 6)) {
return false;
}
// Skip the necessary amount of data to get to the next block
qp_stream_seek(stream, num_unicode_glyphs * sizeof(qff_unicode_glyph_v1_t), SEEK_CUR);
return true;
}
bool qff_validate_stream(qp_stream_t *stream) {
bool has_ascii_table;
uint16_t num_unicode_glyphs;
if (!qff_read_font_descriptor(stream, NULL, &has_ascii_table, &num_unicode_glyphs, NULL, NULL, NULL, NULL)) {
return false;
}
if (has_ascii_table) {
if (!qff_validate_ascii_descriptor(stream)) {
return false;
}
}
if (num_unicode_glyphs > 0) {
if (!qff_validate_unicode_descriptor(stream, num_unicode_glyphs)) {
return false;
}
}
return true;
}
uint32_t qff_get_total_size(qp_stream_t *stream) {
// Get the original location
uint32_t oldpos = qp_stream_tell(stream);
// Read the font descriptor, grabbing the size
uint32_t total_size;
if (!qff_read_font_descriptor(stream, NULL, NULL, NULL, NULL, NULL, NULL, &total_size)) {
return false;
}
// Restore the original location
qp_stream_setpos(stream, oldpos);
return total_size;
}

88
quantum/painter/qff.h Normal file
View file

@ -0,0 +1,88 @@
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// Quantum Font File "QFF" File Format.
// See https://docs.qmk.fm/#/quantum_painter_qff for more information.
#include <stdint.h>
#include <stdbool.h>
#include "qp_stream.h"
#include "qp_internal.h"
#include "qgf.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QFF structures
/////////////////////////////////////////
// Font descriptor
#define QFF_FONT_DESCRIPTOR_TYPEID 0x00
typedef struct __attribute__((packed)) qff_font_descriptor_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 20 }
uint32_t magic : 24; // constant, equal to 0x464651 ("QFF")
uint8_t qff_version; // constant, equal to 0x01
uint32_t total_file_size; // total size of the entire file, starting at offset zero
uint32_t neg_total_file_size; // negated value of total_file_size, used for detecting parsing errors
uint8_t line_height; // glyph height in pixels
bool has_ascii_table; // whether the font has an ascii table of glyphs (0x20...0x7E)
uint16_t num_unicode_glyphs; // the number of glyphs in the unicode table -- no table specified if zero
qp_image_format_t format : 8; // Frame format, see qp.h.
uint8_t flags; // frame flags, see below.
uint8_t compression_scheme; // compression scheme, see below.
uint8_t transparency_index; // palette index used for transparent pixels (not yet implemented)
} qff_font_descriptor_v1_t;
_Static_assert(sizeof(qff_font_descriptor_v1_t) == (sizeof(qgf_block_header_v1_t) + 20), "qff_font_descriptor_v1_t must be 25 bytes in v1 of QFF");
#define QFF_MAGIC 0x464651
/////////////////////////////////////////
// ASCII glyph table descriptor
#define QFF_ASCII_GLYPH_DESCRIPTOR_TYPEID 0x01
#define QFF_GLYPH_WIDTH_BITS 6
#define QFF_GLYPH_WIDTH_MASK ((1 << QFF_GLYPH_WIDTH_BITS) - 1)
#define QFF_GLYPH_OFFSET_BITS 18
#define QFF_GLYPH_OFFSET_MASK (((1 << QFF_GLYPH_OFFSET_BITS) - 1) << QFF_GLYPH_WIDTH_BITS)
typedef struct __attribute__((packed)) qff_ascii_glyph_v1_t {
uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined
} qff_ascii_glyph_v1_t;
_Static_assert(sizeof(qff_ascii_glyph_v1_t) == 3, "qff_ascii_glyph_v1_t must be 3 bytes in v1 of QFF");
typedef struct __attribute__((packed)) qff_ascii_glyph_table_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = 285 }
qff_ascii_glyph_v1_t glyph[95]; // 95 glyphs, 0x20..0x7E
} qff_ascii_glyph_table_v1_t;
_Static_assert(sizeof(qff_ascii_glyph_table_v1_t) == (sizeof(qgf_block_header_v1_t) + (95 * sizeof(qff_ascii_glyph_v1_t))), "qff_ascii_glyph_table_v1_t must be 290 bytes in v1 of QFF");
/////////////////////////////////////////
// Unicode glyph table descriptor
#define QFF_UNICODE_GLYPH_DESCRIPTOR_TYPEID 0x02
typedef struct __attribute__((packed)) qff_unicode_glyph_v1_t {
uint32_t code_point : 24;
uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined
} qff_unicode_glyph_v1_t;
_Static_assert(sizeof(qff_unicode_glyph_v1_t) == 6, "qff_unicode_glyph_v1_t must be 6 bytes in v1 of QFF");
typedef struct __attribute__((packed)) qff_unicode_glyph_table_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = (N * 6) }
qff_unicode_glyph_v1_t glyph[0]; // Extent of '0' signifies that this struct is immediately followed by the glyph data
} qff_unicode_glyph_table_v1_t;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QFF API
bool qff_validate_stream(qp_stream_t *stream);
uint32_t qff_get_total_size(qp_stream_t *stream);
bool qff_read_font_descriptor(qp_stream_t *stream, uint8_t *line_height, bool *has_ascii_table, uint16_t *num_unicode_glyphs, uint8_t *bpp, bool *has_palette, painter_compression_t *compression_scheme, uint32_t *total_bytes);

292
quantum/painter/qgf.c Normal file
View file

@ -0,0 +1,292 @@
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
// Quantum Graphics File "QGF" File Format.
// See https://docs.qmk.fm/#/quantum_painter_qgf for more information.
#include "qgf.h"
#include "qp_draw.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QGF API
bool qgf_validate_block_header(qgf_block_header_v1_t *desc, uint8_t expected_typeid, int32_t expected_length) {
if (desc->type_id != expected_typeid || desc->neg_type_id != ((~expected_typeid) & 0xFF)) {
qp_dprintf("Failed to validate header, expected typeid 0x%02X, was 0x%02X, expected negated typeid 0x%02X, was 0x%02X\n", (int)expected_typeid, (int)desc->type_id, (int)((~desc->type_id) & 0xFF), (int)desc->neg_type_id);
return false;
}
if (expected_length >= 0 && desc->length != expected_length) {
qp_dprintf("Failed to validate header (typeid 0x%02X), expected length %d, was %d\n", (int)desc->type_id, (int)expected_length, (int)desc->length);
return false;
}
return true;
}
bool qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette) {
// clang-format off
static const struct QP_PACKED {
uint8_t bpp;
bool has_palette;
} formats[] = {
[GRAYSCALE_1BPP] = {.bpp = 1, .has_palette = false},
[GRAYSCALE_2BPP] = {.bpp = 2, .has_palette = false},
[GRAYSCALE_4BPP] = {.bpp = 4, .has_palette = false},
[GRAYSCALE_8BPP] = {.bpp = 8, .has_palette = false},
[PALETTE_1BPP] = {.bpp = 1, .has_palette = true},
[PALETTE_2BPP] = {.bpp = 2, .has_palette = true},
[PALETTE_4BPP] = {.bpp = 4, .has_palette = true},
[PALETTE_8BPP] = {.bpp = 8, .has_palette = true},
};
// clang-format on
// Copy out the required info
if (format > PALETTE_8BPP) {
qp_dprintf("Failed to parse frame_descriptor, invalid format 0x%02X\n", (int)format);
return false;
}
// Copy out the required info
if (bpp) {
*bpp = formats[format].bpp;
}
if (has_palette) {
*has_palette = formats[format].has_palette;
}
return true;
}
bool qgf_parse_frame_descriptor(qgf_frame_v1_t *frame_descriptor, uint8_t *bpp, bool *has_palette, bool *is_delta, painter_compression_t *compression_scheme, uint16_t *delay) {
// Decode the format
qgf_parse_format(frame_descriptor->format, bpp, has_palette);
// Copy out the required info
if (is_delta) {
*is_delta = (frame_descriptor->flags & QGF_FRAME_FLAG_DELTA) == QGF_FRAME_FLAG_DELTA;
}
if (compression_scheme) {
*compression_scheme = frame_descriptor->compression_scheme;
}
if (delay) {
*delay = frame_descriptor->delay;
}
return true;
}
bool qgf_read_graphics_descriptor(qp_stream_t *stream, uint16_t *image_width, uint16_t *image_height, uint16_t *frame_count, uint32_t *total_bytes) {
// Seek to the start
qp_stream_setpos(stream, 0);
// Read and validate the graphics descriptor
qgf_graphics_descriptor_v1_t graphics_descriptor;
if (qp_stream_read(&graphics_descriptor, sizeof(qgf_graphics_descriptor_v1_t), 1, stream) != 1) {
qp_dprintf("Failed to read graphics_descriptor, expected length was not %d\n", (int)sizeof(qgf_graphics_descriptor_v1_t));
return false;
}
// Make sure this block is valid
if (!qgf_validate_block_header(&graphics_descriptor.header, QGF_GRAPHICS_DESCRIPTOR_TYPEID, (sizeof(qgf_graphics_descriptor_v1_t) - sizeof(qgf_block_header_v1_t)))) {
return false;
}
// Make sure the magic and version are correct
if (graphics_descriptor.magic != QGF_MAGIC || graphics_descriptor.qgf_version != 0x01) {
qp_dprintf("Failed to validate graphics_descriptor, expected magic 0x%06X was 0x%06X, expected version = 0x%02X was 0x%02X\n", (int)QGF_MAGIC, (int)graphics_descriptor.magic, (int)0x01, (int)graphics_descriptor.qgf_version);
return false;
}
// Make sure the file length is valid
if (graphics_descriptor.neg_total_file_size != ~graphics_descriptor.total_file_size) {
qp_dprintf("Failed to validate graphics_descriptor, expected negated length 0x%08X was 0x%08X\n", (int)(~graphics_descriptor.total_file_size), (int)graphics_descriptor.neg_total_file_size);
return false;
}
// Copy out the required info
if (image_width) {
*image_width = graphics_descriptor.image_width;
}
if (image_height) {
*image_height = graphics_descriptor.image_height;
}
if (frame_count) {
*frame_count = graphics_descriptor.frame_count;
}
if (total_bytes) {
*total_bytes = graphics_descriptor.total_file_size;
}
return true;
}
static bool qgf_read_frame_offset(qp_stream_t *stream, uint16_t frame_number, uint32_t *frame_offset) {
uint16_t frame_count;
if (!qgf_read_graphics_descriptor(stream, NULL, NULL, &frame_count, NULL)) {
return false;
}
// Read the frame offsets descriptor
qgf_frame_offsets_v1_t frame_offsets;
if (qp_stream_read(&frame_offsets, sizeof(qgf_frame_offsets_v1_t), 1, stream) != 1) {
qp_dprintf("Failed to read frame_offsets, expected length was not %d\n", (int)sizeof(qgf_frame_offsets_v1_t));
return false;
}
// Make sure this block is valid
if (!qgf_validate_block_header(&frame_offsets.header, QGF_FRAME_OFFSET_DESCRIPTOR_TYPEID, (frame_count * sizeof(uint32_t)))) {
return false;
}
if (frame_number >= frame_count) {
qp_dprintf("Invalid frame number, was %d but only %d frames in image\n", (int)frame_number, (int)frame_count);
return false;
}
// Skip the necessary amount of data to get to the requested frame offset
qp_stream_seek(stream, frame_number * sizeof(uint32_t), SEEK_CUR);
// Read the frame offset
uint32_t offset = 0;
if (qp_stream_read(&offset, sizeof(uint32_t), 1, stream) != 1) {
qp_dprintf("Failed to read frame offset, expected length was not %d\n", (int)sizeof(uint32_t));
return false;
}
// Copy out the required info
if (frame_offset) {
*frame_offset = offset;
}
return true;
}
void qgf_seek_to_frame_descriptor(qp_stream_t *stream, uint16_t frame_number) {
// Read the offset
uint32_t offset = 0;
qgf_read_frame_offset(stream, frame_number, &offset);
// Move to the offset
qp_stream_setpos(stream, offset);
}
bool qgf_validate_frame_descriptor(qp_stream_t *stream, uint16_t frame_number, uint8_t *bpp, bool *has_palette, bool *is_delta) {
// Seek to the correct location
qgf_seek_to_frame_descriptor(stream, frame_number);
// Read the raw descriptor
qgf_frame_v1_t frame_descriptor;
if (qp_stream_read(&frame_descriptor, sizeof(qgf_frame_v1_t), 1, stream) != 1) {
qp_dprintf("Failed to read frame_descriptor, expected length was not %d\n", (int)sizeof(qgf_frame_v1_t));
return false;
}
// Make sure this block is valid
if (!qgf_validate_block_header(&frame_descriptor.header, QGF_FRAME_DESCRIPTOR_TYPEID, (sizeof(qgf_frame_v1_t) - sizeof(qgf_block_header_v1_t)))) {
return false;
}
return qgf_parse_frame_descriptor(&frame_descriptor, bpp, has_palette, is_delta, NULL, NULL);
}
bool qgf_validate_palette_descriptor(qp_stream_t *stream, uint16_t frame_number, uint8_t bpp) {
// Read the palette descriptor
qgf_palette_v1_t palette_descriptor;
if (qp_stream_read(&palette_descriptor, sizeof(qgf_palette_v1_t), 1, stream) != 1) {
qp_dprintf("Failed to read palette_descriptor, expected length was not %d\n", (int)sizeof(qgf_palette_v1_t));
return false;
}
// Make sure this block is valid
uint32_t expected_length = (1 << bpp) * 3 * sizeof(uint8_t);
if (!qgf_validate_block_header(&palette_descriptor.header, QGF_FRAME_PALETTE_DESCRIPTOR_TYPEID, expected_length)) {
return false;
}
// Move forward in the stream to the next block
qp_stream_seek(stream, expected_length, SEEK_CUR);
return true;
}
bool qgf_validate_delta_descriptor(qp_stream_t *stream, uint16_t frame_number) {
// Read the delta descriptor
qgf_delta_v1_t delta_descriptor;
if (qp_stream_read(&delta_descriptor, sizeof(qgf_delta_v1_t), 1, stream) != 1) {
qp_dprintf("Failed to read delta_descriptor, expected length was not %d\n", (int)sizeof(qgf_delta_v1_t));
return false;
}
// Make sure this block is valid
if (!qgf_validate_block_header(&delta_descriptor.header, QGF_FRAME_DELTA_DESCRIPTOR_TYPEID, (sizeof(qgf_delta_v1_t) - sizeof(qgf_block_header_v1_t)))) {
return false;
}
return true;
}
bool qgf_validate_frame_data_descriptor(qp_stream_t *stream, uint16_t frame_number) {
// Read and validate the data block
qgf_data_v1_t data_descriptor;
if (qp_stream_read(&data_descriptor, sizeof(qgf_data_v1_t), 1, stream) != 1) {
qp_dprintf("Failed to read data_descriptor, expected length was not %d\n", (int)sizeof(qgf_data_v1_t));
return false;
}
if (!qgf_validate_block_header(&data_descriptor.header, QGF_FRAME_DATA_DESCRIPTOR_TYPEID, -1)) {
return false;
}
return true;
}
bool qgf_validate_stream(qp_stream_t *stream) {
uint16_t frame_count;
if (!qgf_read_graphics_descriptor(stream, NULL, NULL, &frame_count, NULL)) {
return false;
}
// Read and validate all the frames (automatically validates the frame offset descriptor in the process)
for (uint16_t i = 0; i < frame_count; ++i) {
// Validate the frame descriptor block
uint8_t bpp;
bool has_palette;
bool has_delta;
if (!qgf_validate_frame_descriptor(stream, i, &bpp, &has_palette, &has_delta)) {
return false;
}
// If we've got a palette block, check it
if (has_palette && !qgf_validate_palette_descriptor(stream, i, bpp)) {
return false;
}
// If we've got a delta block, check it
if (has_delta && !qgf_validate_delta_descriptor(stream, i)) {
return false;
}
// Check the data block
if (!qgf_validate_frame_data_descriptor(stream, i)) {
return false;
}
}
return true;
}
// Work out the total size of an image definition, assuming we can read far enough into the file
uint32_t qgf_get_total_size(qp_stream_t *stream) {
// Get the original location
uint32_t oldpos = qp_stream_tell(stream);
// Read the graphics descriptor, grabbing the size
uint32_t total_size;
if (!qgf_read_graphics_descriptor(stream, NULL, NULL, NULL, &total_size)) {
return false;
}
// Restore the original location
qp_stream_setpos(stream, oldpos);
return total_size;
}

136
quantum/painter/qgf.h Normal file
View file

@ -0,0 +1,136 @@
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// Quantum Graphics File "QGF" File Format.
// See https://docs.qmk.fm/#/quantum_painter_qgf for more information.
#include <stdint.h>
#include <stdbool.h>
#include "qp_stream.h"
#include "qp_internal.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QGF structures
/////////////////////////////////////////
// Common block header
typedef struct QP_PACKED qgf_block_header_v1_t {
uint8_t type_id; // See each respective block type below.
uint8_t neg_type_id; // Negated type ID, used for detecting parsing errors.
uint32_t length : 24; // 24-bit blob length, allowing for block sizes of a maximum of 16MB.
} qgf_block_header_v1_t;
_Static_assert(sizeof(qgf_block_header_v1_t) == 5, "qgf_block_header_v1_t must be 5 bytes in v1 of QGF");
/////////////////////////////////////////
// Graphics descriptor
#define QGF_GRAPHICS_DESCRIPTOR_TYPEID 0x00
typedef struct QP_PACKED qgf_graphics_descriptor_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 18 }
uint32_t magic : 24; // constant, equal to 0x464751 ("QGF")
uint8_t qgf_version; // constant, equal to 0x01
uint32_t total_file_size; // total size of the entire file, starting at offset zero
uint32_t neg_total_file_size; // negated value of total_file_size
uint16_t image_width; // in pixels
uint16_t image_height; // in pixels
uint16_t frame_count; // minimum of 1
} qgf_graphics_descriptor_v1_t;
_Static_assert(sizeof(qgf_graphics_descriptor_v1_t) == (sizeof(qgf_block_header_v1_t) + 18), "qgf_graphics_descriptor_v1_t must be 23 bytes in v1 of QGF");
#define QGF_MAGIC 0x464751
/////////////////////////////////////////
// Frame offset descriptor
#define QGF_FRAME_OFFSET_DESCRIPTOR_TYPEID 0x01
typedef struct QP_PACKED qgf_frame_offsets_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = (N * sizeof(uint32_t)) }
uint32_t offset[0]; // '0' signifies that this struct is immediately followed by the frame offsets
} qgf_frame_offsets_v1_t;
_Static_assert(sizeof(qgf_frame_offsets_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_frame_offsets_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
/////////////////////////////////////////
// Frame descriptor
#define QGF_FRAME_DESCRIPTOR_TYPEID 0x02
typedef struct QP_PACKED qgf_frame_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = 6 }
qp_image_format_t format : 8; // Frame format, see qp.h.
uint8_t flags; // Frame flags, see below.
painter_compression_t compression_scheme : 8; // Compression scheme, see qp.h.
uint8_t transparency_index; // palette index used for transparent pixels (not yet implemented)
uint16_t delay; // frame delay time for animations (in units of milliseconds)
} qgf_frame_v1_t;
_Static_assert(sizeof(qgf_frame_v1_t) == (sizeof(qgf_block_header_v1_t) + 6), "qgf_frame_v1_t must be 11 bytes in v1 of QGF");
#define QGF_FRAME_FLAG_DELTA 0x02
#define QGF_FRAME_FLAG_TRANSPARENT 0x01
/////////////////////////////////////////
// Frame palette descriptor
#define QGF_FRAME_PALETTE_DESCRIPTOR_TYPEID 0x03
typedef struct QP_PACKED qgf_palette_entry_v1_t {
uint8_t h; // hue component: `[0,360)` degrees is mapped to `[0,255]` uint8_t.
uint8_t s; // saturation component: `[0,1]` is mapped to `[0,255]` uint8_t.
uint8_t v; // value component: `[0,1]` is mapped to `[0,255]` uint8_t.
} qgf_palette_entry_v1_t;
_Static_assert(sizeof(qgf_palette_entry_v1_t) == 3, "Palette entry is not 3 bytes in size");
typedef struct QP_PACKED qgf_palette_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x03, .neg_type_id = (~0x03), .length = (N * 3 * sizeof(uint8_t)) }
qgf_palette_entry_v1_t hsv[0]; // N * hsv, where N is the number of palette entries depending on the frame format in the descriptor
} qgf_palette_v1_t;
_Static_assert(sizeof(qgf_palette_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_palette_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
/////////////////////////////////////////
// Frame delta descriptor
#define QGF_FRAME_DELTA_DESCRIPTOR_TYPEID 0x04
typedef struct QP_PACKED qgf_delta_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x04, .neg_type_id = (~0x04), .length = 8 }
uint16_t left; // The left pixel location to draw the delta image
uint16_t top; // The top pixel location to draw the delta image
uint16_t right; // The right pixel location to to draw the delta image
uint16_t bottom; // The bottom pixel location to to draw the delta image
} qgf_delta_v1_t;
_Static_assert(sizeof(qgf_delta_v1_t) == (sizeof(qgf_block_header_v1_t) + 8), "qgf_delta_v1_t must be 13 bytes in v1 of QGF");
/////////////////////////////////////////
// Frame data descriptor
#define QGF_FRAME_DATA_DESCRIPTOR_TYPEID 0x05
typedef struct QP_PACKED qgf_data_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x05, .neg_type_id = (~0x05), .length = N }
uint8_t data[0]; // 0 signifies that this struct is immediately followed by the length of data specified in the header
} qgf_data_v1_t;
_Static_assert(sizeof(qgf_data_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_data_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QGF API
uint32_t qgf_get_total_size(qp_stream_t *stream);
bool qgf_validate_stream(qp_stream_t *stream);
bool qgf_validate_block_header(qgf_block_header_v1_t *desc, uint8_t expected_typeid, int32_t expected_length);
bool qgf_read_graphics_descriptor(qp_stream_t *stream, uint16_t *image_width, uint16_t *image_height, uint16_t *frame_count, uint32_t *total_bytes);
bool qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette);
void qgf_seek_to_frame_descriptor(qp_stream_t *stream, uint16_t frame_number);
bool qgf_parse_frame_descriptor(qgf_frame_v1_t *frame_descriptor, uint8_t *bpp, bool *has_palette, bool *is_delta, painter_compression_t *compression_scheme, uint16_t *delay);

228
quantum/painter/qp.c Normal file
View file

@ -0,0 +1,228 @@
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#include <quantum.h>
#include <utf8.h>
#include "qp_internal.h"
#include "qp_comms.h"
#include "qp_draw.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal driver validation
static bool validate_driver_vtable(struct painter_driver_t *driver) {
return (driver->driver_vtable && driver->driver_vtable->init && driver->driver_vtable->power && driver->driver_vtable->clear && driver->driver_vtable->viewport && driver->driver_vtable->pixdata && driver->driver_vtable->palette_convert && driver->driver_vtable->append_pixels) ? true : false;
}
static bool validate_comms_vtable(struct painter_driver_t *driver) {
return (driver->comms_vtable && driver->comms_vtable->comms_init && driver->comms_vtable->comms_start && driver->comms_vtable->comms_stop && driver->comms_vtable->comms_send) ? true : false;
}
static bool validate_driver_integrity(struct painter_driver_t *driver) {
return validate_driver_vtable(driver) && validate_comms_vtable(driver);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter External API: qp_init
bool qp_init(painter_device_t device, painter_rotation_t rotation) {
qp_dprintf("qp_init: entry\n");
struct painter_driver_t *driver = (struct painter_driver_t *)device;
driver->validate_ok = false;
if (!validate_driver_integrity(driver)) {
qp_dprintf("Failed to validate driver integrity in qp_init\n");
return false;
}
driver->validate_ok = true;
if (!qp_comms_init(device)) {
driver->validate_ok = false;
qp_dprintf("qp_init: fail (could not init comms)\n");
return false;
}
if (!qp_comms_start(device)) {
qp_dprintf("qp_init: fail (could not start comms)\n");
return false;
}
// Set the rotation before init
driver->rotation = rotation;
// Invoke init
bool ret = driver->driver_vtable->init(device, rotation);
qp_comms_stop(device);
qp_dprintf("qp_init: %s\n", ret ? "ok" : "fail");
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter External API: qp_power
bool qp_power(painter_device_t device, bool power_on) {
qp_dprintf("qp_power: entry\n");
struct painter_driver_t *driver = (struct painter_driver_t *)device;
if (!driver->validate_ok) {
qp_dprintf("qp_power: fail (validation_ok == false)\n");
return false;
}
if (!qp_comms_start(device)) {
qp_dprintf("qp_power: fail (could not start comms)\n");
return false;
}
bool ret = driver->driver_vtable->power(device, power_on);
qp_comms_stop(device);
qp_dprintf("qp_power: %s\n", ret ? "ok" : "fail");
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter External API: qp_clear
bool qp_clear(painter_device_t device) {
qp_dprintf("qp_clear: entry\n");
struct painter_driver_t *driver = (struct painter_driver_t *)device;
if (!driver->validate_ok) {
qp_dprintf("qp_clear: fail (validation_ok == false)\n");
return false;
}
if (!qp_comms_start(device)) {
qp_dprintf("qp_clear: fail (could not start comms)\n");
return false;
}
bool ret = driver->driver_vtable->clear(device);
qp_comms_stop(device);
qp_dprintf("qp_clear: %s\n", ret ? "ok" : "fail");
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter External API: qp_flush
bool qp_flush(painter_device_t device) {
qp_dprintf("qp_flush: entry\n");
struct painter_driver_t *driver = (struct painter_driver_t *)device;
if (!driver->validate_ok) {
qp_dprintf("qp_flush: fail (validation_ok == false)\n");
return false;
}
if (!qp_comms_start(device)) {
qp_dprintf("qp_flush: fail (could not start comms)\n");
return false;
}
bool ret = driver->driver_vtable->flush(device);
qp_comms_stop(device);
qp_dprintf("qp_flush: %s\n", ret ? "ok" : "fail");
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter External API: qp_get_geometry
void qp_get_geometry(painter_device_t device, uint16_t *width, uint16_t *height, painter_rotation_t *rotation, uint16_t *offset_x, uint16_t *offset_y) {
qp_dprintf("qp_geometry: entry\n");
struct painter_driver_t *driver = (struct painter_driver_t *)device;
switch (driver->rotation) {
default:
case QP_ROTATION_0:
case QP_ROTATION_180:
if (width) {
*width = driver->panel_width;
}
if (height) {
*height = driver->panel_height;
}
break;
case QP_ROTATION_90:
case QP_ROTATION_270:
if (width) {
*width = driver->panel_height;
}
if (height) {
*height = driver->panel_width;
}
break;
}
if (rotation) {
*rotation = driver->rotation;
}
if (offset_x) {
*offset_x = driver->offset_x;
}
if (offset_y) {
*offset_y = driver->offset_y;
}
qp_dprintf("qp_geometry: ok\n");
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter External API: qp_set_viewport_offsets
void qp_set_viewport_offsets(painter_device_t device, uint16_t offset_x, uint16_t offset_y) {
qp_dprintf("qp_set_viewport_offsets: entry\n");
struct painter_driver_t *driver = (struct painter_driver_t *)device;
driver->offset_x = offset_x;
driver->offset_y = offset_y;
qp_dprintf("qp_set_viewport_offsets: ok\n");
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter External API: qp_viewport
bool qp_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {
qp_dprintf("qp_viewport: entry\n");
struct painter_driver_t *driver = (struct painter_driver_t *)device;
if (!driver->validate_ok) {
qp_dprintf("qp_viewport: fail (validation_ok == false)\n");
return false;
}
if (!qp_comms_start(device)) {
qp_dprintf("qp_viewport: fail (could not start comms)\n");
return false;
}
// Set the viewport
bool ret = driver->driver_vtable->viewport(device, left, top, right, bottom);
qp_dprintf("qp_viewport: %s\n", ret ? "ok" : "fail");
qp_comms_stop(device);
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter External API: qp_pixdata
bool qp_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {
qp_dprintf("qp_pixdata: entry\n");
struct painter_driver_t *driver = (struct painter_driver_t *)device;
if (!driver->validate_ok) {
qp_dprintf("qp_pixdata: fail (validation_ok == false)\n");
return false;
}
if (!qp_comms_start(device)) {
qp_dprintf("qp_pixdata: fail (could not start comms)\n");
return false;
}
bool ret = driver->driver_vtable->pixdata(device, pixel_data, native_pixel_count);
qp_dprintf("qp_pixdata: %s\n", ret ? "ok" : "fail");
qp_comms_stop(device);
return ret;
}

453
quantum/painter/qp.h Normal file
View file

@ -0,0 +1,453 @@
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "deferred_exec.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter global configurables (add to your keyboard's config.h)
#ifndef QUANTUM_PAINTER_NUM_IMAGES
/**
* @def This controls the maximum number of images that Quantum Painter can load at any one time. Images can be loaded
* using \ref qp_load_image_mem, and can be unloaded by calling \ref qp_close_image. Increasing this number in
* order to load more images increases the amount of RAM required. Image data is not held in RAM, just metadata.
*/
# define QUANTUM_PAINTER_NUM_IMAGES 8
#endif // QUANTUM_PAINTER_NUM_IMAGES
#ifndef QUANTUM_PAINTER_NUM_FONTS
/**
* @def This controls the maximum number of fonts that Quantum Painter can load. Fonts can be loaded using
* \ref qp_load_font_mem, and can be unloaded by calling \ref qp_close_font. Increasing this number in order to
* load more fonts increases the amount of RAM required. Font data is not held in RAM, unless
* \ref QUANTUM_PAINTER_LOAD_FONTS_TO_RAM is set to TRUE.
*/
# define QUANTUM_PAINTER_NUM_FONTS 4
#endif // QUANTUM_PAINTER_NUM_FONTS
#ifndef QUANTUM_PAINTER_LOAD_FONTS_TO_RAM
/**
* @def This controls whether or not fonts should be cached in RAM. Under normal circumstances, fonts can have quite
* random access patterns, and due to timing of flash memory or external storage, it may be a significant speedup
* moving the font into RAM before use. Defaults to "off", but if it's enabled it will fallback to reading from the
* original location if corresponding RAM could not be allocated (such as being too large).
*/
# define QUANTUM_PAINTER_LOAD_FONTS_TO_RAM FALSE
#endif
#ifndef QUANTUM_PAINTER_CONCURRENT_ANIMATIONS
/**
* @def This controls the maximum number of animations that Quantum Painter can play simultaneously. Increasing this
* number in order to play more animations at the same time increases the amount of RAM required.
*/
# define QUANTUM_PAINTER_CONCURRENT_ANIMATIONS 4
#endif // QUANTUM_PAINTER_CONCURRENT_ANIMATIONS
#ifndef QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE
/**
* @def This controls the maximum size of the pixel data buffer used for single blocks of transmission. Larger buffers
* means more data is processed at one time, with less frequent transmissions, at the cost of RAM.
*/
# define QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE 32
#endif
#ifndef QUANTUM_PAINTER_SUPPORTS_256_PALETTE
/**
* @def This controls whether 256-color palettes are supported. This has relatively hefty requirements on RAM -- at
* least 1kB extra is required just to store the palette information, with more required for other metadata.
*/
# define QUANTUM_PAINTER_SUPPORTS_256_PALETTE FALSE
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter types
/**
* @typedef A handle to a Quantum Painter device, such as an LCD or OLED. Most Quantum Painter APIs require this
* argument in order to perform operations on the display.
*/
typedef const void *painter_device_t;
/**
* @typedef The desired rotation of a panel. Used as a parameter to \ref qp_init, and can be queried by
* \ref qp_get_geometry.
*/
typedef enum { QP_ROTATION_0, QP_ROTATION_90, QP_ROTATION_180, QP_ROTATION_270 } painter_rotation_t;
/**
* @typedef A descriptor for a Quantum Painter image.
*/
typedef struct painter_image_desc_t {
uint16_t width; ///< Image width
uint16_t height; ///< Image height
uint16_t frame_count; ///< Number of frames in this image
} painter_image_desc_t;
/**
* @typedef A handle to a Quantum Painter image.
*/
typedef const painter_image_desc_t *painter_image_handle_t;
/**
* @typedef A descriptor for a Quantum Painter font.
*/
typedef struct painter_font_desc_t {
uint8_t line_height; ///< The number of pixels in height for each line
} painter_font_desc_t;
/**
* @typedef A handle to a Quantum Painter font.
*/
typedef const painter_font_desc_t *painter_font_handle_t;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter External API
/**
* Initialize a device and set its rotation.
*
* @param device[in] the handle of the device to initialize
* @param rotation[in] the rotation to use
* @return true if initialization succeeded
* @return false if initialization failed
*/
bool qp_init(painter_device_t device, painter_rotation_t rotation);
/**
* Controls whether a display is on or off.
*
* @note If backlighting is used to control brightness (such as for an LCD), it will need to be handled external to
* Quantum Painter.
*
* @param device[in] the handle of the device to control
* @param power_on[in] whether or not the device should be on
* @return true if controlling the power state succeeded
* @return false if controlling the power state failed
*/
bool qp_power(painter_device_t device, bool power_on);
/**
* Clears a device's screen.
*
* @param device[in] the handle of the device to control
* @return true if clearing the screen succeeded
* @return false if clearing the screen failed
*/
bool qp_clear(painter_device_t device);
/**
* Transmits any outstanding data to the screen in order to persist all changes to the display.
*
* @note Drivers without internal framebuffers will likely ignore this API.
*
* @param device[in] the handle of the device to control
* @return true if flushing changes to the screen succeeded
* @return false if flushing changes to the screen failed
*/
bool qp_flush(painter_device_t device);
/**
* Retrieves the size, rotation, and offsets for the display.
*
* @note Any arguments of NULL will be ignored.
*
* @param device[in] the handle of the device to control
* @param width[out] the device's width
* @param height[out] the device's height
* @param rotation[out] the device's rotation
* @param offset_x[out] the device's x-offset applied while drawing
* @param offset_y[out] the device's y-offset applied while drawing
*/
void qp_get_geometry(painter_device_t device, uint16_t *width, uint16_t *height, painter_rotation_t *rotation, uint16_t *offset_x, uint16_t *offset_y);
/**
* Allows repositioning of the viewport if the panel geometry offsets are non-zero.
*
* @param device[in] the handle of the device to control
* @param offset_x[in] the device's x-offset applied while drawing
* @param offset_y[in] the device's y-offset applied while drawing
*/
void qp_set_viewport_offsets(painter_device_t device, uint16_t offset_x, uint16_t offset_y);
/**
* Sets a pixel to the specified color.
*
* @param device[in] the handle of the device to control
* @param x[in] the x-position to draw onto the device
* @param y[in] the y-position to draw onto the device
* @param hue[in] the hue to use, with 0-360 mapped to 0-255
* @param sat[in] the saturation to use, with 0-100% mapped to 0-255
* @param val[in] the value to use, with 0-100% mapped to 0-255
* @return true if setting the pixel succeeded
* @return false if setting the pixel failed
*/
bool qp_setpixel(painter_device_t device, uint16_t x, uint16_t y, uint8_t hue, uint8_t sat, uint8_t val);
/**
* Draws a line using the specified color.
*
* @param device[in] the handle of the device to control
* @param x0[in] the device's x-position to start
* @param y0[in] the device's y-position to start
* @param x1[in] the device's x-position to finish
* @param y1[in] the device's y-position to finish
* @param hue[in] the hue to use, with 0-360 mapped to 0-255
* @param sat[in] the saturation to use, with 0-100% mapped to 0-255
* @param val[in] the value to use, with 0-100% mapped to 0-255
* @return true if drawing the line succeeded
* @return false if drawing the line failed
*/
bool qp_line(painter_device_t device, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t hue, uint8_t sat, uint8_t val);
/**
* Draws a rectangle using the specified color, optionally filled.
*
* @param device[in] the handle of the device to control
* @param left[in] the device's x-position to start
* @param top[in] the device's y-position to start
* @param right[in] the device's x-position to finish
* @param bottom[in] the device's y-position to finish
* @param hue[in] the hue to use, with 0-360 mapped to 0-255
* @param sat[in] the saturation to use, with 0-100% mapped to 0-255
* @param val[in] the value to use, with 0-100% mapped to 0-255
* @param filled[in] whether the rectangle should be filled
* @return true if drawing the rectangle succeeded
* @return false if drawing the rectangle failed
*/
bool qp_rect(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom, uint8_t hue, uint8_t sat, uint8_t val, bool filled);
/**
* Draws a circle using the specified color, optionally filled.
*
* @param device[in] the handle of the device to control
* @param x[in] the x-position of the centre of the circle to draw onto the device
* @param y[in] the y-position of the centre of the circle to draw onto the device
* @param radius[in] the radius of the circle to draw
* @param hue[in] the hue to use, with 0-360 mapped to 0-255
* @param sat[in] the saturation to use, with 0-100% mapped to 0-255
* @param val[in] the value to use, with 0-100% mapped to 0-255
* @param filled[in] whether the circle should be filled
* @return true if drawing the circle succeeded
* @return false if drawing the circle failed
*/
bool qp_circle(painter_device_t device, uint16_t x, uint16_t y, uint16_t radius, uint8_t hue, uint8_t sat, uint8_t val, bool filled);
/**
* Draws a ellipse using the specified color, optionally filled.
*
* @param device[in] the handle of the device to control
* @param x[in] the x-position of the centre of the ellipse to draw onto the device
* @param y[in] the y-position of the centre of the ellipse to draw onto the device
* @param sizex[in] the horizontal size of the ellipse
* @param sizey[in] the vertical size of the ellipse
* @param hue[in] the hue to use, with 0-360 mapped to 0-255
* @param sat[in] the saturation to use, with 0-100% mapped to 0-255
* @param val[in] the value to use, with 0-100% mapped to 0-255
* @param filled[in] whether the ellipse should be filled
* @return true if drawing the ellipse succeeded
* @return false if drawing the ellipse failed
*/
bool qp_ellipse(painter_device_t device, uint16_t x, uint16_t y, uint16_t sizex, uint16_t sizey, uint8_t hue, uint8_t sat, uint8_t val, bool filled);
/**
* Sets up the location on the display to stream raw pixel data to the display, using \ref qp_pixdata.
*
* @note This is for advanced uses only, and should not be required for normal Quantum Painter functionality.
*
* @param device[in] the handle of the device to control
* @param left[in] the device's x-position to start
* @param top[in] the device's y-position to start
* @param right[in] the device's x-position to finish
* @param bottom[in] the device's y-position to finish
* @return true if setting the viewport succeeded
* @return false if setting the viewport failed
*/
bool qp_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom);
/**
* Streams raw pixel data (in the native panel format) to the area previously set by \ref qp_viewport.
*
* @note This is for advanced uses only, and should not be required for normal Quantum Painter functionality.
*
* @param device[in] the handle of the device to control
* @param pixel_data[in] pointer to buffer data
* @param native_pixel_count[in] the number of pixels to transmit
* @return true if streaming of data succeeded
* @return false if streaming of data failed
*/
bool qp_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count);
/**
* Loads an image into memory.
*
* @note Images can be unloaded by calling \ref qp_close_image.
*
* @param buffer[in] the image data to load
* @return an image handle usable with \ref qp_drawimage, \ref qp_drawimage_recolor, \ref qp_animate, and
* \ref qp_animate_recolor.
* @return NULL if loading the image failed
*/
painter_image_handle_t qp_load_image_mem(const void *buffer);
/**
* Closes an image handle when no longer in use.
*
* @param image[in] the handle of the image to unload
* @return true if unloading the image succeeded
* @return false if unloading the image failed
*/
bool qp_close_image(painter_image_handle_t image);
/**
* Draws an image to the display.
*
* @param device[in] the handle of the device to control
* @param x[in] the x-position where the image should be drawn onto the device
* @param y[in] the y-position where the image should be drawn onto the device
* @param image[in] the handle of the image to draw
* @return true if drawing the image succeeded
* @return false if drawing the image failed
*/
bool qp_drawimage(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image);
/**
* Draws an image to the display, recoloring monochrome images to the desired foreground/background.
*
* @param device[in] the handle of the device to control
* @param x[in] the x-position where the image should be drawn onto the device
* @param y[in] the y-position where the image should be drawn onto the device
* @param image[in] the handle of the image to draw
* @param hue_fg[in] the foreground hue to use, with 0-360 mapped to 0-255
* @param sat_fg[in] the foreground saturation to use, with 0-100% mapped to 0-255
* @param val_fg[in] the foreground value to use, with 0-100% mapped to 0-255
* @param hue_bg[in] the background hue to use, with 0-360 mapped to 0-255
* @param sat_bg[in] the background saturation to use, with 0-100% mapped to 0-255
* @param val_bg[in] the background value to use, with 0-100% mapped to 0-255
* @return true if drawing the image succeeded
* @return false if drawing the image failed
*/
bool qp_drawimage_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg);
/**
* Draws an animation to the display.
*
* @param device[in] the handle of the device to control
* @param x[in] the x-position where the image should be drawn onto the device
* @param y[in] the y-position where the image should be drawn onto the device
* @param image[in] the handle of the image to draw
* @return the \ref deferred_token to use with \ref qp_stop_animation in order to stop animating
* @return INVALID_DEFERRED_TOKEN if animating the image failed
*/
deferred_token qp_animate(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image);
/**
* Draws an animation to the display, recoloring monochrome images to the desired foreground/background.
*
* @param device[in] the handle of the device to control
* @param x[in] the x-position where the image should be drawn onto the device
* @param y[in] the y-position where the image should be drawn onto the device
* @param image[in] the handle of the image to draw
* @param hue_fg[in] the foreground hue to use, with 0-360 mapped to 0-255
* @param sat_fg[in] the foreground saturation to use, with 0-100% mapped to 0-255
* @param val_fg[in] the foreground value to use, with 0-100% mapped to 0-255
* @param hue_bg[in] the background hue to use, with 0-360 mapped to 0-255
* @param sat_bg[in] the background saturation to use, with 0-100% mapped to 0-255
* @param val_bg[in] the background value to use, with 0-100% mapped to 0-255
* @return the \ref deferred_token to use with \ref qp_stop_animation in order to stop animating
* @return INVALID_DEFERRED_TOKEN if animating the image failed
*/
deferred_token qp_animate_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg);
/**
* Cancels a running animation.
*
* @param anim_token[in] the animation token returned by \ref qp_animate, or \ref qp_animate_recolor.
*/
void qp_stop_animation(deferred_token anim_token);
/**
* Loads a font into memory.
*
* @note Fonts can be unloaded by calling \ref qp_close_font.
*
* @param buffer[in] the font data to load
* @return an image handle usable with \ref qp_textwidth, \ref qp_drawtext, and \ref qp_drawtext_recolor.
* @return NULL if loading the font failed
*/
painter_font_handle_t qp_load_font_mem(const void *buffer);
/**
* Closes a font handle when no longer in use.
*
* @param font[in] the handle of the font to unload
* @return true if unloading the font succeeded
* @return false if unloading the font failed
*/
bool qp_close_font(painter_font_handle_t font);
/**
* Measures the width (in pixels) of the supplied string, given the specified font.
*
* @param font[in] the handle of the font
* @param str[in] the string to measure
* @return the width (in pixels) needed to draw the specified string
*/
int16_t qp_textwidth(painter_font_handle_t font, const char *str);
/**
* Draws text to the display.
*
* @param device[in] the handle of the device to control
* @param x[in] the x-position where the text should be drawn onto the device
* @param y[in] the y-position where the text should be drawn onto the device
* @param font[in] the handle of the font
* @param str[in] the string to draw
* @return the width (in pixels) used when drawing the specified string
*/
int16_t qp_drawtext(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str);
/**
* Draws text to the display, recoloring monochrome fonts to the desired foreground/background.
*
* @param device[in] the handle of the device to control
* @param x[in] the x-position where the text should be drawn onto the device
* @param y[in] the y-position where the text should be drawn onto the device
* @param font[in] the handle of the font
* @param str[in] the string to draw
* @param hue_fg[in] the foreground hue to use, with 0-360 mapped to 0-255
* @param sat_fg[in] the foreground saturation to use, with 0-100% mapped to 0-255
* @param val_fg[in] the foreground value to use, with 0-100% mapped to 0-255
* @param hue_bg[in] the background hue to use, with 0-360 mapped to 0-255
* @param sat_bg[in] the background saturation to use, with 0-100% mapped to 0-255
* @param val_bg[in] the background value to use, with 0-100% mapped to 0-255
* @return the width (in pixels) used when drawing the specified string
*/
int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter Drivers
#ifdef QUANTUM_PAINTER_ILI9163_ENABLE
# include "qp_ili9163.h"
#endif // QUANTUM_PAINTER_ILI9163_ENABLE
#ifdef QUANTUM_PAINTER_ILI9341_ENABLE
# include "qp_ili9341.h"
#endif // QUANTUM_PAINTER_ILI9341_ENABLE
#ifdef QUANTUM_PAINTER_ST7789_ENABLE
# include "qp_st7789.h"
#endif // QUANTUM_PAINTER_ST7789_ENABLE
#ifdef QUANTUM_PAINTER_GC9A01_ENABLE
# include "qp_gc9a01.h"
#endif // QUANTUM_PAINTER_GC9A01_ENABLE
#ifdef QUANTUM_PAINTER_SSD1351_ENABLE
# include "qp_ssd1351.h"
#endif // QUANTUM_PAINTER_SSD1351_ENABLE

Some files were not shown because too many files have changed in this diff Show more