From f1ed2ce085575754da7e45cd15c0aff6c31227d8 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 1 Jun 2026 22:21:44 -0400 Subject: [PATCH] Fixed user reported bug. Turns out old debugging was still in place. Should be fixed now. --- README.md | 12 +++++------ src/cthulhu/plugins/self_voice/plugin.py | 17 +++++++++++---- src/cthulhu/scripts/default.py | 24 --------------------- tests/test_self_voice_plugin_regressions.py | 19 ++++++++++++++++ 4 files changed, 38 insertions(+), 34 deletions(-) create mode 100644 tests/test_self_voice_plugin_regressions.py diff --git a/README.md b/README.md index 10ad95e..3342f39 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,9 @@ toolkit, OpenOffice/LibreOffice, Gecko, WebKitGtk, and KDE Qt toolkit. - **Preserves exit key**: Only sleep toggle remains active ### Self-Voicing -- **Unix socket interface**: Direct speech output via `/tmp/cthulhu.sock` +- **Unix socket interface**: Direct speech output via `${XDG_RUNTIME_DIR}/cthulhu.sock` - **External integration**: Other applications can speak through Cthulhu -- **Simple protocol**: `echo "text" | socat - UNIX-CLIENT:/tmp/cthulhu.sock` +- **Simple protocol**: `echo "text" | socat - UNIX-CLIENT:"${XDG_RUNTIME_DIR}/cthulhu.sock"` ## D-Bus Remote Controller @@ -275,9 +275,9 @@ Cthulhu offers a mechanism through which messages may be spoken directly by the ```bash # Speak hello world. -echo "Hello world." | socat - UNIX-CLIENT:/tmp/cthulhu.sock +echo "Hello world." | socat - UNIX-CLIENT:"${XDG_RUNTIME_DIR}/cthulhu.sock" # Speak Hello world without interrupting the previous speech. -echo "Hello world." | socat - UNIX-CLIENT:/tmp/cthulhu.sock -# Make hello world persistant in Braille. -echo "Hello world.<#APPEND#>" | socat - UNIX-CLIENT:/tmp/cthulhu.sock +echo "<#APPEND#>Hello world." | socat - UNIX-CLIENT:"${XDG_RUNTIME_DIR}/cthulhu.sock" +# Make hello world persistent in Braille. +echo "Hello world.<#PERSISTENT#>" | socat - UNIX-CLIENT:"${XDG_RUNTIME_DIR}/cthulhu.sock" ``` diff --git a/src/cthulhu/plugins/self_voice/plugin.py b/src/cthulhu/plugins/self_voice/plugin.py index e2893ff..db51b8e 100644 --- a/src/cthulhu/plugins/self_voice/plugin.py +++ b/src/cthulhu/plugins/self_voice/plugin.py @@ -26,6 +26,7 @@ import select import logging import threading from threading import Thread, Lock +from typing import Optional from cthulhu.plugin import Plugin, cthulhu_hookimpl logger = logging.getLogger(__name__) @@ -34,6 +35,13 @@ logger = logging.getLogger(__name__) APPEND_CODE = '<#APPEND#>' PERSISTENT_CODE = '<#PERSISTENT#>' +def _get_socket_file() -> Optional[str]: + runtimeDir = os.environ.get('XDG_RUNTIME_DIR') + if not runtimeDir: + return None + + return os.path.join(runtimeDir, 'cthulhu.sock') + class SelfVoice(Plugin): """Plugin that provides a socket interface for external applications to send text to Cthulhu.""" @@ -115,10 +123,11 @@ class SelfVoice(Plugin): def voiceWorker(self): """Worker thread that listens on a socket for messages to speak.""" - socketFile = '/tmp/cthulhu.sock' - # For testing purposes - # socketFile = '/tmp/cthulhu-plugin.sock' - + socketFile = _get_socket_file() + if not socketFile: + logger.error("XDG_RUNTIME_DIR is not set; Self Voice plugin cannot create its socket") + return + # Clean up any existing socket file if os.path.exists(socketFile): try: diff --git a/src/cthulhu/scripts/default.py b/src/cthulhu/scripts/default.py index 911d2e9..4441c32 100644 --- a/src/cthulhu/scripts/default.py +++ b/src/cthulhu/scripts/default.py @@ -393,8 +393,6 @@ class Script(script.Script): return keyBindings def getExtensionBindings(self): - with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"=== getExtensionBindings() called ===\n") keyBindings = keybindings.KeyBindings() bindings = self.notificationPresenter.get_bindings() @@ -443,35 +441,13 @@ class Script(script.Script): try: if hasattr(cthulhu, 'cthulhuApp') and cthulhu.cthulhuApp: api_helper = cthulhu.cthulhuApp.getAPIHelper() - with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"=== Checking for plugin bindings ===\n") - f.write(f"api_helper exists: {api_helper is not None}\n") - if api_helper: - f.write(f"api_helper has _gestureBindings: {hasattr(api_helper, '_gestureBindings')}\n") - if hasattr(api_helper, '_gestureBindings'): - f.write(f"_gestureBindings content: {api_helper._gestureBindings}\n") - f.write(f"Available contexts: {list(api_helper._gestureBindings.keys())}\n") - if api_helper and hasattr(api_helper, '_gestureBindings'): - with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"=== Adding plugin bindings in getExtensionBindings() ===\n") - for context_name, context_bindings in api_helper._gestureBindings.items(): for binding in context_bindings: keyBindings.add(binding) - with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"Added plugin binding: {binding.keysymstring} modifiers={binding.modifiers} desc={binding.handler.description}\n") - else: - with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"=== No plugin bindings available ===\n") - else: - with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"=== cthulhuApp not available ===\n") except Exception as e: import cthulhu.debug as debug debug.printMessage(debug.LEVEL_WARNING, f"Failed to add plugin bindings: {e}", True) - with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"Exception in plugin binding addition: {e}\n") return keyBindings diff --git a/tests/test_self_voice_plugin_regressions.py b/tests/test_self_voice_plugin_regressions.py new file mode 100644 index 0000000..f04d733 --- /dev/null +++ b/tests/test_self_voice_plugin_regressions.py @@ -0,0 +1,19 @@ +import os +import unittest +from unittest import mock + +from cthulhu.plugins.self_voice import plugin + + +class SelfVoicePluginRegressionTests(unittest.TestCase): + def test_socket_file_is_scoped_to_user_runtime_directory(self) -> None: + with mock.patch.dict(os.environ, {"XDG_RUNTIME_DIR": "/run/user/1000"}, clear=True): + self.assertEqual(plugin._get_socket_file(), "/run/user/1000/cthulhu.sock") + + def test_socket_file_is_unavailable_without_user_runtime_directory(self) -> None: + with mock.patch.dict(os.environ, {}, clear=True): + self.assertIsNone(plugin._get_socket_file()) + + +if __name__ == "__main__": + unittest.main()