Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible bug in eventtap.keyStroke when combined with VimMode.spoon's enterWithSequence('jk') #3685

Open
gurjeet opened this issue Sep 8, 2024 · 0 comments

Comments

@gurjeet
Copy link

gurjeet commented Sep 8, 2024

TL;DR version: See the section "How to Reproduce"

It was quite a frustrating bug to track down. I encountered it when working on a Vim-compatible web app, and while using this app in Firefox, a keypress of 'j' would result in the app producing the letter 'a'; so, instead of moving cursor down in response to 'j' (like you'd expect in Vim) the app would enter "append" mode, and start writing the letter 'a' repeatedly.

I first assumed this to be a bug in the web app, since it was using the KeyboardEvent (henceforth 'e') e.keyCode to detect which key was pressed. According to Mozilla's MDN [1], e.keyCode is deprecated and should ideally be replaced with the use of e.Key property. And in fact when I changed the code to use e.Key, the app correctly detected the keypress as 'j'. So I moved forward with that change as a bug-fix to the app.

Notably, in Chrome the unmodified app behaved normally (correctly detecting 'j' as 'j'). So I assumed this to be a Firefox-specific quirk for a deprecated feature, and hence couldn't be reported to Mozilla.

But I was surprised to see the same behaviour (keypress 'j' showing up as letter 'a') in Ubuntu VM running inside UTM (a macos application wrapper around qemu). This lead me to believe that this must be a bug in my macos installation, perhaps a bit in RAM has been flipped somewhere, and performing a reboot may reset the bug, and fix the problem. But after a few restarts, and after a multi-hour overnight shutdown hoping to reset the RAM did not fix the problem, it was now looking like a corruption somewhere that is persistently stored.

I disabled all the apps that were enabled under the "Privacy & Security" > Accessibility section, and rebooted; the list of apps under that section included Hammerspoon. I was relieved to see that Firefox did not exhibit the buggy behaviour after the reboot. Then it was a matter of time, using the process of elimination to narrow down which app was causing this problem. After it was clear it was Hammerspoon, then an inspection of init.lua, and disabling code selectively further narrowed it down to VimMode.

For testing out the change in behaviour with and without the problematic software enabled, I developed a javascript (see below 2) snippet to console.log() the various attributes of the event object received by the webpage.

How to Reproduce

  1. Install Hammerspoon and VimMode.spoon
  2. Enable enterWithSequence('jk'), as documented in VimMode.spoon readme
  3. Restart Hammerspoon, or reload its config
  4. Open Firefox
  5. Go to example.com (or any website; doesn't really matter)
  6. Paste and execute the javascript from snippet under [3] in the developer console
  7. Wait for the 'Ready' message in the console
  8. Focus/Click on the webpage
  9. Type the lowercase letters H and J alternatively, repeatedly.

You'll notice that in Chrome output below, the keyCode received is as expected in normal operation (keyCode 74), but e.code value is KeyA instead of KeyJ. The keyCode value being correct, 74, explains why the app worked on Chrome despite a wrong e.code value.

To fix the problem, comment out the vim:enterWithSequence('jk') line in Hammerspoon's init.lua, and restart Hammerspoon or reload its configuration.

I have tried to track it down, and I believe line no 120 in VimMode.spoon/lib/key_sequence.lua (see 2) tries to do the right thing by sending forward the characters captured so far, but haven't been found to be interesting to VimMode. I believe it is eventtap.keyStrokes() that sends the wrong e.keyCode and e.code values.

Below is the sample of buggy output in Firefox.

key j, code KeyA, keyCode 65, shiftKey false, ctrlKey false debugger eval code:17:13
key h, code KeyH, keyCode 72, shiftKey false, ctrlKey false debugger eval code:17:13
key j, code KeyA, keyCode 65, shiftKey false, ctrlKey false debugger eval code:17:13
key h, code KeyH, keyCode 72, shiftKey false, ctrlKey false debugger eval code:17:13
key j, code KeyA, keyCode 65, shiftKey false, ctrlKey false debugger eval code:17:13

And following is the sample of buggy output in Chrome.

key h, code KeyH, keyCode 72, shiftKey false, ctrlKey false VM15:11
key j, code KeyA, keyCode 74, shiftKey false, ctrlKey false VM15:11
key h, code KeyH, keyCode 72, shiftKey false, ctrlKey false VM15:11
key j, code KeyA, keyCode 74, shiftKey false, ctrlKey false VM15:11

[1]: MDN: KeyboardEvent.keyCode property
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

[3]:

    let sleep = (ms) => { return new Promise(resolve => setTimeout(resolve, ms)); }
    var jq = document.createElement('script');
    jq.src = "https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js";
    document.getElementsByTagName('head')[0].appendChild(jq);
    // ... give time for script to load, then type 
    await sleep(1000);
    jQuery.noConflict();
    let $ = jQuery
    $(document).keydown(e => {
      //console.log("received keydown event:", e);
      console.log(`key ${e.key}, code ${e.code}, keyCode ${e.keyCode}, shiftKey ${e.shiftKey}, ctrlKey ${e.ctrlKey}`);
    });
    console.log('Ready');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant