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

Avoid unnecessary sleep when handling caret movement to improve responsiveness. #14708

Merged
merged 3 commits into from
Mar 23, 2023

Conversation

jcsteh
Copy link
Contributor

@jcsteh jcsteh commented Mar 8, 2023

Link to issue number:

None.

Summary of the issue:

NVDA frequently takes 30 to 45 ms to respond to cursor movement (arrow keys, etc.) in a text box. This doesn't seem like much, but anything we can do to improve responsiveness here makes for a better experience for the user.

Description of user facing changes

NVDA responds faster when moving the cursor in edit controls.

Description of development approach

  1. Don't sleep or yield in KeyboardInputGesture.send. We sleep for 10 ms, which is at the upper end of acceptable audio latency for "instantaneous". Furthermore, the Windows timer resolution is only accurate to about 15.6 ms most of the time.
    This sleep was apparently added to ensure NVDA's keyboard hook ignored the injected keys. In my testing, removing this didn't seem to have any negative impact. If this does turn out to be a problem, we could watch for the last injected key in the keyboard event hook and fire a Windows event when it arrives. This will be faster to wake than a 10-16 ms sleep.
  2. When waiting for caret movement, spin the first few tries rather than sleeping. Again, we might sleep longer than we need to and sometimes apps respond much faster than we previously allowed for. Spinning for a very short while improves responsiveness without excessive resource usage.
  3. The caret movement waiting code was also refactored a bit to better calculate the elapsed time. We were previously relying on multiplying the sleep amount, which was inaccurate because sleep is inaccurate. For example, if we retried 3 times with a 10 ms retry interval, we would report 30 ms elapsed. However, each 10 ms could have been up to 16 ms in reality, resulting an actual elapsed time of 48 ms. It also didn't account for the time it took to query the control. Now we use the system clock to measure the elapsed time.
  4. Increase the use events timeout from 10 ms to 60 ms. As noted in 3), we were previously multiplying the sleep amount, which meant our supposed elapsed time was actually less than what had truly elapsed. This just updates the timeout to reflect reality. Without this, backspacing occasionally failed in UIA Notepad and the search box in File Explorer.

Note that this does alter some class variables in EditableText. However, these were private (underscore prefixed), so I don't think this should be considered API breaking.

Testing strategy:

I used the "time since input" logging to look at the time taken to speak when moving by character in the Run dialog and a simple text box in Firefox. Before this change, this is usually somewhere between 30 and 50 ms. After the change, this is usually somewhere between 20 and 40 ms, though I've seen it go to 10 ms or lower. This is quite variable, so it's hard to be scientific about this. Combined with #14701, this definitely feels snappier to me.

Known issues with pull request:

None.

Change log entries:

Bug fixes
NVDA responds faster when moving the cursor in edit controls.

Code Review Checklist:

  • Pull Request description:
    • description is up to date
    • change log entries
  • Testing:
    • Unit tests
    • System (end to end) tests
    • Manual testing
  • API is compatible with existing add-ons.
  • Documentation:
    • User Documentation
    • Developer / Technical Documentation
    • Context sensitive help for GUI changes
  • UX of all users considered:
    • Speech
    • Braille
    • Low Vision
    • Different web browsers
    • Localization in other languages / culture than English
  • Security precautions taken.

@AppVeyorBot
Copy link

See test results for failed build of commit d136d6d9ba

@AppVeyorBot
Copy link

See test results for failed build of commit c3a719aca5

Copy link
Member

@michaelDCurran michaelDCurran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.
However, apparently it is common for some people to hold down commands like downArrow or control+downArrow in large documents to skim the structure or something. We had had reports in the past of freezes or keys being locked down when this code is touched. I think you removed the yield? Have you done any testing in scenarios like these?

@cary-rowen
Copy link
Contributor

@michaelDCurran
I tested using the steps in #13334. Still reproducible, but at least this PR didn't make it any worse.

@jcsteh
Copy link
Contributor Author

jcsteh commented Mar 9, 2023

I have tested quite a bit with holding down the downArrow key. I haven't tested with control+downArrow though. I'll do that. It sounds like I should also test with control+shift+downArrow based on #11478.

@jcsteh
Copy link
Contributor Author

jcsteh commented Mar 9, 2023

I've just held down control+downArrow and control+shift+downArrow for a long time in a pretty long document and didn't encounter any freeze or anything like that. Still, I'll watch out for it while using it in daily work.

@Adriani90
Copy link
Collaborator

Might also improve the behavior described in #13484.

@jcsteh
Copy link
Contributor Author

jcsteh commented Mar 9, 2023

I doubt it. This PR is about improving the response time between pressing a key and NVDA checking whether the cursor has moved. That issue sounds like it's more about NVDA not being able to get correct info from the editor for somereason. If anything, that issue probably requires a slower response time.

@amirmahdifard
Copy link

thank you for this, i also have some delay with pressing keys and getting responce from nvda sometimes, i hope this will fix that problem: thanks?

@codeofdusk
Copy link
Contributor

This PR makes tracking of deleted characters in UIA Word much less reliable:

  1. Open a new email message in Outlook.
  2. Tab to the body.
  3. Enter "abc", then press backspace. Observe that the deletion of anything from zero to all three characters are reported.
  4. Repeat steps 2 and 3. Observe possibly different results.

@jcsteh
Copy link
Contributor Author

jcsteh commented Mar 11, 2023

Is this with the latest commit in this branch? If so, we might need to raise _useEvents_maxTimeoutSec even further to 0.03. I already raised it to 0.02, but it might not be enough. I explained why this is necessary in the edited PR description:

  1. Increase the use events timeout slightly from 10 ms to 20 ms. As noted in 3), we were previously multiplying the sleep amount, which meant our supposed elapsed time was actually less than what had truly elapsed. This just updates the timeout to reflect reality. Without this, backspacing occasionally failed in UIA Notepad.

@jcsteh
Copy link
Contributor Author

jcsteh commented Mar 11, 2023

To explain this further, we previously had a retry interval of 0.01 and we just kept multiplying that by the number of retries to calculate the elapsed time; e.g. 0.01, 0.02, 0.03, etc. The problem is that because of the Windows timer resolution, this could be quite inaccurate. If each sleep took 16 ms instead of 10 ms, the elapsed time for 3 tries would be 0.048, 18 ms longer than we calculated.

@codeofdusk
Copy link
Contributor

Is this with the latest commit in this branch?

Yes it was. Initial testing with 0.03 looks promising, but I'll run with it for a while to be sure.

@jcsteh jcsteh marked this pull request as ready for review March 16, 2023 11:06
@jcsteh jcsteh requested a review from a team as a code owner March 16, 2023 11:06
@jcsteh jcsteh requested a review from seanbudd March 16, 2023 11:06
@codeofdusk
Copy link
Contributor

Thanks for the update @jcsteh – so far so good with 0.03!

@codeofdusk
Copy link
Contributor

@jcsteh Found aanother bug with the latest commit in this branch:

  1. Open a folder in Windows Explorer.
  2. Tab to the search field.
  3. enter "abc".
  4. Press backspace three times to delete all three characters.

Expected: NVDA reports "c", "b", "a".

Actual: NVDA reports "c", "b".

@jcsteh
Copy link
Contributor Author

jcsteh commented Mar 18, 2023

I couldn't reproduce the search bug exactly as you described. However, I did see intermittent failures when backspacing.

To address this, I've had to raise the event timeout even higher to 60 ms. It looks like that control responds very slowly, both in firing events and in processing queries. Not only did our elapsed time previously not account for sleep inaccuracy, it also didn't account for query time.

I'm actually not quite sure why we have that event timeout at all. I looked at the various pull requests which introduced the caret event changes for UIA, but I don't see a justification for the event timeout.

@michaelDCurran
Copy link
Member

@codeofdusk is the explorer search field working okay for you now with the latest fix from @jcsteh ?

@codeofdusk
Copy link
Contributor

@michaelDCurran Yes, it does appear to work now!

@michaelDCurran michaelDCurran merged commit e8bf6c2 into nvaccess:master Mar 23, 2023
@nvaccessAuto nvaccessAuto added this to the 2023.2 milestone Mar 23, 2023
michaelDCurran pushed a commit that referenced this pull request Jun 7, 2023
Fixes #14983
Fixes #12200
Fixes #12108?

Summary of the issue:
Moving rapidly in Excel can result in lagging one cell behind when reporting focus.

Description of user facing changes
Rapid movement in Excel will now always report the last cell.

Description of development approach
First and foremost, NVDA was relying on the current focus object to cache the current selection in Excel rather than getting the current selection before executing the gesture. This resulted in the one behind behavior. There was logic to check for selection changes, but it detected a change based on the wrong old selection.
I also revamped the logic a bit based on #14708, i.e. perform three attempts before doing a time.sleep.
seanbudd pushed a commit that referenced this pull request Sep 6, 2023
…hange events (#15377)

Fixes #15230

Summary of the issue:
NVDA's calculation announcements for Win32 calculator relies on a list of predefined keyboard commands, which, when pressed, causes the new value of the display to be announced. This assumes that at the time when gesture is sent the calculator had enough time to process it, and display the result of the calculation. While this worked before prior to #14708, after this PR NVDA no longer sleeps after sending gestures, therefore the value on calculator's display had not enough time to be updated. For users this means that after performing calculations the outdated value was read.

Description of user facing changes
NVDA once again announces correct results of the calculation.

Description of development approach
Rather than announcing the display value immediately after user pressed a key, NVDA now sets a flag signaling that the given command causes result of a calculation to appear. This flag is checked in the name change event for the display. If it is set the result is announced and the flag is restored to its default value.
While at it I have also updated the copyright header of the module based on its log in VCS.
seanbudd pushed a commit that referenced this pull request Dec 27, 2023
Fixes #15822.

Summary of the issue:
With "Handle keys from other applications" enabled, NVDA interferes with certain software which intercepts and re-transmits keyboard commands such as Nudi6.1. For example, this makes it impossible to use backspace while this software is running.

Description of user facing changes
Backspace now works correctly when using Nudi6.1 with NVDA's "Handle keys from other applications" setting enabled.

Description of development approach
Before #14708, we delayed slightly when sending keys, which would mean that any keys sent by an app which captured a key sent by NVDA would be ignored during that period. After #14708, we only ignore any keys received while NVDA is sending keys, but that doesn't include any keys captured and sent afterward by, for example, Nudi6.1. This could result in a loop where NVDA kept receiving the key it sent (re-transmitted by the other app) and sending it again.

To fix this, we now explicitly wait for the key sent by NVDA (e.g. backspace) to be received by the keyboard hook before we stop ignoring keys and thus return from KeyboardInputGesture.send(). This means that if another application intercepts this key and re-transmits it, we will wait for that re-transmission and ignore it. We use a kernel event so that the notification from the keyboard hook can be handled as quickly as possible without being dependent on the system timer resolution.

There is a chance that a key will be intercepted and never re-sent. To deal with this, we wait for a maximum timeout of 10 ms. This means that in the best case scenario, nothing intercepts a key sent by NVDA and we return almost immediately. In the worst case scenario, we wait between 10 and 30 ms (depending on the system timer resolution), which is no worse than the situation before #14708.
Nael-Sayegh pushed a commit to Nael-Sayegh/nvda that referenced this pull request Feb 15, 2024
)

Fixes nvaccess#15822.

Summary of the issue:
With "Handle keys from other applications" enabled, NVDA interferes with certain software which intercepts and re-transmits keyboard commands such as Nudi6.1. For example, this makes it impossible to use backspace while this software is running.

Description of user facing changes
Backspace now works correctly when using Nudi6.1 with NVDA's "Handle keys from other applications" setting enabled.

Description of development approach
Before nvaccess#14708, we delayed slightly when sending keys, which would mean that any keys sent by an app which captured a key sent by NVDA would be ignored during that period. After nvaccess#14708, we only ignore any keys received while NVDA is sending keys, but that doesn't include any keys captured and sent afterward by, for example, Nudi6.1. This could result in a loop where NVDA kept receiving the key it sent (re-transmitted by the other app) and sending it again.

To fix this, we now explicitly wait for the key sent by NVDA (e.g. backspace) to be received by the keyboard hook before we stop ignoring keys and thus return from KeyboardInputGesture.send(). This means that if another application intercepts this key and re-transmits it, we will wait for that re-transmission and ignore it. We use a kernel event so that the notification from the keyboard hook can be handled as quickly as possible without being dependent on the system timer resolution.

There is a chance that a key will be intercepted and never re-sent. To deal with this, we wait for a maximum timeout of 10 ms. This means that in the best case scenario, nothing intercepts a key sent by NVDA and we return almost immediately. In the worst case scenario, we wait between 10 and 30 ms (depending on the system timer resolution), which is no worse than the situation before nvaccess#14708.
SaschaCowley pushed a commit to SaschaCowley/nvda that referenced this pull request Feb 27, 2024
)

Fixes nvaccess#15822.

Summary of the issue:
With "Handle keys from other applications" enabled, NVDA interferes with certain software which intercepts and re-transmits keyboard commands such as Nudi6.1. For example, this makes it impossible to use backspace while this software is running.

Description of user facing changes
Backspace now works correctly when using Nudi6.1 with NVDA's "Handle keys from other applications" setting enabled.

Description of development approach
Before nvaccess#14708, we delayed slightly when sending keys, which would mean that any keys sent by an app which captured a key sent by NVDA would be ignored during that period. After nvaccess#14708, we only ignore any keys received while NVDA is sending keys, but that doesn't include any keys captured and sent afterward by, for example, Nudi6.1. This could result in a loop where NVDA kept receiving the key it sent (re-transmitted by the other app) and sending it again.

To fix this, we now explicitly wait for the key sent by NVDA (e.g. backspace) to be received by the keyboard hook before we stop ignoring keys and thus return from KeyboardInputGesture.send(). This means that if another application intercepts this key and re-transmits it, we will wait for that re-transmission and ignore it. We use a kernel event so that the notification from the keyboard hook can be handled as quickly as possible without being dependent on the system timer resolution.

There is a chance that a key will be intercepted and never re-sent. To deal with this, we wait for a maximum timeout of 10 ms. This means that in the best case scenario, nothing intercepts a key sent by NVDA and we return almost immediately. In the worst case scenario, we wait between 10 and 30 ms (depending on the system timer resolution), which is no worse than the situation before nvaccess#14708.
Adriani90 pushed a commit to Adriani90/nvda that referenced this pull request Mar 13, 2024
)

Fixes nvaccess#15822.

Summary of the issue:
With "Handle keys from other applications" enabled, NVDA interferes with certain software which intercepts and re-transmits keyboard commands such as Nudi6.1. For example, this makes it impossible to use backspace while this software is running.

Description of user facing changes
Backspace now works correctly when using Nudi6.1 with NVDA's "Handle keys from other applications" setting enabled.

Description of development approach
Before nvaccess#14708, we delayed slightly when sending keys, which would mean that any keys sent by an app which captured a key sent by NVDA would be ignored during that period. After nvaccess#14708, we only ignore any keys received while NVDA is sending keys, but that doesn't include any keys captured and sent afterward by, for example, Nudi6.1. This could result in a loop where NVDA kept receiving the key it sent (re-transmitted by the other app) and sending it again.

To fix this, we now explicitly wait for the key sent by NVDA (e.g. backspace) to be received by the keyboard hook before we stop ignoring keys and thus return from KeyboardInputGesture.send(). This means that if another application intercepts this key and re-transmits it, we will wait for that re-transmission and ignore it. We use a kernel event so that the notification from the keyboard hook can be handled as quickly as possible without being dependent on the system timer resolution.

There is a chance that a key will be intercepted and never re-sent. To deal with this, we wait for a maximum timeout of 10 ms. This means that in the best case scenario, nothing intercepts a key sent by NVDA and we return almost immediately. In the worst case scenario, we wait between 10 and 30 ms (depending on the system timer resolution), which is no worse than the situation before nvaccess#14708.
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

Successfully merging this pull request may close these issues.

8 participants