问题
Is there a way to make simultaneous key presses into a keybinding, e.g. for the keys w, e, f, when pressed within 0.05 seconds of each other, to trigger a command?
To be more specific:
If w, e, f are pressed within 0.05 seconds of each other, then upon the pressing of the last one, XMonad should trigger said command. XMonad should also have intercepted the three keys so that they are not superfluously sent to the focused window.
Otherwise (if at least one of them are not pressed within the 0.05 second time period) XMonad should send the keys to the focused window as usual.
My goal in this is to use w, e, f to "Escape" into a vim-like "Normal Mode", a XMonad.Actions.Submap (submap).
Update with a failed method, in case anyone can see a way to fix it:
I attempted to implement this using submaps, so that, for example, if you pressed w you would end up in chord_mode_w
, if you pressed e from there you would end up in chord_mode_we
, and if you pressed f from there you would finally end up in normal_mode
, for instance. The implementation was very messy: I included, in my main keybindings, something like:
("w", spawn "xdotool key <chord_mode_w_keybinding> ; sleep 0.05 ; xdotool key <abort_keybinding>")
(chord_mode_w_keybinding, chord_mode_w)
for detecting w (the rest would be similar), along with (incomplete) submaps such as:
chord_mode_w = submap . mkKeymap c $
[
("e", chord_mode_we )
, ("f", chord_mode_wf )
, (abort_keybinding, pasteString "w")
-- in order for the submap to not eat all other letters,
-- would need to include all mappings like:
, ("a", pasteString "wa")
, ("b", pasteString "wb")
...
]
chord_mode_we = submap . mkKeymap c $
[
("f", normal_mode )
, (abort_keybinding, pasteString "we")
-- in order for the submap to not eat all other letters,
-- would need to include all mappings like:
, ("a", pasteString "wea")
, ("b", pasteString "web")
...
]
chord_mode_wf = submap . mkKeymap c $
[
("e", normal_mode )
, (abort_keybinding, pasteString "wf")
-- in order for the submap to not eat all other letters,
-- would need to include all mappings like:
, ("a", pasteString "wfa")
, ("b", pasteString "wfb")
...
]
A complete implementation would evidently have been very messy, but in theory should have sent me to normal_mode
if I pressed "wef" within 0.05 seconds of each other, aborting and typing out the characters otherwise. There were two problems, however:
pasteString
(as well as the other paste functions inXMonad.Util.Paste
) is too slow for normal typingI would end up in
normal_mode
only a small fraction of the time even if I set the abort delay much higher. Not sure of the reason behind this.
(The reason I used pasteString
when aborting instead of spawning another xdotool
was that output of xdotool
would re-trigger one of the chord_mode_w_keybinding
, chord_mode_e_keybinding
, chord_mode_f_keybinding
, back in the main keybindings, sending me back to the chord modes indefinitely.)
回答1:
https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Actions-Submap.html
Submap really does do almost what you want (it gets you most of the way there) ... and I will suggest that you may want to change what you are trying to do, ever so slightly, and then Submaps handle it perfectly.
You can configure Submap to capture a w
key event, and start waiting for an e
which then waits for an f
. I even tried this out, and confirmed that it works:
, ((0, xK_w), submap . M.fromList $
[ ((0, xK_e), submap . M.fromList $
[ ((0, xK_f), spawn "notify-send \"wef combo detected!\"" ) ])
])
However, the above is almost certainly not something you'd actually want to do... since it is now impossible to send a w
keypress to a window (I had to disable that config before typing this answer, which required sending several w
keypress events to the active window)
The behavior I saw just now when playing with this is: if I press w
, xmonad traps that event (does not send it to the active window) and is now in a state where it is waiting for either e
or something else ... if I press something else, xmonad is no longer in that state, but it does not "replay" those trapped events. So if I press w
and then some other key which isn't e
, the result is only that xmonad is back out of the state listening for keys in the submap. It does not ever allow a w
through to the active window... which I found inconvenient.
Your options as I see it are:
1) settle for the initial keybinding having a modifier, so your multi-key command would be Mod4-w
e
f
2) find a way to hack the delay logic you described into the action in the submap
I started using a config like this, where I nest conceptually similar actions that are infrequently needed under a tree of nested submaps, analogous to what I pasted above. The root of that tree, however, always has a modifier, so it doesn't steal keypresses which I want to forward to the active window. I use Mod3-semicolon
as the root of that tree, and then there are many unmodified keypresses which are just letters (they are mnemonics for the actions).
To me, this seems like a better solution, rather than waiting for a few hundred milliseconds, and then forwarding the events unless they matched. I feel like I would find that annoying, since it would delay any w
keypress event...
YMMV, hope it helps someone
来源:https://stackoverflow.com/questions/27747079/xmonad-is-there-a-way-to-bind-a-simultaneously-triggered-keychord