Skip to content

Instantly share code, notes, and snippets.

@joeynguyen
Last active January 8, 2021 00:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save joeynguyen/c09a5f7cfed683e5db9af6c4cc5c0167 to your computer and use it in GitHub Desktop.
Save joeynguyen/c09a5f7cfed683e5db9af6c4cc5c0167 to your computer and use it in GitHub Desktop.
Joey's Keyboardio Atreus2 Firmware with modifiers on home row
/* -*- mode: c++ -*-
* Atreus -- Chrysalis-enabled Sketch for the Keyboardio Atreus
* Copyright (C) 2018, 2019 Keyboard.io, Inc
*
* 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef BUILD_INFORMATION
#define BUILD_INFORMATION "locally built"
#endif
#include "Kaleidoscope.h"
#include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-EEPROM-Keymap.h"
#include "Kaleidoscope-FocusSerial.h"
#include "Kaleidoscope-Macros.h"
#include "Kaleidoscope-MouseKeys.h"
// #include "Kaleidoscope-OneShot.h"
#include "Kaleidoscope-Qukeys.h"
// #include "Kaleidoscope-SpaceCadet.h"
// #include "Kaleidoscope-ShapeShifter.h"
/* -*- mode: c++ -*-
* Keymaps typically consist mostly of `Key_` definitions. There are many, many keys
* defined as part of the USB HID Keyboard specification. You can find the names
* (if not yet the explanations) for all the standard `Key_` defintions offered by
* Kaleidoscope in these files:
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/key_defs_keyboard.h
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/key_defs_consumerctl.h
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/key_defs_sysctl.h
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/key_defs_keymaps.h
*
* Additional things that should be documented here include:
* using ___ to let keypresses fall through to the previously active layer
* using XXX to mark a keyswitch as 'blocked' on this layer
* keeping NUM and FN consistent and accessible on all layers
*/
#define MO(n) ShiftToLayer(n)
#define TG(n) LockLayer(n)
enum {
MACRO_QWERTY,
MACRO_VERSION_INFO
};
#define Key_Exclamation LSHIFT(Key_1)
#define Key_At LSHIFT(Key_2)
#define Key_Hash LSHIFT(Key_3)
#define Key_Dollar LSHIFT(Key_4)
#define Key_Percent LSHIFT(Key_5)
#define Key_Caret LSHIFT(Key_6)
#define Key_And LSHIFT(Key_7)
#define Key_Star LSHIFT(Key_8)
#define Key_Plus LSHIFT(Key_Equals)
#define Key_PrevTrack Consumer_ScanPreviousTrack
#define Key_NextTrack Consumer_ScanNextTrack
#define Key_PlayPause Consumer_PlaySlashPause
#define Key_VolumeDown Consumer_VolumeDecrement
#define Key_VolumeUp Consumer_VolumeIncrement
#define Key_Mute Consumer_Mute
enum {
QWERTY,
FUN,
UPPER
};
/* *INDENT-OFF* */
KEYMAPS(
[QWERTY] = KEYMAP_STACKED
(
// Left
Key_Q, Key_W, Key_E, Key_R, Key_T,
Key_A, Key_S, Key_D, Key_F, Key_G,
Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Tab,
MO(FUN), Key_Backtick, Key_LeftShift, Key_Space, Key_LeftGui, Key_Esc,
// Right
Key_Y, Key_U, Key_I, Key_O, Key_P,
Key_H, Key_J, Key_K, Key_L, Key_Semicolon,
Key_Enter, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash,
Key_Home, Key_End, Key_Backspace, Key_Minus, Key_Backslash, Key_Quote
),
[FUN] = KEYMAP_STACKED
(
// Left
Key_F1, Key_F2, Key_F3, Key_F4, Key_F5,
XXX, Key_mouseUp, XXX, Key_mouseBtnR, Key_mouseWarpEnd,
Key_mouseL, Key_mouseDn, Key_mouseR, Key_mouseBtnL, Key_mouseWarpNW, Key_mouseWarpNE,
___, ___, ___, Key_mouseBtnM, Key_mouseWarpSW, Key_mouseWarpSE,
// Right
Key_F6, Key_F7, Key_F8, Key_F9, Key_F10,
___, ___, ___, Key_F11, Key_F12,
Key_PageUp, ___, ___, ___, ___, ___,
Key_PageDown, ___, Key_Delete, ___, ___, ___
),
[UPPER] = KEYMAP_STACKED
(
// Left
Key_1, Key_2, Key_3, Key_4, Key_5,
___, ___, ___, ___, ___,
___, Key_PrevTrack, Key_NextTrack, Key_PlayPause, ___, Key_LeftShift,
Key_PrintScreen, Key_Mute, Key_VolumeDown, Key_VolumeUp, Key_LeftGui, Key_LeftControl,
// Right
Key_6, Key_7, Key_8, Key_9, Key_0,
Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, Key_Equals,
LGUI(Key_Enter), Key_LeftCurlyBracket, Key_RightCurlyBracket, Key_LeftBracket, Key_RightBracket, Key_Plus,
___, ___, ___, ___, ___, ___
)
)
/* *INDENT-ON* */
KALEIDOSCOPE_INIT_PLUGINS(
EEPROMSettings,
EEPROMKeymap,
Focus,
FocusEEPROMCommand,
FocusSettingsCommand,
Qukeys,
// OneShot,
// ShapeShifter,
// SpaceCadet,
MouseKeys,
Macros
);
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
switch (macroIndex) {
case MACRO_QWERTY:
// This macro is currently unused, but is kept around for compatibility
// reasons. We used to use it in place of `MoveToLayer(QWERTY)`, but no
// longer do. We keep it so that if someone still has the old layout with
// the macro in EEPROM, it will keep working after a firmware update.
Layer.move(QWERTY);
break;
case MACRO_VERSION_INFO:
if (keyToggledOn(keyState)) {
Macros.type(PSTR("Keyboardio Atreus - Kaleidoscope "));
Macros.type(PSTR(BUILD_INFORMATION));
}
break;
default:
break;
}
return MACRO_NONE;
}
void setup() {
Kaleidoscope.setup();
// SpaceCadet.disable();
EEPROMKeymap.setup(10);
/**
* Qukeys configs
* format Qukey(layer, row, col, alt_keycode)
* (layers, rows and columns are all zero-indexed,
* rows are top to bottom and columns are left to right)
* For the Keyboardio Atreus 2, key coordinates refer to this header file:
* https://github.com/keyboardio/Kaleidoscope/blob/d7e0f49fef92b6f516c991a927ddac79b44fbd5d/src/kaleidoscope/device/keyboardio/Atreus2.h
*/
Qukeys.setOverlapThreshold(100); // default is 80
Qukeys.setHoldTimeout(500); // default is 250
QUKEYS(
kaleidoscope::plugin::Qukey(0, KeyAddr(1, 1), Key_LeftAlt), // S / Alt
kaleidoscope::plugin::Qukey(0, KeyAddr(1, 2), Key_LeftShift), // D / Shift
kaleidoscope::plugin::Qukey(0, KeyAddr(1, 3), Key_LeftControl), // F / Control
kaleidoscope::plugin::Qukey(0, KeyAddr(1, 4), Key_LeftGui), // G / Super
kaleidoscope::plugin::Qukey(0, KeyAddr(1, 7), Key_LeftGui), // H / Super
kaleidoscope::plugin::Qukey(0, KeyAddr(1, 8), Key_LeftControl), // J / Control
kaleidoscope::plugin::Qukey(0, KeyAddr(1, 9), Key_LeftShift), // K / Shift
kaleidoscope::plugin::Qukey(0, KeyAddr(1, 10), Key_LeftAlt), // L / Alt
kaleidoscope::plugin::Qukey(0, KeyAddr(2, 5), Key_LeftShift), // Tab / Shift
kaleidoscope::plugin::Qukey(0, KeyAddr(3, 5), Key_LeftControl), // Esc / Control
kaleidoscope::plugin::Qukey(0, KeyAddr(3, 6), Key_LeftAlt), // Home / Alt
kaleidoscope::plugin::Qukey(0, KeyAddr(3, 7), MO(UPPER)), // End / ShiftToLayer(Upper)
)
}
void loop() {
Kaleidoscope.loop();
}
@gedankenexperimenter
Copy link

It might help to understand how Qukeys works. It decides which key value to assign to a keypress based on the order in which the keys are released. If you press a qukey, then another key, the qukey will get its alternate (modifier) value if the other key is released first. The hold timeout only applies if you hold a qukey by itself for that long, without pressing any other keys; it's really only there so you can use a qukey with a pointing device.

People get unintended results in both directions with Qukeys, because it can't read your mind. All it can do is make a best guess about your intentions, based on input. I have two suggestions for you if it's too difficult to adjust your typing habits to avoid those unintended key values: move the qukeys off the home row (you might find this changes the rollover timing, making unintended modifiers less likely), and add a macro to toggle Qukeys on and off (if you only want to use it occasionally).

Last, I've had an idea for a new config parameter, which would set a minimum amount of time that a qukey must be held in order to produce an alternate key value. Setting that high enough would eliminate unintended modifiers, but it would also require more time when using them. For a very fast typist, 100 ms might be plenty to distinguish taps from holds.

Some time ago, I made a plugin to measure rollover in an effort to improve Qukeys. I could revive it if you're interested in recording some data and sharing it, in the interest of improving it further.

@joeynguyen
Copy link
Author

joeynguyen commented Oct 6, 2020

@gedankenexperimenter Thanks for the thorough explanation. I misunderstood how setHoldTimeout and setOverlapThreshold worked previously and your explanation clarified it for me. I understand why my settings don't work as I had intended now.

I usually have my modifiers on the bottom row so it not working isn't an issue for me. I was just testing out this person's comment suggestion which I thought was interesting - keyboardio/Kaleidoscope#706 (comment).

Regarding your comment about your "plugin to measure rollover", I don't want to cause you unnecessary work just for my sake. If you do want to revive it, I would certainly be willing to help record data and sharing it to improve the plugin, but please don't revive it just for my sake.

Thanks for your time!

@gedankenexperimenter
Copy link

I will keep you in mind. With a number of other people having similar issues, I might want to revive my research project on rollover, which could be very helpful to me in finding the best algorithm. Thanks for volunteering!

I was just testing out this person's comment suggestion which I thought was interesting - keyboardio/Kaleidoscope#706 (comment).

I prefer the order to be shift, control, alt, gui (from index finger to pinky finger), myself. The index finger isn't strongest, but it doesn't share a tendon (unlike the middle two fingers), so it's easier to use on its own, so that the one where I have shift. And I work on macOS most often, so I very rarely need control and command (gui) in combination. The most difficult combo is middle finger and pinky without the ring finger (which is the least independent of all four).

@joeynguyen
Copy link
Author

yea I totally understand how important command is on Macs. I owned an MBA and MBP from 2008-2017 and only switched to Linux/Windows recently. Seeing as how rarely used control is on Macs, I would actually suggest: shift, gui, alt, control (index to pinky) instead since the pinky is the weakest finger. And if you look at my code, you'll actually notice I don't use the pinky finger at all. Instsead of ASDF, I do SDFG and slide my finger(s) over when needed to reduce usage of the pinky finger.

@gedankenexperimenter
Copy link

I did notice the shift. I don't like it, myself, because it complicated modifier combos (and because I have no problem using my pinkies). I actually use control at least as often as command on macOS, mainly because I use Emacs. I agree that those are the ones most likely to get swapped depending on OS, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment