Author SHA1 Message Date
Storm Dragon 64ad1ab3e0 Hopefully fixed weird gstreamer traceback. 2026-04-04 23:04:33 -04:00
Storm Dragon 20e61d6259 Merge branch 'testing' 2026-04-04 21:44:47 -04:00
Storm Dragon a750761f61 Steam UI improvements. 2026-04-04 20:22:54 -04:00
Storm Dragon 03b3880622 Merge branch 'testing' 2026-04-04 19:05:35 -04:00
Storm Dragon f4af54228a Tightened up Steam notifications. Fixed a system notifications regression.? 2026-04-04 19:04:45 -04:00
Storm Dragon 518d2b3bb6 Attempt to make the sound system more robust. Improve some web component detection. Fixed a place where Cthulhu gets stuck on netgear's web interface. 2026-04-04 18:31:11 -04:00
Storm Dragon 7043f08dab Simplify GStreamer sound playback 2026-03-23 13:51:42 -04:00
Storm Dragon d0bc7d8a3a I tested these changes for so long I actually forgot what they were, but they seem to work, so committing. 2026-03-23 12:46:27 -04:00
Storm Dragon 438fae4fef Fix script manager resets and isolate startup sound 2026-03-23 12:44:12 -04:00
Storm Dragon a7cd1d033a Speculative fix on preferences weirdness. 2026-03-09 01:14:01 -04:00
Storm Dragon c4890f31af Fix 2026.03.02 release versioning 2026-03-02 17:56:45 -05:00
Storm Dragon c44245189a Merge branch 'testing' 2026-03-02 15:42:15 -05:00
Storm Dragon c7b8e4a30d Clean stale autostart source references from translations 2026-03-02 15:42:04 -05:00
Storm Dragon 20fe1a7e30 Remove autogenerated autostart desktop template 2026-03-02 15:38:29 -05:00
Storm Dragon 25931758f7 Merge branch 'testing'
# Conflicts:
#	distro-packages/Arch-Linux/PKGBUILD
#	meson.build
#	src/cthulhu/cthulhuVersion.py
2026-03-02 15:37:50 -05:00
Storm Dragon daf57ef22c Removed the autogenerated autostart desktop file. Moved the desktop file to contrib so it's still available if people want it. 2026-02-25 23:20:05 -05:00
Storm Dragon 1d2d727fa2 Requested feature added. It is now possible to customize speech used for echo settings including speech module, voice, rate, volume, pitch. 2026-02-22 01:44:19 -05:00
Storm Dragon 11bd7107d2 Attempt to stop plugins from spontaneously re-enabling themselves. 2026-02-19 02:40:46 -05:00
Storm Dragon 95d33047fa Fix prefs state churn and sound-only role speech
Some settings were being reset when tab changed and a key was pressed, e.g. control.

Radio buttons were still announced even with sound only set.

Controls for AI assistant tab were vanishing after being set.
2026-02-18 07:03:12 -05:00
Storm Dragon 523b896053 Couple minor bug fixes, new speech code added in prefs. 2026-02-18 06:10:57 -05:00
Storm Dragon 9152455227 Speech in settings now uses the voice when switched to it. 2026-02-18 06:07:55 -05:00
Storm Dragon c0fdaca4d0 Merge remote-tracking branch 'origin/web-and-punctuation-fixes' into testing 2026-02-17 14:50:01 -05:00
Storm Dragon ed78ffc248 Visual speech monitor added to speech history plugin. Toggle with cthulhu+shift+D 2026-02-17 08:40:51 -05:00
137 changed files with 4390 additions and 614 deletions
+15
View File
@@ -14,6 +14,16 @@ This repository is a screen reader. Prioritize accessibility, correctness, and s
- `meson compile -C _build` - `meson compile -C _build`
- `meson install -C _build` - `meson install -C _build`
## Runtime target and testing rules
- Make source changes in this repo, not in `~/.local/lib/python*/site-packages/cthulhu/`, unless the user explicitly asks for an installed-package hotfix.
- If you confirm the active import comes from `~/.local/...`, that does **not** mean you should edit there. It means you should update the repo and then run `./build-local.sh` to replace the installed copy for testing.
- Default test/apply workflow for local Cthulhu fixes:
- edit repo files
- run `./build-local.sh`
- reproduce/test against the refreshed `~/.local` install
- If repo and installed behavior differ, prefer rebuilding with `./build-local.sh` over patching the installed package directly.
- Treat direct edits under `~/.local/.../cthulhu/` as an exception path that requires explicit user approval.
## Coding guidelines ## Coding guidelines
- **When modifying existing code:** follow the surrounding codes conventions. - **When modifying existing code:** follow the surrounding codes conventions.
- **When writing new code from scratch:** prefer - **When writing new code from scratch:** prefer
@@ -55,3 +65,8 @@ This repository is a screen reader. Prioritize accessibility, correctness, and s
## Meson install reminder (important) ## Meson install reminder (important)
- If you add new Python modules under `src/cthulhu/`, update `src/cthulhu/meson.build` so they get installed (otherwise imports can fail after install). - If you add new Python modules under `src/cthulhu/`, update `src/cthulhu/meson.build` so they get installed (otherwise imports can fail after install).
- If you add a new plugin directory, update `src/cthulhu/plugins/meson.build` and add a `meson.build` in the plugin directory. - If you add a new plugin directory, update `src/cthulhu/plugins/meson.build` and add a `meson.build` in the plugin directory.
## Common Cthulhu agent mistakes
- Checking the import origin, seeing `~/.local/...`, and then editing the installed package instead of the repo.
- Forgetting that `./build-local.sh` is the normal way to apply repo changes into the installed copy for testing.
- Making repo fixes and then diagnosing the old installed copy without rebuilding.
+2 -2
View File
@@ -98,11 +98,11 @@ git status
## Dependencies ## Dependencies
- **Runtime**: python3, pygobject-3.0, pluggy, AT-SPI2 - **Runtime**: python3, pygobject-3.0, pluggy, tomlkit, AT-SPI2
- **Build**: meson, ninja, gettext - **Build**: meson, ninja, gettext
- **Optional**: dasbus (for D-Bus service), BrlTTY, speech-dispatcher, piper-tts - **Optional**: dasbus (for D-Bus service), BrlTTY, speech-dispatcher, piper-tts
Install build dependencies on Arch Linux: Install build dependencies on Arch Linux:
```bash ```bash
sudo pacman -S meson ninja gettext python-dasbus sudo pacman -S meson ninja gettext python-dasbus python-tomlkit
``` ```
+2
View File
@@ -179,6 +179,7 @@ Ensure you have the development dependencies installed:
```bash ```bash
sudo pacman -S python python-gobject gtk3 at-spi2-core at-spi2-atk \ sudo pacman -S python python-gobject gtk3 at-spi2-core at-spi2-atk \
python-speechd gstreamer python-pluggy python-dasbus \ python-speechd gstreamer python-pluggy python-dasbus \
python-tomlkit \
meson ninja pkgconf intltool gettext meson ninja pkgconf intltool gettext
``` ```
@@ -187,6 +188,7 @@ sudo pacman -S python python-gobject gtk3 at-spi2-core at-spi2-atk \
sudo apt install python3 python3-gi python3-gi-cairo gir1.2-gtk-3.0 \ sudo apt install python3 python3-gi python3-gi-cairo gir1.2-gtk-3.0 \
at-spi2-core libatk-adaptor python3-speechd \ at-spi2-core libatk-adaptor python3-speechd \
gstreamer1.0-plugins-base python3-pluggy python3-dasbus \ gstreamer1.0-plugins-base python3-pluggy python3-dasbus \
python3-tomlkit \
meson ninja-build pkg-config intltool gettext meson ninja-build pkg-config intltool gettext
``` ```
+6
View File
@@ -26,6 +26,12 @@ for cmd in meson ninja python3; do
fi fi
done done
if ! python3 -c "import tomlkit" 2>/dev/null; then
echo "Error: Python module tomlkit is not installed"
echo "Please install: python-tomlkit"
exit 1
fi
# Check for optional dependencies # Check for optional dependencies
missingOptional=() missingOptional=()
if ! python3 -c "import gi" 2>/dev/null; then if ! python3 -c "import gi" 2>/dev/null; then
-8
View File
@@ -8,13 +8,11 @@ set -e # Exit on error
# Colors for output (only if stdout is a terminal) # Colors for output (only if stdout is a terminal)
if [[ -t 1 ]]; then if [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
BLUE='\033[0;34m' BLUE='\033[0;34m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
else else
RED=''
GREEN='' GREEN=''
YELLOW='' YELLOW=''
BLUE='' BLUE=''
@@ -56,12 +54,6 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then
# Remove data files # Remove data files
# Remove desktop files
if [[ -f "$HOME/.local/share/applications/cthulhu-autostart.desktop" ]]; then
rm -f "$HOME/.local/share/applications/cthulhu-autostart.desktop"
echo " Removed: ~/.local/share/applications/cthulhu-autostart.desktop"
fi
# Remove icons # Remove icons
for size in 16x16 22x22 24x24 32x32 48x48 256x256 scalable symbolic; do for size in 16x16 22x22 24x24 32x32 48x48 256x256 scalable symbolic; do
icon_path="$HOME/.local/share/icons/hicolor/$size/apps" icon_path="$HOME/.local/share/icons/hicolor/$size/apps"
-11
View File
@@ -1,11 +0,0 @@
[Desktop Entry]
Type=Application
Name=Cthulhu Screen Reader
Exec=cthulhu
NoDisplay=true
# Desktop-neutral autostart - no GNOME-specific conditions
# Users can enable/disable via their desktop environment's accessibility settings
# or by adding/removing this file from ~/.config/autostart/
X-GNOME-AutoRestart=true
Categories=Accessibility;
Keywords=screen;reader;accessibility;speech;braille;
+2 -7
View File
@@ -1,7 +1,7 @@
# Maintainer: Storm Dragon <storm_dragon@stormux.org> # Maintainer: Storm Dragon <storm_dragon@stormux.org>
pkgname=cthulhu pkgname=cthulhu
pkgver=2026.02.17 pkgver=2026.03.02
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"
@@ -84,7 +84,7 @@ makedepends=(
) )
install=cthulhu.install install=cthulhu.install
source=( source=(
"git+https://git.stormux.org/storm/cthulhu.git" "git+https://git.stormux.org/storm/cthulhu.git#tag=${pkgver}"
"cthulhu.install" "cthulhu.install"
) )
b2sums=( b2sums=(
@@ -92,11 +92,6 @@ b2sums=(
'SKIP' 'SKIP'
) )
pkgver() {
cd cthulhu
grep "^version = " src/cthulhu/cthulhuVersion.py | sed 's/version = "\(.*\)"/\1/'
}
build() { build() {
cd cthulhu cd cthulhu
arch-meson _build arch-meson _build
+1 -11
View File
@@ -1,5 +1,5 @@
project('cthulhu', project('cthulhu',
version: '2026.02.17-master', version: '2026.03.02-master',
meson_version: '>= 1.0.0', meson_version: '>= 1.0.0',
) )
@@ -93,16 +93,6 @@ else
summary += {'sound support': 'no (missing gstreamer)'} summary += {'sound support': 'no (missing gstreamer)'}
endif endif
# Integration with session startup
i18n.merge_file(
input: 'cthulhu-autostart.desktop.in',
output: '@BASENAME@',
type: 'desktop',
po_dir: meson.project_source_root() / 'po',
install: true,
install_dir: get_option('sysconfdir') / 'xdg' / 'autostart',
)
# Update icon cache manually (desktop-neutral) - optional, ignore failures # Update icon cache manually (desktop-neutral) - optional, ignore failures
gtk_update_icon_cache = find_program('gtk4-update-icon-cache', required: false) gtk_update_icon_cache = find_program('gtk4-update-icon-cache', required: false)
if gtk_update_icon_cache.found() if gtk_update_icon_cache.found()
-1
View File
@@ -1,6 +1,5 @@
# List of source files containing translatable strings. # List of source files containing translatable strings.
# Please keep this file sorted alphabetically. # Please keep this file sorted alphabetically.
cthulhu-autostart.desktop.in
src/cthulhu/braille_rolenames.py src/cthulhu/braille_rolenames.py
src/cthulhu/brltablenames.py src/cthulhu/brltablenames.py
src/cthulhu/chnames.py src/cthulhu/chnames.py
-2
View File
@@ -13,11 +13,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-DamnedLies-Scope: partial\n" "X-DamnedLies-Scope: partial\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.6.10\n" "X-Generator: Poedit 1.6.10\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector de pantalla Cthulhu" msgstr "Lector de pantalla Cthulhu"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "lector de pantalla; fabla; braille;" msgstr "lector de pantalla; fabla; braille;"
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" "&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
"X-Poedit-Language: Arabic\n" "X-Poedit-Language: Arabic\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "قارئ الشاشة Cthulhu" msgstr "قارئ الشاشة Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "شاشة;قارئ;إمكانية الوصول;كلام;برايل;" msgstr "شاشة;قارئ;إمكانية الوصول;كلام;برايل;"
-2
View File
@@ -19,11 +19,9 @@ msgstr ""
"X-Poedit-Language: Asturian\n" "X-Poedit-Language: Asturian\n"
"X-Poedit-SourceCharset: utf-8\n" "X-Poedit-SourceCharset: utf-8\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Llector de pantalla d'Cthulhu" msgstr "Llector de pantalla d'Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -17,11 +17,9 @@ msgstr ""
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Чытач з экрана Cthulhu" msgstr "Чытач з экрана Cthulhu"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "чытач з экрана;экран;голас;маўленне;Брайль;" msgstr "чытач з экрана;экран;голас;маўленне;Брайль;"
-2
View File
@@ -23,11 +23,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu — екранен четец" msgstr "Cthulhu — екранен четец"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "екран;четец;достъпност;говор;брайл;" msgstr "екран;четец;достъпност;говор;брайл;"
-2
View File
@@ -21,12 +21,10 @@ msgstr ""
"X-Generator: KBabel 1.9.1\n" "X-Generator: KBabel 1.9.1\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "অর্কা স্ক্রীন রিডার / বিবর্ধন" msgstr "অর্কা স্ক্রীন রিডার / বিবর্ধন"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,12 +18,10 @@ msgstr ""
"X-Generator: KBabel 1.9.1\n" "X-Generator: KBabel 1.9.1\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu'র বৈশিষ্ট্য" msgstr "Cthulhu'র বৈশিষ্ট্য"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -15,11 +15,9 @@ msgstr ""
"X-Launchpad-Export-Date: 2015-02-05 06:46+0000\n" "X-Launchpad-Export-Date: 2015-02-05 06:46+0000\n"
"X-Generator: Poedit 1.7.4\n" "X-Generator: Poedit 1.7.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu čitač ekrana" msgstr "Cthulhu čitač ekrana"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "čitač ekrana;govor;brailleovo pismo;" msgstr "čitač ekrana;govor;brailleovo pismo;"
-2
View File
@@ -27,11 +27,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector de pantalla Cthulhu" msgstr "Lector de pantalla Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "pantalla;lector;accessibilitat;veu;braille;" msgstr "pantalla;lector;accessibilitat;veu;braille;"
-2
View File
@@ -27,11 +27,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Poedit 1.8.11\n" "X-Generator: Poedit 1.8.11\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector de pantalla Cthulhu" msgstr "Lector de pantalla Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "lector de pantalla;pronunciació;braille;" msgstr "lector de pantalla;pronunciació;braille;"
-2
View File
@@ -19,12 +19,10 @@ msgstr ""
"X-Launchpad-Export-Date: 2020-05-04 21:32+0000\n" "X-Launchpad-Export-Date: 2020-05-04 21:32+0000\n"
"X-Generator: Poedit 2.3\n" "X-Generator: Poedit 2.3\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "خوێنەری پەردەی ئۆرکا" msgstr "خوێنەری پەردەی ئۆرکا"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Čtečka obrazovky Cthulhu" msgstr "Čtečka obrazovky Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "obrazovka;čtečka;přístupnost;řeč;braille;" msgstr "obrazovka;čtečka;přístupnost;řeč;braille;"
-2
View File
@@ -17,12 +17,10 @@ msgstr ""
"Plural-Forms: nplurals=4; plural= (n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != " "Plural-Forms: nplurals=4; plural= (n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != "
"11) ? 2 : 3;\n" "11) ? 2 : 3;\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Hoffterau Cthulhu" msgstr "Hoffterau Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -32,11 +32,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.0.1\n" "X-Generator: Poedit 3.0.1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Skærmlæseren Cthulhu" msgstr "Skærmlæseren Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "skærm;læser;tilgængelighed;tale;braille;" msgstr "skærm;læser;tilgængelighed;tale;braille;"
-2
View File
@@ -37,11 +37,9 @@ msgstr ""
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu-Bildschirmleser" msgstr "Cthulhu-Bildschirmleser"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "bildschirm;leser;barrierefreiheit;sprache;braille;" msgstr "bildschirm;leser;barrierefreiheit;sprache;braille;"
-2
View File
@@ -20,12 +20,10 @@ msgstr ""
"X-Poedit-SourceCharset: utf-8\n" "X-Poedit-SourceCharset: utf-8\n"
"Plural-Forms: nplurals=2; plural=(n!=1)\n" "Plural-Forms: nplurals=2; plural=(n!=1)\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ཨོར་ཀ་གསལ་གཞི་ལྷག་བྱེད་/ཆེ་ཤེལ་" msgstr "ཨོར་ཀ་གསལ་གཞི་ལྷག་བྱེད་/ཆེ་ཤེལ་"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -28,11 +28,9 @@ msgstr ""
"X-Generator: Poedit 2.3\n" "X-Generator: Poedit 2.3\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Αναγνώστης οθόνης Cthulhu" msgstr "Αναγνώστης οθόνης Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "οθόνη;αναγνώστης;προσβασιμότητα;ομιλία;μπράιγ;" msgstr "οθόνη;αναγνώστης;προσβασιμότητα;ομιλία;μπράιγ;"
-2
View File
@@ -17,11 +17,9 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -21,11 +21,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 45.3\n" "X-Generator: Gtranslator 45.3\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu Screen Reader" msgstr "Cthulhu Screen Reader"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "screen reader;speech;braille;" msgstr "screen reader;speech;braille;"
-2
View File
@@ -21,11 +21,9 @@ msgstr ""
"X-Generator: Poedit 3.2.2\n" "X-Generator: Poedit 3.2.2\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Ekranlegilo Orko" msgstr "Ekranlegilo Orko"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ekranlegilo;parolo;brajlo;" msgstr "ekranlegilo;parolo;brajlo;"
-2
View File
@@ -28,11 +28,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1)\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Generator: Gtranslator 45.alpha0\n" "X-Generator: Gtranslator 45.alpha0\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector de pantalla Cthulhu" msgstr "Lector de pantalla Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "lector;pantalla;accesibilidad;voz;braille;" msgstr "lector;pantalla;accesibilidad;voz;braille;"
-2
View File
@@ -20,12 +20,10 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu ekraanilugeja ja luup" msgstr "Cthulhu ekraanilugeja ja luup"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu pantaila-irakurlea" msgstr "Cthulhu pantaila-irakurlea"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "pantaila;irakurlea;irisgarritasuna;mintzamena;braillea;" msgstr "pantaila;irakurlea;irisgarritasuna;mintzamena;braillea;"
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.4\n" "X-Generator: Poedit 3.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "صفحه‌خوان اورکا" msgstr "صفحه‌خوان اورکا"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -27,11 +27,9 @@ msgstr ""
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
"X-Poedit-Bookmarks: 814,-1,-1,-1,-1,-1,-1,-1,-1,-1\n" "X-Poedit-Bookmarks: 814,-1,-1,-1,-1,-1,-1,-1,-1,-1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu-näytönlukija" msgstr "Cthulhu-näytönlukija"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "näyttö;lukija;saavutettavuus;puhe;pistekirjoitus;" msgstr "näyttö;lukija;saavutettavuus;puhe;pistekirjoitus;"
-2
View File
@@ -31,11 +31,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lecteur d’écran Cthulhu" msgstr "Lecteur d’écran Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "lecteur;écran;accessibilité;parole;braille;" msgstr "lecteur;écran;accessibilité;parole;braille;"
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.0.3\n" "X-Generator: Poedit 2.0.3\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,12 +18,10 @@ msgstr ""
"Plural-Forms: nplurals=5; plural=n==1 ? 0 : (n%10==1 || n%10==2) ? 1 : " "Plural-Forms: nplurals=5; plural=n==1 ? 0 : (n%10==1 || n%10==2) ? 1 : "
"(n%10>=3 && n%10<= 6) ? 2 : ((n%10>=7 && n%10<=9) || n==10) ? 3 : 4;\n" "(n%10>=3 && n%10<= 6) ? 2 : ((n%10>=7 && n%10<=9) || n==10) ? 3 : 4;\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Léitheoir Scáileáin agus Formhéadaitheoir Cthulhu" msgstr "Léitheoir Scáileáin agus Formhéadaitheoir Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -34,11 +34,9 @@ msgstr ""
"X-DL-Domain: po\n" "X-DL-Domain: po\n"
"X-DL-State: Translating\n" "X-DL-State: Translating\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector da pantalla Cthulhu" msgstr "Lector da pantalla Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "pantalla;lector;accesibilidade;fala;braille;" msgstr "pantalla;lector;accesibilidade;fala;braille;"
-2
View File
@@ -33,12 +33,10 @@ msgstr ""
"\n" "\n"
"\n" "\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu સ્ક્રીન વાંચક અને વિસ્તૃતકારક" msgstr "Cthulhu સ્ક્રીન વાંચક અને વિસ્તૃતકારક"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -14,11 +14,9 @@ msgstr ""
"2 : 3);\n" "2 : 3);\n"
"X-Generator: Poedit 3.4.1\n" "X-Generator: Poedit 3.4.1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "מקריא המסך Cthulhu" msgstr "מקריא המסך Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "מסך;קורא;נגישות;דיבור;ברייל;" msgstr "מסך;קורא;נגישות;דיבור;ברייל;"
-2
View File
@@ -22,11 +22,9 @@ msgstr ""
"\n" "\n"
"X-Generator: Lokalize 1.5\n" "X-Generator: Lokalize 1.5\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu स्क्रीन वाचक" msgstr "Cthulhu स्क्रीन वाचक"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "स्क्रीन रीडर;भाषण;ब्रेल;" msgstr "स्क्रीन रीडर;भाषण;ब्रेल;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"X-Launchpad-Export-Date: 2016-09-15 12:40+0000\n" "X-Launchpad-Export-Date: 2016-09-15 12:40+0000\n"
"X-Generator: Poedit 1.6.10\n" "X-Generator: Poedit 1.6.10\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu čitač zaslona" msgstr "Cthulhu čitač zaslona"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "čitač zaslona;govor;brajica;" msgstr "čitač zaslona;govor;brajica;"
-2
View File
@@ -22,11 +22,9 @@ msgstr ""
"X-Generator: Poedit 2.3\n" "X-Generator: Poedit 2.3\n"
# Megjegyzés: Mivel mindenhol régóta Gáborral egyeztetve az Cthulhu nevet magyarosan Orkaként írjuk a fordításban, így a fordítást megváltoztattam Orka képernyőolvasóra az Cthulhu képernyőolvasó helyett. # Megjegyzés: Mivel mindenhol régóta Gáborral egyeztetve az Cthulhu nevet magyarosan Orkaként írjuk a fordításban, így a fordítást megváltoztattam Orka képernyőolvasóra az Cthulhu képernyőolvasó helyett.
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Orka képernyőolvasó" msgstr "Orka képernyőolvasó"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "képernyő;olvasó;akadálymentesítés;beszéd;braille;" msgstr "képernyő;olvasó;akadálymentesítés;beszéd;braille;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Pembaca Layar Cthulhu" msgstr "Pembaca Layar Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "layar;pembaca;aksesibilitas;ucapan;braille;" msgstr "layar;pembaca;aksesibilitas;ucapan;braille;"
-2
View File
@@ -17,11 +17,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 1.5\n" "X-Generator: Lokalize 1.5\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu skjálestur" msgstr "Cthulhu skjálestur"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -39,11 +39,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1)\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Generator: Gtranslator 42.0\n" "X-Generator: Gtranslator 42.0\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lettore schermo Cthulhu" msgstr "Lettore schermo Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "lettore;schermo;accessibilità;voce;braille;" msgstr "lettore;schermo;accessibilità;voce;braille;"
-2
View File
@@ -25,11 +25,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu スクリーンリーダー" msgstr "Cthulhu スクリーンリーダー"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "スクリーン;リーダー;アクセシビリティ;音声;点字;" msgstr "スクリーン;リーダー;アクセシビリティ;音声;点字;"
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu - ეკრანის მკითხველი" msgstr "Cthulhu - ეკრანის მკითხველი"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ეკრანი;მკითხველი;ხელმისაწვდომობა;მეტყველება;ბრაილი;" msgstr "ეკრანი;მკითხველი;ხელმისაწვდომობა;მეტყველება;ბრაილი;"
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.0\n" "X-Generator: Poedit 3.0\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu экраннан оқитын қолданбасы" msgstr "Cthulhu экраннан оқитын қолданбасы"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,12 +18,10 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: KBabel 1.11.4\n" "X-Generator: KBabel 1.11.4\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ಆದ್ಯತೆಗಳು" msgstr "ಆದ್ಯತೆಗಳು"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -15,11 +15,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu 화면 낭독기" msgstr "Cthulhu 화면 낭독기"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "화면;리더;접근성;음성;점자;" msgstr "화면;리더;접근성;음성;점자;"
-2
View File
@@ -25,11 +25,9 @@ msgstr ""
"(n%100<10 || n%100>=20) ? 1 : 2);\n" "(n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu ekrano skaityklė" msgstr "Cthulhu ekrano skaityklė"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ekrano skaityklė;kalba;brailio raštas;" msgstr "ekrano skaityklė;kalba;brailio raštas;"
-2
View File
@@ -23,11 +23,9 @@ msgstr ""
"2);\n" "2);\n"
"X-Generator: Lokalize 21.12.3\n" "X-Generator: Lokalize 21.12.3\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu ekrāna lasītājs" msgstr "Cthulhu ekrāna lasītājs"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ekrāna lasītājs;runa;Brails;" msgstr "ekrāna lasītājs;runa;Brails;"
-2
View File
@@ -19,12 +19,10 @@ msgstr ""
"\n" "\n"
"\n" "\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu स्क्रीन पाठक / आवर्द्धक" msgstr "Cthulhu स्क्रीन पाठक / आवर्द्धक"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural= n==1 || n%10==1 ? 0 : 1\n" "Plural-Forms: nplurals=2; plural= n==1 || n%10==1 ? 0 : 1\n"
"X-Generator: KBabel 1.11.4\n" "X-Generator: KBabel 1.11.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu читач на екранот" msgstr "Cthulhu читач на екранот"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -22,12 +22,10 @@ msgstr ""
"X-Generator: Poedit 2.0.1\n" "X-Generator: Poedit 2.0.1\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "സ്ക്രീന്‍ വായന അവസാനിപ്പിക്കാം." msgstr "സ്ക്രീന്‍ വായന അവസാനിപ്പിക്കാം."
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,12 +18,10 @@ msgstr ""
"X-Generator: KBabel 1.9.1\n" "X-Generator: KBabel 1.9.1\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "वर्धक भिंग" msgstr "वर्धक भिंग"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 2.0.6\n" "X-Generator: Poedit 2.0.6\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Pembaca skrin Cthulhu" msgstr "Pembaca skrin Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "skrin;pembaca;kebolehcapaian;pertuturan;braille;" msgstr "skrin;pembaca;kebolehcapaian;pertuturan;braille;"
-2
View File
@@ -19,11 +19,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu skjermleser" msgstr "Cthulhu skjermleser"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "skjerm;leser;tilgjengelighet;tale;blindeskrift;" msgstr "skjerm;leser;tilgjengelighet;tale;blindeskrift;"
-2
View File
@@ -13,11 +13,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.0.1\n" "X-Generator: Poedit 3.0.1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ओर्का दृष्टि वाचक" msgstr "ओर्का दृष्टि वाचक"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "दृष्टि वाचक; वाचन;ब्रेल;" msgstr "दृष्टि वाचक; वाचन;ब्रेल;"
-2
View File
@@ -31,11 +31,9 @@ msgstr ""
"X-Generator: Poedit 3.2.2\n" "X-Generator: Poedit 3.2.2\n"
# vergrootglas/loep # vergrootglas/loep
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu schermlezer" msgstr "Cthulhu schermlezer"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "scherm;lezer;toegankelijkheid;spraak;braille;" msgstr "scherm;lezer;toegankelijkheid;spraak;braille;"
-2
View File
@@ -18,12 +18,10 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu skjermlesar og forstørringsglas" msgstr "Cthulhu skjermlesar og forstørringsglas"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -23,11 +23,9 @@ msgstr ""
"X-Launchpad-Export-Date: 2015-05-21 18:05+0000\n" "X-Launchpad-Export-Date: 2015-05-21 18:05+0000\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector d'ecran Cthulhu" msgstr "Lector d'ecran Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
"lector d'ecran;oral;braille;votz;sintèsi vocala;sintesi vocala;lector " "lector d'ecran;oral;braille;votz;sintèsi vocala;sintesi vocala;lector "
-2
View File
@@ -22,12 +22,10 @@ msgstr ""
"\n" "\n"
"\n" "\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ଓର୍କା ସ୍କ୍ରିନ ପାଠକ /ଛୋଟକୁ ବଡ ଦେଖଉଥିବା ଉପକରଣ " msgstr "ଓର୍କା ସ୍କ୍ରିନ ପାଠକ /ଛୋଟକୁ ବଡ ଦେଖଉଥିବା ଉପକରଣ "
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-Bookmarks: 589,-1,-1,-1,-1,-1,-1,-1,-1,-1\n" "X-Poedit-Bookmarks: 589,-1,-1,-1,-1,-1,-1,-1,-1,-1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ਓਰਕਾ ਸਕਰੀਨ ਰੀਡਰ" msgstr "ਓਰਕਾ ਸਕਰੀਨ ਰੀਡਰ"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n" "|| n%100>=20) ? 1 : 2);\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Czytnik ekranowy Cthulhu" msgstr "Czytnik ekranowy Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "czytnik;ekran;dostępność;mowa;braille;" msgstr "czytnik;ekran;dostępność;mowa;braille;"
-2
View File
@@ -28,11 +28,9 @@ msgstr ""
"X-Source-Language: C\n" "X-Source-Language: C\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Leitor de ecrã Cthulhu" msgstr "Leitor de ecrã Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "leitor;ecrã;acessibilidade;voz;braille;" msgstr "leitor;ecrã;acessibilidade;voz;braille;"
-2
View File
@@ -49,11 +49,9 @@ msgstr ""
"X-DL-Domain: po\n" "X-DL-Domain: po\n"
"X-DL-State: Translating\n" "X-DL-State: Translating\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Leitor de tela Cthulhu" msgstr "Leitor de tela Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "leitor;tela;acessibilidade;voz;braille;" msgstr "leitor;tela;acessibilidade;voz;braille;"
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
"X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-SourceCharset: UTF-8\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cititorul de ecran Cthulhu" msgstr "Cititorul de ecran Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ecran;cititor;accesibilitate;vorbire;braille;" msgstr "ecran;cititor;accesibilitate;vorbire;braille;"
-2
View File
@@ -22,11 +22,9 @@ msgstr ""
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Экранный диктор Cthulhu" msgstr "Экранный диктор Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "экран;чтец;доступность;речь;брайль;" msgstr "экран;чтец;доступность;речь;брайль;"
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: KBabel 1.11.4\n" "X-Generator: KBabel 1.11.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -19,11 +19,9 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
"X-Generator: Gtranslator 2.91.7\n" "X-Generator: Gtranslator 2.91.7\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Ukončí čítačku obrazovky" msgstr "Ukončí čítačku obrazovky"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -22,11 +22,9 @@ msgstr ""
"X-Poedit-SourceCharset: utf-8\n" "X-Poedit-SourceCharset: utf-8\n"
"X-Generator: Poedit 3.0.1\n" "X-Generator: Poedit 3.0.1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Zaslonski bralnik Orka" msgstr "Zaslonski bralnik Orka"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "govor;zaslonski bralnik;Braillova pisava;brajica;povečevalnik" msgstr "govor;zaslonski bralnik;Braillova pisava;brajica;povečevalnik"
-2
View File
@@ -17,12 +17,10 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Ekrani Zmadhues (Lupë)" msgstr "Ekrani Zmadhues (Lupë)"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -21,11 +21,9 @@ msgstr ""
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
"X-Generator: Poedit 3.1.1\n" "X-Generator: Poedit 3.1.1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Читач екрана Орка" msgstr "Читач екрана Орка"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "читач екрана;говор;брајева азбука;" msgstr "читач екрана;говор;брајева азбука;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
"X-Generator: Poedit 2.0.3\n" "X-Generator: Poedit 2.0.3\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Čitač ekrana Orka" msgstr "Čitač ekrana Orka"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "čitač ekrana;govor;brajeva azbuka;" msgstr "čitač ekrana;govor;brajeva azbuka;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu skärmläsare" msgstr "Cthulhu skärmläsare"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "skärm;läsare;tillgänglighet;tal;punktskrift;" msgstr "skärm;läsare;tillgänglighet;tal;punktskrift;"
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n!=1);\\n\n" "Plural-Forms: nplurals=2; plural=(n!=1);\\n\n"
"\n" "\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ஆர்கா திரை படிப்பி " msgstr "ஆர்கா திரை படிப்பி "
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "திரைபடிப்பான்;பேசுதல்;ப்ரெய்ல்;" msgstr "திரைபடிப்பான்;பேசுதல்;ப்ரெய்ல்;"
-2
View File
@@ -23,11 +23,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n!=1);\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n"
"X-Generator: KBabel 1.11.4\n" "X-Generator: KBabel 1.11.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ఓర్కా తెరచదువరి" msgstr "ఓర్కా తెరచదువరి"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.6.3\n" "X-Generator: Poedit 1.6.3\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Хонандаи экрани Cthulhu" msgstr "Хонандаи экрани Cthulhu"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "хонандаи экран;нутқ;брайл;" msgstr "хонандаи экран;нутқ;брайл;"
-2
View File
@@ -19,11 +19,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "โปรแกรมอ่านหน้าจอ Cthulhu" msgstr "โปรแกรมอ่านหน้าจอ Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "หน้าจอ;อ่าน;การเข้าถึง;เสียงพูด;อักษรเบรลล์;" msgstr "หน้าจอ;อ่าน;การเข้าถึง;เสียงพูด;อักษรเบรลล์;"
-2
View File
@@ -27,11 +27,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.4\n" "X-Generator: Poedit 3.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu Ekran Okuyucu" msgstr "Cthulhu Ekran Okuyucu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ekran;okuyucu;erişilebilirlik;konuşma;braille;" msgstr "ekran;okuyucu;erişilebilirlik;konuşma;braille;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu ئېكران ئوقۇغۇ" msgstr "Cthulhu ئېكران ئوقۇغۇ"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "screen reader;speech;braille;ئېكران ئوقۇغۇچ;تاۋۇش;ئەمالار ئېلىپبەسى;" msgstr "screen reader;speech;braille;ئېكران ئوقۇغۇچ;تاۋۇش;ئەمالار ئېلىپبەسى;"
-2
View File
@@ -26,11 +26,9 @@ msgstr ""
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Lokalize 20.12.0\n" "X-Generator: Lokalize 20.12.0\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Інструмент читання з екрана «Cthulhu»" msgstr "Інструмент читання з екрана «Cthulhu»"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "екран;читач;доступність;мовлення;брайль;" msgstr "екран;читач;доступність;мовлення;брайль;"
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: LocFactoryEditor 1.8\n" "X-Generator: LocFactoryEditor 1.8\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Trình đọc màn hình Cthulhu" msgstr "Trình đọc màn hình Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "màn hình;đọc;trợ năng;giọng nói;chữ nổi;" msgstr "màn hình;đọc;trợ năng;giọng nói;chữ nổi;"
-2
View File
@@ -29,11 +29,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu 屏幕阅读器" msgstr "Cthulhu 屏幕阅读器"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "屏幕;阅读器;无障碍;语音;盲文;" msgstr "屏幕;阅读器;无障碍;语音;盲文;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 1.6.5\n" "X-Generator: Poedit 1.6.5\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu 螢幕閱讀器" msgstr "Cthulhu 螢幕閱讀器"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "screen reader;speech;braille;螢幕閱讀器;語音;點字;" msgstr "screen reader;speech;braille;螢幕閱讀器;語音;點字;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 1.8.12\n" "X-Generator: Poedit 1.8.12\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu 螢幕閱讀器" msgstr "Cthulhu 螢幕閱讀器"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "螢幕;閱讀器;無障礙;語音;點字;" msgstr "螢幕;閱讀器;無障礙;語音;點字;"
+37 -2
View File
@@ -204,10 +204,21 @@ class AXHypertext:
@staticmethod @staticmethod
def find_child_at_offset(obj: Atspi.Accessible, offset: int) -> Optional[Atspi.Accessible]: def find_child_at_offset(obj: Atspi.Accessible, offset: int) -> Optional[Atspi.Accessible]:
"""Attempts to correct for off-by-one brokenness in implementations""" """Returns the child at offset, correcting for broken hypertext offset mappings."""
if child := AXHypertext.get_child_at_offset(obj, offset): if child := AXHypertext.get_child_at_offset(obj, offset):
return child offset_in_parent = AXHypertext.get_character_offset_in_parent(child)
if offset_in_parent == offset:
return child
tokens = [
f"AXHypertext: Child at offset {offset} in",
obj,
"is",
child,
f"but reports offset {offset_in_parent}",
]
debug.print_tokens(debug.LEVEL_INFO, tokens, True)
if child_before := AXHypertext.get_child_at_offset(obj, offset - 1): if child_before := AXHypertext.get_child_at_offset(obj, offset - 1):
offset_in_parent = AXHypertext.get_character_offset_in_parent(child_before) offset_in_parent = AXHypertext.get_character_offset_in_parent(child_before)
@@ -225,6 +236,30 @@ class AXHypertext:
debug.print_tokens(debug.LEVEL_INFO, tokens, True) debug.print_tokens(debug.LEVEL_INFO, tokens, True)
return child_after return child_after
for i in range(AXHypertext._get_link_count(obj)):
link = AXHypertext._get_link_at_index(obj, i)
if link is None or AXHypertext.get_link_start_offset(link) != offset:
continue
try:
child = Atspi.Hyperlink.get_object(link, 0)
except GLib.GError as error:
msg = f"AXHypertext: Exception in find_child_at_offset: {error}"
debug.print_message(debug.LEVEL_INFO, msg, True)
continue
if child is None:
continue
tokens = [
f"AXHypertext: Child at offset {offset} in",
obj,
"found via link enumeration:",
child,
]
debug.print_tokens(debug.LEVEL_INFO, tokens, True)
return child
return None return None
@staticmethod @staticmethod
+48 -2
View File
@@ -85,12 +85,16 @@ class Backend:
with open(fileName, 'w', encoding='utf-8') as settingsFile: with open(fileName, 'w', encoding='utf-8') as settingsFile:
settingsFile.write(dumps(prefsDoc)) settingsFile.write(dumps(prefsDoc))
def _updateTable(self, targetTable, newValues): def _updateTable(self, targetTable, newValues, preserveMissingKeys=None):
if not isinstance(newValues, dict): if not isinstance(newValues, dict):
return return
preserveMissingKeys = set(preserveMissingKeys or [])
for key in list(targetTable.keys()): for key in list(targetTable.keys()):
if key not in newValues: if key not in newValues:
if key in preserveMissingKeys:
continue
del targetTable[key] del targetTable[key]
continue continue
newValue = newValues[key] newValue = newValues[key]
@@ -173,7 +177,12 @@ class Backend:
profiles[profile] = {} profiles[profile] = {}
profileTable = profiles[profile] profileTable = profiles[profile]
self._updateTable(profileTable, general) # Keep plugin persistence keys when callers provide partial updates.
self._updateTable(
profileTable,
general,
preserveMissingKeys={"activePlugins", "pluginSources"},
)
self._writeDocument(self.settingsFile, prefsDoc) self._writeDocument(self.settingsFile, prefsDoc)
def _getSettings(self): def _getSettings(self):
@@ -213,12 +222,33 @@ class Backend:
return settingsDict return settingsDict
def _hasLikelyCustomEchoVoice(self, echoVoice, defaultVoice):
"""Return True when echoVoice appears intentionally customized."""
if not echoVoice:
return False
echoEstablished = bool(echoVoice.get('established', False))
if not defaultVoice:
return echoEstablished or bool(echoVoice.get(acss.ACSS.FAMILY))
for key in [acss.ACSS.RATE, acss.ACSS.AVERAGE_PITCH, acss.ACSS.GAIN, acss.ACSS.FAMILY]:
echoValue = echoVoice.get(key)
defaultValue = defaultVoice.get(key)
if echoValue is not None and echoValue != defaultValue:
return True
return echoEstablished and bool(echoVoice.get(acss.ACSS.FAMILY))
def getGeneral(self, profile=None): def getGeneral(self, profile=None):
""" Get general settings from default settings and """ Get general settings from default settings and
override with profile values. """ override with profile values. """
self._getSettings() self._getSettings()
generalSettings = self.general.copy() generalSettings = self.general.copy()
generalSettings = self._migrateSettings(generalSettings) generalSettings = self._migrateSettings(generalSettings)
# Plugin state is profile-scoped; ignore legacy/global values.
generalSettings.pop('activePlugins', None)
generalSettings.pop('pluginSources', None)
defaultProfile = generalSettings.get('startingProfile', defaultProfile = generalSettings.get('startingProfile',
['Default', 'default']) ['Default', 'default'])
if profile is None: if profile is None:
@@ -229,8 +259,24 @@ class Backend:
if key == 'voices': if key == 'voices':
for voiceType, voiceDef in value.items(): for voiceType, voiceDef in value.items():
value[voiceType] = acss.ACSS(voiceDef) value[voiceType] = acss.ACSS(voiceDef)
if key == 'echoVoice' and isinstance(value, dict):
value = acss.ACSS(value)
if key not in ['startingProfile', 'activeProfile']: if key not in ['startingProfile', 'activeProfile']:
generalSettings[key] = value generalSettings[key] = value
# Backward compatibility: recover custom echo behavior when legacy
# profiles have echoVoice but are missing custom-echo toggle keys.
if 'useCustomEchoVoice' not in profileSettings:
voices = generalSettings.get('voices') or {}
defaultVoice = voices.get(settings.DEFAULT_VOICE)
echoVoice = generalSettings.get('echoVoice')
if self._hasLikelyCustomEchoVoice(echoVoice, defaultVoice):
generalSettings['useCustomEchoVoice'] = True
if 'useCustomEchoForKey' not in profileSettings:
generalSettings['useCustomEchoForKey'] = True
if 'useCustomEchoForCharacter' not in profileSettings:
generalSettings['useCustomEchoForCharacter'] = True
try: try:
generalSettings['activeProfile'] = profileSettings['profile'] generalSettings['activeProfile'] = profileSettings['profile']
except KeyError: except KeyError:
+360 -8
View File
@@ -136,6 +136,24 @@
<property name="step_increment">0.10000000149</property> <property name="step_increment">0.10000000149</property>
<property name="page_increment">1</property> <property name="page_increment">1</property>
</object> </object>
<object class="GtkAdjustment" id="echoPitchAdjustment">
<property name="upper">10</property>
<property name="value">5</property>
<property name="step_increment">0.10000000149</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="echoRateAdjustment">
<property name="upper">100</property>
<property name="value">50</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="echoVolumeAdjustment">
<property name="upper">10</property>
<property name="value">10</property>
<property name="step_increment">0.10000000149</property>
<property name="page_increment">1</property>
</object>
<object class="GtkDialog" id="cthulhuSetupWindow"> <object class="GtkDialog" id="cthulhuSetupWindow">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="title" translatable="yes">Cthulhu Preferences</property> <property name="title" translatable="yes">Cthulhu Preferences</property>
@@ -553,7 +571,7 @@
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="mnemonic_widget">timeFormatCombo</property> <property name="mnemonic_widget">timeFormatCombo</property>
<accessibility> <accessibility>
<relation type="label-for" target="availableProfilesComboBox1"/> <relation type="label-for" target="timeFormatCombo"/>
</accessibility> </accessibility>
</object> </object>
<packing> <packing>
@@ -570,7 +588,7 @@
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="mnemonic_widget">dateFormatCombo</property> <property name="mnemonic_widget">dateFormatCombo</property>
<accessibility> <accessibility>
<relation type="label-for" target="availableProfilesComboBox2"/> <relation type="label-for" target="dateFormatCombo"/>
</accessibility> </accessibility>
</object> </object>
<packing> <packing>
@@ -1035,6 +1053,51 @@
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">6</property> <property name="spacing">6</property>
<child>
<object class="GtkBox" id="soundSinkHBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="soundSinkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes" comments="Translators: This is the label for a combo box where users can choose which audio backend Cthulhu should use.">Audio _backend:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">soundSinkCombo</property>
<accessibility>
<relation type="label-for" target="soundSinkCombo"/>
</accessibility>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="soundSinkCombo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="soundSinkComboChanged" swapped="no"/>
<accessibility>
<relation type="labelled-by" target="soundSinkLabel"/>
</accessibility>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child> <child>
<object class="GtkBox" id="soundThemeHBox"> <object class="GtkBox" id="soundThemeHBox">
<property name="visible">True</property> <property name="visible">True</property>
@@ -1077,7 +1140,7 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">0</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
@@ -1122,7 +1185,7 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">1</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
</object> </object>
@@ -1133,7 +1196,7 @@
<object class="GtkLabel" id="soundThemeTitleLabel"> <object class="GtkLabel" id="soundThemeTitleLabel">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes" comments="Translators: This is the title of a section in the preferences dialog containing sound theme options.">Sound Theme</property> <property name="label" translatable="yes" comments="Translators: This is the title of a section in the preferences dialog containing sound options.">Sound</property>
<attributes> <attributes>
<attribute name="weight" value="bold"/> <attribute name="weight" value="bold"/>
</attributes> </attributes>
@@ -3037,6 +3100,295 @@
<property name="top_attach">1</property> <property name="top_attach">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkFrame" id="echoVoiceFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="echoVoiceAlignment">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="left_padding">12</property>
<child>
<object class="GtkGrid" id="echoVoiceGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">10</property>
<property name="column_spacing">8</property>
<child>
<object class="GtkCheckButton" id="useCustomEchoVoiceCheckButton">
<property name="label" translatable="yes">Use c_ustom echo voice settings</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="useCustomEchoVoiceToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="useCustomEchoSpeechServerCheckButton">
<property name="label" translatable="yes">Use custom _speech-dispatcher module for echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="useCustomEchoSpeechServerToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="echoSpeechServersLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Echo _module:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">echoSpeechServers</property>
<accessibility>
<relation type="label-for" target="echoSpeechServers"/>
</accessibility>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="echoSpeechServers">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="echoSpeechServersChanged" swapped="no"/>
<accessibility>
<relation type="labelled-by" target="echoSpeechServersLabel"/>
</accessibility>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="echoSpeechFamiliesLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Echo _voice:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">echoSpeechFamilies</property>
<accessibility>
<relation type="label-for" target="echoSpeechFamilies"/>
</accessibility>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="echoSpeechFamilies">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="echoSpeechFamiliesChanged" swapped="no"/>
<accessibility>
<relation type="labelled-by" target="echoSpeechFamiliesLabel"/>
</accessibility>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="echoRateLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Echo ra_te:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">echoRateScale</property>
<accessibility>
<relation type="label-for" target="echoRateScale"/>
</accessibility>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkScale" id="echoRateScale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">echoRateAdjustment</property>
<property name="round_digits">0</property>
<property name="digits">0</property>
<property name="value_pos">right</property>
<signal name="value-changed" handler="echoRateValueChanged" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="echoPitchLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Echo pi_tch:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">echoPitchScale</property>
<accessibility>
<relation type="label-for" target="echoPitchScale"/>
</accessibility>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkScale" id="echoPitchScale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">echoPitchAdjustment</property>
<property name="round_digits">1</property>
<property name="value_pos">right</property>
<signal name="value-changed" handler="echoPitchValueChanged" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="echoVolumeLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Echo vo_lume:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">echoVolumeScale</property>
<accessibility>
<relation type="label-for" target="echoVolumeScale"/>
</accessibility>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkScale" id="echoVolumeScale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">echoVolumeAdjustment</property>
<property name="round_digits">1</property>
<property name="value_pos">right</property>
<signal name="value-changed" handler="echoVolumeValueChanged" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="useCustomEchoForKeyCheckButton">
<property name="label" translatable="yes">Use custom voice for _key echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="checkButtonToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">7</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="useCustomEchoForCharacterCheckButton">
<property name="label" translatable="yes">Use custom voice for _character echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="checkButtonToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">8</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="useCustomEchoForWordCheckButton">
<property name="label" translatable="yes">Use custom voice for _word echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="checkButtonToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">9</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="useCustomEchoForSentenceCheckButton">
<property name="label" translatable="yes">Use custom voice for _sentence echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="checkButtonToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">10</property>
<property name="width">2</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="echoVoiceLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Echo Voice</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child> <child>
<object class="GtkCheckButton" id="enableEchoByCharacterCheckButton"> <object class="GtkCheckButton" id="enableEchoByCharacterCheckButton">
<property name="label" translatable="yes" comments="Translators: When this option is enabled, inserted text of length 1 is spoken.">Enable echo by cha_racter</property> <property name="label" translatable="yes" comments="Translators: When this option is enabled, inserted text of length 1 is spoken.">Enable echo by cha_racter</property>
@@ -3049,7 +3401,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">2</property> <property name="top_attach">3</property>
</packing> </packing>
</child> </child>
<child> <child>
@@ -3064,7 +3416,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">3</property> <property name="top_attach">4</property>
</packing> </packing>
</child> </child>
<child> <child>
@@ -3079,7 +3431,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">4</property> <property name="top_attach">5</property>
</packing> </packing>
</child> </child>
</object> </object>
+10 -2
View File
@@ -769,7 +769,7 @@ def shutdown(script: Optional[Any] = None, inputEvent: Optional[Any] = None) ->
cthulhu_state.activeScript.presentationInterrupt() cthulhu_state.activeScript.presentationInterrupt()
cthulhuApp.getSignalManager().emitSignal('stop-application-completed') cthulhuApp.getSignalManager().emitSignal('stop-application-completed')
sound_theme_manager.getManager().playStopSound(wait=True) sound_theme_manager.getManager().playStopSound(wait=True, timeoutSeconds=1)
cthulhuApp.getPluginSystemManager().unloadAllPlugins(ForceAllPlugins=True) cthulhuApp.getPluginSystemManager().unloadAllPlugins(ForceAllPlugins=True)
# Deactivate the event manager first so that it clears its queue and will not # Deactivate the event manager first so that it clears its queue and will not
@@ -893,7 +893,14 @@ def main() -> int:
debug.printMessage(debug.LEVEL_INFO, "CTHULHU: Initialized.", True) debug.printMessage(debug.LEVEL_INFO, "CTHULHU: Initialized.", True)
script = cthulhu_state.activeScript script = cthulhu_state.activeScript
sound_theme_manager.getManager().playStartSound(wait=True) sound_theme_manager.getManager().playStartSound(wait=False)
soundFailureReason = sound.getSoundSystemFailureReason()
if soundFailureReason:
debug.printMessage(
debug.LEVEL_INFO,
f"CTHULHU: Startup sound failed. Continuing without sound. Reason: {soundFailureReason}",
True
)
cthulhuApp.getSignalManager().emitSignal('start-application-completed') cthulhuApp.getSignalManager().emitSignal('start-application-completed')
if script: if script:
window = script.utilities.activeWindow() window = script.utilities.activeWindow()
@@ -944,6 +951,7 @@ class Cthulhu(GObject.Object):
self.settingsManager: SettingsManager = settings_manager.SettingsManager(self) # Directly instantiate self.settingsManager: SettingsManager = settings_manager.SettingsManager(self) # Directly instantiate
self.eventManager: EventManager = event_manager.EventManager(self) # Directly instantiate self.eventManager: EventManager = event_manager.EventManager(self) # Directly instantiate
self.scriptManager: ScriptManager = script_manager.ScriptManager(self) # Directly instantiate self.scriptManager: ScriptManager = script_manager.ScriptManager(self) # Directly instantiate
script_manager._manager = self.scriptManager
self.logger: logger.Logger = logger.Logger() # Directly instantiate self.logger: logger.Logger = logger.Logger() # Directly instantiate
self.signalManager: SignalManager = signal_manager.SignalManager(self) self.signalManager: SignalManager = signal_manager.SignalManager(self)
self.dynamicApiManager: DynamicApiManager = dynamic_api_manager.DynamicApiManager(self) self.dynamicApiManager: DynamicApiManager = dynamic_api_manager.DynamicApiManager(self)
+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.02.17" version = "2026.03.02"
codeName = "master" codeName = "master"
+755 -20
View File
@@ -38,6 +38,9 @@ gi.require_version("Gtk", "3.0")
from gi.repository import Atspi from gi.repository import Atspi
import os import os
import json
import subprocess
import sys
import threading import threading
from gi.repository import Gdk from gi.repository import Gdk
from gi.repository import GLib from gi.repository import GLib
@@ -57,6 +60,7 @@ from . import cthulhu_gui_profile
from . import cthulhu_state from . import cthulhu_state
from . import settings from . import settings
from . import settings_manager from . import settings_manager
from . import sound_sink
from . import input_event from . import input_event
from . import input_event_manager from . import input_event_manager
from . import keybindings from . import keybindings
@@ -68,6 +72,7 @@ from . import text_attribute_names
from . import sound_theme_manager from . import sound_theme_manager
from . import script_manager from . import script_manager
from .ax_object import AXObject from .ax_object import AXObject
from .ax_utilities import AXUtilities
_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager _settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager
@@ -173,6 +178,16 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self.speechSystemsChoice = None self.speechSystemsChoice = None
self.speechSystemsChoices = None self.speechSystemsChoices = None
self.speechSystemsModel = None self.speechSystemsModel = None
self.echoVoice = None
self.echoSpeechFamiliesChoice = None
self.echoSpeechFamiliesChoices = None
self.echoSpeechFamiliesModel = None
self.echoSpeechServersChoice = None
self.echoSpeechServersChoices = None
self.echoSpeechServersModel = None
self.initializingEchoSpeech = False
self._echoVoiceFetchToken = 0
self._updatingEchoSpeechFamilies = False
self.systemVoice = None self.systemVoice = None
self.uppercaseVoice = None self.uppercaseVoice = None
self.window = None self.window = None
@@ -181,13 +196,16 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self.savedPitch = None self.savedPitch = None
self.savedRate = None self.savedRate = None
self.soundThemeCombo = None self.soundThemeCombo = None
self.soundSinkCombo = None
self.roleSoundPresentationCombo = None self.roleSoundPresentationCombo = None
self._isInitialSetup = False self._isInitialSetup = False
self._updatingSpeechFamilies = False
self.selectedFamilyChoices = {} self.selectedFamilyChoices = {}
self.selectedLanguageChoices = {} self.selectedLanguageChoices = {}
self.profilesCombo = None self.profilesCombo = None
self.profilesComboModel = None self.profilesComboModel = None
self.startingProfileCombo = None self.startingProfileCombo = None
self._initialFocusSyncAttempts = 0
self._capturedKey = [] self._capturedKey = []
self.script = None self.script = None
@@ -385,7 +403,12 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self._initComboBox(self.get_widget("speechLanguages")) self._initComboBox(self.get_widget("speechLanguages"))
self.speechFamiliesModel = \ self.speechFamiliesModel = \
self._initComboBox(self.get_widget("speechFamilies")) self._initComboBox(self.get_widget("speechFamilies"))
self.echoSpeechServersModel = \
self._initComboBox(self.get_widget("echoSpeechServers"))
self.echoSpeechFamiliesModel = \
self._initComboBox(self.get_widget("echoSpeechFamilies"))
self._initSpeechState() self._initSpeechState()
self._initEchoSpeechState()
# TODO - JD: Will this ever be the case?? # TODO - JD: Will this ever be the case??
self._isInitialSetup = \ self._isInitialSetup = \
@@ -1271,7 +1294,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
for familyChoice in self.speechFamiliesChoices: for familyChoice in self.speechFamiliesChoices:
name = familyChoice[speechserver.VoiceFamily.NAME] name = familyChoice[speechserver.VoiceFamily.NAME]
if name == familyName: if name == familyName:
self.get_widget("speechFamilies").set_active(i) self._setSpeechFamiliesActive(i)
self.speechFamiliesChoice = self.speechFamiliesChoices[i] self.speechFamiliesChoice = self.speechFamiliesChoices[i]
familySet = True familySet = True
break break
@@ -1295,13 +1318,22 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
if not familySet: if not familySet:
tokens = ["PREFERENCES DIALOG: Could not find speech family match for", familyName] tokens = ["PREFERENCES DIALOG: Could not find speech family match for", familyName]
debug.printTokens(debug.LEVEL_INFO, tokens, True) debug.printTokens(debug.LEVEL_INFO, tokens, True)
self.get_widget("speechFamilies").set_active(0) self._setSpeechFamiliesActive(0)
self.speechFamiliesChoice = self.speechFamiliesChoices[0] self.speechFamiliesChoice = self.speechFamiliesChoices[0]
if familySet: if familySet:
self.selectedFamilyChoices[self.speechServersChoice, self.selectedFamilyChoices[self.speechServersChoice,
self.speechLanguagesChoice] = i self.speechLanguagesChoice] = i
def _setSpeechFamiliesActive(self, index):
"""Sets speechFamilies active index while suppressing preview chatter."""
self._updatingSpeechFamilies = True
try:
self.get_widget("speechFamilies").set_active(index)
finally:
self._updatingSpeechFamilies = False
def _setupFamilies(self): def _setupFamilies(self):
"""Gets the list of voice variants for the current speech server and """Gets the list of voice variants for the current speech server and
current language. current language.
@@ -1353,7 +1385,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
selectedIndex = self.selectedFamilyChoices[self.speechServersChoice, selectedIndex = self.selectedFamilyChoices[self.speechServersChoice,
self.speechLanguagesChoice] self.speechLanguagesChoice]
self.get_widget("speechFamilies").set_active(selectedIndex) self._setSpeechFamiliesActive(selectedIndex)
def _setSpeechLanguagesChoice(self, languageName): def _setSpeechLanguagesChoice(self, languageName):
"""Sets the active item in the languages ("Language:") combo box """Sets the active item in the languages ("Language:") combo box
@@ -1478,6 +1510,16 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
# #
if not serverInfo: if not serverInfo:
serverInfo = speech.getInfo() serverInfo = speech.getInfo()
if serverInfo and len(serverInfo) >= 2 and serverInfo[1] == 'default':
defaultFamily = None
voices = self.prefsDict.get("voices", {})
defaultVoice = voices.get(settings.DEFAULT_VOICE) if voices else None
if defaultVoice:
defaultFamily = acss.ACSS(defaultVoice).get(acss.ACSS.FAMILY)
resolved = self._resolveSpeechDispatcherServerForFamily(defaultFamily)
if resolved is not None:
serverInfo = resolved.getInfo()
valueSet = False valueSet = False
i = 0 i = 0
@@ -1661,6 +1703,390 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self._setupSpeechSystems(factories) self._setupSpeechSystems(factories)
self.initializingSpeech = False self.initializingSpeech = False
def _getSpeechDispatcherFactory(self):
"""Returns the Speech Dispatcher factory if available."""
factories = cthulhu.cthulhuApp.settingsManager.getSpeechServerFactories()
for factory in factories:
try:
if factory.SpeechServer.getFactoryName() == guilabels.SPEECH_DISPATCHER:
return factory
except Exception:
debug.printException(debug.LEVEL_FINEST)
return None
def _setEchoVoiceFamily(self, family):
"""Sets echo voice family from the selected server family object."""
if not family:
return
name = family.get(speechserver.VoiceFamily.NAME)
language = family.get(speechserver.VoiceFamily.LANG)
dialect = family.get(speechserver.VoiceFamily.DIALECT)
variant = family.get(speechserver.VoiceFamily.VARIANT)
self.echoVoice[acss.ACSS.FAMILY] = {
speechserver.VoiceFamily.NAME: name,
speechserver.VoiceFamily.LANG: language,
speechserver.VoiceFamily.DIALECT: dialect,
speechserver.VoiceFamily.VARIANT: variant,
}
self.echoVoice['established'] = True
def _resolveSpeechDispatcherServerForFamily(self, family):
"""Returns a concrete Speech Dispatcher server matching the voice family."""
if not family or not self.speechServersChoices:
return None
name = family.get(speechserver.VoiceFamily.NAME)
language = family.get(speechserver.VoiceFamily.LANG)
dialect = family.get(speechserver.VoiceFamily.DIALECT)
variant = family.get(speechserver.VoiceFamily.VARIANT)
if not name:
return None
for server in self.speechServersChoices:
info = server.getInfo()
if not info or len(info) < 2 or info[1] == 'default':
continue
try:
families = server.getVoiceFamilies()
except Exception:
debug.printException(debug.LEVEL_FINEST)
continue
for candidate in families:
if candidate.get(speechserver.VoiceFamily.NAME) != name:
continue
if candidate.get(speechserver.VoiceFamily.LANG) != language:
continue
if candidate.get(speechserver.VoiceFamily.DIALECT) != dialect:
continue
if candidate.get(speechserver.VoiceFamily.VARIANT) != variant:
continue
tokens = [
"PREFERENCES DIALOG: Resolved voice family",
name,
"to speech server",
info,
]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
return server
tokens = [
"PREFERENCES DIALOG: Could not resolve speech server for family",
family,
]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
return None
def _getSpeechServerChoiceForSave(self):
"""Returns the server choice that should be persisted for speech settings."""
server = self.speechServersChoice
if not server or self.speechSystemsChoice != self._getSpeechDispatcherFactory():
return server
info = server.getInfo()
if not info or len(info) < 2 or info[1] != 'default':
return server
defaultFamily = None
if self.defaultVoice is not None:
defaultFamily = self.defaultVoice.get(acss.ACSS.FAMILY)
resolved = self._resolveSpeechDispatcherServerForFamily(defaultFamily)
return resolved or server
def _getEchoSpeechServerFamilyForSave(self):
"""Returns the most specific echo family to use for server resolution."""
family = None
if self.echoVoice is not None:
family = self.echoVoice.get(acss.ACSS.FAMILY)
if family:
return family
if self.defaultVoice is not None:
return self.defaultVoice.get(acss.ACSS.FAMILY)
return None
def _getEchoSpeechServerChoiceForSave(self):
"""Returns the echo speech server choice that should be persisted."""
server = self.echoSpeechServersChoice
if server is None:
return None
info = server.getInfo()
if not info or len(info) < 2 or info[1] != 'default':
return server
family = self._getEchoSpeechServerFamilyForSave()
resolved = self._resolveSpeechDispatcherServerForFamily(family)
if resolved is not None:
return resolved
speechServer = self._getSpeechServerChoiceForSave()
if speechServer is not None:
speechInfo = speechServer.getInfo()
if speechInfo and len(speechInfo) >= 2 and speechInfo[1] != 'default':
tokens = [
"PREFERENCES DIALOG: Falling back to main speech server for echo",
speechInfo,
]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
return speechServer
return server
def _populateEchoSpeechFamilies(self, families):
"""Populate the echo family combobox from the provided families list."""
combobox = self.get_widget("echoSpeechFamilies")
combobox.set_model(None)
self.echoSpeechFamiliesModel.clear()
self.echoSpeechFamiliesChoices = list(families or [])
self.echoSpeechFamiliesChoice = None
selectedIndex = 0
selectedMatchFound = False
selectedFamily = self.echoVoice.get(acss.ACSS.FAMILY)
selectedName = selectedLanguage = selectedDialect = selectedVariant = None
if selectedFamily:
selectedName = selectedFamily.get(speechserver.VoiceFamily.NAME)
selectedLanguage = selectedFamily.get(speechserver.VoiceFamily.LANG)
selectedDialect = selectedFamily.get(speechserver.VoiceFamily.DIALECT)
selectedVariant = selectedFamily.get(speechserver.VoiceFamily.VARIANT)
for i, family in enumerate(self.echoSpeechFamiliesChoices):
name = family.get(speechserver.VoiceFamily.NAME) or ""
language = family.get(speechserver.VoiceFamily.LANG) or ""
dialect = family.get(speechserver.VoiceFamily.DIALECT) or ""
variant = family.get(speechserver.VoiceFamily.VARIANT)
display = name
locale = language
if dialect:
locale = f"{language}-{dialect}" if language else dialect
if locale:
display = f"{name} ({locale})"
self.echoSpeechFamiliesModel.append((i, display))
if selectedName == name and selectedLanguage == language \
and selectedDialect == dialect and selectedVariant == variant:
selectedIndex = i
selectedMatchFound = True
combobox.set_model(self.echoSpeechFamiliesModel)
if self.echoSpeechFamiliesChoices:
self._updatingEchoSpeechFamilies = True
try:
combobox.set_active(selectedIndex)
finally:
self._updatingEchoSpeechFamilies = False
self.echoSpeechFamiliesChoice = self.echoSpeechFamiliesChoices[selectedIndex]
def _fetchEchoSpeechFamiliesWorker(self, serverInfo, requestToken):
"""Fetch module-specific voices in a subprocess and apply asynchronously."""
queryScript = """
import json
import sys
from cthulhu import speechdispatcherfactory
from cthulhu import speechserver
serverInfo = json.loads(sys.argv[1])
if isinstance(serverInfo, list):
serverInfo = tuple(serverInfo)
server = speechdispatcherfactory.SpeechServer.getSpeechServer(serverInfo)
families = server.getVoiceFamilies() if server else []
result = []
for family in families:
result.append({
"name": family.get(speechserver.VoiceFamily.NAME),
"lang": family.get(speechserver.VoiceFamily.LANG),
"dialect": family.get(speechserver.VoiceFamily.DIALECT),
"variant": family.get(speechserver.VoiceFamily.VARIANT),
})
print(json.dumps(result))
"""
familyDefs = None
try:
completed = subprocess.run(
[sys.executable, "-c", queryScript, json.dumps(serverInfo)],
capture_output=True,
text=True,
timeout=4.0,
check=False,
)
if completed.returncode == 0 and completed.stdout.strip():
familyDefs = json.loads(completed.stdout.strip())
else:
tokens = [
"PREFERENCES DIALOG: Echo voice fetch failed for",
serverInfo,
"rc=",
completed.returncode,
]
debug.printTokens(debug.LEVEL_WARNING, tokens, True)
except subprocess.TimeoutExpired:
tokens = ["PREFERENCES DIALOG: Echo voice fetch timed out for", serverInfo]
debug.printTokens(debug.LEVEL_WARNING, tokens, True)
except Exception:
debug.printException(debug.LEVEL_WARNING)
GLib.idle_add(self._applyFetchedEchoSpeechFamilies, requestToken, familyDefs)
def _applyFetchedEchoSpeechFamilies(self, requestToken, familyDefs):
"""Apply fetched echo families if this is still the active request."""
if requestToken != self._echoVoiceFetchToken:
return False
if not isinstance(familyDefs, list):
return False
families = []
for familyDef in familyDefs:
if not isinstance(familyDef, dict):
continue
families.append(
speechserver.VoiceFamily({
speechserver.VoiceFamily.NAME: familyDef.get("name"),
speechserver.VoiceFamily.LANG: familyDef.get("lang"),
speechserver.VoiceFamily.DIALECT: familyDef.get("dialect"),
speechserver.VoiceFamily.VARIANT: familyDef.get("variant"),
})
)
if not families:
return False
self._populateEchoSpeechFamilies(families)
return False
def _setupEchoSpeechFamilies(self):
"""Populate echo voice family list for the current echo server."""
self._echoVoiceFetchToken += 1
requestToken = self._echoVoiceFetchToken
useCustomModule = self.get_widget("useCustomEchoSpeechServerCheckButton").get_active()
fallbackFamilies = list(self.speechFamiliesChoices or [])
self._populateEchoSpeechFamilies(fallbackFamilies)
if useCustomModule and self.echoSpeechServersChoice:
serverInfo = self.echoSpeechServersChoice.getInfo()
thread = threading.Thread(
target=self._fetchEchoSpeechFamiliesWorker,
args=(serverInfo, requestToken),
daemon=True,
)
thread.start()
def _setEchoSpeechServersChoice(self, serverInfo):
"""Set the active echo module based on stored server info."""
if not self.echoSpeechServersChoices:
self.echoSpeechServersChoice = None
self._setupEchoSpeechFamilies()
return
if serverInfo and len(serverInfo) >= 2 and serverInfo[1] == 'default':
family = self._getEchoSpeechServerFamilyForSave()
resolved = self._resolveSpeechDispatcherServerForFamily(family)
if resolved is None:
speechServer = self._getSpeechServerChoiceForSave()
if speechServer is not None:
speechInfo = speechServer.getInfo()
if speechInfo and len(speechInfo) >= 2 and speechInfo[1] != 'default':
resolved = speechServer
tokens = [
"PREFERENCES DIALOG: Reusing main speech server for echo",
speechInfo,
]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
if resolved is not None:
serverInfo = resolved.getInfo()
valueSet = False
for i, server in enumerate(self.echoSpeechServersChoices):
info = server.getInfo()
if serverInfo and info == serverInfo:
self.get_widget("echoSpeechServers").set_active(i)
self.echoSpeechServersChoice = server
valueSet = True
break
if not valueSet:
self.get_widget("echoSpeechServers").set_active(0)
self.echoSpeechServersChoice = self.echoSpeechServersChoices[0]
self._setupEchoSpeechFamilies()
def _setupEchoSpeechServers(self):
"""Populate available speech-dispatcher modules for echo."""
combobox = self.get_widget("echoSpeechServers")
combobox.set_model(None)
self.echoSpeechServersModel.clear()
self.echoSpeechServersChoices = []
self.echoSpeechServersChoice = None
if not self.prefsDict.get('enableSpeech', True):
combobox.set_model(self.echoSpeechServersModel)
self._setupEchoSpeechFamilies()
return
factory = self._getSpeechDispatcherFactory()
if factory is None:
combobox.set_model(self.echoSpeechServersModel)
self._setupEchoSpeechFamilies()
return
try:
self.echoSpeechServersChoices = factory.SpeechServer.getSpeechServers()
except Exception:
debug.printException(debug.LEVEL_WARNING)
self.echoSpeechServersChoices = []
for i, server in enumerate(self.echoSpeechServersChoices):
name = server.getInfo()[0]
self.echoSpeechServersModel.append((i, name))
combobox.set_model(self.echoSpeechServersModel)
self._setEchoSpeechServersChoice(self.prefsDict.get("echoSpeechServerInfo"))
def _initEchoSpeechState(self):
"""Initialize echo voice controls and choices."""
self.echoVoice = acss.ACSS(
self.prefsDict.get("echoVoice", settings.echoVoice) or {})
baseVoice = self.defaultVoice or acss.ACSS(
self.prefsDict.get("voices", {}).get(settings.DEFAULT_VOICE, {}))
rate = self.echoVoice.get(acss.ACSS.RATE, baseVoice.get(acss.ACSS.RATE, 50.0))
pitch = self.echoVoice.get(acss.ACSS.AVERAGE_PITCH, baseVoice.get(acss.ACSS.AVERAGE_PITCH, 5.0))
volume = self.echoVoice.get(acss.ACSS.GAIN, baseVoice.get(acss.ACSS.GAIN, 10.0))
self.get_widget("echoRateScale").set_value(rate)
self.get_widget("echoPitchScale").set_value(pitch)
self.get_widget("echoVolumeScale").set_value(volume)
self.initializingEchoSpeech = True
self._setupEchoSpeechServers()
self.initializingEchoSpeech = False
self._setEchoVoiceItems()
def _setSpokenTextAttributes(self, view, setAttributes, def _setSpokenTextAttributes(self, view, setAttributes,
state, moveToTop=False): state, moveToTop=False):
"""Given a set of spoken text attributes, update the model used by the """Given a set of spoken text attributes, update the model used by the
@@ -2394,6 +2820,19 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
prefs["enableEchoByWord"]) prefs["enableEchoByWord"])
self.get_widget("enableEchoBySentenceCheckButton").set_active( \ self.get_widget("enableEchoBySentenceCheckButton").set_active( \
prefs["enableEchoBySentence"]) prefs["enableEchoBySentence"])
self.get_widget("useCustomEchoVoiceCheckButton").set_active(
prefs.get("useCustomEchoVoice", settings.useCustomEchoVoice))
self.get_widget("useCustomEchoSpeechServerCheckButton").set_active(
prefs.get("useCustomEchoSpeechServer", settings.useCustomEchoSpeechServer))
self.get_widget("useCustomEchoForKeyCheckButton").set_active(
prefs.get("useCustomEchoForKey", settings.useCustomEchoForKey))
self.get_widget("useCustomEchoForCharacterCheckButton").set_active(
prefs.get("useCustomEchoForCharacter", settings.useCustomEchoForCharacter))
self.get_widget("useCustomEchoForWordCheckButton").set_active(
prefs.get("useCustomEchoForWord", settings.useCustomEchoForWord))
self.get_widget("useCustomEchoForSentenceCheckButton").set_active(
prefs.get("useCustomEchoForSentence", settings.useCustomEchoForSentence))
self._setEchoVoiceItems()
# Text attributes pane. # Text attributes pane.
# #
@@ -2553,22 +2992,20 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self._updateAIControlsState(enabled) self._updateAIControlsState(enabled)
def _updateAIControlsState(self, enabled): def _updateAIControlsState(self, enabled):
"""Enable or disable AI controls based on AI enabled state.""" """Refresh AI controls while keeping configuration fields editable."""
self.aiProviderCombo.set_sensitive(enabled) _ = enabled # kept for signal/call compatibility
self.aiApiKeyEntry.set_sensitive(enabled) # Keep settings editable even when AI assistant is disabled so users
self.aiOllamaModelEntry.set_sensitive(enabled) # can configure providers/keys before enabling it.
self.aiOllamaEndpointEntry.set_sensitive(enabled) self.aiProviderCombo.set_sensitive(True)
self.aiConfirmationCheckButton.set_sensitive(enabled) self.aiConfirmationCheckButton.set_sensitive(True)
self.aiScreenshotQualityCombo.set_sensitive(enabled) self.aiScreenshotQualityCombo.set_sensitive(True)
try: try:
self.get_widget("aiGetClaudeKeyButton").set_sensitive(enabled) self.get_widget("aiGetClaudeKeyButton").set_sensitive(True)
except: except:
pass # Button might not exist in older UI files pass # Button might not exist in older UI files
# Update provider-specific controls if AI is enabled current_provider = self.prefsDict.get("aiProvider", settings.aiProvider)
if enabled: self._updateProviderControls(current_provider)
current_provider = self.prefsDict.get("aiProvider", settings.aiProvider)
self._updateProviderControls(current_provider)
def _initIndentationState(self): def _initIndentationState(self):
"""Initialize Indentation widgets with current settings.""" """Initialize Indentation widgets with current settings."""
@@ -2667,15 +3104,37 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self.ocrCopyToClipboardCheckButton.set_active(copyToClipboard) self.ocrCopyToClipboardCheckButton.set_active(copyToClipboard)
def _initSoundThemeState(self): def _initSoundThemeState(self):
"""Initialize Sound Theme widgets with current settings.""" """Initialize sound widgets with current settings."""
prefs = self.prefsDict prefs = self.prefsDict
# Get widget references # Get widget references
self.soundSinkCombo = self.get_widget("soundSinkCombo")
self.soundThemeCombo = self.get_widget("soundThemeCombo") self.soundThemeCombo = self.get_widget("soundThemeCombo")
self.roleSoundPresentationCombo = self.get_widget("roleSoundPresentationCombo") self.roleSoundPresentationCombo = self.get_widget("roleSoundPresentationCombo")
self.soundSinkCombo.set_can_focus(False)
self.soundThemeCombo.set_can_focus(False) self.soundThemeCombo.set_can_focus(False)
self.roleSoundPresentationCombo.set_can_focus(False) self.roleSoundPresentationCombo.set_can_focus(False)
self._soundSinkChoices = [
(settings.SOUND_SINK_AUTO, guilabels.SOUND_BACKEND_AUTO),
(settings.SOUND_SINK_PIPEWIRE, guilabels.SOUND_BACKEND_PIPEWIRE),
(settings.SOUND_SINK_PULSE, guilabels.SOUND_BACKEND_PULSE),
(settings.SOUND_SINK_ALSA, guilabels.SOUND_BACKEND_ALSA),
]
self.soundSinkCombo.remove_all()
for _, label in self._soundSinkChoices:
self.soundSinkCombo.append_text(label)
runtimeSoundSink = cthulhu.cthulhuApp.settingsManager.getSetting("soundSink")
currentSink = sound_sink.normalize_sound_sink_choice(
prefs.get("soundSink", runtimeSoundSink if runtimeSoundSink is not None else settings.soundSink)
)
sinkIndex = 0
for index, (value, _) in enumerate(self._soundSinkChoices):
if value == currentSink:
sinkIndex = index
break
self.soundSinkCombo.set_active(sinkIndex)
# Populate sound theme combo box # Populate sound theme combo box
themeManager = sound_theme_manager.getManager() themeManager = sound_theme_manager.getManager()
availableThemes = themeManager.getAvailableThemes() availableThemes = themeManager.getAvailableThemes()
@@ -2724,6 +3183,14 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
if activeText: if activeText:
self.prefsDict["soundTheme"] = activeText self.prefsDict["soundTheme"] = activeText
def soundSinkComboChanged(self, widget):
"""Signal handler for the sound backend combo box."""
activeIndex = widget.get_active()
if activeIndex < 0:
return
value = self._soundSinkChoices[activeIndex][0]
self.prefsDict["soundSink"] = value
def roleSoundPresentationComboChanged(self, widget): def roleSoundPresentationComboChanged(self, widget):
"""Signal handler for the role sound presentation combo box.""" """Signal handler for the role sound presentation combo box."""
activeIndex = widget.get_active() activeIndex = widget.get_active()
@@ -2854,6 +3321,68 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
cthulhuSetupWindow.set_title(title) cthulhuSetupWindow.set_title(title)
cthulhuSetupWindow.show() cthulhuSetupWindow.show()
self._initialFocusSyncAttempts = 0
GLib.idle_add(self._set_initial_window_state)
GLib.idle_add(self._set_initial_gtk_focus)
GLib.timeout_add(50, self._set_initial_window_state)
def _set_initial_gtk_focus(self):
"""Give GTK focus to a real preferences control as soon as the dialog appears."""
candidateIds = [
"generalDesktopButton",
"generalLaptopButton",
"availableProfilesComboBox1",
"speechSupportCheckButton",
"notebook",
]
cthulhuSetupWindow = self.get_widget("cthulhuSetupWindow")
for widgetId in candidateIds:
widget = self.get_widget(widgetId)
if not widget.get_visible() or not widget.get_sensitive():
continue
if not widget.get_can_focus():
continue
debug.printMessage(
debug.LEVEL_INFO,
f"PREFERENCES DIALOG: Setting initial GTK focus to {widgetId}",
True,
)
cthulhuSetupWindow.set_focus(widget)
widget.grab_focus()
return False
debug.printMessage(
debug.LEVEL_INFO,
"PREFERENCES DIALOG: No focusable initial GTK widget found",
True,
)
return False
def _set_initial_window_state(self):
"""Sync Cthulhu's active window to preferences without forcing dialog focus."""
self._initialFocusSyncAttempts += 1
activeWindow = AXUtilities.find_active_window()
if activeWindow is None:
return self._initialFocusSyncAttempts < 5
app = AXObject.get_application(activeWindow)
appName = (AXObject.get_name(app) or "").lower()
if appName != "cthulhu":
return self._initialFocusSyncAttempts < 5
debug.printTokens(
debug.LEVEL_INFO,
["PREFERENCES DIALOG: Syncing active window to", activeWindow],
True,
)
cthulhu.setActiveWindow(activeWindow, notifyScript=False)
tokens = ["PREFERENCES DIALOG: Synced initial window state to", activeWindow]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
return False
def _initComboBox(self, combobox): def _initComboBox(self, combobox):
"""Initialize the given combo box to take a list of int/str pairs. """Initialize the given combo box to take a list of int/str pairs.
@@ -2898,22 +3427,91 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self.get_widget("enableNavigationKeysCheckButton").set_sensitive(enable) self.get_widget("enableNavigationKeysCheckButton").set_sensitive(enable)
self.get_widget("enableDiacriticalKeysCheckButton").set_sensitive( \ self.get_widget("enableDiacriticalKeysCheckButton").set_sensitive( \
enable) enable)
self._setEchoVoiceItems()
def _presentMessage(self, text, interrupt=False): def _setEchoVoiceItems(self):
"""[In]sensitize echo voice controls based on current state."""
useCustomVoice = self.get_widget("useCustomEchoVoiceCheckButton").get_active()
useCustomModule = self.get_widget("useCustomEchoSpeechServerCheckButton").get_active()
keyEchoEnabled = self.get_widget("keyEchoCheckButton").get_active()
charEchoEnabled = self.get_widget("enableEchoByCharacterCheckButton").get_active()
wordEchoEnabled = self.get_widget("enableEchoByWordCheckButton").get_active()
sentenceEchoEnabled = self.get_widget("enableEchoBySentenceCheckButton").get_active()
speechEnabled = self.get_widget("speechSupportCheckButton").get_active()
speechSystemIsDispatcher = False
if self.speechSystemsChoice:
try:
speechSystemIsDispatcher = \
self.speechSystemsChoice.SpeechServer.getFactoryName() == guilabels.SPEECH_DISPATCHER
except Exception:
speechSystemIsDispatcher = False
voiceControlsEnabled = useCustomVoice and speechEnabled
moduleOverrideAvailable = voiceControlsEnabled and speechSystemIsDispatcher
moduleControlsEnabled = moduleOverrideAvailable and useCustomModule
self.get_widget("useCustomEchoSpeechServerCheckButton").set_sensitive(moduleOverrideAvailable)
self.get_widget("echoSpeechServers").set_sensitive(moduleControlsEnabled)
self.get_widget("echoSpeechFamilies").set_sensitive(voiceControlsEnabled)
self.get_widget("echoRateScale").set_sensitive(useCustomVoice)
self.get_widget("echoPitchScale").set_sensitive(useCustomVoice)
self.get_widget("echoVolumeScale").set_sensitive(useCustomVoice)
self.get_widget("useCustomEchoForKeyCheckButton").set_sensitive(useCustomVoice and keyEchoEnabled)
self.get_widget("useCustomEchoForCharacterCheckButton").set_sensitive(
useCustomVoice and charEchoEnabled)
self.get_widget("useCustomEchoForWordCheckButton").set_sensitive(useCustomVoice and wordEchoEnabled)
self.get_widget("useCustomEchoForSentenceCheckButton").set_sensitive(
useCustomVoice and sentenceEchoEnabled)
def _presentMessage(self, text, interrupt=False, voice=None):
"""If the text field is not None, presents the given text, optionally """If the text field is not None, presents the given text, optionally
interrupting anything currently being spoken. interrupting anything currently being spoken.
Arguments: Arguments:
- text: the text to present - text: the text to present
- interrupt: if True, interrupt any speech currently being spoken - interrupt: if True, interrupt any speech currently being spoken
- voice: the voice to use; if None, use the default system voice
""" """
self.script.speakMessage(text, interrupt=interrupt) self.script.speakMessage(text, voice=voice, interrupt=interrupt)
try: try:
self.script.displayBrailleMessage(text, flashTime=-1) self.script.displayBrailleMessage(text, flashTime=-1)
except Exception: except Exception:
pass pass
def _previewVoiceSelection(self, voiceType, name):
"""Present a short preview in the currently selected voice."""
if not name:
return
voice = self._getACSSForVoiceType(voiceType)
if not voice:
return
previewVoice = acss.ACSS(voice)
if self.speechSystemsChoice \
and self.speechSystemsChoice.SpeechServer.getFactoryName() == guilabels.SPEECH_DISPATCHER:
previewMessage = messages.SPEECH_VOICE_VALUE % name
else:
previewMessage = messages.SPEECH_VOICE_VALUE_GENERIC % name
# Speak through the currently-selected preferences speech server so
# preview uses the chosen module/voice, not the globally-active one.
server = self.speechServersChoice
if server and hasattr(server, "speak"):
try:
server.speak(previewMessage, previewVoice, interrupt=True)
return
except Exception:
debug.printException(debug.LEVEL_WARNING)
self._presentMessage(previewMessage, interrupt=True, voice=previewVoice)
def _createNode(self, appName): def _createNode(self, appName):
"""Create a new root node in the TreeStore model with the name of the """Create a new root node in the TreeStore model with the name of the
application. application.
@@ -3194,6 +3792,8 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
enable = widget.get_active() enable = widget.get_active()
self.prefsDict["enableSpeech"] = enable self.prefsDict["enableSpeech"] = enable
self.get_widget("speechOptionsGrid").set_sensitive(enable) self.get_widget("speechOptionsGrid").set_sensitive(enable)
self._setupEchoSpeechServers()
self._setEchoVoiceItems()
def onlySpeakDisplayedTextToggled(self, widget): def onlySpeakDisplayedTextToggled(self, widget):
"""Signal handler for the "toggled" signal for the GtkCheckButton """Signal handler for the "toggled" signal for the GtkCheckButton
@@ -3225,6 +3825,8 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
selectedIndex = widget.get_active() selectedIndex = widget.get_active()
self.speechSystemsChoice = self.speechSystemsChoices[selectedIndex] self.speechSystemsChoice = self.speechSystemsChoices[selectedIndex]
self._setupSpeechServers() self._setupSpeechServers()
self._setupEchoSpeechServers()
self._setEchoVoiceItems()
def speechServersChanged(self, widget): def speechServersChanged(self, widget):
"""Signal handler for the "changed" signal for the speechServers """Signal handler for the "changed" signal for the speechServers
@@ -3257,6 +3859,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
pass pass
self._setupVoices() self._setupVoices()
self._setEchoVoiceItems()
def speechLanguagesChanged(self, widget): def speechLanguagesChanged(self, widget):
"""Signal handler for the "value_changed" signal for the languages """Signal handler for the "value_changed" signal for the languages
@@ -3316,6 +3919,8 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
variant = family[speechserver.VoiceFamily.VARIANT] variant = family[speechserver.VoiceFamily.VARIANT]
voiceType = self.get_widget("voiceTypesCombo").get_active() voiceType = self.get_widget("voiceTypesCombo").get_active()
self._setFamilyNameForVoiceType(voiceType, name, language, dialect, variant) self._setFamilyNameForVoiceType(voiceType, name, language, dialect, variant)
if not self._updatingSpeechFamilies:
self._previewVoiceSelection(voiceType, name)
except Exception: except Exception:
debug.printException(debug.LEVEL_SEVERE) debug.printException(debug.LEVEL_SEVERE)
@@ -3393,6 +3998,75 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
voices.get(settings.DEFAULT_VOICE, {})[acss.ACSS.GAIN] = volume voices.get(settings.DEFAULT_VOICE, {})[acss.ACSS.GAIN] = volume
cthulhu.cthulhuApp.settingsManager.setSetting('voices', voices) cthulhu.cthulhuApp.settingsManager.setSetting('voices', voices)
def useCustomEchoVoiceToggled(self, widget):
"""Signal handler for enabling/disabling custom echo voice settings."""
self.prefsDict["useCustomEchoVoice"] = widget.get_active()
self._setEchoVoiceItems()
self._setupEchoSpeechFamilies()
def useCustomEchoSpeechServerToggled(self, widget):
"""Signal handler for enabling/disabling custom echo module override."""
self.prefsDict["useCustomEchoSpeechServer"] = widget.get_active()
self._setEchoVoiceItems()
self._setupEchoSpeechFamilies()
def echoSpeechServersChanged(self, widget):
"""Signal handler for selecting the echo speech-dispatcher module."""
if self.initializingEchoSpeech:
return
selectedIndex = widget.get_active()
if selectedIndex < 0:
self.echoSpeechServersChoice = None
self.prefsDict["echoSpeechServerInfo"] = None
self._setupEchoSpeechFamilies()
return
self.echoSpeechServersChoice = self.echoSpeechServersChoices[selectedIndex]
self.prefsDict["echoSpeechServerInfo"] = self.echoSpeechServersChoice.getInfo()
self._setupEchoSpeechFamilies()
def echoSpeechFamiliesChanged(self, widget):
"""Signal handler for selecting the echo voice family."""
if self.initializingEchoSpeech or self._updatingEchoSpeechFamilies:
return
selectedIndex = widget.get_active()
if selectedIndex < 0:
self.echoSpeechFamiliesChoice = None
return
self.echoSpeechFamiliesChoice = self.echoSpeechFamiliesChoices[selectedIndex]
self._setEchoVoiceFamily(self.echoSpeechFamiliesChoice)
def echoRateValueChanged(self, widget):
"""Signal handler for changing custom echo rate."""
if self.echoVoice is None:
self.echoVoice = acss.ACSS({})
self.echoVoice[acss.ACSS.RATE] = widget.get_value()
self.echoVoice['established'] = True
def echoPitchValueChanged(self, widget):
"""Signal handler for changing custom echo pitch."""
if self.echoVoice is None:
self.echoVoice = acss.ACSS({})
self.echoVoice[acss.ACSS.AVERAGE_PITCH] = widget.get_value()
self.echoVoice['established'] = True
def echoVolumeValueChanged(self, widget):
"""Signal handler for changing custom echo volume."""
if self.echoVoice is None:
self.echoVoice = acss.ACSS({})
self.echoVoice[acss.ACSS.GAIN] = widget.get_value()
self.echoVoice['established'] = True
def checkButtonToggled(self, widget): def checkButtonToggled(self, widget):
"""Signal handler for "toggled" signal for basic GtkCheckButton """Signal handler for "toggled" signal for basic GtkCheckButton
widgets. The user has altered the state of the checkbox. widgets. The user has altered the state of the checkbox.
@@ -3410,6 +4084,15 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
# strip "CheckButton" from the end. # strip "CheckButton" from the end.
settingName = settingName[:-11] settingName = settingName[:-11]
self.prefsDict[settingName] = widget.get_active() self.prefsDict[settingName] = widget.get_active()
if settingName in [
"enableEchoByCharacter",
"enableEchoByWord",
"enableEchoBySentence",
"useCustomEchoForKey",
"useCustomEchoForCharacter",
"useCustomEchoForWord",
"useCustomEchoForSentence"]:
self._setEchoVoiceItems()
def keyEchoChecked(self, widget): def keyEchoChecked(self, widget):
"""Signal handler for the "toggled" signal for the """Signal handler for the "toggled" signal for the
@@ -4193,6 +4876,11 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
if not self._isInitialSetup: if not self._isInitialSetup:
self.restoreSettings() self.restoreSettings()
if self.soundSinkCombo is not None:
activeIndex = self.soundSinkCombo.get_active()
if activeIndex >= 0:
self.prefsDict["soundSink"] = self._soundSinkChoices[activeIndex][0]
enable = self.get_widget("speechSupportCheckButton").get_active() enable = self.get_widget("speechSupportCheckButton").get_active()
self.prefsDict["enableSpeech"] = enable self.prefsDict["enableSpeech"] = enable
@@ -4200,9 +4888,24 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self.prefsDict["speechServerFactory"] = \ self.prefsDict["speechServerFactory"] = \
self.speechSystemsChoice.__name__ self.speechSystemsChoice.__name__
if self.speechServersChoice: speechServerChoice = self._getSpeechServerChoiceForSave()
if speechServerChoice:
self.prefsDict["speechServerInfo"] = \ self.prefsDict["speechServerInfo"] = \
self.speechServersChoice.getInfo() speechServerChoice.getInfo()
else:
activeSpeechInfo = speech.getInfo()
if activeSpeechInfo:
self.prefsDict["speechServerInfo"] = activeSpeechInfo
runtimeSpeechServerInfo = cthulhu.cthulhuApp.settingsManager.getSetting(
"speechServerInfo"
)
if runtimeSpeechServerInfo and not self.prefsDict.get("speechServerInfo"):
self.prefsDict["speechServerInfo"] = runtimeSpeechServerInfo
existingSpeechServerInfo = self.prefsDict.get("speechServerInfo")
if existingSpeechServerInfo:
self.prefsDict["speechServerInfo"] = existingSpeechServerInfo
if self.defaultVoice is not None: if self.defaultVoice is not None:
self.prefsDict["voices"] = { self.prefsDict["voices"] = {
@@ -4212,6 +4915,36 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
settings.SYSTEM_VOICE: acss.ACSS(self.systemVoice), settings.SYSTEM_VOICE: acss.ACSS(self.systemVoice),
} }
self.prefsDict["useCustomEchoVoice"] = \
self.get_widget("useCustomEchoVoiceCheckButton").get_active()
self.prefsDict["useCustomEchoSpeechServer"] = \
self.get_widget("useCustomEchoSpeechServerCheckButton").get_active()
self.prefsDict["useCustomEchoForKey"] = \
self.get_widget("useCustomEchoForKeyCheckButton").get_active()
self.prefsDict["useCustomEchoForCharacter"] = \
self.get_widget("useCustomEchoForCharacterCheckButton").get_active()
self.prefsDict["useCustomEchoForWord"] = \
self.get_widget("useCustomEchoForWordCheckButton").get_active()
self.prefsDict["useCustomEchoForSentence"] = \
self.get_widget("useCustomEchoForSentenceCheckButton").get_active()
if self.echoVoice is None:
self.echoVoice = acss.ACSS({})
# Persist slider values directly so saving does not depend on
# value-changed signal timing.
self.echoVoice[acss.ACSS.RATE] = self.get_widget("echoRateScale").get_value()
self.echoVoice[acss.ACSS.AVERAGE_PITCH] = self.get_widget("echoPitchScale").get_value()
self.echoVoice[acss.ACSS.GAIN] = self.get_widget("echoVolumeScale").get_value()
self.echoVoice['established'] = True
self.prefsDict["echoVoice"] = acss.ACSS(self.echoVoice)
echoSpeechServerChoice = self._getEchoSpeechServerChoiceForSave()
if echoSpeechServerChoice:
self.prefsDict["echoSpeechServerInfo"] = echoSpeechServerChoice.getInfo()
else:
self.prefsDict["echoSpeechServerInfo"] = None
def applyButtonClicked(self, widget): def applyButtonClicked(self, widget):
"""Signal handler for the "clicked" signal for the applyButton """Signal handler for the "clicked" signal for the applyButton
GtkButton widget. The user has clicked the Apply button. GtkButton widget. The user has clicked the Apply button.
@@ -4244,6 +4977,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self._refresh_dynamic_plugin_tabs() self._refresh_dynamic_plugin_tabs()
braille.checkBrailleSetting() braille.checkBrailleSetting()
self._initSpeechState() self._initSpeechState()
self._initEchoSpeechState()
self._populateKeyBindings() self._populateKeyBindings()
self.__initProfileCombo() self.__initProfileCombo()
@@ -4506,6 +5240,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
braille.checkBrailleSetting() braille.checkBrailleSetting()
self._initSpeechState() self._initSpeechState()
self._initEchoSpeechState()
self._populateKeyBindings() self._populateKeyBindings()
+5
View File
@@ -47,6 +47,11 @@ __license__ = "LGPL"
# #
locusOfFocus: Optional[Any] = None # Actually: Optional[Atspi.Accessible] locusOfFocus: Optional[Any] = None # Actually: Optional[Atspi.Accessible]
# A pending focused object from a Cthulhu-owned window that should be used
# for keyboard-event context until the queued focus event is processed.
#
pendingSelfHostedFocus: Optional[Any] = None # Actually: Optional[Atspi.Accessible]
# The currently active window. # The currently active window.
# #
activeWindow: Optional[Any] = None # Actually: Optional[Atspi.Accessible] activeWindow: Optional[Any] = None # Actually: Optional[Atspi.Accessible]
+141 -1
View File
@@ -70,7 +70,9 @@ class EventManager:
self._dequeueCount: int = 0 self._dequeueCount: int = 0
self._cmdlineCache: Dict[int, str] = {} self._cmdlineCache: Dict[int, str] = {}
self._eventQueue: queue.Queue[Any] = queue.Queue(0) self._eventQueue: queue.Queue[Any] = queue.Queue(0)
self._prioritizedEvent: Optional[Atspi.Event] = None
self._gidleId: int = 0 self._gidleId: int = 0
self._prioritizedIdleId: int = 0
self._gidleLock: threading.Lock = threading.Lock() self._gidleLock: threading.Lock = threading.Lock()
self._gilSleepTime: float = 0.00001 self._gilSleepTime: float = 0.00001
self._synchronousToolkits: List[str] = ['VCL'] self._synchronousToolkits: List[str] = ['VCL']
@@ -284,6 +286,12 @@ class EventManager:
if self._isDuplicateEvent(event): if self._isDuplicateEvent(event):
return _ignore_with_reason("duplicate", "duplicate event") return _ignore_with_reason("duplicate", "duplicate event")
if self._isSelfHostedFocusClearedEvent(event):
return _ignore_with_reason("self-hosted-focus-cleared", "self-hosted focused=false event")
if self._isRedundantSelfHostedPropertyEvent(event):
return _ignore_with_reason("self-hosted-redundant-property", "self-hosted redundant property event")
# Thunderbird spams us with these when a message list thread is expanded or collapsed. # Thunderbird spams us with these when a message list thread is expanded or collapsed.
if event.type.endswith('system') \ if event.type.endswith('system') \
and AXObject.get_name(app).lower().startswith('thunderbird'): and AXObject.get_name(app).lower().startswith('thunderbird'):
@@ -596,6 +604,11 @@ class EventManager:
self._enqueueCount -= 1 self._enqueueCount -= 1
return return
if isObjectEvent and self._prioritizeSelfHostedFocusedEvent(e):
if debug.debugEventQueue:
self._enqueueCount -= 1
return
self._queuePrintln(e) self._queuePrintln(e)
if self._inFlood() and self._prioritizeDuringFlood(e): if self._inFlood() and self._prioritizeDuringFlood(e):
@@ -628,6 +641,73 @@ class EventManager:
if debug.debugEventQueue: if debug.debugEventQueue:
self._enqueueCount -= 1 self._enqueueCount -= 1
def _isSelfHostedFocusedEvent(self, event: Atspi.Event) -> bool:
"""Returns True if the event is a newly-focused event from Cthulhu."""
if not event.type.startswith("object:state-changed:focused") or not event.detail1:
return False
app = AXObject.get_application(event.source)
appName = (AXObject.get_name(app) or "").lower()
return appName == "cthulhu"
def _isSelfHostedFocusClearedEvent(self, event: Atspi.Event) -> bool:
"""Returns True if the event is a focus-lost event from Cthulhu."""
if not event.type.startswith("object:state-changed:focused") or event.detail1:
return False
app = AXObject.get_application(event.source)
appName = (AXObject.get_name(app) or "").lower()
return appName == "cthulhu"
def _isRedundantSelfHostedPropertyEvent(self, event: Atspi.Event) -> bool:
"""Returns True if the event is redundant prefs startup noise from Cthulhu."""
app = AXObject.get_application(event.source)
appName = (AXObject.get_name(app) or "").lower()
if appName != "cthulhu":
return False
if event.type.startswith("object:property-change:accessible-name"):
return AXUtilities.is_combo_box(event.source) or AXUtilities.is_table_cell(event.source)
if event.type.startswith("object:property-change:accessible-value"):
return AXObject.get_role(event.source) == Atspi.Role.SLIDER \
and event.source != cthulhu_state.locusOfFocus
return False
def _prioritizeSelfHostedFocusedEvent(self, event: Atspi.Event) -> bool:
"""Schedules the latest focused-child event from Cthulhu ahead of the normal queue."""
if not self._isSelfHostedFocusedEvent(event):
return False
tokens = ["EVENT MANAGER: Prioritizing self-hosted focused event for", event.source]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
script = self._get_scriptForEvent(event)
if script is not None:
script.eventCache[event.type] = (event, time.time())
self._gidleLock.acquire()
try:
replaced = self._prioritizedEvent is not None
self._prioritizedEvent = event
cthulhu_state.pendingSelfHostedFocus = event.source
if not self._prioritizedIdleId:
self._prioritizedIdleId = GLib.idle_add(
self._dequeuePrioritizedEvent,
priority=GLib.PRIORITY_HIGH_IDLE,
)
finally:
self._gidleLock.release()
msg = f"EVENT MANAGER: Prioritized self-hosted focused event. Replaced pending event: {replaced}"
debug.printMessage(debug.LEVEL_INFO, msg, True)
return True
def _isNoFocus(self) -> bool: def _isNoFocus(self) -> bool:
if cthulhu_state.locusOfFocus or cthulhu_state.activeWindow or cthulhu_state.activeScript: if cthulhu_state.locusOfFocus or cthulhu_state.activeWindow or cthulhu_state.activeScript:
return False return False
@@ -645,6 +725,60 @@ class EventManager:
defaultScript.idleMessage() defaultScript.idleMessage()
return False return False
def _dequeuePrioritizedEvent(self) -> bool:
"""Handles prioritized focused events from Cthulhu-owned windows."""
self._gidleLock.acquire()
try:
event = self._prioritizedEvent
self._prioritizedEvent = None
self._prioritizedIdleId = 0
if event is not None and cthulhu_state.pendingSelfHostedFocus == event.source:
cthulhu_state.pendingSelfHostedFocus = None
finally:
self._gidleLock.release()
if event is None:
return False
tokens = ["EVENT MANAGER: Dequeued prioritized event", event]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
debugging = not debug.eventDebugFilter or debug.eventDebugFilter.match(event.type)
if debugging:
startTime = time.time()
msg = (
f"\nvvvvv PROCESS OBJECT EVENT {event.type} "
f"(prioritized) vvvvv"
)
debug.printMessage(debug.eventDebugLevel, msg, False)
try:
self._processObjectEvent(event)
if self._didSuspendEventsFor(event):
self._unsuspendEvents(event)
elif self._eventsSuspended and self._shouldUnsuspendEventsFor(event):
self._unsuspendEvents(event, force=True)
except Exception:
debug.printException(debug.LEVEL_SEVERE)
if debugging:
msg = (
f"TOTAL PROCESSING TIME: {time.time() - startTime:.4f}"
f"\n^^^^^ PROCESS OBJECT EVENT {event.type} ^^^^^\n"
)
debug.printMessage(debug.eventDebugLevel, msg, False)
self._gidleLock.acquire()
try:
hasMore = self._prioritizedEvent is not None
if not hasMore and self._eventQueue.qsize() and not self._gidleId:
self._gidleId = GLib.idle_add(self._dequeue)
finally:
self._gidleLock.release()
return hasMore
def _dequeue(self) -> bool: def _dequeue(self) -> bool:
"""Handles all events destined for scripts. Called by the GTK """Handles all events destined for scripts. Called by the GTK
idle thread.""" idle thread."""
@@ -657,7 +791,13 @@ class EventManager:
self._dequeueCount += 1 self._dequeueCount += 1
try: try:
event = self._eventQueue.get_nowait() fromPriority = False
self._gidleLock.acquire()
try:
event = self._eventQueue.get_nowait()
finally:
self._gidleLock.release()
self._queuePrintln(event, isEnqueue=False) self._queuePrintln(event, isEnqueue=False)
inputEvents = (input_event.KeyboardEvent, input_event.BrailleEvent) inputEvents = (input_event.KeyboardEvent, input_event.BrailleEvent)
if isinstance(event, inputEvents): if isinstance(event, inputEvents):

Some files were not shown because too many files have changed in this diff Show More