New plugin: Qukeys (yet another SpaceCadet/DualUse-type plugin)

Plugin name: Kaleidoscope-Qukeys
Author: Michael Richters
Source URL: https://github.com/gedankenlab/Kaleidoscope-Qukeys

Schrödinger’s Keys!

The name “qukey” (quantum key) is a reference to the “qubit” (quantum bit) from quantum computing, because it can be in two “states” simultaneously.

Description

Kaleidoscope-Qukeys allows you to define a key in (layer, row, column) coordinates, with an alternate keycode that should be sent if the key is held – the keycode in the keymap will be sent if the key is “tapped”. Note, the idea is to use a modifier as the alternate keycode, but any Key definition is allowed, so if you wanted to have your A key send B if it’s held, you could do that. Also, since it’s not simply a keycode, but a two-byte Key object, you can have the alternate be LSHIFT(LCTRL(Key_LeftAlt) (for example) to have modifier combinations as the “hold” behaviour on a single key.

Obviously, this means that you won’t be able to press and hold a qukey for its primary purpose if this plugin is active.

Background

I was dissatisfied with the existing implementations of the “dual-use” functionality that I could find. Both SpaceCadet and DualUse work very well if you want to add extra functionality to keys that you’d normally use as modifiers (and if that’s all you want, you’re probably better off sticking with one of those plugins), but they don’t work so well if you want to add a secondary (modifier) behaviour to an existing key. I have heard that people have tried configuring dual-use keys on the home row letter keys, but that it often results in accidental modifiers when they have overlap between keys during normal typing. Qukeys is (hopefully) a solution to this problem.

Details

To solve the typing-overlap problem, Kaleidoscope-Qukeys doesn’t add anything to the HID report when a qukey is pressed. At this point the qukey is in an indeterminate state (or, if you like the quantum physics metaphor, a superposition of states), which won’t be resolved until one of three things happens:

  1. A timer expires. This should be something short, but just long enough that anything that’s a reasonable “tap” will not exceed the timeout. In this case the qukey assumes it’s alternate (i.e. modifier) state.
  2. The qukey is released. In this case, it assumes its primary state (from the regular keymap), sending an extra report to the host.
  3. A key pressed after the qukey was pressed is released. In this case, the qukey takes on its alternate state, and a report is sent.

To make this work, once a qukey is pressed, all new keypresses are suppressed and stored in a queue, until either the timer expires or one of those keys is released. When a key in the queue is released, HID reports will be sent to the host – one for each key flushed from the queue, in order, thus preserving the order of keypresses. So, there is still a danger for fast typists, if the order of key releases is not the same as the order of key presses, you could still end up getting unintended modifiers instead of printable characters.

The other caveat is that if you want to use a qukey to modify mouse clicks (for example), you’ll have to wait for the timer to expire. This is probably not a big deal, because the timeout can probably be set quite low (200ms might be long enough).

One last note here: the qukeys key queue is limited to 8 keys – I figure if you’re pressing that many keys in the length of time it takes for a normal tap, you’re probably not really typing anymore, so I give up and the first qukey just gets its primary state.

Status

This plugin isn’t quite finished yet, and depends on at least one change to Kaleidoscope that hasn’t been merged into the master branch, so building it right now will be tricky. I’d be very surprised if it also wasn’t quite buggy.

14 Likes

If anyone really wants to try it out before Kaleidoscope gets updated to enable the necessary core feature(s), I can try to put together a version of Arduino-Boards that has everything you’ll need.

I mostly wanted to get this out of my head to focus on other things, like writing lots of Kaleidoscope documentation. And maybe sleep.

Oh, and thanks to @algernon and @jesse for helping me understand how everything works well enough to be able to put this together.

Love it, is there a cat involved?

2 Likes

@james.nvc (and anyone else interested in giving this a try):

The current head of the master branch of Arduino-Boards (if all the submodules are updated) has everything necessary to build Kaleidoscope-Qukeys now.

Also, I just made a couple of commits that should make it a bit easier to add what’s needed to the sketch: see the Qukeys.ino file for examples.

3 Likes

If you want to try out Qukeys, but setting it up is difficult, I’d be happy to help. The configuration isn’t as straightforward as DualUse or SpaceCadet, but it’s really designed to do what you want (overloading a symbol key with a modifier) as opposed to SpaceCadet (overloading a modifier key with a symbol).

Sounds great! I cloned the repo into Arduino/hardware/keyboardio/avr/libraries/, and just added the include line to see if it installed properly. I get the following errors with Make:

make
BOARD_HARDWARE_PATH="/home/tws/Arduino/hardware" /home/tws/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope/bin//kaleidoscope-builder build-all
Building output/Model01-Firmware/Model01-Firmware (0.0.0-gv1.13-60-g8f58-dirty) ...
/home/tws/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope-Qukeys/src/Kaleidoscope/Qukeys.cpp: In static member function 'static int8_t kaleidoscope::Qukeys::lookupQukey(uint8_t)':
/home/tws/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope-Qukeys/src/Kaleidoscope/Qukeys.cpp:64:38: error: 'class Layer_' has no member named 'lookupActiveLayer'
           (qukeys_[i].layer == Layer.lookupActiveLayer(row, col))) {
                                      ^
/home/tws/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope-Qukeys/src/Kaleidoscope/Qukeys.cpp: In static member function 'static void kaleidoscope::Qukeys::flushKey(int8_t, uint8_t)':
/home/tws/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope-Qukeys/src/Kaleidoscope/Qukeys.cpp:110:39: error: 'class Keyboard_' has no member named 'keyReport'
   memcpy(hid_report.allkeys, Keyboard.keyReport.allkeys, sizeof(hid_report));
                                       ^
/home/tws/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope-Qukeys/src/Kaleidoscope/Qukeys.cpp:112:19: error: 'class Keyboard_' has no member named 'keyReport'
   memcpy(Keyboard.keyReport.allkeys, Keyboard.lastKeyReport.allkeys, sizeof(Keyboard.keyRepor

etc.

What else do I need to do to install it?

You’ll need to update your Arduino-Boards clone. There have been recent changes to Kaleidoscope and KeyboardioHID that are required to build Qukeys.

I’m not sure what that means. I did this:

~/.../keyboardio/avr > git pull

This appeared to do what I needed, but the error persisted. Looking at the documentation here:

I then tried:

~/.../keyboardio/avr > make checkout-submodules
git pull
Already up-to-date.
git submodule update --init --recursive
Submodule 'libraries/Kaleidoscope-NumPad' (https://github.com/Keyboardio/Kaleidoscope-NumPad) registered for path 'libraries/Kaleidoscope-NumPad'
Cloning into '/home/tws/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope-NumPad'...
Submodule path 'libraries/Kaleidoscope-NumPad': checked out '952198a98af9a19174d9ba1ea73d8515a0f28ebb'

I’m still getting the same errors when I try to make with QUkeys included.

Try the command:

git submodule status

You should see the following line in the output:

 c060c5ef7e76f5bce5524d66921a1179724b260d libraries/Kaleidoscope (shipped-model00-xmas-924-gc060c5e)

If it’s showing something different, your Kaleidoscope repo is not up to date.

This is what I get:

67aa56022d5f429000448ca2b4fbaf8cf5d1aef7 libraries/Kaleidoscope (shipped-model00-xmas-881-g67aa560)

I have already pulled from the repos, do I need to switch branches or something?

Yeah, that’s quite out of date now.

Try this: in your Arduino-Boards repo:

git submodule update --remote --init

That did it, thanks!

1 Like

Oh, maybe you have a particular version of Arduino-Boards checked out. Do this first:

git checkout master
git pull

Got it working now, thanks!

1 Like

Excellent! Just in case you cloned Qukeys more than ~an hour ago, I just fixed a bug this morning, so you might want to update.

Ok, moving on from compiling and flashing, I have now set it up to put Shift, Control, Alt, under the middle, ring and pinky finger of home row on each side. This is really cool! It even works for combined modifiers, so I can do hold down l and ;, to get C-A. It’s sort of intuitive too, so I can imagine getting up to speed reasonably quickly.

I’m curious, how are you using your thumb keys? I’ll probably keep dedicated modifer keys on my layout somewhere, but I’m not sure they need to go on the thumbs anymore…

1 Like

Awesome! I’m so happy someone is using my work! Let me know if you have any problems.

As for my keymap… Well, I have plans, but I’m hopeless at typing on it so far, and I’ve been enjoying playing with Kaleidoscope so much, that I haven’t done much. I had planned to use the thumb arcs almost the same as in the default keymap, but my own plugin seems to work better than I’d hoped, so I might use them for layer selection. Or stick with modifiers, because I want to try OneShot modifiers, and those can’t really be combined with Qukeys… Well, they could, but I can’t think of a scenario where that would actually be useful

I’m holding off trying to really type on a Model01 until I can really get my intended keymap done, because I want it to be totally different than a “normal” keyboard so my brain doesn’t confuse the two and overwrite my QWERTY ability. And I need to write one more plugin (working title: “Unshifter”) in order to get the keymap I want.

1 Like

Hi there,

I’m trying to use your plugin but I can’t get it to compile.
I’m getting something like that:

C:\Users\Z017316\Documents\Arduino\libraries\Kaleidoscope-Qukeys\src\Kaleidoscope\Qukeys.cpp:60:37: error: 'class Layer_' has no member named 'lookupActiveLayer'

           (qukeys[i].layer == Layer.lookupActiveLayer(row, col))) {

                                     ^

Any idea ?

Thanks.

Your version of Kaleidoscope is older than the one that Qukeys requires. Did you clone the git repository Arduino-Boards, or did you use the Arduio GUI boards-manager to install Kaleidoscope?