Skip to content
Draft
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
8 changes: 6 additions & 2 deletions source/_magnifier/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from typing import TYPE_CHECKING
from .config import getMagnifiedView
from .utils.types import MagnifiedView
from .fullscreenMagnifier import FullScreenMagnifier

if TYPE_CHECKING:
from .magnifier import Magnifier
Expand Down Expand Up @@ -119,6 +120,9 @@ def terminate() -> None:
Called when NVDA shuts down.
"""
global _magnifier
if _magnifier and _magnifier._isActive:
_magnifier._stopMagnifier()
if _magnifier:
if _magnifier._isActive:
_magnifier._stopMagnifier()
if isinstance(_magnifier, FullScreenMagnifier):
_magnifier._finalUninitializeNativeMagnification()
_magnifier = None
48 changes: 29 additions & 19 deletions source/_magnifier/fullscreenMagnifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def __init__(self):
self._currentCoordinates = Coordinates(0, 0)
self._spotlightManager = SpotlightManager(self)
self._displaySize = Size(self._displayOrientation.width, self._displayOrientation.height)
self._nativeApiInitialized: bool = False
self._startMagnifier()

@Magnifier.filterType.setter
Expand Down Expand Up @@ -89,26 +90,25 @@ def _startMagnifier(self) -> None:

def _initializeNativeMagnification(self) -> None:
"""
Initialize the Magnification API and verify it is fully usable.

Raises OSError if MagInitialize fails or if the initial fullscreen
transform fails (e.g. Windows Magnifier already holds the API). If
MagSetFullscreenTransform fails after a successful MagInitialize, this
method uninitializes the native magnification API before re-raising.
Failures from MagInitialize are propagated to the caller.
"""
magnification.MagInitialize()
log.debug("Magnification API initialized")
Initialize the Magnification API and apply the initial fullscreen transform.
MagInitialize is called only once per instance lifetime: the Initialize →
Uninitialize → Initialize cycle within the same process causes
MagSetFullscreenTransform to fail with WinError 0. To avoid this, the API
stays initialized between magnifier toggles and is only uninitialized on
NVDA shutdown via _finalUninitializeNativeMagnification.
Raises OSError if MagInitialize fails or if MagSetFullscreenTransform fails
(e.g. Windows Magnifier already holds the API).
"""
if not self._nativeApiInitialized:
magnification.MagInitialize()
self._nativeApiInitialized = True
log.debug("Magnification API initialized")
# Applying the first real update verifies the API is usable without
# briefly jumping the magnified view to the top-left corner.
try:
coordinates = self._getCoordinatesForMode(self._currentCoordinates)
# Save screen position for mode continuity, matching _doUpdate.
self._lastScreenPosition = coordinates
self._fullscreenMagnifier(coordinates)
except OSError:
self._uninitializeNativeMagnification()
raise
coordinates = self._getCoordinatesForMode(self._currentCoordinates)
# Save screen position for mode continuity, matching _doUpdate.
self._lastScreenPosition = coordinates
self._fullscreenMagnifier(coordinates)

def _doUpdate(self):
"""
Expand All @@ -123,11 +123,20 @@ def _doUpdate(self):

def _stopMagnifier(self) -> None:
"""
Stop the Full-screen magnifier using windows DLL
Stop the Full-screen magnifier using windows DLL.
The native API is intentionally kept initialized to allow clean restarts;
it is only uninitialized on NVDA shutdown via _finalUninitializeNativeMagnification.
"""
super()._stopMagnifier()
self._resetMagnification()

def _finalUninitializeNativeMagnification(self) -> None:
"""
Uninitialize the Magnification API.
Must be called after _stopMagnifier, which already resets the transform.
"""
self._uninitializeNativeMagnification()
self._nativeApiInitialized = False

@trackNativeMagnifierErrors
def _resetMagnification(self) -> None:
Expand Down Expand Up @@ -172,6 +181,7 @@ def _attemptRecovery(self) -> None:
)

self._uninitializeNativeMagnification()
self._nativeApiInitialized = False

try:
# _initializeNativeMagnification also probes MagSetFullscreenTransform,
Expand Down
Loading