3 Commits

Author SHA1 Message Date
Storm Dragon 46d3c66dc7 A few touch ups before tagged release. 2026-05-26 00:27:40 -04:00
Storm Dragon c39f2310e3 Merge branch 'testing' 2026-05-25 23:06:35 -04:00
Storm Dragon 2d8980051f Tighten terminal check for dropping. 2026-05-25 23:06:07 -04:00
16 changed files with 313 additions and 117 deletions
@@ -2,7 +2,7 @@
pkgname=cthulhu-git pkgname=cthulhu-git
_pkgname=cthulhu _pkgname=cthulhu
pkgver=2026.05.14.r396.ge2f9a7c pkgver=2026.05.25.r407.gc39f231
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"
+1 -1
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.05.14 pkgver=2026.05.25
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"
+27 -35
View File
@@ -3,16 +3,15 @@
# Slackware build script for cthulhu # Slackware build script for cthulhu
# Created based on PKGBUILD from Storm Dragon <storm_dragon@stormux.org> # Created based on PKGBUILD from Storm Dragon <storm_dragon@stormux.org>
cd $(dirname $0) ; CWD=$(pwd) cd "$(dirname "$0")" ; CWD=$(pwd)
PRGNAM=cthulhu PRGNAM=cthulhu
VERSION=${VERSION:-0.4} VERSION=${VERSION:-2026.05.25}
BUILD=${BUILD:-1} BUILD=${BUILD:-1}
TAG=storm TAG=storm
PKGTYPE=txz PKGTYPE=txz
export PYTHON=/usr/bin/python3.11
if [ -z "$ARCH" ]; then if [ -z "${ARCH:-}" ]; then
case "$( uname -m )" in case "$( uname -m )" in
i?86) ARCH=i586 ;; i?86) ARCH=i586 ;;
arm*) ARCH=arm ;; arm*) ARCH=arm ;;
@@ -23,7 +22,7 @@ fi
# If the variable PRINT_PACKAGE_NAME is set, then this script will report what # If the variable PRINT_PACKAGE_NAME is set, then this script will report what
# the name of the created package would be, and then exit. This information # the name of the created package would be, and then exit. This information
# could be useful to other scripts. # could be useful to other scripts.
if [ ! -z "${PRINT_PACKAGE_NAME}" ]; then if [ -n "${PRINT_PACKAGE_NAME:-}" ]; then
echo "$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE" echo "$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE"
exit 0 exit 0
fi fi
@@ -48,12 +47,12 @@ fi
set -e set -e
rm -rf $PKG rm -rf "$PKG"
mkdir -p $TMP $PKG $OUTPUT mkdir -p "$TMP" "$PKG" "$OUTPUT"
cd $TMP cd "$TMP"
rm -rf $PRGNAM-$VERSION rm -rf "$PRGNAM-$VERSION"
git clone https://git.stormux.org/storm/cthulhu.git $PRGNAM-$VERSION git clone --branch "$VERSION" --depth 1 https://git.stormux.org/storm/cthulhu.git "$PRGNAM-$VERSION"
cd $PRGNAM-$VERSION cd "$PRGNAM-$VERSION"
chown -R root:root . chown -R root:root .
find -L . \ find -L . \
\( -perm 777 -o -perm 775 -o -perm 750 -o -perm 711 -o -perm 555 \ \( -perm 777 -o -perm 775 -o -perm 750 -o -perm 711 -o -perm 555 \
@@ -61,38 +60,31 @@ find -L . \
\( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \ \( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \
-o -perm 440 -o -perm 400 \) -exec chmod 644 {} \; -o -perm 440 -o -perm 400 \) -exec chmod 644 {} \;
# Prepare the source
NOCONFIGURE=1 ./autogen.sh
CFLAGS="$SLKCFLAGS" \ CFLAGS="$SLKCFLAGS" \
CXXFLAGS="$SLKCFLAGS" \ CXXFLAGS="$SLKCFLAGS" \
./configure \ meson setup _build \
--prefix=/usr \ --prefix=/usr \
--libdir=/usr/lib${LIBDIRSUFFIX} \ --libdir="/usr/lib${LIBDIRSUFFIX}" \
--sysconfdir=/etc \ --sysconfdir=/etc \
--localstatedir=/var \ --localstatedir=/var \
--mandir=/usr/man \ --mandir=/usr/man \
--docdir=/usr/doc/$PRGNAM-$VERSION \ --buildtype=release
--build=$ARCH-slackware-linux meson compile -C _build
DESTDIR="$PKG" meson install -C _build
make find "$PKG" -print0 | xargs -0 file | grep -e "executable" -e "shared object" | grep ELF \
make install DESTDIR=$PKG
find $PKG -print0 | xargs -0 file | grep -e "executable" -e "shared object" | grep ELF \
| cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null || true | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null || true
mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION rm -f "$PKG/usr/share/icons/hicolor/icon-theme.cache"
cp -a AUTHORS COPYING ChangeLog README.md \
$PKG/usr/doc/$PRGNAM-$VERSION
cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild
mkdir -p $PKG/install mkdir -p "$PKG/usr/doc/$PRGNAM-$VERSION"
cat $CWD/slack-desc > $PKG/install/slack-desc cp -a COPYING HACKING README.md README-DEVELOPMENT.md RELEASE-HOWTO \
cat $CWD/doinst.sh > $PKG/install/doinst.sh "$PKG/usr/doc/$PRGNAM-$VERSION"
cat "$CWD/$PRGNAM.SlackBuild" > "$PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild"
cd $PKG mkdir -p "$PKG/install"
# let's avoid a "bad interpreter error cat "$CWD/slack-desc" > "$PKG/install/slack-desc"
sed "s,#!python3.11,#!/usr/bin/python3.11," usr/bin/cthulhu > dummy cat "$CWD/doinst.sh" > "$PKG/install/doinst.sh"
mv dummy usr/bin/cthulhu
chmod 755 usr/bin/cthulhu cd "$PKG"
/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE /sbin/makepkg -l y -c n "$OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE"
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
if [ -x /usr/bin/update-desktop-database ]; then
/usr/bin/update-desktop-database -q usr/share/applications >/dev/null 2>&1
fi
if [ -e usr/share/icons/hicolor/icon-theme.cache ]; then
if [ -x /usr/bin/gtk-update-icon-cache ]; then
/usr/bin/gtk-update-icon-cache usr/share/icons/hicolor >/dev/null 2>&1
fi
fi
+19
View File
@@ -0,0 +1,19 @@
# HOW TO EDIT THIS FILE:
# The "handy ruler" below makes it easier to edit a package description.
# Line up the first '|' above the ':' following the base package name, and
# the '|' on the right side marks the last column you can put a character in.
# You must make exactly 11 lines for the formatting to be correct. It's also
# customary to leave one space after the ':' except on otherwise blank lines.
|-----handy-ruler------------------------------------------------------|
cthulhu: cthulhu (Screen reader for blind or visually impaired users)
cthulhu:
cthulhu: Cthulhu is a screen reader for individuals who are blind or visually
cthulhu: impaired, forked from Orca. It provides a way to access applications
cthulhu: and toolkits that support the AT-SPI accessibility infrastructure.
cthulhu:
cthulhu: Homepage: https://git.stormux.org/storm/cthulhu
cthulhu:
cthulhu:
cthulhu:
cthulhu:
+8
View File
@@ -25,6 +25,14 @@ This package requires the following packages, all available from SlackBuilds.org
- libwnck3 - libwnck3
- python3-atspi - python3-atspi
- python3-cairo - python3-cairo
- python3-dasbus
- python3-gobject - python3-gobject
- python3-pluggy
- python3-pywayland
- python3-setproctitle - python3-setproctitle
- python3-tomlkit
- speech-dispatcher - speech-dispatcher
BUILD DEPENDENCIES:
- meson
- ninja
+2 -2
View File
@@ -1,10 +1,10 @@
PRGNAM="cthulhu" PRGNAM="cthulhu"
VERSION="0.4" VERSION="2026.05.25"
HOMEPAGE="https://git.stormux.org/storm/cthulhu" HOMEPAGE="https://git.stormux.org/storm/cthulhu"
DOWNLOAD="https://git.stormux.org/storm/cthulhu.git" DOWNLOAD="https://git.stormux.org/storm/cthulhu.git"
MD5SUM="SKIP" MD5SUM="SKIP"
DOWNLOAD_x86_64="" DOWNLOAD_x86_64=""
MD5SUM_x86_64="" MD5SUM_x86_64=""
REQUIRES="at-spi2-core brltty gobject-introspection gstreamer gst-plugins-base gst-plugins-good gtk3 liblouis libwnck3 python3-atspi python3-cairo python3-gobject python3-setproctitle speech-dispatcher" REQUIRES="at-spi2-core brltty gobject-introspection gstreamer gst-plugins-base gst-plugins-good gtk3 liblouis libwnck3 python3-atspi python3-cairo python3-dasbus python3-gobject python3-pluggy python3-pywayland python3-setproctitle python3-tomlkit speech-dispatcher"
MAINTAINER="Storm Dragon" MAINTAINER="Storm Dragon"
EMAIL="storm_dragon@stormux.org" EMAIL="storm_dragon@stormux.org"
+27 -35
View File
@@ -3,16 +3,15 @@
# Slackware build script for cthulhu # Slackware build script for cthulhu
# Created based on PKGBUILD from Storm Dragon <storm_dragon@stormux.org> # Created based on PKGBUILD from Storm Dragon <storm_dragon@stormux.org>
cd $(dirname $0) ; CWD=$(pwd) cd "$(dirname "$0")" ; CWD=$(pwd)
PRGNAM=cthulhu PRGNAM=cthulhu
VERSION=${VERSION:-0.4} VERSION=${VERSION:-2026.05.25}
BUILD=${BUILD:-1} BUILD=${BUILD:-1}
TAG=storm TAG=storm
PKGTYPE=txz PKGTYPE=txz
export PYTHON=/usr/bin/python3.11
if [ -z "$ARCH" ]; then if [ -z "${ARCH:-}" ]; then
case "$( uname -m )" in case "$( uname -m )" in
i?86) ARCH=i586 ;; i?86) ARCH=i586 ;;
arm*) ARCH=arm ;; arm*) ARCH=arm ;;
@@ -23,7 +22,7 @@ fi
# If the variable PRINT_PACKAGE_NAME is set, then this script will report what # If the variable PRINT_PACKAGE_NAME is set, then this script will report what
# the name of the created package would be, and then exit. This information # the name of the created package would be, and then exit. This information
# could be useful to other scripts. # could be useful to other scripts.
if [ ! -z "${PRINT_PACKAGE_NAME}" ]; then if [ -n "${PRINT_PACKAGE_NAME:-}" ]; then
echo "$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE" echo "$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE"
exit 0 exit 0
fi fi
@@ -48,12 +47,12 @@ fi
set -e set -e
rm -rf $PKG rm -rf "$PKG"
mkdir -p $TMP $PKG $OUTPUT mkdir -p "$TMP" "$PKG" "$OUTPUT"
cd $TMP cd "$TMP"
rm -rf $PRGNAM-$VERSION rm -rf "$PRGNAM-$VERSION"
git clone https://git.stormux.org/storm/cthulhu.git $PRGNAM-$VERSION git clone --branch "$VERSION" --depth 1 https://git.stormux.org/storm/cthulhu.git "$PRGNAM-$VERSION"
cd $PRGNAM-$VERSION cd "$PRGNAM-$VERSION"
chown -R root:root . chown -R root:root .
find -L . \ find -L . \
\( -perm 777 -o -perm 775 -o -perm 750 -o -perm 711 -o -perm 555 \ \( -perm 777 -o -perm 775 -o -perm 750 -o -perm 711 -o -perm 555 \
@@ -61,38 +60,31 @@ find -L . \
\( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \ \( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \
-o -perm 440 -o -perm 400 \) -exec chmod 644 {} \; -o -perm 440 -o -perm 400 \) -exec chmod 644 {} \;
# Prepare the source
NOCONFIGURE=1 ./autogen.sh
CFLAGS="$SLKCFLAGS" \ CFLAGS="$SLKCFLAGS" \
CXXFLAGS="$SLKCFLAGS" \ CXXFLAGS="$SLKCFLAGS" \
./configure \ meson setup _build \
--prefix=/usr \ --prefix=/usr \
--libdir=/usr/lib${LIBDIRSUFFIX} \ --libdir="/usr/lib${LIBDIRSUFFIX}" \
--sysconfdir=/etc \ --sysconfdir=/etc \
--localstatedir=/var \ --localstatedir=/var \
--mandir=/usr/man \ --mandir=/usr/man \
--docdir=/usr/doc/$PRGNAM-$VERSION \ --buildtype=release
--build=$ARCH-slackware-linux meson compile -C _build
DESTDIR="$PKG" meson install -C _build
make find "$PKG" -print0 | xargs -0 file | grep -e "executable" -e "shared object" | grep ELF \
make install DESTDIR=$PKG
find $PKG -print0 | xargs -0 file | grep -e "executable" -e "shared object" | grep ELF \
| cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null || true | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null || true
mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION rm -f "$PKG/usr/share/icons/hicolor/icon-theme.cache"
cp -a AUTHORS COPYING ChangeLog README.md \
$PKG/usr/doc/$PRGNAM-$VERSION
cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild
mkdir -p $PKG/install mkdir -p "$PKG/usr/doc/$PRGNAM-$VERSION"
cat $CWD/slack-desc > $PKG/install/slack-desc cp -a COPYING HACKING README.md README-DEVELOPMENT.md RELEASE-HOWTO \
cat $CWD/doinst.sh > $PKG/install/doinst.sh "$PKG/usr/doc/$PRGNAM-$VERSION"
cat "$CWD/$PRGNAM.SlackBuild" > "$PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild"
cd $PKG mkdir -p "$PKG/install"
# let's avoid a "bad interpreter error cat "$CWD/slack-desc" > "$PKG/install/slack-desc"
sed "s,#!python3.11,#!/usr/bin/python3.11," usr/bin/cthulhu > dummy cat "$CWD/doinst.sh" > "$PKG/install/doinst.sh"
mv dummy usr/bin/cthulhu
chmod 755 usr/bin/cthulhu cd "$PKG"
/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE /sbin/makepkg -l y -c n "$OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE"
+2
View File
@@ -1,3 +1,5 @@
#!/bin/sh
if [ -x /usr/bin/update-desktop-database ]; then if [ -x /usr/bin/update-desktop-database ]; then
/usr/bin/update-desktop-database -q usr/share/applications >/dev/null 2>&1 /usr/bin/update-desktop-database -q usr/share/applications >/dev/null 2>&1
fi fi
+1 -1
View File
@@ -1,5 +1,5 @@
project('cthulhu', project('cthulhu',
version: '2026.05.14-master', version: '2026.05.25',
meson_version: '>= 1.0.0', meson_version: '>= 1.0.0',
) )
+25 -6
View File
@@ -59,8 +59,11 @@ fi
cthulhuVersionFile="${scriptDir}/src/cthulhu/cthulhuVersion.py" cthulhuVersionFile="${scriptDir}/src/cthulhu/cthulhuVersion.py"
mesonFile="${scriptDir}/meson.build" mesonFile="${scriptDir}/meson.build"
pkgbuildFile="${scriptDir}/distro-packages/Arch-Linux/cthulhu/PKGBUILD" pkgbuildFile="${scriptDir}/distro-packages/Arch-Linux/cthulhu/PKGBUILD"
slackwareBuildFile="${scriptDir}/distro-packages/Slackware/cthulhu.SlackBuild"
slintBuildFile="${scriptDir}/distro-packages/Slint/cthulhu.SlackBuild"
slintInfoFile="${scriptDir}/distro-packages/Slint/cthulhu-info"
for path in "$cthulhuVersionFile" "$mesonFile" "$pkgbuildFile"; do for path in "$cthulhuVersionFile" "$mesonFile" "$pkgbuildFile" "$slackwareBuildFile" "$slintBuildFile" "$slintInfoFile"; do
if [[ ! -f "$path" ]]; then if [[ ! -f "$path" ]]; then
echo "Error: Missing file: $path" >&2 echo "Error: Missing file: $path" >&2
exit 1 exit 1
@@ -68,18 +71,19 @@ for path in "$cthulhuVersionFile" "$mesonFile" "$pkgbuildFile"; do
done done
sed -i "s/^version = \".*\"/version = \"${pythonVersion}\"/" "$cthulhuVersionFile" sed -i "s/^version = \".*\"/version = \"${pythonVersion}\"/" "$cthulhuVersionFile"
if [[ -n "$codeNameValue" ]]; then sed -i "s/^codeName = \".*\"/codeName = \"${codeNameValue}\"/" "$cthulhuVersionFile"
sed -i "s/^codeName = \".*\"/codeName = \"${codeNameValue}\"/" "$cthulhuVersionFile"
fi
sed -i "s/^ version: '.*',/ version: '${fullVersion}',/" "$mesonFile" sed -i "s/^ version: '.*',/ version: '${fullVersion}',/" "$mesonFile"
sed -i "s/^pkgver=.*/pkgver=${pythonVersion}/" "$pkgbuildFile" sed -i "s/^pkgver=.*/pkgver=${pythonVersion}/" "$pkgbuildFile"
sed -i "s/^pkgrel=.*/pkgrel=1/" "$pkgbuildFile" sed -i "s/^pkgrel=.*/pkgrel=1/" "$pkgbuildFile"
sed -i "s/^VERSION=\${VERSION:-.*}/VERSION=\${VERSION:-${pythonVersion}}/" "$slackwareBuildFile"
sed -i "s/^VERSION=\${VERSION:-.*}/VERSION=\${VERSION:-${pythonVersion}}/" "$slintBuildFile"
sed -i "s/^VERSION=\".*\"/VERSION=\"${pythonVersion}\"/" "$slintInfoFile"
if ! rg -q "^version = \"${pythonVersion}\"" "$cthulhuVersionFile"; then if ! rg -q "^version = \"${pythonVersion}\"" "$cthulhuVersionFile"; then
echo "Error: Failed to update ${cthulhuVersionFile}" >&2 echo "Error: Failed to update ${cthulhuVersionFile}" >&2
exit 1 exit 1
fi fi
if [[ -n "$codeNameValue" ]] && ! rg -q "^codeName = \"${codeNameValue}\"" "$cthulhuVersionFile"; then if ! rg -q "^codeName = \"${codeNameValue}\"" "$cthulhuVersionFile"; then
echo "Error: Failed to update codeName in ${cthulhuVersionFile}" >&2 echo "Error: Failed to update codeName in ${cthulhuVersionFile}" >&2
exit 1 exit 1
fi fi
@@ -95,8 +99,23 @@ if ! rg -q "^pkgrel=1$" "$pkgbuildFile"; then
echo "Error: Failed to reset pkgrel in ${pkgbuildFile}" >&2 echo "Error: Failed to reset pkgrel in ${pkgbuildFile}" >&2
exit 1 exit 1
fi fi
if ! rg -q "^VERSION=\\\$\\{VERSION:-${pythonVersion}\\}$" "$slackwareBuildFile"; then
echo "Error: Failed to update ${slackwareBuildFile}" >&2
exit 1
fi
if ! rg -q "^VERSION=\\\$\\{VERSION:-${pythonVersion}\\}$" "$slintBuildFile"; then
echo "Error: Failed to update ${slintBuildFile}" >&2
exit 1
fi
if ! rg -q "^VERSION=\"${pythonVersion}\"$" "$slintInfoFile"; then
echo "Error: Failed to update ${slintInfoFile}" >&2
exit 1
fi
echo "Updated version to ${fullVersion} in:" \ echo "Updated version to ${fullVersion} in:" \
"${cthulhuVersionFile}" \ "${cthulhuVersionFile}" \
"${mesonFile}" \ "${mesonFile}" \
"${pkgbuildFile}" "${pkgbuildFile}" \
"${slackwareBuildFile}" \
"${slintBuildFile}" \
"${slintInfoFile}"
+2 -2
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.05.14" version = "2026.05.25"
codeName = "master" codeName = ""
+2 -1
View File
@@ -30,7 +30,8 @@ __license__ = "LGPL"
# $CTHULHU_VERSION # $CTHULHU_VERSION
# #
version = f"Cthulhu screen reader version {cthulhuVersion.version}-{cthulhuVersion.codeName}" versionSuffix = f"-{cthulhuVersion.codeName}" if cthulhuVersion.codeName else ""
version = f"Cthulhu screen reader version {cthulhuVersion.version}{versionSuffix}"
# The revision if built from git; otherwise an empty string # The revision if built from git; otherwise an empty string
# #
+92 -32
View File
@@ -429,41 +429,69 @@ class InputEventManager:
identifier = os.path.basename(value.strip().lower()) identifier = os.path.basename(value.strip().lower())
return identifier == "xterm" return identifier == "xterm"
def _active_x11_window_is_xterm(self) -> bool: @staticmethod
"""Returns True when the active X11 window appears to be XTerm.""" def _safe_call(window: Any, attrName: str) -> Optional[Any]:
"""Returns attrName() for window, or None when unavailable."""
attr = getattr(window, attrName, None)
if not callable(attr):
return None
try:
return attr()
except Exception:
return None
def _log_active_x11_window_for_xterm_check(self, window: Any) -> None:
"""Logs X11 window details used for XTerm pass-through decisions."""
classGroup = self._safe_call(window, "get_class_group")
classGroupName = None
classGroupResClass = None
if classGroup is not None:
classGroupName = self._safe_call(classGroup, "get_name")
classGroupResClass = self._safe_call(classGroup, "get_res_class")
tokens = [
"INPUT EVENT MANAGER: Active X11 window for XTerm check:",
"name",
self._safe_call(window, "get_name"),
"class group",
self._safe_call(window, "get_class_group_name") or classGroupName,
"class group res class",
classGroupResClass,
"class instance",
self._safe_call(window, "get_class_instance_name"),
"pid",
self._safe_call(window, "get_pid"),
]
debug.print_tokens(debug.LEVEL_INFO, tokens, True)
def _active_x11_window_xterm_match(self) -> Optional[bool]:
"""Returns whether the active X11 window is XTerm, or None when unknown."""
window = self._get_active_x11_window() window = self._get_active_x11_window()
if window is None: if window is None:
return False msg = "INPUT EVENT MANAGER: XTerm matcher cannot identify active X11 window."
debug.print_message(debug.LEVEL_INFO, msg, True)
return None
self._log_active_x11_window_for_xterm_check(window)
for attrName in ("get_class_group_name", "get_class_instance_name", "get_name"): for attrName in ("get_class_group_name", "get_class_instance_name", "get_name"):
attr = getattr(window, attrName, None) if self._identifier_is_xterm(self._safe_call(window, attrName)):
if not callable(attr): return True
continue
try:
if self._identifier_is_xterm(attr()):
return True
except Exception:
continue
getClassGroup = getattr(window, "get_class_group", None) classGroup = self._safe_call(window, "get_class_group")
if callable(getClassGroup): if classGroup is not None:
try:
classGroup = getClassGroup()
except Exception:
classGroup = None
for attrName in ("get_name", "get_res_class"): for attrName in ("get_name", "get_res_class"):
attr = getattr(classGroup, attrName, None) if self._identifier_is_xterm(self._safe_call(classGroup, attrName)):
if not callable(attr): return True
continue
try:
if self._identifier_is_xterm(attr()):
return True
except Exception:
continue
pid = self._get_active_x11_window_pid() try:
pid = int(window.get_pid())
except Exception:
pid = -1
if pid < 1: if pid < 1:
return False return False
@@ -475,6 +503,11 @@ class InputEventManager:
return self._identifier_is_xterm(executable) return self._identifier_is_xterm(executable)
def _active_x11_window_is_xterm(self) -> bool:
"""Returns True when the active X11 window appears to be XTerm."""
return self._active_x11_window_xterm_match() is True
def _find_active_x11_atspi_window(self) -> Optional[Atspi.Accessible]: def _find_active_x11_atspi_window(self) -> Optional[Atspi.Accessible]:
"""Returns the focused AT-SPI window for the active X11 PID, if possible.""" """Returns the focused AT-SPI window for the active X11 PID, if possible."""
@@ -589,8 +622,22 @@ class InputEventManager:
"""Returns True when XTerm is active and Cthulhu lacks matching AT-SPI context.""" """Returns True when XTerm is active and Cthulhu lacks matching AT-SPI context."""
if pendingFocus is not None: if pendingFocus is not None:
msg = "INPUT EVENT MANAGER: XTerm matcher false; pending focus exists."
debug.print_message(debug.LEVEL_INFO, msg, True)
return False return False
return self._active_x11_window_is_xterm() match = self._active_x11_window_xterm_match()
tokens = ["INPUT EVENT MANAGER: XTerm matcher returned", match]
debug.print_tokens(debug.LEVEL_INFO, tokens, True)
if match is True:
return True
if match is None and self._scriptWithSuspendedGrabsForXterm is not None:
msg = (
"INPUT EVENT MANAGER: Keeping XTerm key-grab suspension; "
"active X11 window is temporarily unknown."
)
debug.print_message(debug.LEVEL_INFO, msg, True)
return True
return False
def _suspend_key_grabs_for_xterm(self) -> None: def _suspend_key_grabs_for_xterm(self) -> None:
"""Suspends active-script key grabs so XTerm/Fenrir can receive them.""" """Suspends active-script key grabs so XTerm/Fenrir can receive them."""
@@ -606,8 +653,11 @@ class InputEventManager:
if not callable(removeGrabs): if not callable(removeGrabs):
return return
msg = "INPUT EVENT MANAGER: Removing active script key grabs while XTerm is focused." tokens = [
debug.print_message(debug.LEVEL_INFO, msg, True) "INPUT EVENT MANAGER: Removing active script key grabs while XTerm is focused:",
script,
]
debug.print_tokens(debug.LEVEL_INFO, tokens, True)
removeGrabs() removeGrabs()
self._scriptWithSuspendedGrabsForXterm = script self._scriptWithSuspendedGrabsForXterm = script
@@ -619,15 +669,25 @@ class InputEventManager:
if script is None: if script is None:
return return
if script is not script_manager.get_manager().get_active_script(): activeScript = script_manager.get_manager().get_active_script()
if script is not activeScript:
tokens = [
"INPUT EVENT MANAGER: Not restoring XTerm-suspended key grabs; active script changed:",
script,
activeScript,
]
debug.print_tokens(debug.LEVEL_INFO, tokens, True)
return return
addGrabs = getattr(script, "addKeyGrabs", None) addGrabs = getattr(script, "addKeyGrabs", None)
if not callable(addGrabs): if not callable(addGrabs):
return return
msg = "INPUT EVENT MANAGER: Restoring active script key grabs after leaving XTerm." tokens = [
debug.print_message(debug.LEVEL_INFO, msg, True) "INPUT EVENT MANAGER: Restoring active script key grabs after leaving XTerm:",
script,
]
debug.print_tokens(debug.LEVEL_INFO, tokens, True)
addGrabs() addGrabs()
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments
+2 -1
View File
@@ -35,7 +35,8 @@ class DisplayVersion(Plugin):
def _get_version_string(self): def _get_version_string(self):
"""Generate the full version string with AT-SPI and session information.""" """Generate the full version string with AT-SPI and session information."""
msg = f'Cthulhu screen reader version {cthulhuVersion.version}-{cthulhuVersion.codeName}' version_suffix = f'-{cthulhuVersion.codeName}' if cthulhuVersion.codeName else ''
msg = f'Cthulhu screen reader version {cthulhuVersion.version}{version_suffix}'
if cthulhu_platform.revision: if cthulhu_platform.revision:
msg += f' revision {cthulhu_platform.revision}' msg += f' revision {cthulhu_platform.revision}'
@@ -173,6 +173,97 @@ class InputEventManagerX11FocusRegressionTests(unittest.TestCase):
findActiveWindow.assert_not_called() findActiveWindow.assert_not_called()
keyboardEvent.process.assert_not_called() keyboardEvent.process.assert_not_called()
def test_lxterminal_does_not_pass_through_as_xterm(self):
manager = input_event_manager.InputEventManager()
window = mock.Mock()
window.get_class_group_name.return_value = "lxterminal"
window.get_class_instance_name.return_value = "lxterminal"
window.get_name.return_value = "LXTerminal"
window.get_class_group.return_value = None
window.get_pid.return_value = -1
with (
mock.patch.object(manager, "_get_active_x11_window", return_value=window),
mock.patch.object(input_event_manager.debug, "print_message"),
mock.patch.object(input_event_manager.debug, "print_tokens"),
):
result = manager._should_pass_through_for_active_xterm(None, None, None)
self.assertFalse(result)
def test_xterm_pass_through_stays_active_when_window_lookup_temporarily_fails(self):
manager = input_event_manager.InputEventManager()
focusManager = mock.Mock()
focusManager.get_active_window.return_value = None
focusManager.get_locus_of_focus.return_value = None
scriptManager = mock.Mock()
activeScript = mock.Mock(app=None)
scriptManager.get_active_script.return_value = activeScript
keyboardEvent = mock.Mock()
manager._scriptWithSuspendedGrabsForXterm = activeScript
with (
mock.patch.object(input_event_manager.cthulhu_state, "capturingKeys", False),
mock.patch.object(input_event_manager.cthulhu_state, "pendingSelfHostedFocus", None),
mock.patch.object(input_event_manager.focus_manager, "get_manager", return_value=focusManager),
mock.patch.object(input_event_manager.script_manager, "get_manager", return_value=scriptManager),
mock.patch.object(input_event_manager.input_event, "KeyboardEvent", return_value=keyboardEvent),
mock.patch.object(manager, "_active_x11_window_xterm_match", return_value=None),
mock.patch.object(input_event_manager.debug, "print_message"),
mock.patch.object(input_event_manager.debug, "print_tokens"),
):
result = manager.process_keyboard_event(
mock.Mock(),
True,
90,
65438,
0,
"KP_Insert",
)
self.assertFalse(result)
self.assertIs(manager._scriptWithSuspendedGrabsForXterm, activeScript)
activeScript.addKeyGrabs.assert_not_called()
keyboardEvent.process.assert_not_called()
def test_xterm_grabs_restore_when_active_window_is_positively_not_xterm(self):
manager = input_event_manager.InputEventManager()
focusManager = mock.Mock()
focusManager.get_active_window.return_value = None
focusManager.get_locus_of_focus.return_value = None
scriptManager = mock.Mock()
activeScript = mock.Mock(app=None)
scriptManager.get_active_script.return_value = activeScript
keyboardEvent = mock.Mock()
keyboardEvent.is_modifier_key.return_value = False
manager._scriptWithSuspendedGrabsForXterm = activeScript
with (
mock.patch.object(input_event_manager.cthulhu_state, "capturingKeys", False),
mock.patch.object(input_event_manager.cthulhu_state, "pendingSelfHostedFocus", None),
mock.patch.object(input_event_manager.focus_manager, "get_manager", return_value=focusManager),
mock.patch.object(input_event_manager.script_manager, "get_manager", return_value=scriptManager),
mock.patch.object(input_event_manager.input_event, "KeyboardEvent", return_value=keyboardEvent),
mock.patch.object(manager, "_active_x11_window_xterm_match", return_value=False),
mock.patch.object(input_event_manager.AXUtilities, "can_be_active_window", return_value=True),
mock.patch.object(manager, "last_event_was_keyboard", return_value=False),
mock.patch.object(input_event_manager.debug, "print_message"),
mock.patch.object(input_event_manager.debug, "print_tokens"),
):
result = manager.process_keyboard_event(
mock.Mock(),
True,
36,
65293,
0,
"Return",
)
self.assertTrue(result)
self.assertIsNone(manager._scriptWithSuspendedGrabsForXterm)
activeScript.addKeyGrabs.assert_called_once_with()
keyboardEvent.process.assert_called_once_with()
def test_finds_focused_atspi_window_for_active_x11_pid(self): def test_finds_focused_atspi_window_for_active_x11_pid(self):
manager = input_event_manager.InputEventManager() manager = input_event_manager.InputEventManager()
app = object() app = object()