-
-
Notifications
You must be signed in to change notification settings - Fork 654
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
Remote Access #17580
base: master
Are you sure you want to change the base?
Remote Access #17580
Changes from all commits
6b9742a
8daee13
855ef1e
c8889a4
f1a3afb
1799693
5ecae9a
e8f1e61
d2b43da
fd0a061
3810a1f
e9a515f
6600c64
2fdb478
54d25c5
40ceeff
893292b
e063085
01359d5
1d2926f
8648eb7
19de99e
5d9e5b1
e19b68d
8d1ca24
8346b17
f0acb7a
984b5c8
42c1112
d82559e
0b3c425
bea5670
92a9eb0
60eeede
1f14357
e76b406
235decb
59166e2
04b81ad
79a2b09
95f7cb4
9a63bec
fe6660d
8dc193d
3fc4047
2bc026c
eacf802
79c5695
90471a6
b2220f8
b1b1446
b101056
9040806
7f6068c
a549de3
23442ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -69,7 +69,7 @@ | |||||
from utils.security import objectBelowLockScreenAndWindowsIsLocked | ||||||
import audio | ||||||
from audio import appsVolume | ||||||
|
||||||
import remoteClient | ||||||
|
||||||
#: Script category for text review commands. | ||||||
# Translators: The name of a category of NVDA commands. | ||||||
|
@@ -119,6 +119,9 @@ | |||||
#: Script category for audio streaming commands. | ||||||
# Translators: The name of a category of NVDA commands. | ||||||
SCRCAT_AUDIO = _("Audio") | ||||||
#: Script category for Remote commands. | ||||||
# Translators: The name of a category of NVDA commands. | ||||||
SCRCAT_REMOTE = _("Remote") | ||||||
|
||||||
# Translators: Reported when there are no settings to configure in synth settings ring | ||||||
# (example: when there is no setting for language). | ||||||
|
@@ -4888,6 +4891,69 @@ def script_toggleApplicationsVolumeAdjuster(self, gesture: "inputCore.InputGestu | |||||
def script_toggleApplicationsMute(self, gesture: "inputCore.InputGesture") -> None: | ||||||
appsVolume._toggleAppsVolumeMute() | ||||||
|
||||||
@script( | ||||||
# Translators: Describes a command. | ||||||
description=_("""Mute or unmute the speech coming from the remote computer"""), | ||||||
category=SCRCAT_REMOTE, | ||||||
) | ||||||
def script_toggle_remote_mute(self, gesture): | ||||||
remoteClient.client.toggleMute() | ||||||
|
||||||
@script( | ||||||
gesture="kb:control+shift+NVDA+c", | ||||||
category=SCRCAT_REMOTE, | ||||||
# 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"), | ||||||
) | ||||||
def script_push_clipboard(self, gesture): | ||||||
remoteClient.client.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"""), | ||||||
category=SCRCAT_REMOTE, | ||||||
) | ||||||
def script_copy_link(self, gesture): | ||||||
remoteClient.client.copyLink() | ||||||
# Translators: A message indicating that a link has been copied to the clipboard. | ||||||
ui.message(_("Copied link")) | ||||||
|
||||||
@script( | ||||||
gesture="kb:alt+NVDA+pageDown", | ||||||
category=SCRCAT_REMOTE, | ||||||
# Translators: Documentation string for the script that disconnects a remote session. | ||||||
description=_("""Disconnect a remote session"""), | ||||||
) | ||||||
@gui.blockAction.when(gui.blockAction.Context.SECURE_MODE) | ||||||
def script_disconnectFromRemote(self, gesture): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this command available from controlled computer? I hope yes; just to confirm the UX. |
||||||
if not remoteClient.client.isConnected: | ||||||
# Translators: A message indicating that the remote client is not connected. | ||||||
ui.message(_("Not connected.")) | ||||||
return | ||||||
remoteClient.client.disconnect() | ||||||
|
||||||
@script( | ||||||
gesture="kb:alt+NVDA+pageUp", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO gestures including direction or opposition (arrows, pg up/down, + and -) are valuable. They should be reserved to usages where they are most suitable. For example, this may be an important point when NVDA Magnifier sees the light of day (see #17416 by @seanbudd and NVDA roadmap). Connect or disconnect commands do not seem to match this usage. Could you think to other shortcut keys? Also, unless it is technically difficult, I think that using the same command to connect and disconnect (toggle command) would be more user-friendly: no error warning the user that he cannot disconnect while already disconnected. Using a simple toggle command would also allow to use only one gesture instead of two. |
||||||
# Translators: Documentation string for the script that invokes the remote session. | ||||||
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) | ||||||
def script_connectToRemote(self, gesture): | ||||||
if remoteClient.client.isConnected() or remoteClient.client.connecting: | ||||||
return | ||||||
remoteClient.client.doConnect() | ||||||
|
||||||
@script( | ||||||
# Translators: Documentation string for the script that toggles the control between guest and host machine. | ||||||
description=_("Toggles the control between guest and host machine"), | ||||||
category=SCRCAT_REMOTE, | ||||||
gesture="kb:f11", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Usually, NVDA gestures include NVDA modifier unless the gesture overrides Windows or application native commands (e.g.
Moreover, the majority of NVDA users do not (and will never) use remote connection feature. Thus, I'd recommend to define a gesture including NVDA modifier, for example:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another option is just to remove the default gesture assignment for this script, if we consider that only gestures used on the controlled machine need to be assigned. |
||||||
) | ||||||
def script_sendKeys(self, gesture): | ||||||
remoteClient.client.toggleRemoteKeyControl(gesture) | ||||||
|
||||||
|
||||||
#: The single global commands instance. | ||||||
#: @type: L{GlobalCommands} | ||||||
|
ctoth marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,22 @@ | ||||||
# A part of NonVisual Desktop Access (NVDA) | ||||||
# Copyright (C) 2015-2025 NV Access Limited, Christopher Toth, Tyler Spivey, Babbage B.V., David Sexton and others. | ||||||
# This file is covered by the GNU General Public License. | ||||||
# See the file COPYING for more details. | ||||||
|
||||||
from .client import RemoteClient | ||||||
|
||||||
client: RemoteClient = None | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I now realize that client is also a submodule. Interestingly the linter doesn't seem to complain about this.
Suggested change
|
||||||
|
||||||
|
||||||
def initialize(): | ||||||
"""Initialise the remote client.""" | ||||||
global client | ||||||
import globalCommands | ||||||
|
||||||
client = RemoteClient() | ||||||
client.registerLocalScript(globalCommands.commands.script_sendKeys) | ||||||
|
||||||
|
||||||
def terminate(): | ||||||
"""Terminate the remote client.""" | ||||||
client.terminate() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be set back to None? |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright header. Also please consider moving this to tones module |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import collections.abc | ||
import threading | ||
import time | ||
from typing import Tuple, Union | ||
|
||
import tones | ||
|
||
local_beep = tones.beep | ||
|
||
BeepElement = Union[int, Tuple[int, int]] # Either delay_ms or (frequency_hz, duration_ms) | ||
BeepSequence = collections.abc.Iterable[BeepElement] | ||
|
||
|
||
def beepSequence(*sequence: BeepElement) -> None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this can safely go into the tones module. |
||
"""Play a simple synchronous monophonic beep sequence | ||
A beep sequence is an iterable containing one of two kinds of elements. | ||
An element consisting of a tuple of two items is interpreted as a frequency and duration. Note, this function plays beeps synchronously, unlike tones.beep | ||
A single integer is assumed to be a delay in ms. | ||
""" | ||
for element in sequence: | ||
if not isinstance(element, collections.abc.Sequence): | ||
time.sleep(float(element) / 1000) | ||
else: | ||
tone, duration = element | ||
time.sleep(float(duration) / 1000) | ||
local_beep(tone, duration) | ||
|
||
|
||
def beepSequenceAsync(*sequence: BeepElement) -> threading.Thread: | ||
"""Play an asynchronous beep sequence. | ||
This is the same as `beepSequence`, except it runs in a thread.""" | ||
thread = threading.Thread(target=beepSequence, args=sequence) | ||
thread.daemon = True | ||
thread.start() | ||
return thread |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is remote copy allowed for controlled computer? If not, I'd say to unassign the default gesture for this command.