4 Commits

Author SHA1 Message Date
Storm Dragon afb2d804d9 Merge branch 'testing' into wine-access 2026-07-18 14:38:07 -04:00
Storm Dragon 394d814ad0 updated PKGBUILDs. 2026-07-18 14:24:42 -04:00
Storm Dragon 006612c1da Merge branch 'testing' 2026-07-18 02:55:04 -04:00
Storm Dragon 49f0d3c612 Tests for the new plugins dialogue updates. 2026-07-18 02:54:53 -04:00
8 changed files with 134 additions and 148 deletions
+22 -45
View File
@@ -1,24 +1,25 @@
# Maintainer: Storm Dragon <storm_dragon@stormux.org> # Maintainer: Storm Dragon <storm_dragon@stormux.org>
# shellcheck shell=bash disable=SC2034,SC2154,SC2164
pkgbase=cthulhu-git pkgbase=cthulhu-git
pkgname=(cthulhu-git cthulhu-wine-access-git) pkgname=(cthulhu-git cthulhu-wine-access-git)
_pkgname=cthulhu _pkgname=cthulhu
pkgver=2026.05.25.r423.g0576b6f pkgver=2026.05.25.r423.g0576b6f
pkgrel=1 pkgrel=2
pkgdesc="Desktop-agnostic screen reader with plugin system, forked from Orca" pkgdesc="Desktop-agnostic screen reader with plugin system, forked from Orca"
url="https://git.stormux.org/storm/cthulhu" url="https://git.stormux.org/storm/cthulhu"
arch=(x86_64 aarch64 armv7h) arch=(x86_64 aarch64 armv7h)
license=(LGPL) license=(LGPL-2.1-or-later)
provides=("${_pkgname}") provides=("${_pkgname}=${pkgver}")
conflicts=("${_pkgname}") conflicts=("${_pkgname}")
depends=( depends=(
# Core AT-SPI accessibility # Core AT-SPI accessibility
at-spi2-core at-spi2-core
glib2
gobject-introspection-runtime gobject-introspection-runtime
python-gobject python-gobject
python-cairo python-cairo
gtk3 gtk3
python-pywayland
# Audio and speech # Audio and speech
speech-dispatcher speech-dispatcher
gstreamer gstreamer
@@ -26,57 +27,44 @@ depends=(
gst-plugins-good # pulsesink, more decoders gst-plugins-good # pulsesink, more decoders
# Braille support # Braille support
brltty python-brltty
liblouis liblouis
# Plugin system and D-Bus remote control # Plugin system and D-Bus remote control
python-pluggy python-pluggy
python-tomlkit python-tomlkit
python-dasbus python-dasbus
python-msgpack
# AI Assistant dependencies (for screenshots, HTTP requests, and actions) python-tornado
python-requests python-xlib
python-pyautogui
# Desktop integration # Desktop integration
hicolor-icon-theme hicolor-icon-theme
libwnck3 libwnck3
pango pango
# System utilities # System utilities
python
python-setproctitle python-setproctitle
socat # for self-voicing feature
xorg-xkbcomp xorg-xkbcomp
xorg-xmodmap
) )
optdepends=( optdepends=(
'espeak-ng: Alternative TTS engine' 'git: Install and update third-party plugin sources'
'festival: Alternative TTS engine' 'python-pywayland: Wayland shared workspace backend'
'flite: Lightweight TTS engine' 'piper-tts: Piper speech backend'
'espeak: Legacy TTS engine' 'socat: Send text to the Self Voice socket from the command line'
# AI Assistant providers (optional) # AI Assistant providers (optional)
'python-requests: AI Assistant plugin and HTTP provider support'
'python-pyautogui: AI Assistant mouse and keyboard actions'
'claude-code: Claude AI provider support' 'claude-code: Claude AI provider support'
'openai-codex: ChatGPT AI provider support' 'openai-codex: OpenAI Codex provider support'
'gemini-cli: Gemini AI provider support' 'gemini-cli: Gemini AI provider support'
'ollama: Local AI model support' 'ollama: Local AI model support'
# OCR plugin dependencies (optional) # OCR plugin dependencies (optional)
'python-pillow: Image processing for OCR and AI Assistant' 'python-pillow: Image processing for OCR'
'python-pytesseract: Python wrapper for Tesseract OCR engine' 'python-pytesseract: Python wrapper for Tesseract OCR engine'
'python-pdf2image: PDF to image conversion for OCR'
'python-scipy: Scientific computing for OCR color analysis'
'python-webcolors: Color name lookup for OCR text decoration'
'tesseract: OCR engine for text recognition' 'tesseract: OCR engine for text recognition'
'tesseract-data-eng: English language data for Tesseract'
# nvda2cthulhu plugin (optional)
'python-msgpack: Msgpack decoding for nvda2cthulhu'
'python-tornado: WebSocket server for nvda2cthulhu'
# Window Title Reader plugin (optional)
'python-xlib: X11 access for Wine window title plugin'
) )
optdepends_x86_64=( optdepends_x86_64=(
'cthulhu-wine-access-git: Wine, Proton, NVDA Controller, and Tolk integration' 'cthulhu-wine-access-git: Wine, Proton, NVDA Controller, and Tolk integration'
@@ -84,24 +72,13 @@ optdepends_x86_64=(
makedepends=( makedepends=(
git git
meson meson
ninja
python-build
python-installer
python-wheel
) )
makedepends_x86_64=( makedepends_x86_64=(
cmake cmake
wine wine
) )
install=cthulhu.install source=("${_pkgname}::git+https://git.stormux.org/storm/${_pkgname}.git#branch=master")
source=( sha512sums=('SKIP')
"${_pkgname}::git+https://git.stormux.org/storm/${_pkgname}.git#branch=master"
"cthulhu.install"
)
b2sums=(
'SKIP'
'SKIP'
)
pkgver() { pkgver() {
cd "${_pkgname}" cd "${_pkgname}"
@@ -125,7 +102,7 @@ build() {
package_cthulhu-git() { package_cthulhu-git() {
arch=(any) arch=(any)
provides=(cthulhu) provides=("cthulhu=${pkgver}")
conflicts=(cthulhu) conflicts=(cthulhu)
cd "${_pkgname}" cd "${_pkgname}"
meson install -C _build --destdir "$pkgdir" meson install -C _build --destdir "$pkgdir"
@@ -1,11 +0,0 @@
post_install() {
gtk-update-icon-cache -q -t -f usr/share/icons/hicolor
}
post_upgrade() {
post_install
}
post_remove() {
gtk-update-icon-cache -q -t -f usr/share/icons/hicolor
}
+27 -50
View File
@@ -1,79 +1,67 @@
# Maintainer: Storm Dragon <storm_dragon@stormux.org> # Maintainer: Storm Dragon <storm_dragon@stormux.org>
# shellcheck shell=bash disable=SC2034,SC2154,SC2164
pkgbase=cthulhu pkgbase=cthulhu
pkgname=(cthulhu cthulhu-wine-access) pkgname=(cthulhu cthulhu-wine-access)
pkgver=2026.05.25 pkgver=2026.07.18
pkgrel=1 pkgrel=1
pkgdesc="Desktop-agnostic screen reader with plugin system, forked from Orca" pkgdesc="Desktop-agnostic screen reader with plugin system, forked from Orca"
url="https://git.stormux.org/storm/cthulhu" url="https://git.stormux.org/storm/cthulhu"
arch=(x86_64 aarch64 armv7h) arch=(x86_64 aarch64 armv7h)
license=(LGPL) license=(LGPL-2.1-or-later)
depends=( depends=(
# Core AT-SPI accessibility # Core AT-SPI accessibility
at-spi2-core at-spi2-core
glib2
gobject-introspection-runtime gobject-introspection-runtime
python-gobject python-gobject
python-cairo python-cairo
gtk3 gtk3
python-pywayland
# Audio and speech # Audio and speech
speech-dispatcher speech-dispatcher
gstreamer gstreamer
gst-plugins-base # playbin, audiotestsrc, basic decoders gst-plugins-base # playbin, audiotestsrc, basic decoders
gst-plugins-good # pulsesink, more decoders gst-plugins-good # pulsesink, more decoders
# Braille support # Braille support
brltty python-brltty
liblouis liblouis
# Plugin system and D-Bus remote control # Plugin system and D-Bus remote control
python-pluggy python-pluggy
python-tomlkit python-tomlkit
python-dasbus python-dasbus
python-msgpack
# AI Assistant dependencies (for screenshots, HTTP requests, and actions) python-tornado
python-requests python-xlib
python-pyautogui
# Desktop integration # Desktop integration
hicolor-icon-theme hicolor-icon-theme
libwnck3 libwnck3
pango pango
# System utilities # System utilities
python
python-setproctitle python-setproctitle
socat # for self-voicing feature
xorg-xkbcomp xorg-xkbcomp
xorg-xmodmap
) )
optdepends=( optdepends=(
'espeak-ng: Alternative TTS engine' 'git: Install and update third-party plugin sources'
'festival: Alternative TTS engine' 'python-pywayland: Wayland shared workspace backend'
'flite: Lightweight TTS engine' 'piper-tts: Piper speech backend'
'espeak: Legacy TTS engine' 'socat: Send text to the Self Voice socket from the command line'
# AI Assistant providers (optional) # AI Assistant providers (optional)
'python-requests: AI Assistant plugin and HTTP provider support'
'python-pyautogui: AI Assistant mouse and keyboard actions'
'claude-code: Claude AI provider support' 'claude-code: Claude AI provider support'
'openai-codex: ChatGPT AI provider support' 'openai-codex: OpenAI Codex provider support'
'gemini-cli: Gemini AI provider support' 'gemini-cli: Gemini AI provider support'
'ollama: Local AI model support' 'ollama: Local AI model support'
# OCR plugin dependencies (optional)
'python-pillow: Image processing for OCR and AI Assistant'
'python-pytesseract: Python wrapper for Tesseract OCR engine'
'python-pdf2image: PDF to image conversion for OCR'
'python-scipy: Scientific computing for OCR color analysis'
'python-webcolors: Color name lookup for OCR text decoration'
'tesseract: OCR engine for text recognition'
'tesseract-data-eng: English language data for Tesseract'
# nvda2cthulhu plugin (optional)
'python-msgpack: Msgpack decoding for nvda2cthulhu'
'python-tornado: WebSocket server for nvda2cthulhu'
# Window Title Reader plugin (optional) # OCR plugin dependencies (optional)
'python-xlib: X11 access for Wine window title plugin' 'python-pillow: Image processing for OCR'
'python-pytesseract: Python wrapper for Tesseract OCR engine'
'tesseract: OCR engine for text recognition'
) )
optdepends_x86_64=( optdepends_x86_64=(
'cthulhu-wine-access: Wine, Proton, NVDA Controller, and Tolk integration' 'cthulhu-wine-access: Wine, Proton, NVDA Controller, and Tolk integration'
@@ -81,24 +69,13 @@ optdepends_x86_64=(
makedepends=( makedepends=(
git git
meson meson
ninja
python-build
python-installer
python-wheel
) )
makedepends_x86_64=( makedepends_x86_64=(
cmake cmake
wine wine
) )
install=cthulhu.install source=("git+https://git.stormux.org/storm/cthulhu.git#tag=${pkgver}")
source=( sha512sums=('SKIP')
"git+https://git.stormux.org/storm/cthulhu.git#tag=${pkgver}"
"cthulhu.install"
)
b2sums=(
'SKIP'
'SKIP'
)
build() { build() {
cd cthulhu cd cthulhu
@@ -108,7 +85,7 @@ build() {
package_cthulhu() { package_cthulhu() {
arch=(any) arch=(any)
provides=(cthulhu) provides=("cthulhu=${pkgver}")
conflicts=(cthulhu) conflicts=(cthulhu)
cd cthulhu cd cthulhu
meson install -C _build --destdir "$pkgdir" meson install -C _build --destdir "$pkgdir"
@@ -1,11 +0,0 @@
post_install() {
gtk-update-icon-cache -q -t -f usr/share/icons/hicolor
}
post_upgrade() {
post_install
}
post_remove() {
gtk-update-icon-cache -q -t -f usr/share/icons/hicolor
}
+1 -5
View File
@@ -1,5 +1,5 @@
project('cthulhu', project('cthulhu',
version: '2026.05.25', version: '2026.07.18',
meson_version: '>= 1.0.0', meson_version: '>= 1.0.0',
) )
@@ -56,12 +56,8 @@ optional_modules = {
'louis': 'contracted braille', 'louis': 'contracted braille',
'speechd': 'speech output', 'speechd': 'speech output',
'dasbus': 'D-Bus remote controller', 'dasbus': 'D-Bus remote controller',
'psutil': 'system information commands',
'pywayland': 'Wayland shared workspace backend', 'pywayland': 'Wayland shared workspace backend',
'gi.repository.Wnck': 'mouse review', 'gi.repository.Wnck': 'mouse review',
'pdf2image': 'PDF processing for OCR',
'scipy': 'Scientific computing for OCR analysis',
'webcolors': 'Color name resolution for OCR',
} }
summary = {} summary = {}
+1 -1
View File
@@ -23,5 +23,5 @@
# Forked from Orca screen reader. # Forked from Orca screen reader.
# Cthulhu project: https://git.stormux.org/storm/cthulhu # Cthulhu project: https://git.stormux.org/storm/cthulhu
version = "2026.07.17" version = "2026.07.18"
codeName = "wine-access" codeName = "wine-access"
+1 -25
View File
@@ -47,28 +47,6 @@ try:
except ImportError: except ImportError:
PYTESSERACT_AVAILABLE = False PYTESSERACT_AVAILABLE = False
# pdf2image
try:
from pdf2image import convert_from_path
PDF2IMAGE_AVAILABLE = True
except ImportError:
PDF2IMAGE_AVAILABLE = False
# scipy
try:
from scipy.spatial import KDTree
SCIPY_AVAILABLE = True
except ImportError:
SCIPY_AVAILABLE = False
# webcolors
try:
from webcolors import CSS3_HEX_TO_NAMES
from webcolors import hex_to_rgb
WEBCOLORS_AVAILABLE = True
except ImportError:
WEBCOLORS_AVAILABLE = False
# GTK/GDK # GTK/GDK
try: try:
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
@@ -594,9 +572,7 @@ class OCRDesktop(Plugin):
"""Get color information for OCR text (simplified version).""" """Get color information for OCR text (simplified version)."""
if not self._colorCalculation: if not self._colorCalculation:
return 'unknown' return 'unknown'
if not SCIPY_AVAILABLE or not WEBCOLORS_AVAILABLE:
return 'unknown'
# Simplified color calculation - just return "unknown" for now # Simplified color calculation - just return "unknown" for now
# Full implementation would require the color analysis from ocrdesktop # Full implementation would require the color analysis from ocrdesktop
return 'unknown' return 'unknown'
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Regression tests for the plugin controls in Preferences."""
import unittest
from unittest import mock
from cthulhu import cthulhu # Import first to satisfy the application's initialization order.
from cthulhu import cthulhu_gui_prefs
class PluginPreferencesRegressionTests(unittest.TestCase):
"""Keep plugin state changes independent from their presentation widgets."""
@staticmethod
def _create_gui():
gui = cthulhu_gui_prefs.CthulhuSetupGUI.__new__(
cthulhu_gui_prefs.CthulhuSetupGUI
)
gui.prefsDict = {
"activePlugins": ["HiddenPlugin", "SourceOne"],
"pluginSources": ["https://example.com/plugins.git"],
}
gui._available_plugins = {"HiddenPlugin", "SourceOne", "SourceTwo", "Required"}
gui._plugin_entries = {
"SourceOne": {"active": True, "can_toggle": True},
"SourceTwo": {"active": False, "can_toggle": True},
"Required": {"active": True, "can_toggle": False},
}
gui._plugin_canonical_map = {
"SourceOne": "SharedPlugin",
"SourceTwo": "SharedPlugin",
"Required": "Required",
}
gui._plugin_group_map = {
"SharedPlugin": ["SourceOne", "SourceTwo"],
"Required": ["Required"],
}
gui._update_selected_plugin_controls = mock.Mock()
gui._update_plugin_tabs = mock.Mock()
return gui
def test_active_plugins_include_hidden_and_selected_plugins(self):
gui = self._create_gui()
self.assertEqual(
gui._get_active_plugins_from_ui(),
["HiddenPlugin", "SourceOne", "Required"],
)
def test_enabling_plugin_disables_other_copy_with_same_name(self):
gui = self._create_gui()
gui._set_plugin_active("SourceTwo", True)
self.assertFalse(gui._plugin_entries["SourceOne"]["active"])
self.assertTrue(gui._plugin_entries["SourceTwo"]["active"])
gui._update_selected_plugin_controls.assert_called_once_with()
gui._update_plugin_tabs.assert_called_once_with()
def test_required_plugin_cannot_be_disabled(self):
gui = self._create_gui()
gui._set_plugin_active("Required", False)
self.assertTrue(gui._plugin_entries["Required"]["active"])
gui._update_plugin_tabs.assert_not_called()
def test_apply_does_not_modify_plugin_sources(self):
gui = self._create_gui()
gui._populate_plugin_list = mock.Mock()
gui._apply_plugin_changes()
self.assertEqual(
gui.prefsDict["pluginSources"],
["https://example.com/plugins.git"],
)
if __name__ == "__main__":
unittest.main()