No longer freeze or restart when pressing control+shift+downArrow in MS Word#11478
Conversation
…the given script passing it this gesture, by using scriptHandler.executeScript. this is implemented to allow Gesture subclasses to perform actions before / after script execution. * scriptHandler._queueScriptCallback: call Gesture.executeScript rather than executeScript directly. * keyboardHandler.KeyboardInputGesture: track the number of times this Gesture instance is sent as input onto the Operating System (I.e. how many times Gesture.send is called). this is recorded on a sendCount instance variable. This is useful to see if a script has actually sent its gesture on or not. * keyboardHandler.KeyboardInputGesture: implement executeScript, which after executing the script normally, checks to see if the gesture was never actually sent, and that the gesture's modifiers could possibly perform an OS action (such as an input language change) and if so then sends key down and key up for the special VK_NONE key. This code was moved from internal_keyDownEvent. Having this code execute from the main thread as opposed to the keyboard hook, stops a deadlock with the ignoreInjection hook and stops deliberate reentrancy of the keyboard hook.
|
I'm slightly worried that add-ons call scriptHandler.executeScript directly. Pretty sure NVDA Remote does, may be Braille Extender (@Andre9642) and stuff @JulienCochuyt wrote as well. I'd like to propose the following:
|
|
To clarify, the only reason that gesture.executeScript
should really be called is to ensure that KeyboardInputGesture injects
VK_NONE if particular modifiers have been physically pressed. If code
wants to arbitrarily execute a script it is not necessary, and possibly
even inappropriate, to run that code.
Perhaps I am miss-understanding why these add-ons are executing
scripts... But assuming these scripts are not being executed due to the
keyboard hook, then this change will not affect them at all.
But I'm happy to make the proposed change if a specific exaple can be
provided.
|
| #: All normal modifier keys, where modifier vk codes are mapped to a more general modifier vk code | ||
| # or C{None} if not applicable. |
There was a problem hiding this comment.
The indentation appears incorrect here, missing a tab?
|
NVDA Remote only uses it for braille display gestures, so shouldn't be hurt in that case. Wonder what @feerrenrut thinks. |
|
@LeonarddeR it took me a while to unwind this, and I'm not 100% that I have interpreted correctly, apologies if I'm way off track. The PR currently has the following call chain: Looking at the suggestion:
If implemented then you have following call chain: addon calls I think your concern is about addons who currently call If an addon is currently calling |
|
@feerrenrut Thanks for summing this all up. I just wanted to make sure that we're not going to break backwards compat with this. I had a look at brailleExtender, and my suggestion actually introduces a regression, as it calls scriptHandler.executeScript with gesture set to None, so that's definitely not the solution here. I think it's safest to go with this change as is. |
* keyboardHandler.injectRawKeyboardInput: Previously, NVDA would send the input manually to its keyboard input hook, and then if the hook didn't handle the input, calling keybd_event. This now avoids assumptions about how NVDA's input hook worked, which are no longer true since #11478 and #11597 were merged. * Papenmeier braille driver: More gesture bindings and fix detection of keyboard input.
An alternative of pr #11477
This time, send the VK_NONE from the main thread, if the script does not send the gesture. This completely avoids deliberate reentrancy in the keyboard hook, and also avoids a time.sleep and the call to wx.yield in the winInputHook thread (which causes a wx assertion). Users report that no only does this fix the issue but this also massively speeds up holding down of control+shift+downArrow in MS Word, no doubt due to no longer sleeping or yielding in the keyboard hook.
Link to issue number:
Fixes #9463
Summary of the issue:
background
As NVDA detects key presses in its key hook, it may block a certain key from continuing through to the Operating System if that key press is bound to a particular NvDA script. The script may or may not then choose to send the key onto the Operating system later, depending on the action it is taking.
However, certain key combinations such as control+shift, may cause an action in Windows such as changing the input language, if a main key is not also included with that key combination. E.g. control+shift by itself changes input languages, but control+shift+downArrow does not. But, if NVDA blocks control+shift+downArrow, and does not send any key, then the input language is still changed. To get around this, in its key hook, NVDA sends a special VK_NONE (0xff) when modifiers like control+shift are pressed, (whether or not NVDA scripts later send a key) to ensure that the Operating System does not change input languages.
Actual problem
To send the VK_NONE key press in its key hook, currently NVDA uses KeyboardInputGesture.send, which among other things, enters a lock and sets a global variable which flags that NVDA should ignore any injected keys at the moment (I.e. this one).
The assumption was that during the call to KeyboardInputGesture.send (or keybd_event more specifically) only the key press NVDA sent would be executed in NVDA's key hook. And because the OS would have marked it as injected, and because ignoreInjected was true, then we would exit from the reentered key hook early.
But, on some Operating systems (E.g. users in #9463 ) other key presses currently pending, may also execute in the key hook inside keybd_event. In this case though, they are not injected, and therefore the entire key hook runs, and as these key presses are most likely repeats of modifier keys that require us to send the VK_NONE, then we do this, but cause a deadlock in the winInputHook thread as our ignoreInjected lock is not reentrant. Further to this, NVDA's main thread may then actually itself call KeyboardInputGesture.send in a script (perhaps for control+shift+downArrow) which also tries to take the lock, and waits for ever as well.
See the following Python stacks:
and
Description of how this pull request fixes the issue:
Rather than sending the VK_NONE from the keyboard hook itself, instead send it from the main thread, by doing the following:
Testing performed:
Ran the steps in #9463 locally, confirming the freeze / exit cannot be reproduced. Also provided a try build on that issue and had multiple users confirm the freeze / exit is gone for them.
Known issues with pull request:
None.
Change log entry:
Bug fixes: