Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 2 additions & 1 deletion source/addonStore/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Optional,
)

import systemUtils
from logHandler import log

from .dataManager import (
Expand Down Expand Up @@ -83,7 +84,7 @@ def installAddon(addonPath: PathLike) -> None:
prevAddon = _getPreviouslyInstalledAddonById(bundle)

try:
installAddonBundle(bundle)
systemUtils.ExecAndPump(installAddonBundle, bundle)
if prevAddon:
prevAddon.requestRemove()
except AddonError: # Handle other exceptions as they are known
Expand Down
43 changes: 9 additions & 34 deletions source/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

import time
import os
import threading
import ctypes
import wx
import wx.adv
Expand Down Expand Up @@ -116,6 +114,15 @@ def __getattr__(attrName: str) -> Any:
stack_info=True,
)
return SettingsPanel
if attrName == "ExecAndPump" and NVDAState._allowDeprecatedAPI():
log.warning(
"Importing ExecAndPump from here is deprecated. "
"Import ExecAndPump from systemUtils instead. ",
# Include stack info so testers can report warning to add-on author.
stack_info=True,
)
import systemUtils
return systemUtils.ExecAndPump
raise AttributeError(f"module {repr(__name__)} has no attribute {repr(attrName)}")


Expand Down Expand Up @@ -771,38 +778,6 @@ def run():
wx.CallAfter(run)


class ExecAndPump(threading.Thread):
"""Executes the given function with given args and kwargs in a background thread while blocking and pumping in the current thread."""

def __init__(self,func,*args,**kwargs):
self.func=func
self.args=args
self.kwargs=kwargs
fname = repr(func)
super().__init__(
name=f"{self.__class__.__module__}.{self.__class__.__qualname__}({fname})"
)
self.threadExc=None
self.start()
time.sleep(0.1)
threadHandle=ctypes.c_int()
threadHandle.value=ctypes.windll.kernel32.OpenThread(0x100000,False,self.ident)
msg=ctypes.wintypes.MSG()
while ctypes.windll.user32.MsgWaitForMultipleObjects(1,ctypes.byref(threadHandle),False,-1,255)==1:
while ctypes.windll.user32.PeekMessageW(ctypes.byref(msg),None,0,0,1):
ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
ctypes.windll.user32.DispatchMessageW(ctypes.byref(msg))
if self.threadExc:
raise self.threadExc

def run(self):
try:
self.func(*self.args,**self.kwargs)
except Exception as e:
self.threadExc=e
log.debugWarning("task had errors",exc_info=True)


class IndeterminateProgressDialog(wx.ProgressDialog):

def __init__(self, parent: wx.Window, title: str, message: str):
Expand Down
3 changes: 2 additions & 1 deletion source/gui/addonGui.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .dpiScalingHelper import DpiScalingHelperMixinWithoutInit
import gui.contextHelp
import ui
import systemUtils


def promptUserForRestart():
Expand Down Expand Up @@ -217,7 +218,7 @@ def doneAndDestroy(window):
try:
# Use context manager to ensure that `done` and `Destroy` are called on the progress dialog afterwards
with doneAndDestroy(progressDialog):
gui.ExecAndPump(addonHandler.installAddonBundle, bundle)
systemUtils.ExecAndPump(addonHandler.installAddonBundle, bundle)
if prevAddon:
from addonStore.dataManager import addonDataManager
assert addonDataManager
Expand Down
4 changes: 2 additions & 2 deletions source/gui/installerGui.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def doInstall(
installedUserConfigPath=config.getInstalledUserConfigPath()
if installedUserConfigPath:
if _canPortableConfigBeCopied():
gui.ExecAndPump(installer.copyUserConfig, installedUserConfigPath)
systemUtils.ExecAndPump(installer.copyUserConfig, installedUserConfigPath)
except Exception as e:
res=e
log.error("Failed to execute installer",exc_info=True)
Expand Down Expand Up @@ -457,7 +457,7 @@ def doCreatePortable(
_("Please wait while a portable copy of NVDA is created.")
)
try:
gui.ExecAndPump(installer.createPortableCopy, portableDirectory, copyUserConfig)
systemUtils.ExecAndPump(installer.createPortableCopy, portableDirectory, copyUserConfig)
except Exception as e:
log.error("Failed to create portable copy", exc_info=True)
d.done()
Expand Down
3 changes: 2 additions & 1 deletion source/gui/settingsDialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
)
import languageHandler
import speech
import systemUtils
import gui
import gui.contextHelp
import globalVars
Expand Down Expand Up @@ -918,7 +919,7 @@ def onCopySettings(self,evt):
)
while True:
try:
gui.ExecAndPump(config.setSystemConfigToCurrentConfig)
systemUtils.ExecAndPump(config.setSystemConfigToCurrentConfig)
res=True
break
except installer.RetriableFailure:
Expand Down
43 changes: 43 additions & 0 deletions source/systemUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,28 @@

""" System related functions."""
import ctypes
import time
import threading
from collections.abc import (
Callable,
)
from ctypes import (
byref,
create_unicode_buffer,
sizeof,
windll,
)
from typing import (
Any,
)
import winKernel
import winreg
import shellapi
import winUser
import functools
import shlobj
from os import startfile
from logHandler import log
from NVDAState import WritePaths


Expand Down Expand Up @@ -194,3 +203,37 @@ def _isSystemClockSecondsVisible() -> bool:
return False
except OSError:
return False


class ExecAndPump(threading.Thread):
"""Executes the given function with given args and kwargs in a background thread,
while blocking and pumping in the current thread.
"""

def __init__(self, func: Callable[..., Any], *args, **kwargs) -> None:
self.func = func
self.args = args
self.kwargs = kwargs
fname = repr(func)
super().__init__(
name=f"{self.__class__.__module__}.{self.__class__.__qualname__}({fname})"
)
self.threadExc: Exception | None = None
self.start()
time.sleep(0.1)
threadHandle = ctypes.c_int()
threadHandle.value = winKernel.kernel32.OpenThread(0x100000, False, self.ident)
msg = ctypes.wintypes.MSG()
while winUser.user32.MsgWaitForMultipleObjects(1, ctypes.byref(threadHandle), False, -1, 255) == 1:
while winUser.user32.PeekMessageW(ctypes.byref(msg), None, 0, 0, 1):
winUser.user32.TranslateMessage(ctypes.byref(msg))
winUser.user32.DispatchMessageW(ctypes.byref(msg))
if self.threadExc:
raise self.threadExc

def run(self):
try:
self.func(*self.args, **self.kwargs)
except Exception as e:
self.threadExc = e
log.debugWarning("task had errors", exc_info=True)
2 changes: 2 additions & 0 deletions user_docs/en/changes.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ It is also updated with UIA enabled, when typing text and braille is tethered to
- NVDA no longer sometimes freezes when speaking a large amount of text. (#15752, @jcsteh)
- More objects which contain useful text are detected, and text content is displayed in braille. (#15605)
- When reinstalling an incompatible add-on it is no longer forcefully disabled. (#15584, @lukaszgo1)
- When installing add-ons in the add-on store install prompts are no longer overlapped by the restart dialog. (#15613, @lukaszgo1)
Comment thread
seanbudd marked this conversation as resolved.
Resolved
- When accessing Microsoft Edge using UI Automation, NVDA is able to activate more controls in browse mode. (#14612)
- NVDA no longer freezes briefly when multiple sounds are played in rapid succession. (#15757, @jcsteh)
- NVDA will not fail to start anymore when the configuration file is corrupted, but it will restore the configuration to default as it did in the past. (#15690, @CyrilleB79)
Expand Down Expand Up @@ -218,6 +219,7 @@ Code which imports from one of them, should instead import from the replacement
- The ``bdDetect.KEY_*`` constants have been deprecated.
Use ``bdDetect.DeviceType.*`` instead. (#15772, @LeonarddeR).
- The ``bdDetect.DETECT_USB`` and ``bdDetect.DETECT_BLUETOOTH`` constants have been deprecated with no public replacement. (#15772, @LeonarddeR).
- Using ``gui.ExecAndPump`` is deprecated - please use ``systemUtils.ExecAndPump`` instead. (#15852, @lukaszgo1)
-


Expand Down