Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/add-new-language.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:

jobs:
add-new-languages:
runs-on: windows-2025-vs2026
runs-on: windows-2025
permissions:
contents: write
pull-requests: write
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/clearCaches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:
required: false

env:
CACHE_EXPIRATION_HOUR: ${{ inputs.cache_expiration || fromJSON(vars.CACHE_EXPIRATION_HOUR || '6') }}
CACHE_EXPIRATION_HOUR: ${{ inputs.cache_expiration || fromJSON(vars.CACHE_EXPIRATION_HOUR) || 6 }}

jobs:
cleanupCache:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: windows-2025-vs2026
runs-on: windows-2025
permissions:
# required for all workflows
security-events: write
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/fetch-crowdin-translations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ on:

jobs:
download-from-crowdin:
runs-on: windows-2025-vs2026
runs-on: windows-2025
permissions:
contents: write
pull-requests: write
Expand Down Expand Up @@ -44,7 +44,7 @@ jobs:
- name: Set up python
uses: actions/setup-python@v6
with:
python-version: '3.13.13'
python-version: '3.13.12'
architecture: 'x64'

- name: setup uv
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ permissions:

jobs:
rebuild-translation-source:
runs-on: windows-2025-vs2026
runs-on: windows-2025

steps:
- name: Early exit
Expand Down
29 changes: 4 additions & 25 deletions .github/workflows/testAndPublish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ env:
VSCMD_SKIP_SENDTELEMETRY: 1
# Cache details about available MSVC tooling for subsequent SCons invocations to the provided file.
SCONS_CACHE_MSVC_CONFIG: ".scons_msvc_cache.json"
defaultRunner: 'windows-2025-vs2026'
supportedRunners: '["windows-2022", "windows-2025-vs2026"]'
defaultRunner: 'windows-2025'
supportedRunners: '["windows-2022", "windows-2025"]'
defaultArch: x64
supportedArchitectures: '["x64"]'
defaultPythonVersion: '3.13.13'
supportedPythonVersions: '["3.13.13"]'
defaultPythonVersion: '3.13.12'
supportedPythonVersions: '["3.13.12"]'

jobs:
matrix:
Expand Down Expand Up @@ -772,24 +772,3 @@ jobs:
--repo ${{ github.repository }} \
--clobber \
$NVDA_EXE_NAME#Installer

submit-winget:
name: Submit manifest to WinGet Packages Repository
needs: [matrix, release]
runs-on: ${{ needs.matrix.outputs.defaultRunner }}
if: startsWith(github.ref_name, 'release-')
permissions:
contents: read
steps:
- name: Checkout NVDA
uses: actions/checkout@v6
with:
submodules: false

- name: Submit package using wingetcreate
env:
GH_TOKEN: ${{ github.token }}
# wingetcreate will read the token from the below environment variable
# Reference: https://aka.ms/winget-create-token
WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_CREATE_GITHUB_TOKEN }}
run: ci/scripts/submitWinGet.ps1
2 changes: 1 addition & 1 deletion .python-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cpython-3.13.13-windows-x86_64-none
cpython-3.13.12-windows-x86_64-none
11 changes: 0 additions & 11 deletions ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ Some of these steps run concurrently.
* On snapshot builds, deploy to the server.
* On beta branch builds, upload translation to Crowdin.
* On release builds, publish the release on GitHub and deploy to the server.
* On release builds, submit a PR for the new version to the WinGet community repository.
* Clean up build cache.

### Build behaviours
Expand Down Expand Up @@ -183,16 +182,6 @@ To ensure this step of tagged builds succeeds, set:

* `VT_API_KEY` as a secret.

### WinGet manifest submission

On release builds, a PR is automatically submitted to the [WinGet community repository](https://github.com/microsoft/winget-pkgs) with the new version's manifest.

To ensure this step of release builds succeeds, set:

* `WINGET_CREATE_GITHUB_TOKEN` as a secret with a GitHub personal access token that has permission to fork and open pull requests against `microsoft/winget-pkgs`.

See [the winget-create documentation](https://aka.ms/winget-create-token) for the required token scopes.

### GitHub Discussions category

This is only used when building tagged builds.
Expand Down
23 changes: 0 additions & 23 deletions ci/scripts/submitWinGet.ps1

This file was deleted.

2 changes: 1 addition & 1 deletion include/nvda-cldr
4 changes: 2 additions & 2 deletions projectDocs/dev/createDevEnvironment.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The following dependencies need to be installed on your system:

#### Python

[Python](https://www.python.org/), version 3.13.13, 64-bit.
[Python](https://www.python.org/), version 3.13.12, 64-bit.
Install the python version listed in [.python-versions](../../.python-versions)

#### uv
Expand Down Expand Up @@ -89,7 +89,7 @@ If you aren't sure, run `git submodule update` after every git pull, merge or ch
* [Sonic](https://github.com/waywardgeek/sonic), commit `b93885dcb70aae50c6f76b0fe4e0868f029a077e`
* [IAccessible2](https://wiki.linuxfoundation.org/accessibility/iaccessible2/start), commit `c9ae003d9c85eb707716928de97e055f5b77189c`
* [liblouis](http://www.liblouis.io/), version 3.37.0
* [Unicode Common Locale Data Repository (CLDR)](http://cldr.unicode.org/), version 48.2
* [Unicode Common Locale Data Repository (CLDR)](http://cldr.unicode.org/), version 48.0
* [Adobe Acrobat accessibility interface, version XI](https://download.macromedia.com/pub/developer/acrobat/AcrobatAccess.zip)
* [Microsoft Detours](https://github.com/microsoft/Detours), commit `9764cebcb1a75940e68fa83d6730ffaf0f669401`
* brlapi Python bindings, version 0.8.7 or later, built with [GitHub Actions in the NV Access fork of the brlTTY repository](https://github.com/nvaccess/brltty/actions)
Expand Down
32 changes: 16 additions & 16 deletions projectDocs/dev/developerGuide/developerGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Add-ons relying on private symbols do so at their own risk.
* **Pip packages:**
These may be updated, downgraded or removed at any time.
It is recommended to package any pip dependency directly with your add-on, rather than using NVDA's version of the package.
The exception to this is `wxPython`, where any breaking changes from `wxPython` will be treated as an API breaking change.
The exception to this is `wxPython`, which must be used from NVDA.

#### The API release cycle

Expand Down Expand Up @@ -75,7 +75,7 @@ We aim to avoid silent breaks of add-on code.
When removing or changing APIs in a breaking release (e.g. 2026.1) or due to an urgent security improvement (e.g. 2026.1.2):

1. We will mark functions as deprecated, where possible, in the releases leading up to the break (e.g. raising a `DeprecationWarning` in 2025.4).
1. All API breaking changes will be listed in the "Changes for Developers" section of the What's New document, and the [NVDA Add-on API Announcements](https://groups.google.com/a/nvaccess.org/g/nvda-api) email group.
2. All API breaking changes will be listed in the "Changes for Developers" section of the What's New document, and the [NVDA Add-on API Announcements](https://groups.google.com/a/nvaccess.org/g/nvda-api) email group.

#### Stability of transitive imports in the API {#APIImports}

Expand Down Expand Up @@ -1190,13 +1190,13 @@ For example:

```ini
[symbolDictionaries]
[[greek]]
displayName = Greek
mandatory = false
[[greek]]
displayName = Greek
mandatory = false

[[hebrew]]
displayName = Biblical Hebrew
mandatory = true
[[hebrew]]
displayName = Biblical Hebrew
mandatory = true
```

In the above example, `greek` is a symbol dictionary that is optional and will be listed in the speech category of NVDA's settings dialog under the "Extra dictionaries for character and symbol processing" setting.
Expand All @@ -1211,8 +1211,8 @@ For example, add the following to `locale\fr\manifest.ini`:

```ini
[symbolDictionaries]
[[hebrew]]
displayName = Hébreu Biblique
[[hebrew]]
displayName = Hébreu Biblique
```

### Speech dictionaries {#AddonSpeechDictionaries}
Expand All @@ -1228,9 +1228,9 @@ For example:

```ini
[speechDictionaries]
[[pronunciation]]
displayName = Dodgy Dictionary
mandatory = false
[[pronunciation]]
displayName = Dodgy Dictionary
mandatory = false
```

In the above example, `pronunciation` is a dictionary that is optional and will be listed in the speech category of NVDA's settings dialog under the "Speech Dictionaries" setting.
Expand All @@ -1242,8 +1242,8 @@ For example, add the following to `locale\fr\manifest.ini`:

```ini
[speechDictionaries]
[[pronunciation]]
displayName = Dictionnaire douteux
[[pronunciation]]
displayName = Dictionnaire douteux
```

Unlike symbol dictionaries, speech dictionaries are currently locale-agnostic.
Expand Down Expand Up @@ -1310,7 +1310,7 @@ This means that the word "LOL" (case insensitive, whole word) should be spoken a

This uses Unix shell-style wildcards to match any string ending in ".txt" and replaces it with "text file".

For more information on speech dictionaries, refer to the [User Guide](https://download.nvaccess.org/documentation/userGuide.html#SpeechDictionaries).
For more information on speech dictionaries, see the NVDA user guide section on speech.

### Add-on Documentation {#AddonDoc}

Expand Down
12 changes: 6 additions & 6 deletions projectDocs/dev/userGuideStandards.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ If necessary, a description of a common use case that is supported by each optio

| . {.hideHeaderRow} |.|
| --- | --- |
| Options | Default (Enabled), Disabled, Enabled |
| Default | Enabled |
| Toggle command | `NVDA+shift+e` |
|Options |Default (Enabled), Disabled, Enabled |
|Default |Enabled |
|Toggle command |`NVDA+shift+e` |

| Option | Behaviour |
|Option |Behaviour |
| --- | --- |
| Enabled | behaviour of enabled |
| Disabled | behaviour of disabled |
|Enabled |behaviour of enabled |
|Disabled |behaviour of disabled |
```

## Generation of Key Commands
Expand Down
2 changes: 1 addition & 1 deletion runtime-builders/synthDriverHost32/.python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cpython-3.13.13-windows-x86-none
cpython-3.13.12-windows-x86-none
13 changes: 0 additions & 13 deletions source/IAccessibleHandler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,19 +560,6 @@ def winEventToNVDAEvent( # noqa: C901
f"Ghosted hung window. Dropping winEvent {getWinEventLogInfo(window, objectID, childID, eventID)}",
)
return None
# If the owning application has stopped responding, drop the event. Building
# the object would make a synchronous cross-process call (e.g. IAccessible
# accParent during the focus ancestor walk) that blocks the core until the
# watchdog cancels it. This engages as soon as the system flags the app,
# without waiting for its DWM ghost window to be created (which is the gap
# that previously froze NVDA for several seconds on first contact, and on
# every subsequent interaction with the hung window).
if winUser.isHungAppWindow(window):
if isMSAADebugLoggingEnabled():
log.debugWarning(
f"Hung application. Dropping winEvent {getWinEventLogInfo(window, objectID, childID, eventID)}",
)
return None
# We do not support MSAA object proxied from native UIA
if UIAHandler.handler and UIAHandler.handler.isUIAWindow(window, isDebug=isMSAADebugLoggingEnabled()):
if isMSAADebugLoggingEnabled():
Expand Down
10 changes: 1 addition & 9 deletions source/NVDAHelper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ def onSpeechCanceled():

def prefixCallback():
synthDriverHandler.synthDoneSpeaking.register(onDoneSpeaking)
speech.speechCanceled.register(onSpeechCanceled)

def markCallable(name: str):
markQueue.put_nowait(name)
Expand All @@ -147,15 +148,6 @@ def markCallable(name: str):
log.error("Error parsing SSML", exc_info=True)
return SystemErrorCodes.INVALID_PARAMETER

if not asynchronous:
# onSpeechCanceled must be registered before speech.speak is queued.
# If registered inside prefixCallback (a CallbackCommand), it runs on the synth
# thread after the sequence starts — leaving a window where speechCanceled fires
# between queueFunction returning and the synth reaching the CallbackCommand.
# onDoneSpeaking stays in prefixCallback because synthDoneSpeaking fires per
# sequence: registering it here would also catch a previous sequence that is still finishing.
speech.speechCanceled.register(onSpeechCanceled)

queueHandler.queueFunction(
queueHandler.eventQueue,
speech.speak,
Expand Down
30 changes: 4 additions & 26 deletions source/NVDAObjects/IAccessible/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1877,12 +1877,9 @@ def _getIA2TargetsForRelationsOfType(
)
if config.conf["debugLog"]["annotations"]:
log.debug(f"Got {count} relations, given maxRelations: {maxRelations}")
try:
if count == 0:
return
yield from (targets[i] for i in range(min(maxRelations, count)))
finally:
winBindings.ole32.CoTaskMemFree(targets)
if count == 0:
return
yield from (targets[i] for i in range(min(maxRelations, count)))

def _getIA2RelationFirstTarget(
self,
Expand Down Expand Up @@ -2038,27 +2035,8 @@ def event_alert(self) -> None:
return
speech.speakObject(self, reason=controlTypes.OutputReason.FOCUS, priority=speech.Spri.NOW)
braille.handler.message(braille.getPropertiesBraille(name=self.name, role=self.role))
hasDescription = bool(self.description)
for child in self.recursiveDescendants:
isFocusable = controlTypes.State.FOCUSABLE in child.states
if hasDescription and not isFocusable:
shouldSpeak = isFocusable
else:
shouldSpeak = (
isFocusable
or child.role
in (
controlTypes.Role.HEADING,
controlTypes.Role.LIST,
controlTypes.Role.LISTITEM,
)
or (
child.role == controlTypes.Role.STATICTEXT
and child.parent is not None
and child.parent.role == controlTypes.Role.PARAGRAPH
)
)
if shouldSpeak:
if controlTypes.State.FOCUSABLE in child.states:
speech.speakObject(child, reason=controlTypes.OutputReason.FOCUS, priority=speech.Spri.NOW)
braille.handler.message(braille.getPropertiesBraille(name=self.name, role=self.role))

Expand Down
8 changes: 1 addition & 7 deletions source/NVDAObjects/UIA/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1690,13 +1690,7 @@ def _get_selectionContainer(self) -> "typing.Optional[UIA]":
p = self.UIASelectionItemPattern
if not p:
return None
try:
e = p.currentSelectionContainer
except COMError:
# Some providers (e.g. Qt's QWindowsUiaSelectionItemProvider when the
# accessible has no actionInterface) raise instead of returning a value.
# Treat the same as no selection container so focus speech is not aborted.
return None
e = p.currentSelectionContainer
if not e:
# Some implementations of SelectionItemPattern, such as the Outlook attachment list
# give back a NULL selectionContainer
Expand Down
Loading
Loading