Skip to content

Commit

Permalink
Add new extension points for keyboard input and speech pause
Browse files Browse the repository at this point in the history
- Add decide_handleRawKey extension point to allow intercepting raw keyboard events
- Add speechPaused extension point for speech pause/resume notifications
- Update documentation to reflect new extension points
- Add unit tests for speechPaused extension point
  • Loading branch information
ctoth committed Nov 23, 2024
1 parent c32e0e7 commit af49d20
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 2 deletions.
2 changes: 2 additions & 0 deletions projectDocs/dev/developerGuide/developerGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,7 @@ For examples of how to define and use new extension points, please see the code

| Type |Extension Point |Description|
|---|---|---|
|`Decider` |`decide_handleRawKey` |Notifies when a raw keyboard event is received, before any NVDA processing, allowing other code to decide if it should be handled.|
|`Decider` |`decide_executeGesture` |Notifies when a gesture is about to be executed, allowing other code to decide if it should be.|

### logHandler {#logHandlerExtPts}
Expand All @@ -1382,6 +1383,7 @@ For examples of how to define and use new extension points, please see the code
|`Action` |`speechCanceled` |Triggered when speech is canceled.|
|`Action` |`pre_speechCanceled` |Triggered before speech is canceled.|
|`Action` |`pre_speech` |Triggered before NVDA handles prepared speech.|
|`Action` |`speechPaused` |Triggered when speech is paused or resumed.|
|`Filter` |`filter_speechSequence` |Allows components or add-ons to filter speech sequence before it passes to the synth driver.|

### synthDriverHandler {#synthDriverHandlerExtPts}
Expand Down
11 changes: 11 additions & 0 deletions source/inputCore.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,17 @@ def __eq__(self, other: Any) -> bool:
return NotImplemented


decide_handleRawKey = extensionPoints.Decider()
"""
Notifies when a raw keyboard event is received, before any NVDA processing.
Handlers can decide whether the key should be processed by NVDA and/or passed to the OS.
@param vkCode: The virtual key code
@param scanCode: The scan code
@param extended: Whether this is an extended key
@param pressed: Whether this is a key press or release
@return: True to allow normal processing, False to block the key
"""

decide_executeGesture = extensionPoints.Decider()
"""
Notifies when a gesture is about to be executed,
Expand Down
14 changes: 14 additions & 0 deletions source/keyboardHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ def shouldUseToUnicodeEx(focus: Optional["NVDAObject"] = None):

def internal_keyDownEvent(vkCode, scanCode, extended, injected):
"""Event called by winInputHook when it receives a keyDown."""
if not inputCore.decide_handleRawKey.decide(
vkCode=vkCode,
scanCode=scanCode,
extended=extended,
pressed=True,
):
return False
gestureExecuted = False
try:
global \
Expand Down Expand Up @@ -313,6 +320,13 @@ def internal_keyDownEvent(vkCode, scanCode, extended, injected):

def internal_keyUpEvent(vkCode, scanCode, extended, injected):
"""Event called by winInputHook when it receives a keyUp."""
if not inputCore.decide_handleRawKey.decide(
vkCode=vkCode,
scanCode=scanCode,
extended=extended,
pressed=False,
):
return False
try:
global \
lastNVDAModifier, \
Expand Down
3 changes: 2 additions & 1 deletion source/speech/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
spellTextInfo,
splitTextIndentation,
)
from .extensions import speechCanceled
from .extensions import speechCanceled, speechPaused
from .priorities import Spri

from .types import (
Expand Down Expand Up @@ -142,6 +142,7 @@
"spellTextInfo",
"splitTextIndentation",
"speechCanceled",
"speechPaused",
]

import synthDriverHandler
Expand Down
8 changes: 8 additions & 0 deletions source/speech/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
Handlers are called without arguments.
"""

speechPaused = Action()
"""
Notifies when speech is paused.
@param switch: True if speech is paused, False if speech is resumed.
@type switch: bool
"""

pre_speech = Action()
"""
Notifies when code attempts to speak text.
Expand Down
3 changes: 2 additions & 1 deletion source/speech/speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from textUtils import unicodeNormalize
from textUtils.uniscribe import splitAtCharacterBoundaries
from . import manager
from .extensions import speechCanceled, pre_speechCanceled, pre_speech
from .extensions import speechCanceled, speechPaused, pre_speechCanceled, pre_speech
from .extensions import filter_speechSequence
from .commands import (
# Commands that are used in this file.
Expand Down Expand Up @@ -211,6 +211,7 @@ def cancelSpeech():

def pauseSpeech(switch):
getSynth().pause(switch)
speechPaused.notify(switch=switch)
_speechState.isPaused = switch
_speechState.beenCanceled = False

Expand Down
9 changes: 9 additions & 0 deletions tests/unit/test_speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
_getSpellingSpeechAddCharMode,
_getSpellingSpeechWithoutCharMode,
cancelSpeech,
pauseSpeech,
speechCanceled,
speechPaused,
)
from speech.commands import (
BeepCommand,
Expand Down Expand Up @@ -591,3 +593,10 @@ def test_speechCanceledExtensionPoint(self):
speechCanceled,
):
cancelSpeech()

def test_speechPausedExtensionPoint(self):
with actionTester(self, speechPaused, switch=True):
pauseSpeech(True)

with actionTester(self, speechPaused, switch=False):
pauseSpeech(False)
4 changes: 4 additions & 0 deletions user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ Add-ons will need to be re-tested and have their manifest updated.
* Removed the requirement to indent function parameter lists by two tabs from NVDA's Coding Standards, to be compatible with modern automatic linting. (#17126, @XLTechie)
* Added the [VS Code workspace configuration for NVDA](https://nvaccess.org/nvaccess/vscode-nvda) as a git submodule. (#17003)
* In the `brailleTables` module, a `getDefaultTableForCurrentLang` function has been added (#17222, @nvdaes)
* Added the following extension points:
* inputCore.decide_handleRawKey: called on each keypress
* speech.extensions.speechPaused: Called when speech is paused or unpaused


#### API Breaking Changes

Expand Down

0 comments on commit af49d20

Please sign in to comment.