Skip to content

Add ability to disable Remote #17825

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Mar 28, 2025
1 change: 1 addition & 0 deletions source/config/configSpec.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@

# Remote Settings
[remote]
enabled = boolean(default=False)
[[connections]]
last_connected = list(default=list())
[[controlserver]]
Expand Down
8 changes: 5 additions & 3 deletions source/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -898,10 +898,11 @@ def main():
log.debug("Initializing global plugin handler")
globalPluginHandler.initialize()

log.debug("Initializing remote client")
import remoteClient

remoteClient.initialize()
if config.conf["remote"]["enabled"]:
log.debug("Initializing remote client")
remoteClient.initialize()

if globalVars.appArgs.install or globalVars.appArgs.installSilent:
import gui.installerGui
Expand Down Expand Up @@ -1055,7 +1056,8 @@ def _doPostNvdaStartupAction():
" This likely indicates NVDA is exiting due to WM_QUIT.",
)
queueHandler.pumpAll()
_terminate(remoteClient)
if remoteClient.remoteRunning():
_terminate(remoteClient)
_terminate(gui)
config.saveOnExit()

Expand Down
22 changes: 16 additions & 6 deletions source/globalCommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4919,9 +4919,10 @@ def script_toggleApplicationsMute(self, gesture: "inputCore.InputGesture") -> No

@script(
# Translators: Describes a command.
description=_("""Mute or unmute the speech coming from the remote computer"""),
description=_("Mute or unmute the speech coming from the remote computer"),
category=SCRCAT_REMOTE,
)
@gui.blockAction.when(gui.blockAction.Context.REMOTE_ACCESS_DISABLED)
def script_toggleRemoteMute(self, gesture: "inputCore.InputGesture"):
remoteClient._remoteClient.toggleMute()

Expand All @@ -4931,14 +4932,16 @@ def script_toggleRemoteMute(self, gesture: "inputCore.InputGesture"):
# Translators: Documentation string for the script that sends the contents of the clipboard to the remote machine.
description=_("Sends the contents of the clipboard to the remote machine"),
)
@gui.blockAction.when(gui.blockAction.Context.REMOTE_ACCESS_DISABLED)
def script_pushClipboard(self, gesture: "inputCore.InputGesture"):
remoteClient._remoteClient.pushClipboard()

@script(
# Translators: Documentation string for the script that copies a link to the remote session to the clipboard.
description=_("""Copies a link to the remote session to the clipboard"""),
description=_("Copies a link to the remote session to the clipboard"),
category=SCRCAT_REMOTE,
)
@gui.blockAction.when(gui.blockAction.Context.REMOTE_ACCESS_DISABLED)
def script_copyRemoteLink(self, gesture: "inputCore.InputGesture"):
remoteClient._remoteClient.copyLink()
# Translators: A message indicating that a link has been copied to the clipboard.
Expand All @@ -4950,7 +4953,10 @@ def script_copyRemoteLink(self, gesture: "inputCore.InputGesture"):
# Translators: Documentation string for the script that disconnects a remote session.
description=_("Disconnect a remote session"),
)
@gui.blockAction.when(gui.blockAction.Context.SECURE_MODE)
@gui.blockAction.when(
gui.blockAction.Context.REMOTE_ACCESS_DISABLED,
gui.blockAction.Context.SECURE_MODE,
)
def script_disconnectFromRemote(self, gesture: "inputCore.InputGesture"):
if not remoteClient._remoteClient.isConnected:
# Translators: A message indicating that the remote client is not connected.
Expand All @@ -4961,11 +4967,14 @@ def script_disconnectFromRemote(self, gesture: "inputCore.InputGesture"):
@script(
gesture="kb:alt+NVDA+pageUp",
# Translators: Documentation string for the script that invokes the remote session.
description=_("""Connect to a remote computer"""),
description=_("Connect to a remote computer"),
category=SCRCAT_REMOTE,
)
@gui.blockAction.when(gui.blockAction.Context.MODAL_DIALOG_OPEN)
@gui.blockAction.when(gui.blockAction.Context.SECURE_MODE)
@gui.blockAction.when(
gui.blockAction.Context.REMOTE_ACCESS_DISABLED,
gui.blockAction.Context.MODAL_DIALOG_OPEN,
gui.blockAction.Context.SECURE_MODE,
)
def script_connectToRemote(self, gesture: "inputCore.InputGesture"):
if remoteClient._remoteClient.isConnected() or remoteClient._remoteClient.connecting:
# Translators: A message indicating that the remote client is already connected.
Expand All @@ -4979,6 +4988,7 @@ def script_connectToRemote(self, gesture: "inputCore.InputGesture"):
category=SCRCAT_REMOTE,
gesture="kb:NVDA+f11",
)
@gui.blockAction.when(gui.blockAction.Context.REMOTE_ACCESS_DISABLED)
def script_sendKeys(self, gesture: "inputCore.InputGesture"):
remoteClient._remoteClient.toggleRemoteKeyControl(gesture)

Expand Down
13 changes: 13 additions & 0 deletions source/gui/blockAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ def _modalDialogOpenCallback():
MessageDialog.focusBlockingInstances()


def _isRemoteAccessDisabled() -> bool:
"""Whether Remote Access functionality is **disabled**."""
# Import late to avoid circular import
from remoteClient import remoteRunning

return not remoteRunning()


@dataclass
class _Context:
blockActionIf: Callable[[], bool]
Expand Down Expand Up @@ -80,6 +88,11 @@ class Context(_Context, Enum):
# Translators: Reported when trying to toggle an unsupported setting in speech output mode.
_("Action unavailable while the braille mode is set to speech output"),
)
REMOTE_ACCESS_DISABLED = (
_isRemoteAccessDisabled,
# Translators: Reported when an action cannot be performed because Remote Access functionality is disabled.
_("Action unavailable when Remote Access is disabled"),
)


def when(*contexts: Context):
Expand Down
16 changes: 16 additions & 0 deletions source/gui/settingsDialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3381,7 +3381,11 @@ class RemoteSettingsPanel(SettingsPanel):

def makeSettings(self, sizer):
self.config = configuration.getRemoteConfig()
import gui.guiHelper

sHelper = gui.guiHelper.BoxSizerHelper(self, sizer=sizer)
self.enableRemote = sHelper.addItem(wx.CheckBox(self, label="Enable Remote Access"))
self.enableRemote.SetValue(self.config["enabled"])
self.autoconnect = wx.CheckBox(
parent=self,
id=wx.ID_ANY,
Expand Down Expand Up @@ -3517,6 +3521,18 @@ def onSave(self):
cs["port"] = int(self.port.GetValue())
cs["key"] = self.key.GetValue()
self.config["ui"]["play_sounds"] = self.playSounds.GetValue()
enabled = self.enableRemote.GetValue()
oldEnabled = self.config["enabled"]
self.config["enabled"] = enabled
if enabled != oldEnabled:
import remoteClient

if enabled and not remoteClient.remoteRunning():
log.debug("Initializing Remote")
remoteClient.initialize()
elif not enabled and remoteClient.remoteRunning():
log.debug("Terminating remote")
remoteClient.terminate()


class TouchInteractionPanel(SettingsPanel):
Expand Down
9 changes: 7 additions & 2 deletions source/remoteClient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,10 @@ def initialize():
def terminate():
"""Terminate the remote client."""
global _remoteClient
_remoteClient.terminate()
_remoteClient = None
if _remoteClient is not None:
_remoteClient.terminate()
_remoteClient = None


def remoteRunning() -> bool:
return _remoteClient is not None
4 changes: 3 additions & 1 deletion source/remoteClient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ def terminate(self):
self.disconnect()
self.localMachine.terminate()
self.localMachine = None
self.menu = None
if self.menu is not None:
self.menu.terminate()
self.menu = None
self.localScripts.clear()
core.postNvdaStartup.unregister(self.performAutoconnect)
inputCore.decide_handleRawKey.unregister(self.processKeyInput)
Expand Down