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
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)
- 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