Some funky shift customizations I’d like to make :)

I would like to make keys that work like this:

Normal Shift
/ \
? !
. :
, ;

I want to use the standard en-US QWERTY layout on the OS side.

Is this possible? If so, I’d love some guidance!

There is a similar thread, which I’ve read through, which mentions the ShapeShifter and TopsyTurvy plugins, but it’s not clear if those plugins are able to do the things I’d like:

Here’s my attempt at using the ShapeShifter plugin:

static const kaleidoscope::ShapeShifter::dictionary_t shape_shift_dictionary[] PROGMEM = {
  // Non-shift: `/`, OK. Shift: `|`, wanted `\`.
  {Key_Slash, Key_Backslash},
  // Non-shift: `?1?1?1?1?1?1?1?1?1?1?1?1?1?1`, wanted `?`. Shift: `!`, OK.
  {LSHIFT(Key_Slash), Key_1},
  // Non-shift: `.`, OK. Shift: `:`, OK.
  {Key_Period, Key_Semicolon},
  // Non-shift: `,`, OK. Shift: `:`, wanted `;`.
  {Key_Comma, Key_Semicolon},
  {Key_NoKey, Key_NoKey},
};

Here’s my WIP layout so far:

1 Like

ShapeShifter will keep shift held when inputting the replacement, that’s why you get : (Shift+;) instead of ;. It only replaces the shifted part, and can’t invert shift, hence the first issue (that also triggers a bug I’m yet to solve).

I’m afraid you’ll need a new plugin for this kind of thing. Or a set of macros, that check if Shift is held, and do their thing based on that information. There’s… a few corner cases there, though. For starters, kaleidoscope::hid::wasModifierActive() will be of great help with this…

I’ll try to help more, when time allows, and if needed.

1 Like

Thanks for the super quick reply!

I tried this, and it is pretty close:

static void slashBackslashMacro(uint8_t keyState) {
  if (keyToggledOn(keyState) || keyIsPressed(keyState)) {
    if (
      kaleidoscope::hid::wasModifierKeyActive(Key_LeftShift) ||
      kaleidoscope::hid::wasModifierKeyActive(Key_RightShift)
    ) {
      kaleidoscope::hid::releaseKey(Key_LeftShift);
      kaleidoscope::hid::releaseKey(Key_RightShift);
      kaleidoscope::hid::pressKey(Key_Backslash);
    } else {
      kaleidoscope::hid::pressKey(Key_Slash);
    }
  }
}

When pressing the key without shift, / is printed as expected.
With shift, \?\?\?\?\?\?\?\?\?\?\?\?\?\?\ is printed.

Yeah… hm. Perhaps releaseing shifts, then sending a report, then the backslash would help? And do the release+report combo only on keyToggledOn, not while keyIsPressed? Just a hunch.

(Sorry for the brevity, kinda time-pressed here and can’t elaborate :/)

1 Like

Adding kaleidoscope::hid::sendKeyboardReport(); after releasing the shift keys does not seem to make much of a difference as far as I can tell.

Only releasing shifts on keyToggledOn prints this: \?|

Here’s a version that seems to work pretty good:

static void slashBackslashMacro(uint8_t keyState) {
  if (keyToggledOn(keyState)) {
    if (
      kaleidoscope::hid::wasModifierKeyActive(Key_LeftShift) ||
      kaleidoscope::hid::wasModifierKeyActive(Key_RightShift)
    ) {
      kaleidoscope::hid::releaseKey(Key_LeftShift);
      kaleidoscope::hid::releaseKey(Key_RightShift);
      kaleidoscope::hid::pressKey(Key_Backslash);
    } else {
      kaleidoscope::hid::pressKey(Key_Slash);
    }
  }
}

I lose key repeat, but it’s not the end of the world. I haven’t tested extensively, so there might be some problem I’ve missed.

2 Likes

Those are some of the same mappings I intend to use. I’ve got a plan for a way to do this, but I still need to write the code for the plugin that would do the best job of it, since none of the existing plugins really do the trick. I’ll definitely let you know when I’ve got something better than a kludgy macro to do it.

1 Like

The really tricky part about what we want to do (arbitrary reassignment of symbols on shifted and unshifted “layers”) is dealing with key overlap. So, if I’m holding down two keys, one of which should produce a symbol that’s shifted (as far as the OS keymap is concerned) and one of which is not, we have to decide what to do. I think one of the two keys should “win”, and the other’s keycode should be suppressed until at least one keyswitch state changes. And that “winner” should not be decided in a way that seems arbitrary to the user. Probably the best way is to decide that either the one that was pressed first wins, or the most recent keypress wins (my inclination is for the latter). Then we’d suppress either every key that wants the “wrong” shift-state, or simply suppress every other keycode.

I haven’t started trying to write any code yet, but I’m interested in the opinions of other people who want this feature before I settle on a design.

4 Likes

Could you give an example of when one might wish to hold down two keys simultaneously? I am not coming up with anything but chording that would be an intentional case of doing that.

Not often, outside of certain types of games (for which it would be better to use a different layer or keymap, anyway), but it also applies to accidental overlap during normal typing.

I don’t know about you, but if I biff my typing the best outcome is… nothing. Which I guess just expands your options rather than limiting them. :slight_smile:

I think I would document that as a “so don’t do that” in the plugin and return nothing. Bonus points for a flash to indicate there was an entry error.

1 Like

Another idea I have is:

  1. Change Key_LeftShift and Key_RightShift into ShiftToLayer(SHIFT).
  2. Duplicate the main layer into a new SHIFT layer, adding LSHIFT() around keys where needed.

That might work pretty good. But shift-clicking won’t work. But perhaps I can add an extra, real, shift key somewhere for such occasions. I don’t shift-click much anyway.

I’ve implemented the above idea now: https://github.com/lydell/Model01-Firmware/commit/5d9632022889eb54d62db3925f49b139b2becd33

Seems to be working great so far. Just remember that if you need to hold down shift for real, you need to hold down fn+shift.

4 Likes