def __init__(self, channels, samplesPerSec, bitsPerSample, outputDevice=WAVE_MAPPER, closeWhenIdle=True,wantDucking=True): """Constructor. @param channels: The number of channels of audio; e.g. 2 for stereo, 1 for mono. @type channels: int @param samplesPerSec: Samples per second (hz). @type samplesPerSec: int @param bitsPerSample: The number of bits per sample. @type bitsPerSample: int @param outputDevice: The device ID or name of the audio output device to use. @type outputDevice: int or basestring @param closeWhenIdle: If C{True}, close the output device when no audio is being played. @type closeWhenIdle: bool @param wantDucking: if true then background audio will be ducked on Windows 8 and higher @type wantDucking: bool @note: If C{outputDevice} is a name and no such device exists, the default device will be used. @raise WindowsError: If there was an error opening the audio output device. """ self.channels=channels self.samplesPerSec=samplesPerSec self.bitsPerSample=bitsPerSample if isinstance(outputDevice, basestring): outputDevice = outputDeviceNameToID(outputDevice, True) self.outputDeviceID = outputDevice if wantDucking: import audioDucking if audioDucking.isAudioDuckingSupported(): self._audioDucker=audioDucking.AudioDucker() #: If C{True}, close the output device when no audio is being played. #: @type: bool self.closeWhenIdle = closeWhenIdle self._waveout = None self._waveout_event = winKernel.kernel32.CreateEventW(None, False, False, None) self._waveout_lock = threading.RLock() self._lock = threading.RLock() self.open()
def __init__(self, channels, samplesPerSec, bitsPerSample, outputDevice=WAVE_MAPPER, closeWhenIdle=True, wantDucking=True, buffered=False): """Constructor. @param channels: The number of channels of audio; e.g. 2 for stereo, 1 for mono. @type channels: int @param samplesPerSec: Samples per second (hz). @type samplesPerSec: int @param bitsPerSample: The number of bits per sample. @type bitsPerSample: int @param outputDevice: The device ID or name of the audio output device to use. @type outputDevice: int or str @param closeWhenIdle: If C{True}, close the output device when no audio is being played. @type closeWhenIdle: bool @param wantDucking: if true then background audio will be ducked on Windows 8 and higher @type wantDucking: bool @param buffered: Whether to buffer small chunks of audio to prevent audio glitches. @type buffered: bool @note: If C{outputDevice} is a name and no such device exists, the default device will be used. @raise WindowsError: If there was an error opening the audio output device. """ self.channels = channels self.samplesPerSec = samplesPerSec self.bitsPerSample = bitsPerSample if isinstance(outputDevice, str): outputDevice = outputDeviceNameToID(outputDevice, True) self.outputDeviceID = outputDevice if wantDucking: import audioDucking if audioDucking.isAudioDuckingSupported(): self._audioDucker = audioDucking.AudioDucker() #: If C{True}, close the output device when no audio is being played. #: @type: bool self.closeWhenIdle = closeWhenIdle if buffered: #: Minimum size of the buffer before audio is played. #: However, this is ignored if an C{onDone} callback is provided to L{feed}. BITS_PER_BYTE = 8 MS_PER_SEC = 1000 self._minBufferSize = samplesPerSec * channels * ( bitsPerSample / BITS_PER_BYTE) / MS_PER_SEC * self.MIN_BUFFER_MS self._buffer = b"" else: self._minBufferSize = None #: Function to call when the previous chunk of audio has finished playing. self._prevOnDone = None self._waveout = None self._waveout_event = winKernel.kernel32.CreateEventW( None, False, False, None) self._waveout_lock = threading.RLock() self._lock = threading.RLock() self.open()
def ensureWaveOutHooks(): if not _waveOutHooks and audioDucking.isAudioDuckingSupported(): sapiPath = os.path.join(os.path.expandvars("$SYSTEMROOT"), "system32", "speech", "common", "sapi.dll") _waveOutHooks.append( FunctionHooker(sapiPath, "WINMM.dll", "waveOutOpen", waveOutOpen)) _waveOutHooks.append( FunctionHooker(sapiPath, "WINMM.dll", "waveOutClose", waveOutClose))
def __init__(self,_defaultVoiceToken=None): """ @param _defaultVoiceToken: an optional sapi voice token which should be used as the default voice (only useful for subclasses) @type _defaultVoiceToken: ISpeechObjectToken """ if audioDucking.isAudioDuckingSupported(): self._audioDucker = audioDucking.AudioDucker() self._pitch=50 self._initTts(_defaultVoiceToken)
def resetConfiguration(factoryDefaults=False): """Loads the configuration, installs the correct language support and initialises audio so that it will use the configured synth and speech settings. """ import config import braille import brailleInput import speech import vision import inputCore import tones log.debug("Terminating vision") vision.terminate() log.debug("Terminating braille") braille.terminate() log.debug("Terminating brailleInput") brailleInput.terminate() log.debug("terminating speech") speech.terminate() log.debug("terminating tones") tones.terminate() log.debug("terminating addonHandler") addonHandler.terminate() log.debug("Reloading config") config.conf.reset(factoryDefaults=factoryDefaults) logHandler.setLogLevelFromConfig() # Language if languageHandler.isLanguageForced(): lang = globalVars.appArgs.language else: lang = config.conf["general"]["language"] log.debug("setting language to %s" % lang) languageHandler.setLanguage(lang) # Addons addonHandler.initialize() # Tones tones.initialize() #Speech log.debug("initializing speech") speech.initialize() #braille log.debug("Initializing brailleInput") brailleInput.initialize() log.debug("Initializing braille") braille.initialize() # Vision log.debug("initializing vision") vision.initialize() log.debug("Reloading user and locale input gesture maps") inputCore.manager.loadUserGestureMap() inputCore.manager.loadLocaleGestureMap() import audioDucking if audioDucking.isAudioDuckingSupported(): audioDucking.handlePostConfigProfileSwitch() log.info("Reverted to saved configuration")
def resetConfiguration(factoryDefaults=False): """Loads the configuration, installs the correct language support and initialises audio so that it will use the configured synth and speech settings. """ import config import braille import brailleInput import speech import languageHandler import inputCore log.debug("Terminating braille") braille.terminate() log.debug("Terminating brailleInput") brailleInput.terminate() log.debug("terminating speech") speech.terminate() log.debug("terminating addonHandler") addonHandler.terminate() log.debug("Reloading config") config.conf.reset(factoryDefaults=factoryDefaults) logHandler.setLogLevelFromConfig() #Language lang = config.conf["general"]["language"] log.debug("setting language to %s"%lang) languageHandler.setLanguage(lang) # Addons addonHandler.initialize() #Speech log.debug("initializing speech") speech.initialize() #braille log.debug("Initializing brailleInput") brailleInput.initialize() log.debug("Initializing braille") braille.initialize() log.debug("Reloading user and locale input gesture maps") inputCore.manager.loadUserGestureMap() inputCore.manager.loadLocaleGestureMap() import audioDucking if audioDucking.isAudioDuckingSupported(): audioDucking.handlePostConfigProfileSwitch() log.info("Reverted to saved configuration")
def resetConfiguration(factoryDefaults=False): """Loads the configuration, installs the correct language support and initialises audio so that it will use the configured synth and speech settings. """ import config import braille import speech import languageHandler import inputCore log.debug("Terminating braille") braille.terminate() log.debug("terminating speech") speech.terminate() log.debug("terminating addonHandler") addonHandler.terminate() log.debug("Reloading config") config.conf.reset(factoryDefaults=factoryDefaults) logHandler.setLogLevelFromConfig() logHandler.setPlayErrorSoundFromConfig() #Language lang = config.conf["general"]["language"] log.debug("setting language to %s" % lang) languageHandler.setLanguage(lang) # Addons addonHandler.initialize() #Speech log.debug("initializing speech") speech.initialize() #braille log.debug("Initializing braille") braille.initialize() log.debug("Reloading user and locale input gesture maps") inputCore.manager.loadUserGestureMap() inputCore.manager.loadLocaleGestureMap() import audioDucking if audioDucking.isAudioDuckingSupported(): audioDucking.handleConfigProfileSwitch() log.info("Reverted to saved configuration")
def main(): """NVDA's core main loop. This initializes all modules such as audio, IAccessible, keyboard, mouse, and GUI. Then it initialises the wx application object and sets up the core pump, which checks the queues and executes functions when requested. Finally, it starts the wx main loop. """ log.debug("Core starting") ctypes.windll.user32.SetProcessDPIAware() import config if not globalVars.appArgs.configPath: globalVars.appArgs.configPath = config.getUserDefaultConfigPath( useInstalledPathIfExists=globalVars.appArgs.launcher) #Initialize the config path (make sure it exists) config.initConfigPath() log.info(f"Config dir: {globalVars.appArgs.configPath}") log.debug("loading config") import config config.initialize() if config.conf['development']['enableScratchpadDir']: log.info("Developer Scratchpad mode enabled") if not globalVars.appArgs.minimal and config.conf["general"][ "playStartAndExitSounds"]: try: nvwave.playWaveFile( os.path.join(globalVars.appDir, "waves", "start.wav")) except: pass logHandler.setLogLevelFromConfig() try: lang = config.conf["general"]["language"] import languageHandler log.debug("setting language to %s" % lang) languageHandler.setLanguage(lang) except: log.warning("Could not set language to %s" % lang) log.info(f"Windows version: {winVersion.getWinVer()}") log.info("Using Python version %s" % sys.version) log.info("Using comtypes version %s" % comtypes.__version__) import configobj log.info("Using configobj version %s with validate version %s" % (configobj.__version__, configobj.validate.__version__)) # Set a reasonable timeout for any socket connections NVDA makes. import socket socket.setdefaulttimeout(10) log.debug("Initializing add-ons system") addonHandler.initialize() if globalVars.appArgs.disableAddons: log.info("Add-ons are disabled. Restart NVDA to enable them.") import appModuleHandler log.debug("Initializing appModule Handler") appModuleHandler.initialize() import NVDAHelper log.debug("Initializing NVDAHelper") NVDAHelper.initialize() log.debug("Initializing tones") import tones tones.initialize() import speechDictHandler log.debug("Speech Dictionary processing") speechDictHandler.initialize() import speech log.debug("Initializing speech") speech.initialize() if not globalVars.appArgs.minimal and (time.time() - globalVars.startTime) > 5: log.debugWarning("Slow starting core (%.2f sec)" % (time.time() - globalVars.startTime)) # Translators: This is spoken when NVDA is starting. speech.speakMessage(_("Loading NVDA. Please wait...")) import wx import six log.info("Using wx version %s with six version %s" % (wx.version(), six.__version__)) class App(wx.App): def OnAssert(self, file, line, cond, msg): message = "{file}, line {line}:\nassert {cond}: {msg}".format( file=file, line=line, cond=cond, msg=msg) log.debugWarning(message, codepath="WX Widgets", stack_info=True) def InitLocale(self): # Backport of `InitLocale` from wx Python 4.1.2 as the current version tries to set a Python # locale to an nonexistent one when creating an instance of `wx.App`. # This causes a crash when running under a particular version of Universal CRT (#12160) import locale locale.setlocale(locale.LC_ALL, "C") app = App(redirect=False) # We support queryEndSession events, but in general don't do anything for them. # However, when running as a Windows Store application, we do want to request to be restarted for updates def onQueryEndSession(evt): if config.isAppX: # Automatically restart NVDA on Windows Store update ctypes.windll.kernel32.RegisterApplicationRestart(None, 0) app.Bind(wx.EVT_QUERY_END_SESSION, onQueryEndSession) def onEndSession(evt): # NVDA will be terminated as soon as this function returns, so save configuration if appropriate. config.saveOnExit() speech.cancelSpeech() if not globalVars.appArgs.minimal and config.conf["general"][ "playStartAndExitSounds"]: try: nvwave.playWaveFile(os.path.join(globalVars.appDir, "waves", "exit.wav"), asynchronous=False) except: pass log.info("Windows session ending") app.Bind(wx.EVT_END_SESSION, onEndSession) log.debug("Initializing braille input") import brailleInput brailleInput.initialize() import braille log.debug("Initializing braille") braille.initialize() import vision log.debug("Initializing vision") vision.initialize() import displayModel log.debug("Initializing displayModel") displayModel.initialize() log.debug("Initializing GUI") import gui gui.initialize() import audioDucking if audioDucking.isAudioDuckingSupported(): # the GUI mainloop must be running for this to work so delay it wx.CallAfter(audioDucking.initialize) # #3763: In wxPython 3, the class name of frame windows changed from wxWindowClassNR to wxWindowNR. # NVDA uses the main frame to check for and quit another instance of NVDA. # To remain compatible with older versions of NVDA, create our own wxWindowClassNR. # We don't need to do anything else because wx handles WM_QUIT for all windows. import windowUtils class MessageWindow(windowUtils.CustomWindow): className = u"wxWindowClassNR" # Windows constants for power / display changes WM_POWERBROADCAST = 0x218 PBT_APMPOWERSTATUSCHANGE = 0xA UNKNOWN_BATTERY_STATUS = 0xFF AC_ONLINE = 0X1 NO_SYSTEM_BATTERY = 0X80 #States for screen orientation ORIENTATION_NOT_INITIALIZED = 0 ORIENTATION_PORTRAIT = 1 ORIENTATION_LANDSCAPE = 2 def __init__(self, windowName=None): super(MessageWindow, self).__init__(windowName) self.oldBatteryStatus = None self.orientationStateCache = self.ORIENTATION_NOT_INITIALIZED self.orientationCoordsCache = (0, 0) self.handlePowerStatusChange() def windowProc(self, hwnd, msg, wParam, lParam): post_windowMessageReceipt.notify(msg=msg, wParam=wParam, lParam=lParam) if msg == self.WM_POWERBROADCAST and wParam == self.PBT_APMPOWERSTATUSCHANGE: self.handlePowerStatusChange() elif msg == winUser.WM_DISPLAYCHANGE: self.handleScreenOrientationChange(lParam) def handleScreenOrientationChange(self, lParam): import ui import winUser # Resolution detection comes from an article found at https://msdn.microsoft.com/en-us/library/ms812142.aspx. #The low word is the width and hiword is height. width = winUser.LOWORD(lParam) height = winUser.HIWORD(lParam) self.orientationCoordsCache = (width, height) if width > height: # If the height and width are the same, it's actually a screen flip, and we do want to alert of those! if self.orientationStateCache == self.ORIENTATION_LANDSCAPE and self.orientationCoordsCache != ( width, height): return #Translators: The screen is oriented so that it is wider than it is tall. ui.message(_("Landscape")) self.orientationStateCache = self.ORIENTATION_LANDSCAPE else: if self.orientationStateCache == self.ORIENTATION_PORTRAIT and self.orientationCoordsCache != ( width, height): return #Translators: The screen is oriented in such a way that the height is taller than it is wide. ui.message(_("Portrait")) self.orientationStateCache = self.ORIENTATION_PORTRAIT def handlePowerStatusChange(self): #Mostly taken from script_say_battery_status, but modified. import ui import winKernel sps = winKernel.SYSTEM_POWER_STATUS() if not winKernel.GetSystemPowerStatus( sps) or sps.BatteryFlag is self.UNKNOWN_BATTERY_STATUS: return if sps.BatteryFlag & self.NO_SYSTEM_BATTERY: return if self.oldBatteryStatus is None: #Just initializing the cache, do not report anything. self.oldBatteryStatus = sps.ACLineStatus return if sps.ACLineStatus == self.oldBatteryStatus: #Sometimes, this double fires. This also fires when the battery level decreases by 3%. return self.oldBatteryStatus = sps.ACLineStatus if sps.ACLineStatus & self.AC_ONLINE: #Translators: Reported when the battery is plugged in, and now is charging. ui.message( _("Charging battery. %d percent") % sps.BatteryLifePercent) else: #Translators: Reported when the battery is no longer plugged in, and now is not charging. ui.message( _("Not charging battery. %d percent") % sps.BatteryLifePercent) import versionInfo messageWindow = MessageWindow(versionInfo.name) # initialize wxpython localization support locale = wx.Locale() wxLang = getWxLangOrNone() if hasattr(sys, 'frozen'): locale.AddCatalogLookupPathPrefix( os.path.join(globalVars.appDir, "locale")) if wxLang: try: locale.Init(wxLang.Language) except: log.error("Failed to initialize wx locale", exc_info=True) finally: # Revert wx's changes to the python locale languageHandler.setLocale(languageHandler.curLang) log.debug("Initializing garbageHandler") garbageHandler.initialize() import api import winUser import NVDAObjects.window desktopObject = NVDAObjects.window.Window( windowHandle=winUser.getDesktopWindow()) api.setDesktopObject(desktopObject) api.setFocusObject(desktopObject) api.setNavigatorObject(desktopObject) api.setMouseObject(desktopObject) import JABHandler log.debug("initializing Java Access Bridge support") try: JABHandler.initialize() log.info("Java Access Bridge support initialized") except NotImplementedError: log.warning("Java Access Bridge not available") except: log.error("Error initializing Java Access Bridge support", exc_info=True) import winConsoleHandler log.debug("Initializing legacy winConsole support") winConsoleHandler.initialize() import UIAHandler log.debug("Initializing UIA support") try: UIAHandler.initialize() except RuntimeError: log.warning("UIA disabled in configuration") except: log.error("Error initializing UIA support", exc_info=True) import IAccessibleHandler log.debug("Initializing IAccessible support") IAccessibleHandler.initialize() log.debug("Initializing input core") import inputCore inputCore.initialize() import keyboardHandler log.debug("Initializing keyboard handler") keyboardHandler.initialize() import mouseHandler log.debug("initializing mouse handler") mouseHandler.initialize() import touchHandler log.debug("Initializing touchHandler") try: touchHandler.initialize() except NotImplementedError: pass import globalPluginHandler log.debug("Initializing global plugin handler") globalPluginHandler.initialize() if globalVars.appArgs.install or globalVars.appArgs.installSilent: import gui.installerGui wx.CallAfter(gui.installerGui.doSilentInstall, copyPortableConfig=globalVars.appArgs.copyPortableConfig, startAfterInstall=not globalVars.appArgs.installSilent) elif globalVars.appArgs.portablePath and ( globalVars.appArgs.createPortable or globalVars.appArgs.createPortableSilent): import gui.installerGui wx.CallAfter( gui.installerGui.doCreatePortable, portableDirectory=globalVars.appArgs.portablePath, silent=globalVars.appArgs.createPortableSilent, startAfterCreate=not globalVars.appArgs.createPortableSilent) elif not globalVars.appArgs.minimal: try: # Translators: This is shown on a braille display (if one is connected) when NVDA starts. braille.handler.message(_("NVDA started")) except: log.error("", exc_info=True) if globalVars.appArgs.launcher: from gui.startupDialogs import LauncherDialog LauncherDialog.run() # LauncherDialog will call doStartupDialogs() afterwards if required. else: wx.CallAfter(doStartupDialogs) import queueHandler # Queue the handling of initial focus, # as API handlers might need to be pumped to get the first focus event. queueHandler.queueFunction(queueHandler.eventQueue, _setInitialFocus) import watchdog import baseObject # Doing this here is a bit ugly, but we don't want these modules imported # at module level, including wx. log.debug("Initializing core pump") class CorePump(gui.NonReEntrantTimer): "Checks the queues and executes functions." def run(self): global _isPumpPending _isPumpPending = False watchdog.alive() try: if touchHandler.handler: touchHandler.handler.pump() JABHandler.pumpAll() IAccessibleHandler.pumpAll() queueHandler.pumpAll() mouseHandler.pumpAll() braille.pumpAll() vision.pumpAll() except: log.exception("errors in this core pump cycle") baseObject.AutoPropertyObject.invalidateCaches() watchdog.asleep() if _isPumpPending and not _pump.IsRunning(): # #3803: Another pump was requested during this pump execution. # As our pump is not re-entrant, schedule another pump. _pump.Start(PUMP_MAX_DELAY, True) global _pump _pump = CorePump() requestPump() log.debug("Initializing watchdog") watchdog.initialize() try: import updateCheck except RuntimeError: updateCheck = None log.debug("Update checking not supported") else: log.debug("initializing updateCheck") updateCheck.initialize() log.info("NVDA initialized") # Queue the firing of the postNVDAStartup notification. # This is queued so that it will run from within the core loop, # and initial focus has been reported. def _doPostNvdaStartupAction(): log.debug("Notify of postNvdaStartup action") postNvdaStartup.notify() queueHandler.queueFunction(queueHandler.eventQueue, _doPostNvdaStartupAction) log.debug("entering wx application main loop") app.MainLoop() log.info("Exiting") # If MainLoop is terminated through WM_QUIT, such as starting an NVDA instance older than 2021.1, # triggerNVDAExit has not been called yet if triggerNVDAExit(): log.debug("NVDA not already exiting, hit catch-all exit trigger." " This likely indicates NVDA is exiting due to WM_QUIT.") queueHandler.pumpAll() _terminate(gui) config.saveOnExit() try: if globalVars.focusObject and hasattr(globalVars.focusObject, "event_loseFocus"): log.debug("calling lose focus on object with focus") globalVars.focusObject.event_loseFocus() except: log.exception("Lose focus error") try: speech.cancelSpeech() except: pass import treeInterceptorHandler _terminate(treeInterceptorHandler) _terminate(IAccessibleHandler, name="IAccessible support") _terminate(UIAHandler, name="UIA support") _terminate(winConsoleHandler, name="Legacy winConsole support") _terminate(JABHandler, name="Java Access Bridge support") _terminate(appModuleHandler, name="app module handler") _terminate(tones) _terminate(NVDAHelper) _terminate(touchHandler) _terminate(keyboardHandler, name="keyboard handler") _terminate(mouseHandler) _terminate(inputCore) _terminate(vision) _terminate(brailleInput) _terminate(braille) _terminate(speech) _terminate(addonHandler) _terminate(garbageHandler) # DMP is only started if needed. # Terminate manually (and let it write to the log if necessary) # as core._terminate always writes an entry. try: import diffHandler diffHandler._dmp._terminate() except Exception: log.exception("Exception while terminating DMP") if not globalVars.appArgs.minimal and config.conf["general"][ "playStartAndExitSounds"]: try: nvwave.playWaveFile(os.path.join(globalVars.appDir, "waves", "exit.wav"), asynchronous=False) except: pass # #5189: Destroy the message window as late as possible # so new instances of NVDA can find this one even if it freezes during exit. messageWindow.destroy() log.debug("core done")
def ensureWaveOutHooks(): if not _waveOutHooks and audioDucking.isAudioDuckingSupported(): sapiPath=os.path.join(os.path.expandvars("$SYSTEMROOT"),"system32","speech","common","sapi.dll") _waveOutHooks.append(FunctionHooker(sapiPath,"WINMM.dll","waveOutOpen",waveOutOpen)) _waveOutHooks.append(FunctionHooker(sapiPath,"WINMM.dll","waveOutClose",waveOutClose))
def main(): """NVDA's core main loop. This initializes all modules such as audio, IAccessible, keyboard, mouse, and GUI. Then it initialises the wx application object and sets up the core pump, which checks the queues and executes functions when requested. Finally, it starts the wx main loop. """ log.debug("Core starting") ctypes.windll.user32.SetProcessDPIAware() import config if not globalVars.appArgs.configPath: globalVars.appArgs.configPath=config.getUserDefaultConfigPath(useInstalledPathIfExists=globalVars.appArgs.launcher) #Initialize the config path (make sure it exists) config.initConfigPath() log.info("Config dir: %s"%os.path.abspath(globalVars.appArgs.configPath)) log.debug("loading config") import config config.initialize() if not globalVars.appArgs.minimal and config.conf["general"]["playStartAndExitSounds"]: try: nvwave.playWaveFile("waves\\start.wav") except: pass logHandler.setLogLevelFromConfig() try: lang = config.conf["general"]["language"] import languageHandler log.debug("setting language to %s"%lang) languageHandler.setLanguage(lang) except: log.warning("Could not set language to %s"%lang) import versionInfo log.info("NVDA version %s" % versionInfo.version) log.info("Using Windows version %s" % winVersion.winVersionText) log.info("Using Python version %s"%sys.version) log.info("Using comtypes version %s"%comtypes.__version__) import configobj log.info("Using configobj version %s with validate version %s"%(configobj.__version__,configobj.validate.__version__)) # Set a reasonable timeout for any socket connections NVDA makes. import socket socket.setdefaulttimeout(10) log.debug("Initializing add-ons system") addonHandler.initialize() if globalVars.appArgs.disableAddons: log.info("Add-ons are disabled. Restart NVDA to enable them.") import appModuleHandler log.debug("Initializing appModule Handler") appModuleHandler.initialize() import NVDAHelper log.debug("Initializing NVDAHelper") NVDAHelper.initialize() import speechDictHandler log.debug("Speech Dictionary processing") speechDictHandler.initialize() import speech log.debug("Initializing speech") speech.initialize() if not globalVars.appArgs.minimal and (time.time()-globalVars.startTime)>5: log.debugWarning("Slow starting core (%.2f sec)" % (time.time()-globalVars.startTime)) # Translators: This is spoken when NVDA is starting. speech.speakMessage(_("Loading NVDA. Please wait...")) import wx # wxPython 4 no longer has either of these constants (despite the documentation saying so), some add-ons may rely on # them so we add it back into wx. https://wxpython.org/Phoenix/docs/html/wx.Window.html#wx.Window.Centre wx.CENTER_ON_SCREEN = wx.CENTRE_ON_SCREEN = 0x2 log.info("Using wx version %s"%wx.version()) class App(wx.App): def OnAssert(self,file,line,cond,msg): message="{file}, line {line}:\nassert {cond}: {msg}".format(file=file,line=line,cond=cond,msg=msg) log.debugWarning(message,codepath="WX Widgets",stack_info=True) app = App(redirect=False) # We support queryEndSession events, but in general don't do anything for them. # However, when running as a Windows Store application, we do want to request to be restarted for updates def onQueryEndSession(evt): if config.isAppX: # Automatically restart NVDA on Windows Store update ctypes.windll.kernel32.RegisterApplicationRestart(None,0) app.Bind(wx.EVT_QUERY_END_SESSION, onQueryEndSession) def onEndSession(evt): # NVDA will be terminated as soon as this function returns, so save configuration if appropriate. config.saveOnExit() speech.cancelSpeech() if not globalVars.appArgs.minimal and config.conf["general"]["playStartAndExitSounds"]: try: nvwave.playWaveFile("waves\\exit.wav",async=False) except: pass log.info("Windows session ending") app.Bind(wx.EVT_END_SESSION, onEndSession) log.debug("Initializing braille input") import brailleInput brailleInput.initialize() import braille log.debug("Initializing braille") braille.initialize() import displayModel log.debug("Initializing displayModel") displayModel.initialize() log.debug("Initializing GUI") import gui gui.initialize() import audioDucking if audioDucking.isAudioDuckingSupported(): # the GUI mainloop must be running for this to work so delay it wx.CallAfter(audioDucking.initialize) # #3763: In wxPython 3, the class name of frame windows changed from wxWindowClassNR to wxWindowNR. # NVDA uses the main frame to check for and quit another instance of NVDA. # To remain compatible with older versions of NVDA, create our own wxWindowClassNR. # We don't need to do anything else because wx handles WM_QUIT for all windows. import windowUtils class MessageWindow(windowUtils.CustomWindow): className = u"wxWindowClassNR" #Just define these constants here, so we don't have to import win32con WM_POWERBROADCAST = 0x218 WM_DISPLAYCHANGE = 0x7e PBT_APMPOWERSTATUSCHANGE = 0xA UNKNOWN_BATTERY_STATUS = 0xFF AC_ONLINE = 0X1 NO_SYSTEM_BATTERY = 0X80 #States for screen orientation ORIENTATION_NOT_INITIALIZED = 0 ORIENTATION_PORTRAIT = 1 ORIENTATION_LANDSCAPE = 2 def __init__(self, windowName=None): super(MessageWindow, self).__init__(windowName) self.oldBatteryStatus = None self.orientationStateCache = self.ORIENTATION_NOT_INITIALIZED self.orientationCoordsCache = (0,0) self.handlePowerStatusChange() def windowProc(self, hwnd, msg, wParam, lParam): post_windowMessageReceipt.notify(msg=msg, wParam=wParam, lParam=lParam) if msg == self.WM_POWERBROADCAST and wParam == self.PBT_APMPOWERSTATUSCHANGE: self.handlePowerStatusChange() elif msg == self.WM_DISPLAYCHANGE: self.handleScreenOrientationChange(lParam) def handleScreenOrientationChange(self, lParam): import ui import winUser # Resolution detection comes from an article found at https://msdn.microsoft.com/en-us/library/ms812142.aspx. #The low word is the width and hiword is height. width = winUser.LOWORD(lParam) height = winUser.HIWORD(lParam) self.orientationCoordsCache = (width,height) if width > height: # If the height and width are the same, it's actually a screen flip, and we do want to alert of those! if self.orientationStateCache == self.ORIENTATION_LANDSCAPE and self.orientationCoordsCache != (width,height): return #Translators: The screen is oriented so that it is wider than it is tall. ui.message(_("Landscape" )) self.orientationStateCache = self.ORIENTATION_LANDSCAPE else: if self.orientationStateCache == self.ORIENTATION_PORTRAIT and self.orientationCoordsCache != (width,height): return #Translators: The screen is oriented in such a way that the height is taller than it is wide. ui.message(_("Portrait")) self.orientationStateCache = self.ORIENTATION_PORTRAIT def handlePowerStatusChange(self): #Mostly taken from script_say_battery_status, but modified. import ui import winKernel sps = winKernel.SYSTEM_POWER_STATUS() if not winKernel.GetSystemPowerStatus(sps) or sps.BatteryFlag is self.UNKNOWN_BATTERY_STATUS: return if sps.BatteryFlag & self.NO_SYSTEM_BATTERY: return if self.oldBatteryStatus is None: #Just initializing the cache, do not report anything. self.oldBatteryStatus = sps.ACLineStatus return if sps.ACLineStatus == self.oldBatteryStatus: #Sometimes, this double fires. This also fires when the battery level decreases by 3%. return self.oldBatteryStatus = sps.ACLineStatus if sps.ACLineStatus & self.AC_ONLINE: #Translators: Reported when the battery is plugged in, and now is charging. ui.message(_("Charging battery. %d percent") % sps.BatteryLifePercent) else: #Translators: Reported when the battery is no longer plugged in, and now is not charging. ui.message(_("Not charging battery. %d percent") %sps.BatteryLifePercent) messageWindow = MessageWindow(unicode(versionInfo.name)) # initialize wxpython localization support locale = wx.Locale() lang=languageHandler.getLanguage() wxLang=locale.FindLanguageInfo(lang) if not wxLang and '_' in lang: wxLang=locale.FindLanguageInfo(lang.split('_')[0]) if hasattr(sys,'frozen'): locale.AddCatalogLookupPathPrefix(os.path.join(os.getcwdu(),"locale")) # #8064: Wx might know the language, but may not actually contain a translation database for that language. # If we try to initialize this language, wx will show a warning dialog. # Therefore treat this situation like wx not knowing the language at all. if not locale.IsAvailable(wxLang.Language): wxLang=None if wxLang: try: locale.Init(wxLang.Language) except: log.error("Failed to initialize wx locale",exc_info=True) else: log.debugWarning("wx does not support language %s" % lang) import api import winUser import NVDAObjects.window desktopObject=NVDAObjects.window.Window(windowHandle=winUser.getDesktopWindow()) api.setDesktopObject(desktopObject) api.setFocusObject(desktopObject) api.setNavigatorObject(desktopObject) api.setMouseObject(desktopObject) import JABHandler log.debug("initializing Java Access Bridge support") try: JABHandler.initialize() except NotImplementedError: log.warning("Java Access Bridge not available") except: log.error("Error initializing Java Access Bridge support", exc_info=True) import winConsoleHandler log.debug("Initializing winConsole support") winConsoleHandler.initialize() import UIAHandler log.debug("Initializing UIA support") try: UIAHandler.initialize() except NotImplementedError: log.warning("UIA not available") except: log.error("Error initializing UIA support", exc_info=True) import IAccessibleHandler log.debug("Initializing IAccessible support") IAccessibleHandler.initialize() log.debug("Initializing input core") import inputCore inputCore.initialize() import keyboardHandler log.debug("Initializing keyboard handler") keyboardHandler.initialize() import mouseHandler log.debug("initializing mouse handler") mouseHandler.initialize() import touchHandler log.debug("Initializing touchHandler") try: touchHandler.initialize() except NotImplementedError: pass import globalPluginHandler log.debug("Initializing global plugin handler") globalPluginHandler.initialize() if globalVars.appArgs.install or globalVars.appArgs.installSilent: import gui.installerGui wx.CallAfter(gui.installerGui.doSilentInstall,startAfterInstall=not globalVars.appArgs.installSilent) elif globalVars.appArgs.portablePath and (globalVars.appArgs.createPortable or globalVars.appArgs.createPortableSilent): import gui.installerGui wx.CallAfter(gui.installerGui.doCreatePortable,portableDirectory=globalVars.appArgs.portablePath, silent=globalVars.appArgs.createPortableSilent,startAfterCreate=not globalVars.appArgs.createPortableSilent) elif not globalVars.appArgs.minimal: try: # Translators: This is shown on a braille display (if one is connected) when NVDA starts. braille.handler.message(_("NVDA started")) except: log.error("", exc_info=True) if globalVars.appArgs.launcher: gui.LauncherDialog.run() # LauncherDialog will call doStartupDialogs() afterwards if required. else: wx.CallAfter(doStartupDialogs) import queueHandler # Queue the handling of initial focus, # as API handlers might need to be pumped to get the first focus event. queueHandler.queueFunction(queueHandler.eventQueue, _setInitialFocus) import watchdog import baseObject # Doing this here is a bit ugly, but we don't want these modules imported # at module level, including wx. log.debug("Initializing core pump") class CorePump(gui.NonReEntrantTimer): "Checks the queues and executes functions." def run(self): global _isPumpPending _isPumpPending = False watchdog.alive() try: if touchHandler.handler: touchHandler.handler.pump() JABHandler.pumpAll() IAccessibleHandler.pumpAll() queueHandler.pumpAll() mouseHandler.pumpAll() braille.pumpAll() except: log.exception("errors in this core pump cycle") baseObject.AutoPropertyObject.invalidateCaches() watchdog.asleep() if _isPumpPending and not _pump.IsRunning(): # #3803: Another pump was requested during this pump execution. # As our pump is not re-entrant, schedule another pump. _pump.Start(PUMP_MAX_DELAY, True) global _pump _pump = CorePump() requestPump() log.debug("Initializing watchdog") watchdog.initialize() try: import updateCheck except RuntimeError: updateCheck=None log.debug("Update checking not supported") else: log.debug("initializing updateCheck") updateCheck.initialize() log.info("NVDA initialized") postNvdaStartup.notify() log.debug("entering wx application main loop") app.MainLoop() log.info("Exiting") if updateCheck: _terminate(updateCheck) _terminate(watchdog) _terminate(globalPluginHandler, name="global plugin handler") _terminate(gui) config.saveOnExit() try: if globalVars.focusObject and hasattr(globalVars.focusObject,"event_loseFocus"): log.debug("calling lose focus on object with focus") globalVars.focusObject.event_loseFocus() except: log.exception("Lose focus error") try: speech.cancelSpeech() except: pass import treeInterceptorHandler _terminate(treeInterceptorHandler) _terminate(IAccessibleHandler, name="IAccessible support") _terminate(UIAHandler, name="UIA support") _terminate(winConsoleHandler, name="winConsole support") _terminate(JABHandler, name="Java Access Bridge support") _terminate(appModuleHandler, name="app module handler") _terminate(NVDAHelper) _terminate(touchHandler) _terminate(keyboardHandler, name="keyboard handler") _terminate(mouseHandler) _terminate(inputCore) _terminate(brailleInput) _terminate(braille) _terminate(speech) _terminate(addonHandler) if not globalVars.appArgs.minimal and config.conf["general"]["playStartAndExitSounds"]: try: nvwave.playWaveFile("waves\\exit.wav",async=False) except: pass # #5189: Destroy the message window as late as possible # so new instances of NVDA can find this one even if it freezes during exit. messageWindow.destroy() log.debug("core done")
def main(): """NVDA's core main loop. This initializes all modules such as audio, IAccessible, keyboard, mouse, and GUI. Then it initialises the wx application object and sets up the core pump, which checks the queues and executes functions when requested. Finally, it starts the wx main loop. """ log.debug("Core starting") try: # Windows >= Vista ctypes.windll.user32.SetProcessDPIAware() except AttributeError: pass import config if not globalVars.appArgs.configPath: globalVars.appArgs.configPath = config.getUserDefaultConfigPath( useInstalledPathIfExists=globalVars.appArgs.launcher) #Initialize the config path (make sure it exists) config.initConfigPath() log.info("Config dir: %s" % os.path.abspath(globalVars.appArgs.configPath)) log.debug("loading config") import config config.initialize() if not globalVars.appArgs.minimal and config.conf["general"][ "playStartAndExitSounds"]: try: nvwave.playWaveFile("waves\\start.wav") except: pass logHandler.setLogLevelFromConfig() logHandler.setPlayErrorSoundFromConfig() try: lang = config.conf["general"]["language"] import languageHandler log.debug("setting language to %s" % lang) languageHandler.setLanguage(lang) except: log.warning("Could not set language to %s" % lang) import versionInfo log.info("NVDA version %s" % versionInfo.version) log.info("Using Windows version %s" % winVersion.winVersionText) log.info("Using Python version %s" % sys.version) log.info("Using comtypes version %s" % comtypes.__version__) # Set a reasonable timeout for any socket connections NVDA makes. import socket socket.setdefaulttimeout(10) log.debug("Initializing add-ons system") addonHandler.initialize() if globalVars.appArgs.disableAddons: log.info("Add-ons are disabled. Restart NVDA to enable them.") import appModuleHandler log.debug("Initializing appModule Handler") appModuleHandler.initialize() import NVDAHelper log.debug("Initializing NVDAHelper") NVDAHelper.initialize() import speechDictHandler log.debug("Speech Dictionary processing") speechDictHandler.initialize() import speech log.debug("Initializing speech") speech.initialize() if not globalVars.appArgs.minimal and (time.time() - globalVars.startTime) > 5: log.debugWarning("Slow starting core (%.2f sec)" % (time.time() - globalVars.startTime)) # Translators: This is spoken when NVDA is starting. speech.speakMessage(_("Loading NVDA. Please wait...")) import wx log.info("Using wx version %s" % wx.version()) class App(wx.App): def OnAssert(self, file, line, cond, msg): message = "{file}, line {line}:\nassert {cond}: {msg}".format( file=file, line=line, cond=cond, msg=msg) log.debugWarning(message, codepath="WX Widgets", stack_info=True) app = App(redirect=False) # We do support QueryEndSession events, but we don't want to do anything for them. app.Bind(wx.EVT_QUERY_END_SESSION, lambda evt: None) def onEndSession(evt): # NVDA will be terminated as soon as this function returns, so save configuration if appropriate. config.saveOnExit() speech.cancelSpeech() if not globalVars.appArgs.minimal and config.conf["general"][ "playStartAndExitSounds"]: try: nvwave.playWaveFile("waves\\exit.wav", async=False) except: pass log.info("Windows session ending") app.Bind(wx.EVT_END_SESSION, onEndSession) import braille log.debug("Initializing braille") braille.initialize() log.debug("Initializing braille input") import brailleInput brailleInput.initialize() import displayModel log.debug("Initializing displayModel") displayModel.initialize() log.debug("Initializing GUI") import gui gui.initialize() import audioDucking if audioDucking.isAudioDuckingSupported(): # the GUI mainloop must be running for this to work so delay it wx.CallAfter(audioDucking.initialize) # #3763: In wxPython 3, the class name of frame windows changed from wxWindowClassNR to wxWindowNR. # NVDA uses the main frame to check for and quit another instance of NVDA. # To remain compatible with older versions of NVDA, create our own wxWindowClassNR. # We don't need to do anything else because wx handles WM_QUIT for all windows. import windowUtils class MessageWindow(windowUtils.CustomWindow): className = u"wxWindowClassNR" messageWindow = MessageWindow(unicode(versionInfo.name)) # initialize wxpython localization support locale = wx.Locale() lang = languageHandler.getLanguage() wxLang = locale.FindLanguageInfo(lang) if not wxLang and '_' in lang: wxLang = locale.FindLanguageInfo(lang.split('_')[0]) if hasattr(sys, 'frozen'): locale.AddCatalogLookupPathPrefix(os.path.join(os.getcwdu(), "locale")) if wxLang: try: locale.Init(wxLang.Language) except: log.error("Failed to initialize wx locale", exc_info=True) else: log.debugWarning("wx does not support language %s" % lang) import api import winUser import NVDAObjects.window desktopObject = NVDAObjects.window.Window( windowHandle=winUser.getDesktopWindow()) api.setDesktopObject(desktopObject) api.setFocusObject(desktopObject) api.setNavigatorObject(desktopObject) api.setMouseObject(desktopObject) import JABHandler log.debug("initializing Java Access Bridge support") try: JABHandler.initialize() except NotImplementedError: log.warning("Java Access Bridge not available") except: log.error("Error initializing Java Access Bridge support", exc_info=True) import winConsoleHandler log.debug("Initializing winConsole support") winConsoleHandler.initialize() import UIAHandler log.debug("Initializing UIA support") try: UIAHandler.initialize() except NotImplementedError: log.warning("UIA not available") except: log.error("Error initializing UIA support", exc_info=True) import IAccessibleHandler log.debug("Initializing IAccessible support") IAccessibleHandler.initialize() log.debug("Initializing input core") import inputCore inputCore.initialize() import keyboardHandler log.debug("Initializing keyboard handler") keyboardHandler.initialize() import mouseHandler log.debug("initializing mouse handler") mouseHandler.initialize() import touchHandler log.debug("Initializing touchHandler") try: touchHandler.initialize() except NotImplementedError: pass import globalPluginHandler log.debug("Initializing global plugin handler") globalPluginHandler.initialize() if globalVars.appArgs.install or globalVars.appArgs.installSilent: import wx import gui.installerGui wx.CallAfter(gui.installerGui.doSilentInstall, startAfterInstall=not globalVars.appArgs.installSilent) elif not globalVars.appArgs.minimal: try: # Translators: This is shown on a braille display (if one is connected) when NVDA starts. braille.handler.message(_("NVDA started")) except: log.error("", exc_info=True) if globalVars.appArgs.launcher: gui.LauncherDialog.run() # LauncherDialog will call doStartupDialogs() afterwards if required. else: wx.CallAfter(doStartupDialogs) import queueHandler # Queue the handling of initial focus, # as API handlers might need to be pumped to get the first focus event. queueHandler.queueFunction(queueHandler.eventQueue, _setInitialFocus) import watchdog import baseObject # Doing this here is a bit ugly, but we don't want these modules imported # at module level, including wx. log.debug("Initializing core pump") class CorePump(wx.Timer): "Checks the queues and executes functions." def Notify(self): global _isPumpPending _isPumpPending = False watchdog.alive() try: if touchHandler.handler: touchHandler.handler.pump() JABHandler.pumpAll() IAccessibleHandler.pumpAll() queueHandler.pumpAll() mouseHandler.pumpAll() braille.pumpAll() except: log.exception("errors in this core pump cycle") baseObject.AutoPropertyObject.invalidateCaches() watchdog.asleep() if _isPumpPending and not _pump.IsRunning(): # #3803: A pump was requested, but the timer was ignored by a modal loop # because timers aren't re-entrant. # Therefore, schedule another pump. _pump.Start(PUMP_MAX_DELAY, True) global _pump _pump = CorePump() requestPump() log.debug("Initializing watchdog") watchdog.initialize() try: import updateCheck except RuntimeError: updateCheck = None log.debug("Update checking not supported") else: log.debug("initializing updateCheck") updateCheck.initialize() log.info("NVDA initialized") log.debug("entering wx application main loop") app.MainLoop() log.info("Exiting") if updateCheck: _terminate(updateCheck) _terminate(watchdog) _terminate(globalPluginHandler, name="global plugin handler") _terminate(gui) config.saveOnExit() try: if globalVars.focusObject and hasattr(globalVars.focusObject, "event_loseFocus"): log.debug("calling lose focus on object with focus") globalVars.focusObject.event_loseFocus() except: log.exception("Lose focus error") try: speech.cancelSpeech() except: pass import treeInterceptorHandler _terminate(treeInterceptorHandler) _terminate(IAccessibleHandler, name="IAccessible support") _terminate(UIAHandler, name="UIA support") _terminate(winConsoleHandler, name="winConsole support") _terminate(JABHandler, name="Java Access Bridge support") _terminate(appModuleHandler, name="app module handler") _terminate(NVDAHelper) _terminate(touchHandler) _terminate(keyboardHandler, name="keyboard handler") _terminate(mouseHandler) _terminate(inputCore) _terminate(brailleInput) _terminate(braille) _terminate(speech) _terminate(addonHandler) if not globalVars.appArgs.minimal and config.conf["general"][ "playStartAndExitSounds"]: try: nvwave.playWaveFile("waves\\exit.wav", async=False) except: pass # #5189: Destroy the message window as late as possible # so new instances of NVDA can find this one even if it freezes during exit. messageWindow.destroy() log.debug("core done")
def main(): """NVDA's core main loop. This initializes all modules such as audio, IAccessible, keyboard, mouse, and GUI. Then it initialises the wx application object and sets up the core pump, which checks the queues and executes functions when requested. Finally, it starts the wx main loop. """ log.debug("Core starting") try: # Windows >= Vista ctypes.windll.user32.SetProcessDPIAware() except AttributeError: pass import config if not globalVars.appArgs.configPath: globalVars.appArgs.configPath=config.getUserDefaultConfigPath(useInstalledPathIfExists=globalVars.appArgs.launcher) #Initialize the config path (make sure it exists) config.initConfigPath() log.info("Config dir: %s"%os.path.abspath(globalVars.appArgs.configPath)) log.debug("loading config") import config config.initialize() if not globalVars.appArgs.minimal and config.conf["general"]["playStartAndExitSounds"]: try: nvwave.playWaveFile("waves\\start.wav") except: pass logHandler.setLogLevelFromConfig() try: lang = config.conf["general"]["language"] import languageHandler log.debug("setting language to %s"%lang) languageHandler.setLanguage(lang) except: log.warning("Could not set language to %s"%lang) import versionInfo log.info("NVDA version %s" % versionInfo.version) log.info("Using Windows version %s" % winVersion.winVersionText) log.info("Using Python version %s"%sys.version) log.info("Using comtypes version %s"%comtypes.__version__) # Set a reasonable timeout for any socket connections NVDA makes. import socket socket.setdefaulttimeout(10) log.debug("Initializing add-ons system") addonHandler.initialize() if globalVars.appArgs.disableAddons: log.info("Add-ons are disabled. Restart NVDA to enable them.") import appModuleHandler log.debug("Initializing appModule Handler") appModuleHandler.initialize() import NVDAHelper log.debug("Initializing NVDAHelper") NVDAHelper.initialize() import speechDictHandler log.debug("Speech Dictionary processing") speechDictHandler.initialize() import speech log.debug("Initializing speech") speech.initialize() if not globalVars.appArgs.minimal and (time.time()-globalVars.startTime)>5: log.debugWarning("Slow starting core (%.2f sec)" % (time.time()-globalVars.startTime)) # Translators: This is spoken when NVDA is starting. speech.speakMessage(_("Loading NVDA. Please wait...")) import wx log.info("Using wx version %s"%wx.version()) class App(wx.App): def OnAssert(self,file,line,cond,msg): message="{file}, line {line}:\nassert {cond}: {msg}".format(file=file,line=line,cond=cond,msg=msg) log.debugWarning(message,codepath="WX Widgets",stack_info=True) app = App(redirect=False) # We do support QueryEndSession events, but we don't want to do anything for them. app.Bind(wx.EVT_QUERY_END_SESSION, lambda evt: None) def onEndSession(evt): # NVDA will be terminated as soon as this function returns, so save configuration if appropriate. config.saveOnExit() speech.cancelSpeech() if not globalVars.appArgs.minimal and config.conf["general"]["playStartAndExitSounds"]: try: nvwave.playWaveFile("waves\\exit.wav",async=False) except: pass log.info("Windows session ending") app.Bind(wx.EVT_END_SESSION, onEndSession) import braille log.debug("Initializing braille") braille.initialize() log.debug("Initializing braille input") import brailleInput brailleInput.initialize() import displayModel log.debug("Initializing displayModel") displayModel.initialize() log.debug("Initializing GUI") import gui gui.initialize() import audioDucking if audioDucking.isAudioDuckingSupported(): # the GUI mainloop must be running for this to work so delay it wx.CallAfter(audioDucking.initialize) # #3763: In wxPython 3, the class name of frame windows changed from wxWindowClassNR to wxWindowNR. # NVDA uses the main frame to check for and quit another instance of NVDA. # To remain compatible with older versions of NVDA, create our own wxWindowClassNR. # We don't need to do anything else because wx handles WM_QUIT for all windows. import windowUtils class MessageWindow(windowUtils.CustomWindow): className = u"wxWindowClassNR" messageWindow = MessageWindow(unicode(versionInfo.name)) # initialize wxpython localization support locale = wx.Locale() lang=languageHandler.getLanguage() wxLang=locale.FindLanguageInfo(lang) if not wxLang and '_' in lang: wxLang=locale.FindLanguageInfo(lang.split('_')[0]) if hasattr(sys,'frozen'): locale.AddCatalogLookupPathPrefix(os.path.join(os.getcwdu(),"locale")) if wxLang: try: locale.Init(wxLang.Language) except: log.error("Failed to initialize wx locale",exc_info=True) else: log.debugWarning("wx does not support language %s" % lang) import api import winUser import NVDAObjects.window desktopObject=NVDAObjects.window.Window(windowHandle=winUser.getDesktopWindow()) api.setDesktopObject(desktopObject) api.setFocusObject(desktopObject) api.setNavigatorObject(desktopObject) api.setMouseObject(desktopObject) import JABHandler log.debug("initializing Java Access Bridge support") try: JABHandler.initialize() except NotImplementedError: log.warning("Java Access Bridge not available") except: log.error("Error initializing Java Access Bridge support", exc_info=True) import winConsoleHandler log.debug("Initializing winConsole support") winConsoleHandler.initialize() import UIAHandler log.debug("Initializing UIA support") try: UIAHandler.initialize() except NotImplementedError: log.warning("UIA not available") except: log.error("Error initializing UIA support", exc_info=True) import IAccessibleHandler log.debug("Initializing IAccessible support") IAccessibleHandler.initialize() log.debug("Initializing input core") import inputCore inputCore.initialize() import keyboardHandler log.debug("Initializing keyboard handler") keyboardHandler.initialize() import mouseHandler log.debug("initializing mouse handler") mouseHandler.initialize() import touchHandler log.debug("Initializing touchHandler") try: touchHandler.initialize() except NotImplementedError: pass import globalPluginHandler log.debug("Initializing global plugin handler") globalPluginHandler.initialize() if globalVars.appArgs.install or globalVars.appArgs.installSilent: import wx import gui.installerGui wx.CallAfter(gui.installerGui.doSilentInstall,startAfterInstall=not globalVars.appArgs.installSilent) elif not globalVars.appArgs.minimal: try: # Translators: This is shown on a braille display (if one is connected) when NVDA starts. braille.handler.message(_("NVDA started")) except: log.error("", exc_info=True) if globalVars.appArgs.launcher: gui.LauncherDialog.run() # LauncherDialog will call doStartupDialogs() afterwards if required. else: wx.CallAfter(doStartupDialogs) import queueHandler # Queue the handling of initial focus, # as API handlers might need to be pumped to get the first focus event. queueHandler.queueFunction(queueHandler.eventQueue, _setInitialFocus) import watchdog import baseObject # Doing this here is a bit ugly, but we don't want these modules imported # at module level, including wx. log.debug("Initializing core pump") class CorePump(wx.Timer): "Checks the queues and executes functions." def Notify(self): global _isPumpPending _isPumpPending = False watchdog.alive() try: if touchHandler.handler: touchHandler.handler.pump() JABHandler.pumpAll() IAccessibleHandler.pumpAll() queueHandler.pumpAll() mouseHandler.pumpAll() braille.pumpAll() except: log.exception("errors in this core pump cycle") baseObject.AutoPropertyObject.invalidateCaches() watchdog.asleep() if _isPumpPending and not _pump.IsRunning(): # #3803: A pump was requested, but the timer was ignored by a modal loop # because timers aren't re-entrant. # Therefore, schedule another pump. _pump.Start(PUMP_MAX_DELAY, True) global _pump _pump = CorePump() requestPump() log.debug("Initializing watchdog") watchdog.initialize() try: import updateCheck except RuntimeError: updateCheck=None log.debug("Update checking not supported") else: log.debug("initializing updateCheck") updateCheck.initialize() log.info("NVDA initialized") log.debug("entering wx application main loop") app.MainLoop() log.info("Exiting") if updateCheck: _terminate(updateCheck) _terminate(watchdog) _terminate(globalPluginHandler, name="global plugin handler") _terminate(gui) config.saveOnExit() try: if globalVars.focusObject and hasattr(globalVars.focusObject,"event_loseFocus"): log.debug("calling lose focus on object with focus") globalVars.focusObject.event_loseFocus() except: log.exception("Lose focus error") try: speech.cancelSpeech() except: pass import treeInterceptorHandler _terminate(treeInterceptorHandler) _terminate(IAccessibleHandler, name="IAccessible support") _terminate(UIAHandler, name="UIA support") _terminate(winConsoleHandler, name="winConsole support") _terminate(JABHandler, name="Java Access Bridge support") _terminate(appModuleHandler, name="app module handler") _terminate(NVDAHelper) _terminate(touchHandler) _terminate(keyboardHandler, name="keyboard handler") _terminate(mouseHandler) _terminate(inputCore) _terminate(brailleInput) _terminate(braille) _terminate(speech) _terminate(addonHandler) if not globalVars.appArgs.minimal and config.conf["general"]["playStartAndExitSounds"]: try: nvwave.playWaveFile("waves\\exit.wav",async=False) except: pass # #5189: Destroy the message window as late as possible # so new instances of NVDA can find this one even if it freezes during exit. messageWindow.destroy() log.debug("core done")
def speak(self, speechSequence): textList = [] # NVDA SpeechCommands are linear, but XML is hierarchical. # Therefore, we track values for non-empty tags. # When a tag changes, we close all previously opened tags and open new ones. tags = {} # We have to use something mutable here because it needs to be changed by the inner function. tagsChanged = [True] openedTags = [] def outputTags(): if not tagsChanged[0]: return for tag in reversed(openedTags): textList.append("</%s>" % tag) del openedTags[:] for tag, attrs in tags.items(): textList.append("<%s" % tag) for attr, val in attrs.items(): textList.append(' %s="%s"' % (attr, val)) textList.append(">") openedTags.append(tag) tagsChanged[0] = False pitch = self._pitch # Pitch must always be specified in the markup. tags["pitch"] = {"absmiddle": self._percentToPitch(pitch)} rate = self.rate volume = self.volume for item in speechSequence: if isinstance(item, str): outputTags() textList.append(item.replace("<", "<")) elif isinstance(item, IndexCommand): textList.append('<Bookmark Mark="%d" />' % item.index) elif isinstance(item, CharacterModeCommand): if item.state: tags["spell"] = {} else: try: del tags["spell"] except KeyError: pass tagsChanged[0] = True elif isinstance(item, BreakCommand): textList.append('<silence msec="%d" />' % item.time) elif isinstance(item, PitchCommand): tags["pitch"] = {"absmiddle": self._percentToPitch(int(pitch * item.multiplier))} tagsChanged[0] = True elif isinstance(item, VolumeCommand): if item.multiplier == 1: try: del tags["volume"] except KeyError: pass else: tags["volume"] = {"level": int(volume * item.multiplier)} tagsChanged[0] = True elif isinstance(item, RateCommand): if item.multiplier == 1: try: del tags["rate"] except KeyError: pass else: tags["rate"] = {"absspeed": self._percentToRate(int(rate * item.multiplier))} tagsChanged[0] = True elif isinstance(item, PhonemeCommand): try: textList.append(u'<pron sym="%s">%s</pron>' % (self._convertPhoneme(item.ipa), item.text or u"")) except LookupError: log.debugWarning("Couldn't convert character in IPA string: %s" % item.ipa) if item.text: textList.append(item.text) elif isinstance(item, SpeechCommand): log.debugWarning("Unsupported speech command: %s" % item) else: log.error("Unknown speech: %s" % item) # Close any tags that are still open. tags.clear() tagsChanged[0] = True outputTags() text = "".join(textList) flags = SpeechVoiceSpeakFlags.IsXML | SpeechVoiceSpeakFlags.Async # Ducking should be complete before the synth starts producing audio. # For this to happen, the speech method must block until ducking is complete. # Ducking should be disabled when the synth is finished producing audio. # Note that there may be calls to speak with a string that results in no audio, # it is important that in this case the audio does not get stuck ducked. # When there is no audio produced the startStream and endStream handlers are not called. # To prevent audio getting stuck ducked, it is unducked at the end of speech. # There are some known issues: # - When there is no audio produced by the synth, a user may notice volume lowering (ducking) temporarily. # - If the call to startStream handler is delayed significantly, users may notice a variation in volume # (as ducking is disabled at the end of speak, and re-enabled when the startStream handler is called) # A note on the synchronicity of components of this approach: # SAPISink.StartStream event handler (callback): # the synth speech is not blocked by this event callback. # SAPISink.EndStream event handler (callback): # assumed also to be async but not confirmed. Synchronicity is irrelevant to the current approach. # AudioDucker.disable returns before the audio is completely unducked. # AudioDucker.enable() ducking will complete before the function returns. # It is not possible to "double duck the audio", calling twice yields the same result as calling once. # AudioDucker class instances count the number of enables/disables, # in order to unduck there must be no remaining enabled audio ducker instances. # Due to this a temporary audio ducker is used around the call to speak. # SAPISink.StartStream: Ducking here may allow the early speech to start before ducking is completed. if audioDucking.isAudioDuckingSupported(): tempAudioDucker = audioDucking.AudioDucker() else: tempAudioDucker = None if tempAudioDucker: if audioDucking._isDebug(): log.debug("Enabling audio ducking due to speak call") tempAudioDucker.enable() try: self.tts.Speak(text, flags) finally: if tempAudioDucker: if audioDucking._isDebug(): log.debug("Disabling audio ducking after speak call") tempAudioDucker.disable()