diff --git a/.gitignore b/.gitignore index dc13008..e26e815 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,7 @@ __pycache__/ # Fallback location used for the language model. /model +/venv/ +/package/python +!/package/python/readme.rst +!/package/python/setup.py \ No newline at end of file diff --git a/nerd-dictation b/nerd-dictation index c313dca..20ab897 100755 --- a/nerd-dictation +++ b/nerd-dictation @@ -39,6 +39,14 @@ USER_CONFIG = "nerd-dictation.py" SIMULATE_INPUT_CODE_COMMAND = -1 +try: + from pynput.keyboard import Key, Controller # type: ignore + + keyboard = Controller() + TAP_DELAY = 0.012 +except ImportError: + sys.stderr.write("Module 'pynput' is not installed. Defaulting input method to xdotool.") + # ----------------------------------------------------------------------------- # General Utilities @@ -135,6 +143,24 @@ def execfile(filepath: str, mod: Optional[ModuleType] = None) -> Optional[Module return mod +# ----------------------------------------------------------------------------- +# Simulate Input: PYNPUT +# +def simulate_typing_with_pynput(delete_prev_chars: int, text: str) -> None: + + # No setup/tear-down. + if delete_prev_chars == SIMULATE_INPUT_CODE_COMMAND: + return + + if delete_prev_chars: + for _ in range(delete_prev_chars): + keyboard.tap(Key.backspace) + time.sleep(TAP_DELAY) + for c in text: + keyboard.type(c) + time.sleep(TAP_DELAY) + + # ----------------------------------------------------------------------------- # Simulate Input: XDOTOOL # @@ -748,7 +774,8 @@ class from_words_to_digits: continue word_list[i:i_next] = [ - ("{:,d}".format(int(number)) if (numbers_use_separator and allow_reformat) else number) + suffix + ("{:,d}".format(int(number)) if (numbers_use_separator and allow_reformat) else number) + + suffix ] if (i_number_prev != -1) and (i_number_prev + 1 != i): @@ -1404,7 +1431,9 @@ def main_begin( # Handled the resulting text # if output == "SIMULATE_INPUT": - if simulate_input_tool == "XDOTOOL": + if simulate_input_tool == "PYNPUT": + handle_fn = simulate_typing_with_pynput + elif simulate_input_tool == "XDOTOOL": handle_fn = simulate_typing_with_xdotool elif simulate_input_tool == "YDOTOOL": handle_fn = simulate_typing_with_ydotool @@ -1790,11 +1819,12 @@ def argparse_create_begin(subparsers: "argparse._SubParsersAction[argparse.Argum "--simulate-input-tool", dest="simulate_input_tool", default="XDOTOOL", - choices=("XDOTOOL", "DOTOOL", "DOTOOLC", "YDOTOOL", "WTYPE", "STDOUT"), + choices=("PYNPUT", "XDOTOOL", "DOTOOL", "DOTOOLC", "YDOTOOL", "WTYPE", "STDOUT"), metavar="SIMULATE_INPUT_TOOL", help=( "Program used to simulate keystrokes (default).\n" "\n" + "- ``PYNPUT`` Compatible with all Linux distributions and Wayland.\n" "- ``XDOTOOL`` Compatible with the X server only (default).\n" "- ``DOTOOL`` Compatible with all Linux distributions and Wayland.\n" "- ``DOTOOLC`` Same as DOTOOL but for use with the `dotoold` daemon.\n" diff --git a/package/python/setup.py b/package/python/setup.py index 697d2b6..6622574 100644 --- a/package/python/setup.py +++ b/package/python/setup.py @@ -59,7 +59,7 @@ def main(base_dir): packages=[""], package_data={"": [NERD_DICTATION_DST, README_DST]}, include_package_data=True, - install_requires=["vosk"], + install_requires=["vosk", "pynput"], python_requires=">=3.8", )