keymapper icon indicating copy to clipboard operation
keymapper copied to clipboard

mapping shift to be shift or parenthesis

Open kbilsted opened this issue 10 months ago • 19 comments

Inpired from the space cadet keyboard I I decided to try out remapping my Shift keys to work like so:

  • When held while pressing other keys, act like Shift.
  • When pressed and released on their own, type an opening or closing parenthesis (left and right shift respectively).

I have tried

ShiftLeft{!200ms} >> '('
ShiftRight{!200ms} >> ')'

alas u i face some weird timing issues. Shift-t require me to hold down shift for a "long time" before pressing T. otherwise i get a t rather than a T. Other characters do not have the same issue.

Also when holding down shift and pressing abc I get Abc where I would have expected ABC.

I then tried combining it with a ShiftRight{Any} but I think it only made matters worse.

What am I doing wrong?

kbilsted avatar Mar 24 '24 22:03 kbilsted

I'm pretty confident you'll be hitting some unexpected behavior if remapping shift-keys. With that said, try if this would work closer to what you want:

ShiftLeft{Any} >> ShiftLeft{Any}
ShiftRight{Any} >> ShiftRight{Any}
ShiftLeft{100ms} >> ShiftLeft
ShiftRight{100ms} >> ShiftRight
ShiftLeft >> '('
ShiftRight >> ')'

Personally I've done this instead (assuming US/UK layout):

AltRight{BracketLeft}                  >> !AltRight Shift{9} # '('
AltRight{BracketRight}                 >> !AltRight Shift{0} # ')'

It makes sense when using US/UK key layout. I can then get [{( and )}] all from the samt two keys.

ristomatti avatar Mar 25 '24 00:03 ristomatti

Slightly improved version that also supports ().

ShiftLeft !150ms ShiftRight >> '()'
ShiftLeft{ShiftRight} >> '()'
ShiftLeft{Any} >> ShiftLeft{Any}
ShiftRight{Any} >> ShiftRight{Any}
ShiftLeft{100ms} >> ShiftLeft
ShiftRight{100ms} >> ShiftRight
ShiftLeft >> '('
ShiftRight >> ')'

ristomatti avatar Mar 25 '24 01:03 ristomatti

Many thanks for your input. It has really bin a rollercoaster ride not only with this remapper tool, but also with so many other tools. They give off the impression you can do anything but remapping alt, shift - keys that screams for remapping you run into a ton of problems! There should be specific guides somewhere instructing you on how you go about remapping those keys. I see many mod CAPS key but I don't think it is an easy reach for me.

I'm very interested in remapping all my special keys for programming, so ill look into your definitions. Im on a nordic keyboard. I guess it is also very editor dependent. My editor autocompletes ), }, ] for me, so thef are less critical. I like the feel when I remodded J-K as you read in an earlier post, but perhaps it will cause more grief over time.

Any thoughts on this approach that places all layers on ALT's https://github.com/Roman-/redblue/blob/main/img/main.png

kbilsted avatar Mar 25 '24 10:03 kbilsted

It has really bin a rollercoaster ride not only with this remapper tool, but also with so many other tools

Tell me about it! Once you let the genie out of the bottle... I jumped ~5 months ago after receiving my first QMK enabled Keychron K3 Pro (ISO Nordic as well btw). It was quite a battle after using a ThinkPad Compact keyboard for the past 10 years. ThinkPad layout by itself was a lot more ergonomic, mainly because of it's shorter spacebar and reversed Fn/Control keys. It effectively had the thumb keys one letter closer which allowed reaching both Win and Alt key with my thumb. It took a month of fiddling with QMK to reach the same level of ergonomics.

At that point, I was pretty pleased with my setup. So pleased, that after I saw the release of Keychron K15 Pro (low profile, alice layout), I decided to take the plunge and order my first US ANSI keyboard (ISO Nordic not yet available)... Then it was time to start from the beginning to find the most practical way of using ÄÖ without changing the input language. :sweat_smile:

Around that time I ran across this article comparing Linux keymapping tools and found Keymapper. After receiving my K15 Pro, I thought I'll try if I could get away with Keymapper instead of QMK. Now ~4 months later, I've ended up using both (but mostly Keymapper). Recently I've also added Espanso to the mix after a friend tipped about it. It neatly complements the setup with the hotstring feature I loved AutoHotkey for back when I used Windows (~12y ago).

It's been a period full of relaxing tweaking, pleasing outcomes, frustration and headaches. So many hours of work time lost during this quest, I for sure aren't going to get it "paid back" increased productivity :joy:. Now it's only about ergonomics, reducing friction (...and an obsession).

Any thoughts on this approach that places all layers on ALT's

I don't think about layers as much now with Keymapper as I do app specific remapping. With that said, I use Alt-keys in my remaps the most of the traditional mod-keys. I've got so small hands, using Control on it's normal place is only doable for ASDF/ZXCV keys. I've put it on Caps Lock but like you, I find it hard to reach and don't use it much.

For QMK layers, I've found Tab more comfortable. On the K3 Pro (ISO Nordic), I found these keys convenient for layers while allowing use of most shortcuts I was already using:

image

...I also put Esc in place of Å as it's not used in Finnish.

ristomatti avatar Mar 25 '24 13:03 ristomatti

On my K15 Pro, I've got many layer keys. Ones in green I find ergonomic, the ones in red are for specific uses:

image

Layer 1 is mostly empty, just QMK macros on the F-keys. Layer 2 is the one I've primarily used to complement Keymapper to get keys that are typically not used by the OS or apps and also for navigation. I use the Esc between space bars as the layer toggle and love it! This is what it looks like currently:

image

I've put numpad keys for use IJKL-navigation everywhere but also because they can be remapped while keeping the arrow keys as they are (for the rare situations I do find them, preferable). The special keys are mostly for Keymapper remaps for WebStorm.

Layer 3 (accessed from Tab) has some default QMK related keys for RGB (which I rarely use). The mouse keys on the right side I occassionally use:

image

The Esc between the space bars is what I use the most for application shortcuts. I mostly use it as a kind of like leader key is used in Vim, e.g. press Esc T rather than Esc-T. I'm not sure if it's related to what QMK does or just a feature of mechanical keyboards in general, but if I type Esc T at my typical speed, the keys behave like Esc-T. Due to this, I use the X{Y} syntax in keymapper.conf, even though most of the time I press them in sequence.

You can see a lot of that on this (incomplete) config I use with WebStorm. It might be difficult to get an idea of some of the mappings as they're handled in WebStorm. I've used some features from the next branch there as well. This is just for inspiration, I'm just adjusting to it. 🙂

# Fast key sequences
Combo     = $0 !250ms $1{!250ms}
DoubleTap = $0 !250ms $0{!250ms}
Tap       = $0 !250ms !$0

# Key sequences
OneShot   = $0 !2000ms
TwoShot   = $0 !2000ms $1 !1000ms

Hold      = $0{200ms}

# ...

# JetBrains IDE // Aliases
[class=JetBrainsIDE]
  TabFocus  = U | O
  EscTap    = Tap[Escape]
  Esc_G     = OneShot[Escape{G}] $0

# JetBrains IDE // Pull request, commit or stash diff view
[class=JetBrainsIDE title=/Diff for Pull Request |Commit: |Shelved: |History: /i]
  ContextActive >> context["JetBrainsIDE pull request commit or shelve diff"]
  NavLeft                 >> next_difference
  NavDown                 >> prev_difference
  Forward                 >> compare_next_file
  Back                    >> compare_prev_file
  Control{J}              >> next_difference
  Control{K}              >> prev_difference
  Control{N}              >> compare_next_file
  Control{P}              >> compare_prev_file
  DoubleTap[Escape]       >> Escape
  Escape                  >>

# JetBrains IDE // VCS changes
[class=JetBrainsIDE modifier="!AltGr" title=/Settings|Commit Changes|Commit: /i]
  ContextActive >> context["JetBrainsIDE VCS changes"]
  Control{J}              >> next_difference
  Control{K}              >> prev_difference
  Control{N}              >> compare_next_file
  Control{Shift{N}}       >> compare_prev_file

# JetBrains IDE // Keychron K15 Pro mappings
[class=JetBrainsIDE device=KeyboardKeychron]
  Shift{Find}             >> replace_in_files
  Find                    >> find_in_files
  Help                    >> show_hover_info
  Exec                    >> prefix_git_action
  Erase                   >> rollback_change
  AltGr{Shift{Delete}}    >> cut_to_line_end
  AltGr{Delete}           >> delete_to_line_end

# JetBrains IDE // Default mappings
[class=JetBrainsIDE]
  ContextActive >> context["JetBrainsIDE mappings"]

  Jump = Combo[Escape, Space]

  # Use numpad as arrow keys
  NavUp                   >> ArrowUp
  NavLeft                 >> ArrowLeft
  NavDown                 >> ArrowDown
  NavRight                >> ArrowRight

  # Tool window shortcuts
  Hold[Digit]             >> AltLeft{Digit} ^

  # Function key shortcuts
  Hold[FunctionKey]       >> Control{FunctionKey} ^

  (Control AltLeft){Any}  >> (Control AltLeft){Any}
  Tap[Escape] Direction   >> (Control AltGr){Direction}
  Tap[Escape] TabFocus    >> (Control AltGr){TabFocus}
  AltLeft{Direction}      >> (AltLeft AltGr){Direction}
  ControlRight{Direction} >> (Control AltGr){Direction}

  EscTap Shift{7}         >> toggle_block_comment
  EscTap 7                >> toggle_comment
  EscTap Shift{Slash}     >> toggle_block_comment
  EscTap Slash            >> toggle_comment

  _Shift{Escape}          >> close_tool_window
  BrowserBack             >> navigate_back
  BrowserForward          >> navigate_forward

  Control{BracketLeft}    >> Control{BracketLeft}  # move caret to previous paragraph
  Control{BracketRight}   >> Control{BracketRight} # move caret to next paragraph

  AceJump                 = Combo[Escape, Space]
  JTarget                 = I | M | L | W | J | A | B | T | D | Space | Comma | Enter
  AceJump Shift{JTarget}  >> Control{Comma} Shift{JTarget}
  AceJump JTarget         >> Control{Comma} JTarget

  Esc_G[T]                >> jump_to_top
  Esc_G[B]                >> jump_to_bottom
  Esc_G[I]                >> jump_to_text_start
  Esc_G[K]                >> jump_to_text_end
  Esc_G[L]                >> jump_to_line
  Esc_G[W]                >> jump_to_word
  Esc_G[C]                >> jump_to_character

  Esc_G[P]                >> toggle_problems

  Escape{F}               >> find
  Escape{R}               >> replace

  Escape{W}               >> close_tab
  Escape{T}               >> toggle_terminal
  Escape{A}               >> show_actions
  Escape{Q}               >> show_quick_documentation
  F1                      >> show_hover_info

  Escape{D}               >> goto_declaration
  Escape{Y}               >> goto_type_declaration
  Escape{B}               >> goto_implementation

  Escape{P}               >> goto_file
  Escape{S}               >> goto_symbol

  Escape{Semicolon}       >> jump_to_line
  Escape{5}               >> jump_to_matching_brace
  Escape{Quote}           >> jump_to_last_edit_loc
  Escape{N}               >> next_difference
  Escape{E}               >> next_error

  Escape{J}               >> join_lines
  Escape{Z}               >> rollback_change
  Escape                  >> Escape

  BracketLeft{BracketRight} >> BracketLeft BracketRight

  BracketLeft{100ms}      >>
  BracketLeft{E}          >> prev_error
  BracketLeft{C}          >> prev_change
  BracketLeft{F}          >> prev_function
  BracketLeft{Any}        >>

  BracketRight{100ms}     >>
  BracketRight{E}         >> next_error
  BracketRight{C}         >> next_change
  BracketRight{F}         >> next_function
  BracketRight{Any}       >>

  AltGr{Enter}            >> extend_selection
  (Shift AltGr){Enter}    >> shrink_selection

# JetBrainsIDE // IDE actions
[class=JetBrainsIDE]
  ContextActive             >> context["JetBrainsIDE actions"]

  prefix_git_action         >> _Alt{G}

  close_tab                 >> AltLeft{W}
  close_tool_window         >> Shift{Escape}

  find                      >> Control{F}
  find_in_files             >> (Control Shift){F}
  replace                   >> Control{R}
  replace_in_files          >> (Control Shift){R}

  show_actions              >> _Alt{A}
  show_context_actions      >> _Alt{Enter}
  show_hover_info           >> Control{F1}
  show_quick_documentation  >> Control{Q}

  toggle_terminal           >> AltLeft{T}
  toggle_problems           >> AltLeft{8}

  goto_file                 >> AltLeft{P}
  goto_symbol               >> AltLeft{S}

  goto_declaration          >> Control{B}
  goto_type_declaration     >> (Control Shift){B}
  goto_implementation       >> Control{AltLeft{B}}

  jump_to_text_start        >> Control{Home}
  jump_to_text_end          >> Control{End}
  jump_to_top               >> Control{PageUp}
  jump_to_bottom            >> Control{PageDown}
  jump_to_line              >> Control{G}
  jump_to_character         >> AltLeft{Comma}
  jump_to_word              >> Control{Comma}
  jump_to_matching_brace    >> Control{Shift{M}}
  jump_to_last_edit_loc     >> AltLeft{Shift{E}}

  next_error                >> _Alt{Escape}
  prev_error                >> _Alt{Shift{Escape}}

  next_function             >> _Alt{ArrowDown}
  prev_function             >> _Alt{ArrowUp}

  rollback_change           >> (Control AltLeft){Z}
  next_change               >> (Control AltLeft){ArrowDown}
  prev_change               >> (Control AltLeft){ArrowUp}
  next_difference           >> (Control AltLeft){ArrowDown}
  prev_difference           >> (Control AltLeft){ArrowUp}
  compare_next_file         >> (AltLeft Shift){N}
  compare_prev_file         >> (AltLeft Shift){J}

  extend_selection          >> _Alt{W}
  shrink_selection          >> _Alt{Shift{W}}

  cut_to_line_end           >> !AltGr (Shift Control AltLeft){Delete}
  delete_to_line_end        >> !AltGr (Control AltLeft){Delete}
  join_lines                >> _Control{J}
  toggle_comment            >> !Escape _Control{7}
  toggle_block_comment      >> !Escape _Control{Shift{7}}

ristomatti avatar Mar 25 '24 13:03 ristomatti

@kbilsted It should work with your initial configuration. I think I know what's going wrong and how to fix it.

houmain avatar Mar 26 '24 09:03 houmain

@kbilsted It should work with your initial configuration. I think I know what's going wrong and how to fix it.

@houmain Let me know if you want me to test something.

kbilsted avatar Mar 26 '24 18:03 kbilsted

Hi, with the latest commits this should work as expected:

ShiftLeft{!200ms} >> '('
ShiftRight{!200ms} >> ')'

houmain avatar Apr 10 '24 18:04 houmain

will test on latest version 4.0.1

Testing done on windows 10. Timing works very well when pressing 1 key at a time. All behaved as expected.

Unfortunately I found a weird issue.. when combining shift + cursor down to select text in an editor then you easily get in a situation where it doesn't select, but only moves down..

Then I tried clicking shift + many keys which should produce all uppercase letters, but often I can trigger it to do lower case... eg IEieie and TIEATIEEAIIETNAIEAIeanieieaieaieaieaieaieaieaieaaieieaaieieaIAEiea the upper case at the end of the last sequence was me pressing again

kbilsted avatar Apr 11 '24 16:04 kbilsted

@kbilsted Try with:

ShiftLeft{!120ms}  >> '('
ShiftRight{!120ms} >> ')'
ShiftLeft{Any}     >> ShiftLeft{Any}
ShiftRight{Any}    >> ShiftRight{Any}

It will likely still mess up with any other mapping involving the shift keys if you have any. I'd confine this to apps where you need it regardless.

(try fiddling with the timing to be the smallest number you can reliably trigger it)

ristomatti avatar Apr 11 '24 16:04 ristomatti

Sorry, I thought you were building from source. With latest commits I meant the ones after 4.0.1. But I think they are worth creating a 4.0.2.

houmain avatar Apr 11 '24 17:04 houmain

You probably replied to @kbilsted but I usually am. This time I figured you were talking about the earlier changes.

P.S. What is the proper way of signaling CMake to disable Wayland support? I've hard coded it on the build file, committed that change and always rebase the new changes. It's beginning to feel silly. (With Wayland support, the build fails under my crusty Ubuntu 20.04 based system.

ristomatti avatar Apr 11 '24 18:04 ristomatti

I've even created a mise workspace config to simplify building, starting/stopping the installed version and running a freshly built version for testing. :joy:

image

ristomatti avatar Apr 11 '24 18:04 ristomatti

With 4.0.2 the workaround I suggested is not anymore necessary. Initially I was seeing a regression with some of the mouse mappings that you fixed with 4.0.1, but it seems like restarting keymapper fixed it. I'll run with it for some time to see if I run into some other issues.

If nothing breaks, this has a lot of potential to allow other use cases as well :crossed_fingers:! I've reverted my changes back several times due to modifiers misbehaving futher down the config.

ristomatti avatar Apr 11 '24 18:04 ristomatti

with v4.0.2 The result is much better! but i can still easily make it print lower case when it should print upper!

with one hand steadily press the same key again and again. Then with the other press shift. hold it a while, release it and hold again fast

    AAAAAAAAAAaaaaaaaaaa  AAAAAAAAAAAAAAAAAaaaaaaaaaa
              ^                  ^         ^
     arrow denote release and press shift

so not all the time but frequent when you use shift and a letter fast in succession it become lowercase

kbilsted avatar Apr 12 '24 14:04 kbilsted

I noticed the same. Also it seemed that when it occurred, I had to do SpaceLeft, SpaceRight multiple times to get (), after which they worked again. Like the bindings would've been moved out of cache or something. Then again, my config is really complex and at the time I had 4 input devices at the same time with device specific configs. I had just added this novelty to the mix https://www.aliexpress.com/item/1005006099525907.html (bought mainly for decoration, space around my keyboard is reserved to mice for both hands). :slightly_smiling_face:

ristomatti avatar Apr 12 '24 16:04 ristomatti

Thanks for the feedback. The last commit should at least fix the phenomenon described by @kbilsted.

@ristomatti I would love to see a photo of your desktop when everything is deployed. 😂

houmain avatar Apr 12 '24 17:04 houmain

@houmain perhaps @ristomatti has watched too many cyberpunk movies :)

kbilsted avatar Apr 12 '24 19:04 kbilsted

@ristomatti I would love to see a photo of your desktop when everything is deployed. 😂

"Deployed" :rofl:

Here we go:

deskie

ristomatti avatar Apr 12 '24 23:04 ristomatti

Making good use of two mice is already quite impressive!

houmain avatar Apr 13 '24 17:04 houmain

I've had RSI more than once. It balances a little bit. For the same reason I'm also using the lightest switches I could find, Gateron NuPhy Aloe's. They've got opering force of only 37±15gf. It took "a bit" of type to not typo every other word. but after that, these have been an absolute breeze to use. I tried going with the stock red switches but after just 2-3 weeks I started getting wrist pain.

ristomatti avatar Apr 13 '24 19:04 ristomatti

have you tried https://www.maxgaming.dk/dk/switchar/choc-low-profile-pink-pro i want a glove 80 with red pro i think i'll enjoy flat and light

kbilsted avatar Apr 13 '24 20:04 kbilsted

AFAIK only Gateron's low profile switches are compatible with Keychron's low profile models. I've got the K3 Pro as well, so I think I'll confine myself to ones that are compatible. Besides, I really like stock keycaps as well. I did order two sets of hopefully compatible keycaps from AliExpress for my K3 Pro. My plan is to convert it to ISO UK layout now that I've gotten used to ANSI US on this split keyboard.

20g sounds insaly light thought. Prior to my jump to mech keyboards half a year ago, I had used Lenovo ThinkPad Compact TrackPoint keyboards (and to some extent ThinkPad integrated keyboards) for maybe 8 years. The 50±15 gf of Gateron Red switches felt really stiff compared to 39gf of the ThinkPad switches. I've got small and relatively dexterous fingers/hands but I was surprised how light the Aloe's were. It really felt like ice scating for the first weeks :joy:.

Personally I think I could like Gateron's optical white switches available for the non-pro Keychron K-series models though:

image

Unfortunately QMK firmware does not support optical switches even though they'd otherwise fit (or at least this is what I've read).

ristomatti avatar Apr 13 '24 21:04 ristomatti

Thanks for the feedback. The last commit should at least fix the phenomenon described by @kbilsted.

Hi, this is finally easily testable in the 4.1.0 release.

houmain avatar Apr 17 '24 17:04 houmain

I hope it works this time. Tell me when ready

kbilsted avatar Apr 17 '24 18:04 kbilsted

I hope it works this time. Tell me when ready

Well, it is.

houmain avatar Apr 19 '24 15:04 houmain

I found this to work pretty much perfectly a few hours of actual use while working but the mapping itself didn't work out well with my workflow. I decided to continue using my old way of

AltGr = AltRight
AltGr{BracketLeft} >> Shift{9}
AltGr{BracketRight} >> Shift{0}

Aside - @houmain, I want to say version 4.1 has so far worked stellar (besides the few "cosmetic" issues discussed at #126 and #131). It really shows you know what you're doing. Despite my non-existant C++ skills, I've read and written enough code to say the codebase looks very "clean" and well thought out. May I ask how long you've been writing C++, is it something you do for work as well and if so, what type of industry? I'm guessing game development based on your other repos. 🙂

ristomatti avatar Apr 19 '24 21:04 ristomatti

...to continue on the previous comment, I just today noticed this inverse condition syntax has appeared and found use to it right away.

I also noticed aliases within parameterized aliases now work as well I mentioned on some other issue thread. Just this evening I added this section to streamline Tmux pane and window navigation (I've included some other related parts here):

# Key groups
Digit           = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

# Directional navigation
KeyUp           = I
KeyLeft         = J
KeyDown         = K
KeyRight        = L
Direction       = KeyUp | KeyLeft | KeyDown | KeyRight

# Class matchers
Terminal        = /kitty|tmux/i

# Passtrough common modifiers
[default]
  AltLeft       >> AltLeft
  AltRight      >> AltRight
  ControlLeft   >> ControlLeft

[class != Terminal, title != "tmux"]
  ControlRight  >> ControlRight

# Terminal // Tmux
[class = Terminal, title = "tmux"]
  TmuxMod     = Tab
  TmuxTap     = Tap[ControlRight]

  # Bindings used in tmux.conf
  TmuxBinding = Direction | N | P | O | W | Comma | Period | Digit

  TmuxPrefix  = !TmuxMod (ControlRight A){10ms} 10ms $0
  
  # Navigate with Tab+X
  TmuxMod{TmuxBinding}  >> TmuxPrefix[TmuxBinding]
  
  # Navigate with Control followed by X
  TmuxTap TmuxBinding   >> TmuxPrefix[TmuxBinding]

  ControlRight          >> ControlRight

ristomatti avatar Apr 19 '24 21:04 ristomatti

For the curious, I've just updated my public (slightly redacted) gist of the full config here.

ristomatti avatar Apr 19 '24 22:04 ristomatti

Last night I noticed I had also missed the "optional comma" within context definitions. It's like you've read my thoughts on this one, or did you spot it from some screenshot I've posted. Just recently changed the ContextActive enter/leave logs to look like this:

image

After I spotted the change by diffing README.md, I immediately reformatted all the context blocks to use the new [class = /foo|bar/i, title = "baz"] format. It's so much easier to read. :+1:

ristomatti avatar Apr 20 '24 19:04 ristomatti