Modifying OneShot to ignore space key

Has anyone modified the oneshot plugin to ignore spaces? By this I mean instead of outputting e.g. shift+space, the space key would be passed through normally while oneshot stays active, until I press any other key like a letter.

I imagine the change needs to be done here (https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/plugin/OneShot.cpp). I will continue studying it until I can find a way to do this, but it would be great to receive any tips or pointers.

Let me know what you can find.

I wanted to modify OneShot to include another time out period that would cause the keyboard layout to return to the standard one after a long time (preferably something that we could set from on the .ino file).

My logic: I have the keyboard layout change on OneShot, but sometimes I walk away from the machine, and forget to check the layout. For the moment, I have made changes to some other module that forces the layout back to the standard one, but it does not clear the OneShot from the Fn Button I use to trigger the layout (one click: one shot layout change, two clicks for permanent layout change).
It works more or less ok, except that you need to check the color on the button. If the button still has a color indicating that the layout is active, the first press will remove that, but not really activate one-shot for the layout. The second press will activate one-shot, and a third press activate the layout. This is not really what I had in mind, but I could not figure out yet how to add another timeout to the One Shot module.

I figured it out! I had to make the following changes to OneShot.cpp.

152c152,153
<         !(mapped_key.getFlags() == (KEY_FLAGS | SYNTHETIC | SWITCH_TO_KEYMAP))) {
---
>         !(mapped_key.getFlags() == (KEY_FLAGS | SYNTHETIC | SWITCH_TO_KEYMAP)) &&
>         mapped_key != Key_Spacebar) {

Next step would be to have this only apply to my OSM for LeftShift, as I only need it there so I don’t have to slow down when capitalizing words.

I managed to make this change in ~/.arduino15/packages/keyboardio/hardware/avr/1.98-beta/libraries/Kaleidoscope/src/kaleidoscope/plugin/OneShot.cpp after following https://kaleidoscope.readthedocs.io/en/latest/setup_toolchain.html to setup the Arduino IDE. When uploading the firmware from the IDE, it will end up building from these sources. Surprisingly, it didn’t overwrite the layout I had configured in Chrysalis!

1 Like

Are you on the latest experimental (the one that includes plugins) firmware from Chrysalis?

I think it provides what you want. If I use OneShot fn (the palm key) and wait a few seconds, it will automatically deactivate and the next keypress will apply to the default layer.

Yes, that works fine.

My problem is slightly different: I press the palm key twice, so it changes the layout for all future keys.

Then, for some reason or another, I am pulled away from the keyboard, and when I come back to the computer, I forget to look at the colors of the keys (I am using functional colors)

I was able to put a timeout that switches the keyboard layout to the default after 10 minutes of inactivity, but the OneShot still has the status set to the other layout, even though the layout itself is the primary.

So, the issue I have is this: the palm key still shows red (indicating that the OneShot thinks that it is active), but the layout is back to the primary. When I press the palm key once after that, it cancels the OneShot. The second click on the same key activates the one-shot for the new layer, but after the first key, it goes back to the primary layer (which is what OneShot means). So you need to do three taps to put it locked on the new layer, but only if you timed out on the second layer.

What I would like to happen is for the layer locking to timeout and revert all keys to the primary mode (including modes like shift, control, and so on) so that if you walk away from the keyboard for an extended (configurable) amount of time, you know that it will be back to a default mode.

Sorry for the confusing description. Let me know if I am not the only one that needs something like that. Ideally, we would like to have an extra parameter, so if that is set to 0, there would not be a timeout, and the keyboard stays in the modified layer for anybody that thinks this is too complex. If you set to a value larger than the SingleShot timeout, that would be how long you have between key presses before the layout reverts. Not sure what would be correct syntax if you set the double-

Beware that with some layout [Space] and [Shift]+[Space] are not the same character (eg. with BÉPO shifted space give an unbreakable space), so make change in the core of OSM may add some strange behaviors for others :game_die:

I think I see what you mean now. You want a timeout for stickability. It’s configuration should probably be similar to .time_out just called .stickability_time_out instead, and like you say, it would default to 0 for the current behaviour but any greater value would specify the timeout in ms.

Don’t worry! I won’t attempt to upstream my changes, as I know it’s very specific. I’m still thinking about how to make it configurable through plugin properties since I know it’s something other speed-typists using oneshot for shift will bump into. I have come up with one approach using arrays, but my C++ fu is not good enough to do this in a way compatible with the constraints of Kaleidoscope.

Here you go, I hope it solves your problem. Configure it by adding OneShot.sticky_time_out = 4000; to your setup() (I decided to call it sticky instead of stickability as the latter wasn’t previously used in the code). Let me know if you need any help. I also think this would be useful to others, so it would be great if you could test it for a while (both with sticky_time_out defined and not defined) to see there are no bugs. Once you think it’s good I’ll PR it.

M src/kaleidoscope/plugin/OneShot.cpp
@@ -28,6 +28,7 @@ uint16_t OneShot::start_time_ = 0;
 uint16_t OneShot::time_out = 2500;
 uint16_t OneShot::hold_time_out = 250;
 int16_t OneShot::double_tap_time_out = -1;
+uint16_t OneShot::sticky_time_out = 0;
 OneShot::key_state_t OneShot::state_[OneShot::ONESHOT_KEY_COUNT];
 Key OneShot::prev_key_;
 bool OneShot::should_cancel_ = false;
@@ -152,6 +153,8 @@ EventHandlerResult OneShot::onKeyswitchEvent(Key &mapped_key, KeyAddr key_addr,
         !(mapped_key.getFlags() == (KEY_FLAGS | SYNTHETIC | SWITCH_TO_KEYMAP))) {
       should_cancel_ = true;
     }
+    if (sticky_time_out > 0 && isSticky())
+      start_time_ = Runtime.millisAtCycleStart();
   }
 
   return EventHandlerResult::OK;
@@ -175,8 +178,13 @@ EventHandlerResult OneShot::afterEachCycle() {
       break;
     }
   }
-  if (oneshot_active && hasTimedOut())
-    cancel();
+  if (oneshot_active) {
+    if (hasStickyTimedOut()) {
+      cancel(true);
+    } else if (hasTimedOut()){
+      cancel();
+    }
+  }
 
   bool is_cancelled = false;
 
@@ -214,7 +222,7 @@ bool OneShot::isActive(void) {
   for (uint8_t i = 0; i < ONESHOT_KEY_COUNT; i++) {
     if ((state_[i].active && !hasTimedOut()) ||
         state_[i].pressed ||
-        state_[i].sticky)
+        (state_[i].sticky && !hasStickyTimedOut()))
       return true;
   }
   return false;
@@ -225,7 +233,7 @@ bool OneShot::isActive(Key key) {
 
   return (state_[idx].active && !hasTimedOut()) ||
          state_[idx].pressed ||
-         state_[idx].sticky;
+         (state_[idx].sticky && !hasStickyTimedOut());
 }
 
 bool OneShot::isSticky(Key key) {
M src/kaleidoscope/plugin/OneShot.h
@@ -45,6 +45,7 @@ class OneShot : public kaleidoscope::Plugin {
   static void cancel(bool with_stickies = false);
 
   static uint16_t time_out;
+  static uint16_t sticky_time_out;
   static int16_t double_tap_time_out;
   static uint16_t hold_time_out;
 
@@ -105,6 +106,9 @@ class OneShot : public kaleidoscope::Plugin {
   static bool hasTimedOut() {
     return Runtime.hasTimeExpired(start_time_, time_out);
   }
+  static bool hasStickyTimedOut() {
+    return sticky_time_out > 0 && Runtime.hasTimeExpired(start_time_, sticky_time_out);
+  }
 };
 }
 }

Thanks. I will try tonight after work, and I’ll run it for a while to make sure it works. I’ll let you know how it goes.

–Luis Nakano.

It worked almost perfectly. Only one small issue: I had to change the stick_time_out from uint16_t to uint_32t (I wanted something around 5 to 10 minutes, but 16 bits would only let me go to 65.535 seconds).
I also had to translate it to the older version of Kaleidoscope I am using (nothing too bad, but I still do not know if I can get ActiveModColor and FunctionalColor to run together with the other stuff I have on the newer version), but nothing too complicated.
I am running more tests soon with the sticky_time_out not defined to be sure it works without breaking anything for anyone else.
Thanks for the help.

1 Like

@lgnakano — What are you using to reset the keymap (active layers)? It would probably be much simpler to just call kaleidoscope::plugin::OneShot.cancel() along with whatever calls are resetting the active layer(s).

I was using IdleLayers to reset it, but it had not occurred to me to make the call to OneShot.cancel() from there. It seems to work fine too.

I think I see what you mean now. you would like a timeout for stackability. Its configuration should probably be almost like .time_out just called .stickability_time_out instead, and such as you say, it might default to 0 for the present behavior but any greater value would specify the timeout in ms.