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

Prototype for Windows Terminal: Use notifications instead of monitoring for new text #14047

Merged
merged 27 commits into from
Oct 25, 2022

Conversation

LeonarddeR
Copy link
Collaborator

@LeonarddeR LeonarddeR commented Aug 22, 2022

Note, with many thanks to @codeofdusk

Link to issue number:

Closes #13781

Summary of the issue:

Windows Terminal supports UIA notification events to notify of new text. This is way quicker than the current diffing we use to calculate new text to speak in a terminal.

Description of user facing changes

Note: This is hidden behind a feature flag.
When enabled, this should improve responsivity whereas having minimal user impact. The only thing I noticed is that when typing echo is on, the last typed character/word is spoken after pressing enter, whereas in the old situation, the last typed char/word was always silenced. I'd say this is an improvement.

Description of development approach

Handle the Windows Terminal object as regular Editable Text, i.e. remove the terminal specific stuff from it.

Testing strategy:

Did a short Test in Windows Terminal. Observed that incoming notifications are spoken.

Known issues with pull request:

Character/word echo for passwords doubles as asterisks are written to the terminal and they are picked up by notification events.

Change log entries:

=== New Features ===
- Added an experimental option to leverage the UIA notification support in Windows Terminal to report new or changed text in the terminal, resulting in improved stability and responsivity. (#13781)
 - Consult the user guide for limitations of this experimental option.
 - 
-

=== Changes for Developers ===
- ``NVDAObjects.UIA.winConsoleUIA.WinTerminalUIA`` has been replaced with ``NVDAObjects.UIA.winConsoleUIA._DiffBasedWinTerminalUIA`` and ``NVDAObjects.UIA.winConsoleUIA._NotificationsBasedWinTerminalUIA``. (#14047)

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.

@codeofdusk
Copy link
Contributor

codeofdusk commented Aug 22, 2022

Also, if this PR is merged, I'm really thinking it should be gated behind a feature flag. This is a fundamental change to how terminals work in NVDA.

As a note for reviewers, I've written a few thoughts on doing this at #13781 (comment).

@LeonarddeR
Copy link
Collaborator Author

While a fundamental change, it shouldn't have that much user visible differences on the front side. I'm pretty reluctant to introduce a feature flag for every single fundamental change. Feature flags tend to stay for ages without bringing the feature any further.
I'd rather see the approach thoroughly tested before it lands in a release.

@codeofdusk
Copy link
Contributor

Also CCing @carlos-zamora.

@AppVeyorBot
Copy link

See test results for failed build of commit 3ed04af591

@mzanm
Copy link
Contributor

mzanm commented Aug 22, 2022

I think that some check should be done on the windows terminal version as not all support UIA notifications

@codeofdusk
Copy link
Contributor

I think that some check should be done on the windows terminal version as not all support UIA notifications

No, because:

  • With "undocking", the version number isn't always reliable.
  • The versions that don't support notifications are no longer supported, and per @DHowett people really shouldn't run them.

@mzanm
Copy link
Contributor

mzanm commented Aug 23, 2022

I've noticed that when it announces new text, everything is spoken as one line, so that this code

for i in range(10):
    print(i)

It would read it as 012345... Instead of each one on one line like it should

@codeofdusk
Copy link
Contributor

codeofdusk commented Aug 23, 2022

I've noticed that when it announces new text, everything is spoken as one line

@LeonarddeR I've provided a patched notification handler that should work around this (you'll need to import api, then import config at the top of the file, please place above import ctypes). CC @mazen428

@tspivey
Copy link
Collaborator

tspivey commented Aug 23, 2022

I tested this briefly.

  1. When typing certain punctuation with speak typed characters off (e.g. @#$%), I hear the character. Others don't do it (e.g. ., ,, /). This might be a bug in the terminal.
  2. Toggling report dynamic content changes off doesn't stop the notifications from reading. This is sometimes useful.
  3. The reading experience isn't that good when reading long text, partially because Windows Terminal has a limit of 1000 characters per notification.
    It's also not guaranteed that you'll get an entire line of text; connection delays might give you half a line no matter how large the notifications are.
    To get around this, notifications can be batched for a short time before speaking them. This won't be perfect, but it should be better than it is now.

@codeofdusk
Copy link
Contributor

  1. Toggling report dynamic content changes off doesn't stop the notifications from reading. This is sometimes useful.

Fixed in the suggested change to the UIA notification handler above.

As for the others, I think these are probably terminal issues. I'll ping @carlos-zamora.

@carlos-zamora
Copy link

  1. When typing certain punctuation with speak typed characters off (e.g. @#$%), I hear the character. Others don't do it (e.g. ., ,, /). This might be a bug in the terminal.

Strange! We shouldn't be doing anything differently, but I'll take a closer look at this later this week.

  1. The reading experience isn't that good when reading long text, partially because Windows Terminal has a limit of 1000 characters per notification.
    It's also not guaranteed that you'll get an entire line of text; connection delays might give you half a line no matter how large the notifications are.
    To get around this, notifications can be batched for a short time before speaking them. This won't be perfect, but it should be better than it is now.

I could change this on the Terminal side and make it do less than the limit to fit an entire word/sentence in. But I'm concerned we would see some weird behavior when the newly output text is overwriting existing text or something like that. I think batching the notifications is the right approach here.

Fun fact: @lhecker and I were just discussing batching notifications and the buffer this past Monday haha

@josephsl
Copy link
Collaborator

Hi,

Note that the base implementation of UIA notification event handler will ignore background notifications. If it works differently in Windows Terminal object, then this might be something to think about. Also, keep in mind that the nature of UIA notification event is not really that of a dynamic content announcer - it is there to announce accessible event notifications. This is why I intentionally did not tie this setting to dynamic content changes when I first introduced notification event support in 2018, and that is also embedded in Windows App Essentials - to turn off UIA notification via that add-on, one must turn off "report notifications" from NVDA's object presentation settings panel. It is also another reason why I did work on a dedicated setting for UIA notification announcement.

Thanks.

@mzanm
Copy link
Contributor

mzanm commented Aug 23, 2022

Wouldn't making this based on EnhancedTermTypedCharSupport, excluding liveText, fix the punctuation issues along with clearing typed characters buffer when pressing tab or enter?

@codeofdusk
Copy link
Contributor

codeofdusk commented Aug 24, 2022

  • EnhancedTermTypedCharSupport subclasses LiveText.
  • EnhancedTermTypedCharSupport depends on event_textChange for password suppression (in addition to LiveText using it to diff). Registering for this event in terminals introduces serious performance problems (see UIA freezes after a large number of events is received #11002).

@LeonarddeR
Copy link
Collaborator Author

How would we implement batching? Does Terminal know that it is sending a batch? If yes, I think it would help if Terminal communicates in the notification that this is a part of a batch that's not yet finished so NVDA can queue. If that isn't possible, I'm afraid we're getting into business with waiting and timeouts all over again, and I really want to avoid that if possible.

@codeofdusk
Copy link
Contributor

@LeonarddeR Can we find a way not to register for or receive textChange events in wt now that we're not using them? This should seriously improve performance.

@LeonarddeR
Copy link
Collaborator Author

Do you have any proof of that statement (i.e. that it improves performance)? In what circumstances?

Anyhow, you're raising an interesting question. I'm afraid the short answer is no, since we register to text changed notifications in the UIA handler, either globally or locally depending on selective event registration. However, as soon as we register, every text changed notification enters NVDA's python code and therefore can slow things down. I guess we can come up with a clone of AppModule.shouldProcessUIAPropertyChangedEvent, but that's again and again putting the real problem off before us.

@codeofdusk
Copy link
Contributor

codeofdusk commented Aug 24, 2022

Do you have any proof of that statement (i.e. that it improves performance)? In what circumstances?

Yes. In particular, when we're flooded with new text:

  1. Run the wtNotifications branch of NVDA.
  2. Open a wt instance. Start a Python interpreter, and run the following code: for i in range(10000): print("test")
    • Observe that NVDA reads "test" for a few seconds, then UIA dies.
  3. Comment out the mapping UIA.UIA_Text_TextChangedEventId: "textChange" (around line 209 of UIAHandler/__init__.py) and restart NVDA.
  4. Repeat step 3.
    • Observe that NVDA remains functional.

@LeonarddeR
Copy link
Collaborator Author

That's pretty disconcerting.
Would you be able to prototype something like AppModule.shouldProcessUIAEvent to see whether that fixes or at least improves it?

@carlos-zamora
Copy link

Does Terminal know that it is sending a batch? If yes, I think it would help if Terminal communicates in the notification that this is a part of a batch that's not yet finished so NVDA can queue. If that isn't possible, I'm afraid we're getting into business with waiting and timeouts all over again, and I really want to avoid that if possible.

Terminal won't be able to batch notifications because we get all the text from the shell we're connected to. So we have no knowledge if more text is on the way or not. You could loosely assume that if you received a large amount of text, a bunch more is on the way since you're presumably reading out the content of a large text file. But other than that (and that's a very loose heuristic), we can't really do much.

@lhecker and I chatted a bit more about this earlier today. I've written down some notes in the Terminal repo here: microsoft/terminal#10822 (comment)

@AppVeyorBot
Copy link

See test results for failed build of commit a35cfcff40

source/UIAHandler/__init__.py Outdated Show resolved Hide resolved
source/NVDAObjects/UIA/__init__.py Outdated Show resolved Hide resolved
Co-authored-by: Sean Budd <seanbudd123@gmail.com>
@AppVeyorBot
Copy link

  • PASS: Translation comments check.
  • PASS: Unit tests.
  • FAIL: Lint check. See test results for more information.
  • Build execution time has reached the maximum allowed time for your plan (60 minutes).

See test results for failed build of commit 28db16dd22

Copy link
Member

@Qchristensen Qchristensen left a comment

Choose a reason for hiding this comment

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

Userguide changes read well. Thanks!

@seanbudd
Copy link
Member

Is there a way to retain backwards compatibility by keeping NVDAObjects.UIA.winConsoleUIA.WinTerminalUIA?

@codeofdusk
Copy link
Contributor

Is there a way to retain backwards compatibility by keeping NVDAObjects.UIA.winConsoleUIA.WinTerminalUIA?

I could make it an alias for the currently default implementation (diff-based for now, then notifications based later), but I don't think this would add very much value. This is an internal class that I highly doubt plugins would use.

@seanbudd
Copy link
Member

This is an internal class that I highly doubt plugins would use.

If the new classes are also intended to be internal classes, please prefix them with underscores.
This was marked as part of the public API. According to current policy we aim to keep aliases where there is low ongoing maintenance cost.

@@ -419,14 +424,50 @@ def findExtraOverlayClasses(obj, clsList):
clsList.append(consoleUIAWindow)


class WinTerminalUIA(EnhancedTermTypedCharSupport):
class DiffBasedWinTerminalUIA(EnhancedTermTypedCharSupport):
Copy link
Member

Choose a reason for hiding this comment

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

should these classes be marked as internal?

Copy link
Contributor

Choose a reason for hiding this comment

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

No, for symmetry with the rest of the module.

Copy link
Member

Choose a reason for hiding this comment

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

What do you mean by symmetry?

Copy link
Contributor

Choose a reason for hiding this comment

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

The other Win*UIA classes in this module don't have underscores.

Copy link
Member

@seanbudd seanbudd Oct 24, 2022

Choose a reason for hiding this comment

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

I don't think that it is too late to fix the current practice, if these should be marked as internal, we should mark them.

Copy link
Contributor

Choose a reason for hiding this comment

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

This would be a significant divergence from other similar classes in NVDAObjects.UIA. A few examples:

  • NVDAObjects.UIA.VisualStudio.IntelliSenseList
  • NVDAObjects.UIA.wordDocument.UIACustomAttributeID
  • NVDAObjects.UIA.wordDocument.ElementsListDialog

Copy link
Member

Choose a reason for hiding this comment

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

I think this is the correct divergence to make.
What do you think @feerrenrut ? Note earlier conversation from #14047 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

I can acknowledge this is inconsistent with the existing code, however the inconsistency is due to a change process around add-on APIs. We need to be able to delineate where we commit to a stable add-on API and where we don't. If there is no expectation of add-ons needing the new code, it is safest to mark it internal only. This allows it to change more easily without going through a deprecation process. If add-on authors identify the code as useful for their add-on, and want it exposed, then we can have a discussion around that and consider if it should be exposed directly or via some level of abstraction.

source/NVDAObjects/UIA/winConsoleUIA.py Outdated Show resolved Hide resolved
@@ -419,14 +424,50 @@ def findExtraOverlayClasses(obj, clsList):
clsList.append(consoleUIAWindow)


class WinTerminalUIA(EnhancedTermTypedCharSupport):
class DiffBasedWinTerminalUIA(EnhancedTermTypedCharSupport):
Copy link
Member

Choose a reason for hiding this comment

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

What do you mean by symmetry?

Co-authored-by: Sean Budd <seanbudd123@gmail.com>
@AppVeyorBot

This comment was marked as outdated.

@tmthywynn8
Copy link
Sponsor

FYI, I don't know if this is off topic for this request, but it looks like there is a spelling error in the change log: responsivity maybe should be responsiveness?

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.

Windows Terminal: Leverage new UI Automation notifications support