Skip to content

Commit

Permalink
fix restore on talon sleep
Browse files Browse the repository at this point in the history
  • Loading branch information
C-Loftus committed Mar 8, 2024
1 parent 038b9e0 commit b494e1b
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 49 deletions.
17 changes: 7 additions & 10 deletions core/screenreader_ipc/ipc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ def send_ipc_commands(commands: list[IPC_COMMAND]):
if settings.get("user.addon_debug"):
print(f"Sending {commands} to {ip}:{port}")

# Default response if nothing is set
response = b"No response from NVDA addon server"

# Although the screenreader server will block while processing commands,
# having a lock client-side prevents errors when sending multiple commands
with lock:
Expand All @@ -92,26 +95,20 @@ def send_ipc_commands(commands: list[IPC_COMMAND]):
if settings.get("user.addon_debug"):
print("Received", repr(response))

if "debug" in commands:
actions.user.tts(
f"Sent Message to NVDA Successfully with server response: {response.decode('utf-8')}"
)

except socket.timeout as e:
print(f"Clientside connection with {ip}:{port} timed out")
print(e)
if "debug" in commands:
actions.user.tts("Clientside connection timed out")
response = b"Clientside connection with NVDA timed out"
except Exception as e:
print(f"Error Communicating with NVDA extension: {e}")
if "debug" in commands:
actions.user.tts("Error Communicating with NVDA extension")
response = b"Error communicating with NVDA extension"
finally:
sock.close()
return response.decode()

def send_ipc_command(command: IPC_COMMAND):
"""Sends a single command to the screenreader"""
actions.user.send_ipc_commands([command])
return actions.user.send_ipc_commands([command])


ORCAContext = Context()
Expand Down
14 changes: 3 additions & 11 deletions core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
mod.setting(
"tts_speed",
type=float,
default=1.0,
default=8,
desc="Speed of text to speech",
)

Expand All @@ -20,7 +20,7 @@
mod.setting(
"tts_via_screenreader",
type=bool,
default=False,
default=True,
desc="If true, plays back dictation with text to speech through the screenreader, not within Talon",
)

Expand Down Expand Up @@ -71,17 +71,10 @@
mod.setting(
"tts_volume",
type=int,
default=100,
default=80,
desc="The volume of the text to speech",
)

mod.setting(
"enable_break_timer",
type=bool,
default=False,
desc="If True, enables the eyestrain break timer to display a notification every 20 minutes",
)


mod.setting(
"disable_keypresses",
Expand All @@ -97,7 +90,6 @@
desc="If True, plays a sound on every keypress to help prevent accidental keypresses",
)

mod.setting("min_until_break", type=int, default=10)

mod.setting(
"orca_key",
Expand Down
40 changes: 28 additions & 12 deletions nvda/nvda.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def set_nvda_running_tag():
dll_path = os.path.join(dir_path, "nvdaControllerClient64.dll")
nvda_client: ctypes.WinDLL = ctypes.windll.LoadLibrary(dll_path)
cron.interval("3s", set_nvda_running_tag.update)
SPEC_FILE = os.path.expanduser("~\\AppData\\Roaming\\nvda\\talon_server_spec.json")

else:
nvda_client = None
Expand Down Expand Up @@ -87,7 +88,8 @@ def test_controller_client():

def test_reader_addon():
"""Tests the reader addon"""
actions.user.send_ipc_command("debug")
result = actions.user.send_ipc_command("debug")
actions.user.tts(f"Reader addon result: {result}")


ctxWindowsNVDARunning = Context()
Expand Down Expand Up @@ -141,20 +143,22 @@ def switch_voice():
actions.user.tts("You must switch voice in NVDA manually")


def _ready_to_send_ipc():
SLEEP_MODE = "sleep" in scope.get("mode")
SPEC_FILE = os.path.expanduser("~\\AppData\\Roaming\\nvda\\talon_server_spec.json")
return (
actions.user.is_nvda_running() and not SLEEP_MODE and os.path.exists(SPEC_FILE)
)
# Only send post:phrase callback if we sent a pre:phrase callback successfully
pre_phrase_sent = False


# By default the screen reader will allow you to press a key and interrupt the ph
# rase however this does not work alongside typing given the fact that we are pres
# sing keys. So we need to temporally disable it then re enable it at the end of
# the phrase
def disable_interrupt(_):
if not _ready_to_send_ipc():
global pre_phrase_sent
SLEEP_MODE = "sleep" in scope.get("mode")
if (
not actions.user.is_nvda_running()
or SLEEP_MODE
or not os.path.exists(SPEC_FILE)
):
return

# bundle the commands into a single messge
Expand All @@ -164,10 +168,20 @@ def disable_interrupt(_):
"disableSpeakTypedCharacters",
]
actions.user.send_ipc_commands(commands)
pre_phrase_sent = True


def restore_interrupt_setting(_):
if not _ready_to_send_ipc():
def enable_interrupt(_):
global pre_phrase_sent
SLEEP_MODE = "sleep" in scope.get("mode")
if (
not actions.user.is_nvda_running()
# If we are in sleep mode, we still send the interrupt
# assuming the pre_phrase was sent, given the fact
# we still want `talon sleep` to restore the setting at the end
or (SLEEP_MODE and not pre_phrase_sent)
or not os.path.exists(SPEC_FILE)
):
return

# bundle the commands into a single message
Expand All @@ -179,9 +193,11 @@ def restore_interrupt_setting(_):

# this is kind of a hack since we don't know exactly when to re enable it
# because we don't have a callback at the end of the last keypress
cron.after("1s", lambda: actions.user.send_ipc_commands(commands))
cron.after("400ms", lambda: actions.user.send_ipc_commands(commands))
# Reset the pre_phrase_sent flag to prevent another post:phrase callback during sleep mode
pre_phrase_sent = False


if os.name == "nt":
speech_system.register("pre:phrase", disable_interrupt)
speech_system.register("post:phrase", restore_interrupt_setting)
speech_system.register("post:phrase", enable_interrupt)
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ This repository integrates with:
- Install `piper` to use the `omnx` model for more natural speech
- run `pipx install piper` to install it (thus `pipx` is a dependency)
- Windows

- Using NVDA:
- Install the NVDA addon file from the repo [releases page](https://github.com/C-Loftus/sight-free-talon/releases/)
- If you do not want to install the addon, disable `Speech interrupt for typed characters` in NVDA settings to make sure typing text from Talon is not interrupted with every typed character.
Expand Down
20 changes: 4 additions & 16 deletions sight-free-settings.talon → sight-free-settings.talon.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# COPY THIS FILE INTO YOUR USER FOLDER AND CHANGE THE SETTINGS AS DESIRED
# THIS FILE IS IGNORED BY DEFAULT AND JUST AN EXAMPLE

settings():
# Echo the subtitles from talon back via tts
user.echo_dictation = true
user.echo_dictation = false

# How fast to play back text-to-speech -10 to 10
# Ignored if using screenreader tts
Expand Down Expand Up @@ -36,18 +39,3 @@ settings():

# Display debugging output for NVDA
user.addon_debug = false

# Every given number of minutes, send a notification, prompting you to rest your eyes
# user.enable_break_timer = true
# user.user.min_until_break = 10

### Relevant Community Settings Below ###
# Change key_wait if you want the screen reader to speak words
# at the same pace as if you were typing them manually.
# key_wait = 0

# Setting from community repository
# Manually typing keys through Talon can jumble the audio output since nvda can also say out
# keys and/or words. By having this setting enabled, we can echo out the text but
# not need to disable the screenreader's speech for characters and words for normal typing
# user.paste_to_insert_threshold = -1

0 comments on commit b494e1b

Please sign in to comment.