1
0
Fork 0
forked from forks/qmk_firmware

Callum style layout improvements and my layout changes (#16174)

This commit is contained in:
Dalius Dobravolskas 2022-10-25 09:23:35 +03:00 committed by GitHub
parent 846e9d4c53
commit f100de88e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 769 additions and 393 deletions

View file

@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#define UNICODE_SELECTED_MODES UC_MAC, UC_LNX
#define UNICODE_SELECTED_MODES UC_LNX
#define MOUSEKEY_INTERVAL 12
#define MOUSEKEY_MAX_SPEED 6
@ -30,3 +30,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MOUSEKEY_WHEEL_INTERVAL 50
// The default is 40
#define MOUSEKEY_WHEEL_TIME_TO_MAX 100
#define FLOW_COUNT 6
#define FLOW_LAYERS_COUNT 5

View file

@ -0,0 +1,336 @@
/* Copyright 2022 @daliusd
*
* 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 "flow.h"
extern const uint16_t flow_config[FLOW_COUNT][2];
extern const uint16_t flow_layers_config[FLOW_LAYERS_COUNT][2];
// Represents the states a flow key can be in
typedef enum {
flow_up_unqueued,
flow_up_queued,
flow_up_queued_used,
flow_down_unused,
flow_down_used,
} flow_state_t;
#ifdef FLOW_ONESHOT_TERM
const int g_flow_oneshot_term = FLOW_ONESHOT_TERM;
#else
const int g_flow_oneshot_term = 500;
#endif
#ifdef FLOW_ONESHOT_WAIT_TERM
const int g_flow_oneshot_wait_term = FLOW_ONESHOT_WAIT_TERM;
#else
const int g_flow_oneshot_wait_term = 500;
#endif
flow_state_t flow_state[FLOW_COUNT] = { [0 ... FLOW_COUNT - 1] = flow_up_unqueued };
bool flow_pressed[FLOW_COUNT][2] = { [0 ... FLOW_COUNT - 1] = {false, false} };
uint16_t flow_timers[FLOW_COUNT] = { [0 ... FLOW_COUNT - 1] = 0 };
bool flow_timeout_timers_active[FLOW_COUNT] = { [0 ... FLOW_COUNT - 1] = false };
uint16_t flow_timeout_timers_value[FLOW_COUNT] = { [0 ... FLOW_COUNT - 1] = 0 };
uint16_t flow_timeout_wait_timers_value[FLOW_COUNT] = { [0 ... FLOW_COUNT - 1] = 0 };
flow_state_t flow_layers_state[FLOW_LAYERS_COUNT] = {
[0 ... FLOW_LAYERS_COUNT - 1] = flow_up_unqueued
};
bool flow_layer_timeout_timers_active[FLOW_LAYERS_COUNT] = { [0 ... FLOW_LAYERS_COUNT - 1] = false };
uint16_t flow_layer_timeout_timers_value[FLOW_LAYERS_COUNT] = { [0 ... FLOW_LAYERS_COUNT - 1] = 0 };
uint16_t flow_layer_timeout_wait_timers_value[FLOW_LAYERS_COUNT] = { [0 ... FLOW_LAYERS_COUNT - 1] = 0 };
bool is_flow_ignored_key(uint16_t keycode) {
for (int i = 0; i < FLOW_COUNT; i++) {
if (flow_config[i][0] == keycode) {
return true;
}
}
for (int i = 0; i < FLOW_LAYERS_COUNT; i++) {
if (flow_layers_config[i][0] == keycode) {
return true;
}
}
if (keycode == KC_LSFT || keycode == KC_RSFT
|| keycode == KC_LCTL || keycode == KC_RCTL
|| keycode == KC_LALT || keycode == KC_RALT
|| keycode == KC_LGUI || keycode == KC_RGUI) {
return true;
}
return false;
}
bool update_flow_mods(
uint16_t keycode,
bool pressed
) {
bool pass = true;
bool flow_key_list_triggered[FLOW_COUNT] = { [0 ... FLOW_COUNT - 1] = false };
bool flow_key_list_pressed[FLOW_COUNT] = { [0 ... FLOW_COUNT - 1] = false };
bool flow_triggered = false;
for (uint8_t i = 0; i < FLOW_COUNT; i++) {
// Layer key
if (keycode == flow_config[i][0]) {
if (pressed) {
flow_pressed[i][0] = true;
} else {
flow_pressed[i][0] = false;
}
// KC mod key
} else if (keycode == flow_config[i][1]) {
if (pressed) {
if (flow_pressed[i][0]) {
flow_pressed[i][1] = true;
flow_key_list_triggered[i] = true;
flow_triggered = true;
flow_key_list_pressed[i] = true;
pass = false;
}
} else if (flow_pressed[i][1]) {
flow_pressed[i][1] = false;
if (flow_pressed[i][0]) {
flow_key_list_triggered[i] = true;
flow_triggered = true;
pass = false;
} else if ((flow_state[i] == flow_down_unused)
|| (flow_state[i] == flow_down_used)) {
flow_key_list_triggered[i] = true;
flow_triggered = true;
pass = false;
}
}
}
}
for (uint8_t i = 0; i < FLOW_COUNT; i++) {
if (flow_key_list_triggered[i]) {
if (flow_key_list_pressed[i]) {
if (flow_state[i] == flow_up_unqueued) {
register_code(flow_config[i][1]);
}
flow_timeout_wait_timers_value[i] = timer_read();
flow_state[i] = flow_down_unused;
} else {
// Trigger keyup
switch (flow_state[i]) {
case flow_down_unused:
if (!flow_pressed[i][1]) {
if (timer_elapsed(flow_timeout_wait_timers_value[i]) > g_flow_oneshot_wait_term) {
flow_state[i] = flow_up_unqueued;
unregister_code(flow_config[i][1]);
} else {
// If we didn't use the mod while trigger was held, queue it.
flow_state[i] = flow_up_queued;
flow_timeout_timers_active[i] = true;
flow_timeout_timers_value[i] = timer_read();
}
}
break;
case flow_down_used:
// If we did use the mod while trigger was held, unregister it.
if (!flow_pressed[i][1]) {
flow_state[i] = flow_up_unqueued;
unregister_code(flow_config[i][1]);
}
break;
default:
break;
}
}
} else if (!flow_triggered) {
if (pressed) {
if (!is_flow_ignored_key(keycode)) {
switch (flow_state[i]) {
case flow_up_queued:
flow_state[i] = flow_up_queued_used;
flow_timeout_timers_active[i] = false;
break;
case flow_up_queued_used:
flow_state[i] = flow_up_unqueued;
unregister_code(flow_config[i][1]);
break;
default:
break;
}
}
} else {
if (!is_flow_ignored_key(keycode)) {
// On non-ignored keyup, consider the oneshot used.
switch (flow_state[i]) {
case flow_down_unused:
flow_state[i] = flow_down_used;
break;
case flow_up_queued:
flow_state[i] = flow_up_unqueued;
unregister_code(flow_config[i][1]);
break;
case flow_up_queued_used:
flow_state[i] = flow_up_unqueued;
unregister_code(flow_config[i][1]);
break;
default:
break;
}
}
}
}
}
return pass;
}
void change_pressed_status(uint16_t keycode, bool pressed) {
for (int i = 0; i < FLOW_COUNT; i++) {
if (flow_config[i][0] == keycode) {
flow_pressed[i][0] = pressed;
}
}
}
bool update_flow_layers(
uint16_t keycode,
bool pressed,
keypos_t key_position
) {
uint8_t key_layer = read_source_layers_cache(key_position);
bool pass = true;
for (int i = 0; i < FLOW_LAYERS_COUNT; i++) {
uint16_t trigger = flow_layers_config[i][0];
uint16_t layer = flow_layers_config[i][1];
if (keycode == trigger) {
if (pressed) {
// Trigger keydown
if (flow_layers_state[i] == flow_up_unqueued) {
layer_on(layer);
change_pressed_status(trigger, true);
}
flow_layer_timeout_wait_timers_value[i] = timer_read();
flow_layers_state[i] = flow_down_unused;
pass = false;
} else {
// Trigger keyup
switch (flow_layers_state[i]) {
case flow_down_unused:
if (timer_elapsed(flow_layer_timeout_wait_timers_value[i]) > g_flow_oneshot_wait_term) {
flow_layers_state[i] = flow_up_unqueued;
layer_off(layer);
change_pressed_status(trigger, false);
pass = false;
} else {
// If we didn't use the layer while trigger was held, queue it.
flow_layers_state[i] = flow_up_queued;
flow_layer_timeout_timers_active[i] = true;
flow_layer_timeout_timers_value[i] = timer_read();
pass = false;
change_pressed_status(trigger, true);
}
break;
case flow_down_used:
// If we did use the layer while trigger was held, turn off it.
flow_layers_state[i] = flow_up_unqueued;
layer_off(layer);
change_pressed_status(trigger, false);
pass = false;
break;
default:
break;
}
}
} else {
if (pressed) {
if (key_layer == layer) {
// On non-ignored keyup, consider the oneshot used.
switch (flow_layers_state[i]) {
case flow_down_unused:
flow_layers_state[i] = flow_down_used;
break;
case flow_up_queued:
flow_layers_state[i] = flow_up_queued_used;
flow_layer_timeout_timers_active[i] = false;
break;
case flow_up_queued_used:
flow_layers_state[i] = flow_up_unqueued;
layer_off(layer);
change_pressed_status(trigger, false);
pass = false;
break;
default:
break;
}
}
} else {
// Ignore key ups from other layers
if (key_layer == layer) {
// On non-ignored keyup, consider the oneshot used.
switch (flow_layers_state[i]) {
case flow_up_queued:
flow_layers_state[i] = flow_up_unqueued;
layer_off(layer);
change_pressed_status(trigger, false);
break;
case flow_up_queued_used:
flow_layers_state[i] = flow_up_unqueued;
layer_off(layer);
change_pressed_status(trigger, false);
break;
default:
break;
}
}
}
}
}
return pass;
}
bool update_flow(
uint16_t keycode,
bool pressed,
keypos_t key_position
) {
bool pass = update_flow_mods(keycode, pressed);
pass = update_flow_layers(keycode, pressed, key_position) & pass;
return pass;
}
void flow_matrix_scan(void) {
for (int i = 0; i < FLOW_COUNT; i++) {
if (flow_timeout_timers_active[i]
&& timer_elapsed(flow_timeout_timers_value[i]) > g_flow_oneshot_term) {
flow_timeout_timers_active[i] = false;
flow_state[i] = flow_up_unqueued;
unregister_code(flow_config[i][1]);
}
}
for (int i = 0; i < FLOW_LAYERS_COUNT; i++) {
if (flow_layer_timeout_timers_active[i]
&& timer_elapsed(flow_layer_timeout_timers_value[i]) > g_flow_oneshot_term) {
flow_layer_timeout_timers_active[i] = false;
flow_layers_state[i] = flow_up_unqueued;
layer_off(flow_layers_config[i][1]);
change_pressed_status(flow_layers_config[i][0], false);
}
}
}

View file

@ -0,0 +1,27 @@
/*
Copyright 2022 Dalius Dobravolskas <dalius.dobravolskas@gmail.com>
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 QMK_KEYBOARD_H
bool update_flow(
uint16_t keycode,
bool pressed,
keypos_t key_position
);
void flow_matrix_scan(void);

View file

@ -15,7 +15,7 @@
*/
#include QMK_KEYBOARD_H
#include "oneshot.h"
#include "flow.h"
// Each layer gets a name for readability, which is then used in the keymap matrix below.
// The underscores don't mean anything - you can have a layer called STUFF or any other name.
@ -25,11 +25,12 @@ enum layers {
_QWERTY,
_SYM,
_NAV,
_NUMB,
_MISC,
_TMUX,
_MOUSE,
_MISC,
_FUNC,
_LT_MAC,
_LT_LINUX,
};
enum custom_keycodes {
@ -41,22 +42,40 @@ enum custom_keycodes {
TM_SLCT,
TM_SRCH,
TM_URL,
OS_CTRL,
OS_ALT,
OS_GUI,
OS_TMUX,
OS_MISC,
OS_TMUX,
OS_FUNC,
LT_OSLNX,
};
// Shortcut to make keymap more readable
#define L_NAV MO(_NAV)
#define L_SYM MO(_SYM)
#define L_MOUSE TG(_MOUSE)
#define L_MOUSE MO(_MOUSE)
#define K_PRINT (QK_LCTL | QK_LSFT | QK_LGUI | KC_4)
#define K_VIDEO (QK_LSFT | QK_LGUI | KC_5)
// flow_config should correspond to following format:
// * layer keycode
// * modifier keycode
const uint16_t flow_config[FLOW_COUNT][2] = {
{L_NAV, KC_LALT},
{L_NAV, KC_LGUI},
{L_NAV, KC_LCTL},
{L_SYM, KC_RCTL},
{L_SYM, KC_RGUI},
{L_SYM, KC_RALT},
};
const uint16_t flow_layers_config[FLOW_LAYERS_COUNT][2] = {
{OS_MISC, _MISC},
{OS_TMUX, _TMUX},
{OS_FUNC, _FUNC},
};
// Unicode characters
enum unicode_names {
SNEK,
EURO,
@ -96,7 +115,7 @@ const uint32_t PROGMEM unicode_map[] = {
[LT_S_I] = 0x12f, // į
[LT_L_I] = 0x12e, // Į
[LT_S_S] = 0x161, // š
[LT_L_S] = 0x160, // Š'
[LT_L_S] = 0x160, // Š
[LT_S_U1] = 0x173, // ų
[LT_L_U1] = 0x172, // Ų
[LT_S_U2] = 0x16b, // ū
@ -110,14 +129,23 @@ const uint32_t PROGMEM unicode_map[] = {
#define K_SNEK X(SNEK)
#define K_EURO X(EURO)
#define K_LT_A XP(LT_S_A, LT_L_A)
#define K_LT_AU X(LT_L_A)
#define K_LT_C XP(LT_S_C, LT_L_C)
#define K_LT_CU X(LT_L_C)
#define K_LT_E1 XP(LT_S_E1, LT_L_E1)
#define K_LT_E1U X(LT_L_E1)
#define K_LT_E2 XP(LT_S_E2, LT_L_E2)
#define K_LT_E2U X(LT_L_E2)
#define K_LT_I XP(LT_S_I, LT_L_I)
#define K_LT_IU X(LT_L_I)
#define K_LT_S XP(LT_S_S, LT_L_S)
#define K_LT_SU X(LT_L_S)
#define K_LT_U1 XP(LT_S_U1, LT_L_U1)
#define K_LT_U1U X(LT_L_U1)
#define K_LT_U2 XP(LT_S_U2, LT_L_U2)
#define K_LT_U2U X(LT_L_U2)
#define K_LT_Z XP(LT_S_Z, LT_L_Z)
#define K_LT_ZU X(LT_L_Z)
#define K_LT_OB X(LT_OB)
#define K_LT_CB X(LT_CB)
@ -139,35 +167,35 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
//┌────────┬────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┬────────┐
KC_EXLM ,KC_AT ,KC_HASH ,KC_DLR ,KC_PERC , KC_CIRC ,KC_AMPR ,KC_ASTR ,KC_LPRN ,KC_RPRN ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
KC_GRV ,KC_PLUS ,KC_LBRC ,KC_RBRC ,K_LT_OB , KC_MINS ,OS_ALT ,OS_CTRL ,OS_GUI ,KC_PIPE ,
XXXXXXX ,KC_GRV ,KC_LBRC ,KC_RBRC ,KC_PLUS , KC_MINS ,KC_PIPE ,KC_RCTL ,KC_RGUI ,KC_RALT ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
K_SNEK ,KC_EQL ,KC_LCBR ,KC_RCBR ,K_LT_CB , KC_UNDS ,KC_QUOT ,KC_DQT ,K_EURO ,KC_BSLS ,
KC_DEL ,KC_BSPC ,KC_LCBR ,KC_RCBR ,KC_EQL , KC_UNDS ,KC_QUOT ,KC_DQT ,OS_MISC ,KC_BSLS ,
//└────────┴────────┴────────┴────┬───┴────┬───┼────────┐ ┌────────┼───┬────┴───┬────┴────────┴────────┴────────┘
_______ , _______ , _______ , _______
_______ , _______ , _______ , XXXXXXX
// └────────┘ └────────┘ └────────┘ └────────┘
),
[_NAV] = LAYOUT(
//┌────────┬────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┬────────┐
KC_TILDE,L_MOUSE ,OS_FUNC ,OS_MISC ,OS_TMUX , K_LT_A ,K_LT_C ,K_LT_E1 ,K_LT_E2 ,K_LT_I ,
KC_1 ,KC_2 ,KC_3 ,KC_4 ,KC_5 , KC_6 ,KC_7 ,KC_8 ,KC_9 ,KC_0 ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
KC_TAB ,OS_GUI ,OS_CTRL ,OS_ALT ,KC_ENT , KC_LEFT ,KC_DOWN ,KC_UP ,KC_RIGHT,KC_END ,
KC_LALT ,KC_LGUI ,KC_LCTL ,KC_TAB ,KC_ENT , KC_LEFT ,KC_DOWN ,KC_UP ,KC_RIGHT,KC_PGUP ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
KC_DELT ,KC_BSPC ,KC_ESC ,KC_PGDN ,KC_PGUP , KC_HOME ,K_LT_S ,K_LT_U1 ,K_LT_U2 ,K_LT_Z ,
KC_LSFT ,KC_BSPC ,KC_ESC ,KC_TILDE,OS_TMUX , OS_FUNC ,L_MOUSE ,KC_COMM ,KC_DOT ,KC_PGDN ,
//└────────┴────────┴────────┴────┬───┴────┬───┼────────┐ ┌────────┼───┬────┴───┬────┴────────┴────────┴────────┘
_______ , _______ , _______ , _______
XXXXXXX , _______ , _______ , _______
// └────────┘ └────────┘ └────────┘ └────────┘
),
[_NUMB] = LAYOUT(
[_MISC] = LAYOUT(
//┌────────┬────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┬────────┐
KC_1 ,KC_2 ,KC_3 ,KC_4 ,KC_5 , XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
RESET ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX , KC_BRID ,KC_BRIU ,KC_PSCR ,XXXXXXX ,K_PRINT ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
KC_6 ,KC_7 ,KC_8 ,KC_9 ,KC_0 , XXXXXXX ,OS_ALT ,OS_CTRL ,OS_GUI ,XXXXXXX ,
XXXXXXX ,XXXXXXX ,DEBUG ,LT_OSLNX,XXXXXXX , KC_MPRV ,KC_MPLY ,KC_MNXT ,XXXXXXX ,K_VIDEO ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
KC_DELT ,KC_BSPC ,XXXXXXX ,XXXXXXX ,XXXXXXX , XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX , KC_VOLD ,KC_VOLU ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
//└────────┴────────┴────────┴────┬───┴────┬───┼────────┐ ┌────────┼───┬────┴───┬────┴────────┴────────┴────────┘
_______ , _______ , _______ , _______
XXXXXXX , XXXXXXX , XXXXXXX , XXXXXXX
// └────────┘ └────────┘ └────────┘ └────────┘
),
@ -185,127 +213,59 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_MOUSE] = LAYOUT(
//┌────────┬────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┬────────┐
XXXXXXX ,L_MOUSE ,KC_MS_U ,KC_BTN3 ,KC_WH_U , XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
XXXXXXX ,XXXXXXX ,KC_MS_U ,KC_BTN3 ,KC_WH_U , XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
XXXXXXX ,KC_MS_L ,KC_MS_D ,KC_MS_R ,KC_WH_D , XXXXXXX ,KC_LALT ,KC_LCTL ,KC_LGUI ,XXXXXXX ,
XXXXXXX ,KC_MS_L ,KC_MS_D ,KC_MS_R ,KC_WH_D , XXXXXXX ,XXXXXXX ,KC_RCTL ,KC_RGUI ,KC_RALT ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
XXXXXXX ,XXXXXXX ,KC_ESC ,XXXXXXX ,XXXXXXX , XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX , XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
//└────────┴────────┴────────┴────┬───┴────┬───┼────────┐ ┌────────┼───┬────┴───┬────┴────────┴────────┴────────┘
KC_BTN1 , KC_BTN2 , XXXXXXX , XXXXXXX
// └────────┘ └────────┘ └────────┘ └────────┘
),
[_MISC] = LAYOUT(
//┌────────┬────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┬────────┐
QK_BOOT,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX , KC_BRID ,KC_BRIU ,XXXXXXX ,KC_PSCR ,K_PRINT ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
XXXXXXX ,XXXXXXX ,DEBUG ,XXXXXXX ,XXXXXXX , KC_MPRV ,KC_MPLY ,XXXXXXX ,KC_MNXT ,XXXXXXX ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX , KC_VOLD ,KC_VOLU ,XXXXXXX ,XXXXXXX ,UC_MOD ,
//└────────┴────────┴────────┴────┬───┴────┬───┼────────┐ ┌────────┼───┬────┴───┬────┴────────┴────────┴────────┘
XXXXXXX , XXXXXXX , XXXXXXX , XXXXXXX
KC_BTN1 , KC_BTN2 , _______ , XXXXXXX
// └────────┘ └────────┘ └────────┘ └────────┘
),
[_FUNC] = LAYOUT(
//┌────────┬────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┬────────┐
XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX , KC_F1 ,KC_F2 ,KC_F3 ,KC_F4 ,KC_F5 ,
KC_F1 ,KC_F2 ,KC_F3 ,KC_F4 ,KC_F5 , XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX , KC_F6 ,KC_F7 ,KC_F8 ,KC_F9 ,KC_F10 ,
KC_F6 ,KC_F7 ,KC_F8 ,KC_F9 ,KC_F10 , XXXXXXX ,XXXXXXX ,KC_RCTL ,KC_RGUI ,KC_RALT ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX , KC_F11 ,KC_F12 ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
KC_F11 ,KC_F12 ,XXXXXXX ,XXXXXXX ,XXXXXXX , XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
//└────────┴────────┴────────┴────┬───┴────┬───┼────────┐ ┌────────┼───┬────┴───┬────┴────────┴────────┴────────┘
XXXXXXX , XXXXXXX , XXXXXXX , XXXXXXX
// └────────┘ └────────┘ └────────┘ └────────┘
),
[_LT_MAC] = LAYOUT(
//┌────────┬────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┬────────┐
KC_EXLM ,KC_AT ,KC_HASH ,KC_DLR ,KC_PERC , KC_CIRC ,KC_AMPR ,KC_ASTR ,KC_PLUS ,XXXXXXX ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
KC_1 ,KC_2 ,KC_3 ,KC_4 ,KC_5 , KC_6 ,KC_7 ,KC_8 ,KC_EQL ,XXXXXXX ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
XXXXXXX ,KC_BSPC ,XXXXXXX ,XXXXXXX ,XXXXXXX , XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
//└────────┴────────┴────────┴────┬───┴────┬───┼────────┐ ┌────────┼───┬────┴───┬────┴────────┴────────┴────────┘
XXXXXXX , XXXXXXX , _______ , XXXXXXX
// └────────┘ └────────┘ └────────┘ └────────┘
),
[_LT_LINUX] = LAYOUT(
//┌────────┬────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┬────────┐
K_LT_AU ,K_LT_CU ,K_LT_E1U,K_LT_E2U,K_LT_IU , K_LT_SU ,K_LT_U1U,K_LT_U2U,K_LT_ZU ,XXXXXXX ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
K_LT_A ,K_LT_C ,K_LT_E1 ,K_LT_E2 ,K_LT_I , K_LT_S ,K_LT_U1 ,K_LT_U2 ,K_LT_Z ,XXXXXXX ,
//├────────┼────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┼────────┤
XXXXXXX ,KC_BSPC ,XXXXXXX ,K_SNEK ,K_LT_OB , K_LT_CB ,K_EURO ,XXXXXXX ,XXXXXXX ,XXXXXXX ,
//└────────┴────────┴────────┴────┬───┴────┬───┼────────┐ ┌────────┼───┬────┴───┬────┴────────┴────────┴────────┘
XXXXXXX , XXXXXXX , _______ , XXXXXXX
// └────────┘ └────────┘ └────────┘ └────────┘
),
};
#define TMUX_PREFIX SS_DOWN(X_LCTL) "b" SS_UP(X_LCTL)
bool is_oneshot_cancel_key(uint16_t keycode) {
switch (keycode) {
case L_SYM:
case L_NAV:
return true;
default:
return false;
}
}
bool is_oneshot_layer_cancel_key(uint16_t keycode) {
switch (keycode) {
case L_SYM:
case L_NAV:
return true;
default:
return false;
}
}
bool is_oneshot_ignored_key(uint16_t keycode) {
switch (keycode) {
case L_SYM:
case L_NAV:
case OS_CTRL:
case OS_ALT:
case OS_GUI:
case OS_TMUX:
case OS_MISC:
case KC_LSFT:
return true;
default:
return false;
}
}
bool is_oneshot_mod_key(uint16_t keycode) {
switch (keycode) {
case OS_CTRL:
case OS_ALT:
case OS_GUI:
return true;
default:
return false;
}
}
oneshot_state os_ctrl_state = os_up_unqueued;
oneshot_state os_alt_state = os_up_unqueued;
oneshot_state os_cmd_state = os_up_unqueued;
oneshot_state os_tmux_state = os_up_unqueued;
oneshot_state os_misc_state = os_up_unqueued;
oneshot_state os_func_state = os_up_unqueued;
bool lt_os_is_linux = false;
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
update_oneshot(
&os_ctrl_state, KC_LCTL, OS_CTRL,
keycode, record
);
update_oneshot(
&os_alt_state, KC_LALT, OS_ALT,
keycode, record
);
update_oneshot(
&os_cmd_state, KC_LGUI, OS_GUI,
keycode, record
);
bool handled = true;
handled = update_oneshot_layer(
&os_tmux_state, _TMUX, OS_TMUX,
keycode, record
) & handled;
handled = update_oneshot_layer(
&os_misc_state, _MISC, OS_MISC,
keycode, record
) & handled;
handled = update_oneshot_layer(
&os_func_state, _FUNC, OS_FUNC,
keycode, record
) & handled;
if (!handled) return false;
if (!update_flow(keycode, record->event.pressed, record->event.key)) return false;
switch (keycode) {
case TM_LEFT:
@ -340,10 +300,35 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!record->event.pressed) return true;
SEND_STRING(TMUX_PREFIX SS_LCTL("u"));
return false;
case LT_OSLNX:
if (!record->event.pressed) return true;
lt_os_is_linux = !lt_os_is_linux;
return false;
}
return true;
}
layer_state_t layer_state_set_user(layer_state_t state) {
return update_tri_layer_state(state, _SYM, _NAV, _NUMB);
void matrix_scan_user(void) {
flow_matrix_scan();
}
bool lang_layer_on = false;
layer_state_t layer_state_set_user(layer_state_t state) {
state = update_tri_layer_state(state, _SYM, _NAV, lt_os_is_linux ? _LT_LINUX : _LT_MAC);
uint8_t hl = get_highest_layer(state);
if (hl == _LT_MAC) {
if (!lang_layer_on) {
tap_code16(LCTL(KC_SPC));
lang_layer_on = true;
}
} else {
if (lang_layer_on) {
tap_code16(LCTL(KC_SPC));
lang_layer_on = false;
}
}
return state;
}

View file

@ -1,195 +0,0 @@
/* Copyright 2021 @daliusd
*
* 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 "print.h"
#include "oneshot.h"
void update_oneshot(
oneshot_state *state,
uint16_t mod,
uint16_t trigger,
uint16_t keycode,
keyrecord_t *record
) {
if (keycode == trigger) {
if (record->event.pressed) {
// Trigger keydown
if (*state == os_up_unqueued) {
register_code(mod);
}
*state = os_down_unused;
dprintf("trigger down (on?), mod: %d, ? -> os_down_unused\n", mod);
} else {
// Trigger keyup
switch (*state) {
case os_down_unused:
// If we didn't use the mod while trigger was held, queue it.
*state = os_up_queued;
dprintf("trigger up, mod: %d, os_down_unused -> os_up_queued\n", mod);
break;
case os_down_used:
// If we did use the mod while trigger was held, unregister it.
*state = os_up_unqueued;
unregister_code(mod);
dprintf("trigger up (off), mod: %d, os_down_used -> os_up_unqueued\n", mod);
break;
default:
break;
}
}
} else {
if (record->event.pressed) {
if (is_oneshot_cancel_key(keycode) && *state != os_up_unqueued) {
// Cancel oneshot on designated cancel keydown.
*state = os_up_unqueued;
unregister_code(mod);
dprintf("cancel (off), mod: %d, ? -> os_up_unqueued\n", mod);
}
if (!is_oneshot_ignored_key(keycode)) {
switch (*state) {
case os_up_queued:
*state = os_up_queued_used;
dprintf("key up (off), mod: %d, os_up_queued -> os_up_queued_used\n", mod);
break;
case os_up_queued_used:
*state = os_up_unqueued;
unregister_code(mod);
dprintf("key up (off), mod: %d, os_up_queued_used -> os_up_unqueued\n", mod);
break;
default:
break;
}
}
} else {
if (!is_oneshot_ignored_key(keycode)) {
// On non-ignored keyup, consider the oneshot used.
switch (*state) {
case os_down_unused:
*state = os_down_used;
dprintf("key up, mod: %d, os_down_unused -> os_down_used\n", mod);
break;
case os_up_queued:
*state = os_up_unqueued;
unregister_code(mod);
dprintf("key up (off), mod: %d, os_up_queued -> os_up_unqueued\n", mod);
break;
case os_up_queued_used:
*state = os_up_unqueued;
unregister_code(mod);
dprintf("key up (off), mod: %d, os_up_queued_used -> os_up_unqueued\n", mod);
break;
default:
break;
}
}
}
}
}
bool update_oneshot_layer(
oneshot_state *state,
uint16_t layer,
uint16_t trigger,
uint16_t keycode,
keyrecord_t *record
) {
if (keycode == trigger) {
if (record->event.pressed) {
// Trigger keydown
if (*state == os_up_unqueued) {
layer_on(layer);
}
*state = os_down_unused;
dprintf("trigger down (on?), layer: %d, ? -> os_down_unused\n", layer);
return false;
} else {
// Trigger keyup
switch (*state) {
case os_down_unused:
// If we didn't use the layer while trigger was held, queue it.
*state = os_up_queued;
dprintf("trigger up, layer: %d, os_down_unused -> os_up_queued\n", layer);
return false;
case os_down_used:
// If we did use the layer while trigger was held, turn off it.
*state = os_up_unqueued;
layer_off(layer);
dprintf("trigger up (off), layer: %d, os_down_used -> os_up_unqueued\n", layer);
return false;
default:
break;
}
}
} else {
if (record->event.pressed) {
if (is_oneshot_layer_cancel_key(keycode) && *state != os_up_unqueued) {
// Cancel oneshot layer on designated cancel keydown.
*state = os_up_unqueued;
layer_off(layer);
dprintf("cancel (off), layer: %d, ? -> os_up_unqueued\n", layer);
return false;
}
uint8_t key_layer = read_source_layers_cache(record->event.key);
if (key_layer == layer) {
// On non-ignored keyup, consider the oneshot used.
switch (*state) {
case os_down_unused:
*state = os_down_used;
dprintf("key down, layer: %d, os_down_unused -> os_down_used\n", layer);
return true;
case os_up_queued:
if (is_oneshot_mod_key(keycode)) {
*state = os_up_unqueued;
layer_off(layer);
dprintf("key down, layer: %d, os_up_queued -> os_up_unqueued\n", layer);
return false;
} else {
*state = os_up_queued_used;
dprintf("key down, layer: %d, os_up_queued -> os_up_queued_used\n", layer);
}
return true;
case os_up_queued_used:
*state = os_up_unqueued;
layer_off(layer);
dprintf("key down (off), layer: %d, os_up_queued_used -> os_up_unqueued\n", layer);
return false;
default:
break;
}
}
} else {
// Ignore key ups from other layers
uint8_t key_layer = read_source_layers_cache(record->event.key);
if (key_layer == layer) {
// On non-ignored keyup, consider the oneshot used.
switch (*state) {
case os_up_queued:
*state = os_up_unqueued;
layer_off(layer);
dprintf("key up (off), layer: %d, os_up_queued -> os_up_unqueued\n", layer);
return true;
case os_up_queued_used:
*state = os_up_unqueued;
layer_off(layer);
dprintf("key up (off), layer: %d, os_up_queued_used -> os_up_unqueued\n", layer);
return true;
default:
break;
}
}
}
}
return true;
}

View file

@ -1,65 +0,0 @@
/* Copyright 2021 @daliusd
*
* 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 QMK_KEYBOARD_H
// Represents the four states a oneshot key can be in
typedef enum {
os_up_unqueued,
os_up_queued,
os_up_queued_used,
os_down_unused,
os_down_used,
} oneshot_state;
// Custom oneshot mod implementation that doesn't rely on timers. If a mod is
// used while it is held it will be unregistered on keyup as normal, otherwise
// it will be queued and only released after the next non-mod keyup.
void update_oneshot(
oneshot_state *state,
uint16_t mod,
uint16_t trigger,
uint16_t keycode,
keyrecord_t *record
);
// Oneshot implementation for layers
bool update_oneshot_layer(
oneshot_state *state,
uint16_t layer,
uint16_t trigger,
uint16_t keycode,
keyrecord_t *record
);
// To be implemented by the consumer. Layers one shot implementation needs to
// know which keys are used as oneshot mods
bool is_oneshot_mod_key(
uint16_t keycode
);
// To be implemented by the consumer. Defines keys to cancel oneshot mods.
bool is_oneshot_cancel_key(uint16_t keycode);
// To be implemented by the consumer. Defines keys to cancel oneshot layers.
bool is_oneshot_layer_cancel_key(uint16_t keycode);
// To be implemented by the consumer. Defines keys to ignore when determining
// whether a oneshot mod has been used. Setting this to modifiers and layer
// change keys allows stacking multiple oneshot modifiers, and carrying them
// between layers.
bool is_oneshot_ignored_key(uint16_t keycode);

View file

@ -1,21 +1,306 @@
# My 34 keys layout
This are my principles for layout:
This is my principles for layout:
* I am using Callum style layout. Here you can read explanation by
Callum himself and his reasoning for not using mod-tap:
[here](../../../../users/callum/readme.md)
* There should be only one way to type key. Key can be on
different layer but it must maintain its physical location.
different layer but it must maintain its physical location. I
broke this rule for Shift key only.
* The less features are used the better.
* trilayer is cool.
* There is simple TMUX layer.
* There is 🐍 key for no reason.
* Common keys must be accessible using two keys if possible.
As well I have added one shot layers compatible with Callum's one
shot keys.
* It should be possible to work with left keyboard side and mouse
in right hand without lifting hands for some scenarios (that's
why I had to duplicate Shift key).
There is simple TMUX layer as well.
## Improvements over Callum
* I have added one shot layers compatible with Callum's one shot
keys.
* There is one issue with accidental uppercase characters fixed
that exists in original Callum layout's implementation.
* Another annoying feature of Callum layer is one shot keys are
frozen until you cancel them. This is problem when you use one
hand for keyboard and another for mouse. E.g. you click Ctrl and
mouse to get some menu (on Mac OS X), and then you want to click
some item in that menu. You have to remember to cancel one shot in such
situation. I have added two settings two handle situations like
this:
* `FLOW_ONESHOT_WAIT_TERM` - if hold one shot key longer than
`FLOW_ONESHOT_WAIT_TERM` ms then mod key / layer key is not
treated as one shot key (defaults to 500ms).
* `FLOW_ONESHOT_TERM` - if you do not click another key in
`FLOW_ONESHOT_TERM` ms then one shot key / layer key is treated
as normal key. Therefore if you lift it after `FLOW_ONESHOT_TERM`
it will not be treated as one shot (defaults to 500ms).
After adding those two settings I have found out that I don't
need one shot cancel key anymore so I have removed it.
Since differences are significant I named this layout `flow`.
## Using flow with your keyboard
Copy `flow.c` and `flow.h` to keyboard folder.
Add following line to `rules.mk`:
```make
SRC += flow.c
```
Define following in `config.h` for modifiers and layers:
```c
#define FLOW_COUNT 7
#define FLOW_LAYERS_COUNT 3
```
In your `keymap.c` add and configure like this:
```c
#include "flow.h"
...
// flow_config should correspond to following format:
// * layer keycode
// * modifier keycode
const uint16_t flow_config[FLOW_COUNT][2] = {
{L_NAV, KC_LALT},
{L_NAV, KC_LGUI},
{L_NAV, KC_LCTL},
{L_NAV, KC_LSFT},
{L_SYM, KC_LCTL},
{L_SYM, KC_LGUI},
{L_SYM, KC_LALT},
};
// for layers configuration follow this format:
// * custom layer key
// * layer name
const uint16_t flow_layers_config[FLOW_LAYERS_COUNT][2] = {
{OS_TMUX, _TMUX},
{OS_MISC, _MISC},
{OS_FUNC, _FUNC},
};
...
// Add following to handle flow
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!update_flow(keycode, record->event.pressed, record->event.key)) return false;
return true;
}
void matrix_scan_user(void) {
flow_matrix_scan();
}
```
## Lithuanian letters
There are at least two ways how to enter Lithuanian letters: to
use Unicode support from QMK or to switch OS language when
necessary. Unicode support has some problems:
* it is OS specific (you need to change Unicode input mode based
on your OS and I sometimes switch between Mac OS X and Ubuntu).
This is minor issue but it is still issue.
* There is bug in Mac OS X and I can't enter `Š` using unicode
input method.
* Unicode Hex Input in Mac OS X is not perfect and there are some
minor issue while using it.
On Linux Unicode support meanwhile works perfectly.
This leaves us with other option to use OS language switching as
you most probably have done before. Still there is space for
improvement. E.g. I have added Lithuanian letters to trilayer and
trilayer activation toggles OS language (this works because I use
only two languages). Check `layer_state_set_user` implementation
for details.
# Rejected ideas
## Mods as combos
Sometimes when I press `NAV (layer key) + S + Tab` to get `Command
+ Tab` I ended up with `S + Nav + Tab`. This happened because I
did that really fast and sometimes clicked S slightly earlier than
NAV layer key. Initially I have solved this problem using Combo
keys, but that's additional dependency and combo keys are not
ideal for Callum layer. You need to release both keys to trigger
Combo key release. Therefore I have written custom code that
allows pressing S some milliseconds earlier. This is controlled by
FLOW_TERM and defaults to 10. I do not recommend setting this to
higher than 30.
This idea was rejected because it looks like 10ms did not made
that big difference.
## Swapper
Idea of swapper is to have key that registers Mode key (e.g.
Command while layer and some key is pressed) to simulate two key
combo, e.g. Command + Tab. Overall I found that 3 keys combo that
I have currently for swapping windows is equally good as 2 keys
swapper. Another problem with swapper is that it is OS specific.
Still if you want here is swapper implementation I have used:
```c
bool active;
void update_swapper(
uint16_t trigger,
uint16_t keycode,
bool pressed
) {
if (keycode == trigger) {
if (pressed) {
if (!active) {
active = true;
register_code(KC_LGUI);
}
register_code(KC_TAB);
} else {
unregister_code(KC_TAB);
}
} else if (active && keycode != KC_LSFT && keycode != KC_LEFT && keycode != KC_RIGHT) {
unregister_code(KC_LGUI);
active = false;
}
}
```
## Combos
I have seen that some people use two letter horizontal combos for
some actions, e.g. XC for Command+C, CV for Command+V, JK for ESC
and etc. I found that this kind of kicks me out of the flow when
working as it requires different kind of action and I need to
pause to make that action.
## Comma-space
I have noticed that I put space after comma `,` usually. That
means I can use comma + letter for something else with backspace,
e.g. for Lithuanian letters. Performance wise that works OK, but
practically that does not feel really good. Trilayer with language
layer switch works better.
Still if you are interested here is comma-space implementation:
```c
void swap_layout(void) {
uint8_t saved_mods = get_mods();
clear_mods();
tap_code16(LCTL(KC_SPC));
set_mods(saved_mods);
}
void press_with_layout_swap(uint16_t keycode) {
tap_code16(KC_BSPC);
swap_layout();
tap_code16(keycode);
swap_layout();
}
bool comma_pressed = false;
bool update_commaspace(
uint16_t keycode,
bool pressed
) {
if (keycode == KC_COMM) {
if (!(get_mods() & MOD_MASK_SHIFT)) {
comma_pressed = true;
}
} else if (comma_pressed) {
if (keycode != KC_LSFT) {
comma_pressed = false;
}
switch(keycode) {
case KC_Q:
if (pressed) {
press_with_layout_swap(KC_1);
return false;
}
break;
case KC_W:
if (pressed) {
press_with_layout_swap(KC_2);
return false;
}
break;
case KC_E:
if (pressed) {
press_with_layout_swap(KC_3);
return false;
}
break;
case KC_R:
if (pressed) {
press_with_layout_swap(KC_4);
return false;
}
break;
case KC_T:
if (pressed) {
press_with_layout_swap(KC_5);
return false;
}
break;
case KC_Y:
if (pressed) {
press_with_layout_swap(KC_6);
return false;
}
break;
case KC_U:
if (pressed) {
press_with_layout_swap(KC_7);
return false;
}
break;
case KC_I:
if (pressed) {
press_with_layout_swap(KC_8);
return false;
}
break;
case KC_O:
if (pressed) {
press_with_layout_swap(KC_EQL);
return false;
}
break;
}
}
return true;
};
```
## Using one shot layers on top layer keys (NAV and SYM)
While this looked promising and fun it was really easy to get lost
in which layer you actually are. You can still use it as `flow`
supports this scenario, but I do not recommend it.

View file

@ -2,4 +2,4 @@ UNICODE_ENABLE = no
UNICODEMAP_ENABLE = yes
#CONSOLE_ENABLE = yes
SRC += oneshot.c
SRC += flow.c