Compare commits

..

No commits in common. "master" and "v0.3" have entirely different histories.
master ... v0.3

689 changed files with 10773 additions and 14629 deletions

View File

@ -29,8 +29,6 @@ Cthulhu has the following dependencies:
* liblouis - Liblouis (<http://liblouis.org/>) support for contracted braille (optional)
* py-setproctitle - Python library to set the process title (optional)
* gstreamer-1.0 - GStreamer - Streaming media framework (optional)
* socat - Used for self-voicing functionality.
* libpeas - For the plugin system.
You are strongly encouraged to also have the latest stable versions
of AT-SPI2 and ATK.

1
TODO
View File

@ -1,3 +1,4 @@
- Add Simple Cthulhu Plugin System as native code for Cthulhu
- Merge in sleep mode.
- Add in ocrdesktop code so that Cthulhu has native ocr support.
- Get rid of build systems. My hope is git clone and run so long as requirements are satisfied.

View File

@ -1,7 +1,4 @@
m4_define([get_version], [m4_esyscmd_s([
grep "^version = " src/cthulhu/cthulhuVersion.py | sed 's/version = "\(.*\)"/\1/'
])])
m4_define([cthulhu_version], [get_version])
m4_define([cthulhu_version], [0.3])
m4_define(pygobject_required_version, 3.18)
m4_define(atspi_required_version, 2.48)
@ -31,7 +28,6 @@ PKG_CHECK_MODULES([PYGOBJECT], [pygobject-3.0 >= pygobject_required_version])
PKG_CHECK_MODULES([ATSPI2], [atspi-2 >= atspi_required_version])
PKG_CHECK_MODULES([ATKBRIDGE], [atk-bridge-2.0 >= atkbridge_required_version])
PKG_CHECK_MODULES([GSTREAMER], [gstreamer-1.0], [gstreamer="yes"], [gstreamer="no"])
PKG_CHECK_MODULES([LIBPEAS], [libpeas-1.0])
dnl Needed programs
AC_PROG_INSTALL
@ -46,7 +42,6 @@ AM_PATH_PYTHON(3.3)
AM_CHECK_PYMOD(gi,,,[AC_MSG_ERROR(Could not find python module: gi)])
AM_CHECK_PYMOD(json,,,[AC_MSG_ERROR(Could not find python module: json)])
AM_CHECK_PYMOD(pluggy,,[pluggy_available="yes"],[pluggy_available="no"])
AM_CHECK_PYMOD(brlapi,,[brlapi_available="yes"],[brlapi_available="no"])
AM_CHECK_PYMOD(speechd,,[speechd_available="yes"],[speechd_available="no"])
AC_ARG_WITH([liblouis],
@ -111,7 +106,6 @@ src/cthulhu/scripts/apps/SeaMonkey/Makefile
src/cthulhu/scripts/apps/smuxi-frontend-gnome/Makefile
src/cthulhu/scripts/apps/Thunderbird/Makefile
src/cthulhu/scripts/apps/xfwm4/Makefile
src/cthulhu/scripts/sleepmode/Makefile
src/cthulhu/scripts/switcher/Makefile
src/cthulhu/scripts/terminal/Makefile
src/cthulhu/scripts/web/Makefile
@ -127,11 +121,15 @@ src/cthulhu/scripts/toolkits/gtk/Makefile
src/cthulhu/plugins/Makefile
src/cthulhu/plugins/ByeCthulhu/Makefile
src/cthulhu/plugins/HelloCthulhu/Makefile
src/cthulhu/plugins/PluginManager/Makefile
src/cthulhu/plugins/Clipboard/Makefile
src/cthulhu/plugins/DisplayVersion/Makefile
src/cthulhu/plugins/hello_world/Makefile
src/cthulhu/plugins/self_voice/Makefile
src/cthulhu/plugins/SimplePluginSystem/Makefile
src/cthulhu/plugins/HelloWorld/Makefile
src/cthulhu/plugins/CapsLockHack/Makefile
src/cthulhu/plugins/SelfVoice/Makefile
src/cthulhu/plugins/Date/Makefile
src/cthulhu/plugins/Time/Makefile
src/cthulhu/plugins/MouseReview/Makefile
src/cthulhu/plugins/ClassicPreferences/Makefile
src/cthulhu/backends/Makefile
src/cthulhu/cthulhu_bin.py
src/cthulhu/cthulhu_i18n.py
@ -159,12 +157,7 @@ echo
echo "NOTE: Sound support requires gstreamer-1.0."
fi
if test "$have_libpeas" = "no"; then
AC_MSG_ERROR([libpeas-1.0 >= 1.20 is required])
fi
echo
echo Use pluggy: $pluggy_available
echo Use speech-dispatcher: $speechd_available
echo Use brltty: $brlapi_available
echo Use liblouis: $louis_available

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<schemalist gettext-domain="slint">
<schema id="toggle.screenreader" path="/toggle/screenreader/">
<key name="binding" type="s">
<default>'&lt;Shift&gt;&lt;Alt&gt;v'</default>
<summary>Keybinding</summary>
<description>Keybinding associated to toggle screen reader.</description>
</key>
<key name="action" type="s">
<default>'/opt/I38/scripts/toggle_screenreader.sh 1>/dev/null'</default>
<summary>Command</summary>
<description>Command to toggle the screen reader between orca and cthulhu.</description>
</key>
<key name="name" type="s">
<default>'Toggle screenreader'</default>
<summary>Name</summary>
<description>Description associated to toggle screen reader.</description>
</key>
</schema>
</schemalist>

View File

@ -1,7 +1,7 @@
# Maintainer: Storm Dragon <storm_dragon@stormux.org>
pkgname=cthulhu
pkgver=0.4
pkgver=0.3
pkgrel=1
pkgdesc="Screen reader for individuals who are blind or visually impaired forked from Orca"
url="https://git.stormux.org/storm/cthulhu"
@ -44,11 +44,6 @@ prepare() {
NOCONFIGURE=1 ./autogen.sh
}
pkgver() {
cd cthulhu
grep "^version = " src/cthulhu/cthulhuVersion.py | sed 's/version = "\(.*\)"/\1/'
}
build() {
cd cthulhu
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var

View File

@ -1,98 +0,0 @@
#!/bin/bash
# Slackware build script for cthulhu
# Created based on PKGBUILD from Storm Dragon <storm_dragon@stormux.org>
cd $(dirname $0) ; CWD=$(pwd)
PRGNAM=cthulhu
VERSION=${VERSION:-0.4}
BUILD=${BUILD:-1}
TAG=storm
PKGTYPE=txz
export PYTHON=/usr/bin/python3.11
if [ -z "$ARCH" ]; then
case "$( uname -m )" in
i?86) ARCH=i586 ;;
arm*) ARCH=arm ;;
*) ARCH=$( uname -m ) ;;
esac
fi
# 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
# could be useful to other scripts.
if [ ! -z "${PRINT_PACKAGE_NAME}" ]; then
echo "$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE"
exit 0
fi
TMP=$CWD
PKG=$TMP/package-$PRGNAM
OUTPUT=$CWD
if [ "$ARCH" = "i586" ]; then
SLKCFLAGS="-O2 -march=i586 -mtune=i686"
LIBDIRSUFFIX=""
elif [ "$ARCH" = "i686" ]; then
SLKCFLAGS="-O2 -march=i686 -mtune=i686"
LIBDIRSUFFIX=""
elif [ "$ARCH" = "x86_64" ]; then
SLKCFLAGS="-O2 -fPIC"
LIBDIRSUFFIX="64"
else
SLKCFLAGS="-O2"
LIBDIRSUFFIX=""
fi
set -e
rm -rf $PKG
mkdir -p $TMP $PKG $OUTPUT
cd $TMP
rm -rf $PRGNAM-$VERSION
git clone https://git.stormux.org/storm/cthulhu.git $PRGNAM-$VERSION
cd $PRGNAM-$VERSION
chown -R root:root .
find -L . \
\( -perm 777 -o -perm 775 -o -perm 750 -o -perm 711 -o -perm 555 \
-o -perm 511 \) -exec chmod 755 {} \; -o \
\( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \
-o -perm 440 -o -perm 400 \) -exec chmod 644 {} \;
# Prepare the source
NOCONFIGURE=1 ./autogen.sh
CFLAGS="$SLKCFLAGS" \
CXXFLAGS="$SLKCFLAGS" \
./configure \
--prefix=/usr \
--libdir=/usr/lib${LIBDIRSUFFIX} \
--sysconfdir=/etc \
--localstatedir=/var \
--mandir=/usr/man \
--docdir=/usr/doc/$PRGNAM-$VERSION \
--build=$ARCH-slackware-linux
make
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
mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION
cp -a AUTHORS COPYING ChangeLog NEWS README.md \
$PKG/usr/doc/$PRGNAM-$VERSION
cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild
mkdir -p $PKG/install
cat $CWD/slack-desc > $PKG/install/slack-desc
cat $CWD/doinst.sh > $PKG/install/doinst.sh
cd $PKG
# let's avoid a "bad interpreter error
sed "s,#!python3.11,#!/usr/bin/python3.11," usr/bin/cthulhu > dummy
mv dummy usr/bin/cthulhu
chmod 755 usr/bin/cthulhu
/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE

View File

@ -1,28 +0,0 @@
Cthulhu is a screen reader for individuals who are blind or visually impaired,
forked from Orca. It provides access to applications and toolkits that support
the AT-SPI (e.g., the GNOME desktop).
This screen reader helps users navigate their desktop environment and applications
through speech synthesis and braille output.
After installation, you can start Cthulhu through the GNOME desktop environment
or by running 'cthulhu' from the command line.
DEPENDENCIES:
This package requires the following packages, all available from SlackBuilds.org:
- at-spi2-core
- brltty
- gobject-introspection
- gsettings-desktop-schemas
- gstreamer
- gst-plugins-base
- gst-plugins-good
- gtk3
- liblouis
- libpeas
- libwnck3
- python3-atspi
- python3-cairo
- python3-gobject
- python3-setproctitle
- speech-dispatcher

View File

@ -1,10 +0,0 @@
PRGNAM="cthulhu"
VERSION="0.4"
HOMEPAGE="https://git.stormux.org/storm/cthulhu"
DOWNLOAD="https://git.stormux.org/storm/cthulhu.git"
MD5SUM="SKIP"
DOWNLOAD_x86_64=""
MD5SUM_x86_64=""
REQUIRES="at-spi2-core brltty gobject-introspection gsettings-desktop-schemas gstreamer gst-plugins-base gst-plugins-good gtk3 liblouis libpeas libwnck3 python3-atspi python3-cairo python3-gobject python3-setproctitle speech-dispatcher"
MAINTAINER="Storm Dragon"
EMAIL="storm_dragon@stormux.org"

View File

@ -1,98 +0,0 @@
#!/bin/bash
# Slackware build script for cthulhu
# Created based on PKGBUILD from Storm Dragon <storm_dragon@stormux.org>
cd $(dirname $0) ; CWD=$(pwd)
PRGNAM=cthulhu
VERSION=${VERSION:-0.4}
BUILD=${BUILD:-1}
TAG=storm
PKGTYPE=txz
export PYTHON=/usr/bin/python3.11
if [ -z "$ARCH" ]; then
case "$( uname -m )" in
i?86) ARCH=i586 ;;
arm*) ARCH=arm ;;
*) ARCH=$( uname -m ) ;;
esac
fi
# 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
# could be useful to other scripts.
if [ ! -z "${PRINT_PACKAGE_NAME}" ]; then
echo "$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE"
exit 0
fi
TMP=$CWD
PKG=$TMP/package-$PRGNAM
OUTPUT=$CWD
if [ "$ARCH" = "i586" ]; then
SLKCFLAGS="-O2 -march=i586 -mtune=i686"
LIBDIRSUFFIX=""
elif [ "$ARCH" = "i686" ]; then
SLKCFLAGS="-O2 -march=i686 -mtune=i686"
LIBDIRSUFFIX=""
elif [ "$ARCH" = "x86_64" ]; then
SLKCFLAGS="-O2 -fPIC"
LIBDIRSUFFIX="64"
else
SLKCFLAGS="-O2"
LIBDIRSUFFIX=""
fi
set -e
rm -rf $PKG
mkdir -p $TMP $PKG $OUTPUT
cd $TMP
rm -rf $PRGNAM-$VERSION
git clone https://git.stormux.org/storm/cthulhu.git $PRGNAM-$VERSION
cd $PRGNAM-$VERSION
chown -R root:root .
find -L . \
\( -perm 777 -o -perm 775 -o -perm 750 -o -perm 711 -o -perm 555 \
-o -perm 511 \) -exec chmod 755 {} \; -o \
\( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \
-o -perm 440 -o -perm 400 \) -exec chmod 644 {} \;
# Prepare the source
NOCONFIGURE=1 ./autogen.sh
CFLAGS="$SLKCFLAGS" \
CXXFLAGS="$SLKCFLAGS" \
./configure \
--prefix=/usr \
--libdir=/usr/lib${LIBDIRSUFFIX} \
--sysconfdir=/etc \
--localstatedir=/var \
--mandir=/usr/man \
--docdir=/usr/doc/$PRGNAM-$VERSION \
--build=$ARCH-slackware-linux
make
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
mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION
cp -a AUTHORS COPYING ChangeLog NEWS README.md \
$PKG/usr/doc/$PRGNAM-$VERSION
cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild
mkdir -p $PKG/install
cat $CWD/slack-desc > $PKG/install/slack-desc
cat $CWD/doinst.sh > $PKG/install/doinst.sh
cd $PKG
# let's avoid a "bad interpreter error
sed "s,#!python3.11,#!/usr/bin/python3.11," usr/bin/cthulhu > dummy
mv dummy usr/bin/cthulhu
chmod 755 usr/bin/cthulhu
/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE

View File

@ -1,9 +0,0 @@
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

View File

@ -1,19 +0,0 @@
# 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 (e.g., the GNOME desktop).
cthulhu:
cthulhu: Homepage: https://git.stormux.org/storm/cthulhu
cthulhu:
cthulhu:
cthulhu:
cthulhu:

View File

@ -1,27 +1,10 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "cthulhu"
dynamic = ["version"]
description = "Fork of the Orca screen reader based on gnome-45"
readme = "README.md"
requires-python = ">=3.3"
license = { text = "LGPL-2.1-or-later" }
dependencies = [
"pygobject>=3.18",
"python-atspi>=2.48",
"brlapi; extra == 'braille'",
"python-speechd; extra == 'speech'",
"louis; extra == 'braille'"
# Ruff linter.
#
# Repository: https://github.com/astral-sh/ruff
#
# Documentation: https://beta.ruff.rs/docs/
[tool.ruff]
line-length = 100 # same as rustfmt
ignore = [
"E402", # Module level import not at top of file
]
[project.scripts]
cthulhu = "cthulhu.cthulhu:main"
[tool.hatch.version]
path = "src/cthulhu/__init__.py"
[tool.hatch.build.targets.wheel]
packages = ["src/cthulhu"]

View File

@ -1,294 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
import argparse
import gi
gi.require_version("Atspi", "2.0")
from gi.repository import Atspi
import os
import signal
import subprocess
import sys
import time
def setup_paths():
"""Configure paths for both installed and source directory execution."""
currentDir = os.path.dirname(os.path.abspath(__file__))
# Check if running from source
if os.path.exists(os.path.join(currentDir, 'plugins')):
# Running from source directory
sys.path.insert(0, os.path.dirname(currentDir))
pythondir = currentDir
else:
# Running installed
sys.prefix = '/usr'
pythondir = os.path.join(sys.prefix, 'lib', f'python{sys.version_info.major}.{sys.version_info.minor}', 'site-packages')
sys.path.insert(1, pythondir)
# Set environment variables for resource paths
if 'CTHULHU_DATA_DIR' not in os.environ:
if os.path.exists(os.path.join(currentDir, 'plugins')):
os.environ['CTHULHU_DATA_DIR'] = currentDir
else:
os.environ['CTHULHU_DATA_DIR'] = os.path.join(sys.prefix, 'share', 'cthulhu')
# Set up paths before importing Cthulhu modules
setup_paths()
from cthulhu import debug
from cthulhu import messages
from cthulhu import settings
from cthulhu.ax_object import AXObject
from cthulhu.ax_utilities import AXUtilities
from cthulhu.cthulhu_platform import version
class ListApps(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
desktop = AXUtilities.get_desktop()
for app in AXObject.iter_children(desktop):
pid = AXObject.get_process_id(app)
try:
name = Atspi.Accessible.get_name(app) or "(none)"
except Exception:
name = "[DEAD]"
try:
cmdline = subprocess.getoutput('cat /proc/%s/cmdline' % pid)
except Exception:
cmdline = '(exception encountered)'
else:
cmdline = cmdline.replace('\x00', ' ')
print(time.strftime('%H:%M:%S', time.localtime()),
' pid: %5s %-25s %s' % (pid, name, cmdline))
parser.exit()
class Settings(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
settingsDict = getattr(namespace, 'settings', {})
invalid = getattr(namespace, 'invalid', [])
for value in values.split(','):
item = str.title(value).replace('-', '')
try:
test = 'enable%s' % item
eval('settings.%s' % test)
except AttributeError:
try:
test = 'show%s' % item
eval('settings.%s' % test)
except AttributeError:
invalid.append(value)
continue
settingsDict[test] = self.const
setattr(namespace, 'settings', settingsDict)
setattr(namespace, 'invalid', invalid)
class HelpFormatter(argparse.HelpFormatter):
def __init__(self, prog, indent_increment=2, max_help_position=32,
width=None):
super().__init__(prog, indent_increment, max_help_position, width)
def add_usage(self, usage, actions, groups, prefix=None):
super().add_usage(usage, actions, groups, messages.CLI_USAGE)
class Parser(argparse.ArgumentParser):
def __init__(self, *args, **kwargs):
super(Parser, self).__init__(
epilog=messages.CLI_EPILOG, formatter_class=HelpFormatter, add_help=False)
self.add_argument(
"-h", "--help", action="help", help=messages.CLI_HELP)
self.add_argument(
"-v", "--version", action="version", version=version, help=messages.CLI_VERSION)
self.add_argument(
"-r", "--replace", action="store_true", help=messages.CLI_REPLACE)
self.add_argument(
"-s", "--setup", action="store_true", help=messages.CLI_GUI_SETUP)
self.add_argument(
"-l", "--list-apps", action=ListApps, nargs=0,
help=messages.CLI_LIST_APPS)
self.add_argument(
"-e", "--enable", action=Settings, const=True,
help=messages.CLI_ENABLE_OPTION, metavar=messages.CLI_OPTION)
self.add_argument(
"-d", "--disable", action=Settings, const=False,
help=messages.CLI_DISABLE_OPTION, metavar=messages.CLI_OPTION)
self.add_argument(
"-p", "--profile", action="store",
help=messages.CLI_LOAD_PROFILE, metavar=messages.CLI_PROFILE_NAME)
self.add_argument(
"-u", "--user-prefs", action="store",
help=messages.CLI_LOAD_PREFS, metavar=messages.CLI_PREFS_DIR)
self.add_argument(
"--debug-file", action="store",
help=messages.CLI_DEBUG_FILE, metavar=messages.CLI_DEBUG_FILE_NAME)
self.add_argument(
"--debug", action="store_true", help=messages.CLI_ENABLE_DEBUG)
self._optionals.title = messages.CLI_OPTIONAL_ARGUMENTS
def parse_known_args(self, *args, **kwargs):
opts, invalid = super(Parser, self).parse_known_args(*args, **kwargs)
try:
invalid.extend(opts.invalid)
except Exception:
pass
if invalid:
print((messages.CLI_INVALID_OPTIONS + " ".join(invalid)))
if opts.debug_file:
opts.debug = True
elif opts.debug:
opts.debug_file = time.strftime('debug-%Y-%m-%d-%H:%M:%S.out')
return opts, invalid
def setProcessName(name):
"""Attempts to set the process name to the specified name."""
sys.argv[0] = name
try:
from setproctitle import setproctitle
except ImportError:
pass
else:
setproctitle(name)
return True
try:
from ctypes import cdll, byref, create_string_buffer
libc = cdll.LoadLibrary('libc.so.6')
stringBuffer = create_string_buffer(len(name) + 1)
stringBuffer.value = bytes(name, 'UTF-8')
libc.prctl(15, byref(stringBuffer), 0, 0, 0)
return True
except Exception:
pass
return False
def inGraphicalDesktop():
"""Returns True if we are in a graphical desktop."""
# TODO - JD: Make this desktop environment agnostic
try:
import gi
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk
display = Gdk.Display.get_default()
except Exception:
return False
return display is not None
def otherCthulhus():
"""Returns the pid of any other instances of Cthulhu owned by this user."""
openFile = subprocess.Popen('pgrep -u %s -x cthulhu' % os.getuid(),
shell=True,
stdout=subprocess.PIPE).stdout
pids = openFile.read()
openFile.close()
cthulhus = [int(p) for p in pids.split()]
pid = os.getpid()
return [p for p in cthulhus if p != pid]
def cleanup(sigval):
"""Tries to clean up any other running Cthulhu instances owned by this user."""
cthulhusToKill = otherCthulhus()
debug.printMessage(debug.LEVEL_INFO, "INFO: Cleaning up these PIDs: %s" % cthulhusToKill)
def onTimeout(signum, frame):
cthulhusToKill = otherCthulhus()
debug.printMessage(debug.LEVEL_INFO, "INFO: Timeout cleaning up: %s" % cthulhusToKill)
for pid in cthulhusToKill:
os.kill(pid, signal.SIGKILL)
for pid in cthulhusToKill:
os.kill(pid, sigval)
signal.signal(signal.SIGALRM, onTimeout)
signal.alarm(2)
while otherCthulhus():
time.sleep(0.5)
def main():
setProcessName('cthulhu')
parser = Parser()
args, invalid = parser.parse_known_args()
if args.debug:
debug.debugLevel = debug.LEVEL_ALL
debug.eventDebugLevel = debug.LEVEL_OFF
debug.debugFile = open(args.debug_file, 'w')
if args.replace:
cleanup(signal.SIGKILL)
settingsDict = getattr(args, 'settings', {})
if not inGraphicalDesktop():
print(messages.CLI_NO_DESKTOP_ERROR)
return 1
debug.printMessage(debug.LEVEL_INFO, "INFO: Preparing to launch.", True)
from cthulhu import cthulhu
manager = cthulhu.getSettingsManager()
if not manager:
print(messages.CLI_SETTINGS_MANAGER_ERROR)
return 1
debug.printMessage(debug.LEVEL_INFO, "INFO: About to activate settings manager.", True)
manager.activate(args.user_prefs, settingsDict)
sys.path.insert(0, manager.getPrefsDir())
if args.profile:
try:
manager.setProfile(args.profile)
except Exception:
print(messages.CLI_LOAD_PROFILE_ERROR % args.profile)
manager.setProfile()
if args.setup:
cleanup(signal.SIGKILL)
cthulhu.showPreferencesGUI()
if otherCthulhus():
print(messages.CLI_OTHER_CTHULHUS_ERROR)
return 1
debug.printMessage(debug.LEVEL_INFO, "INFO: About to launch Cthulhu.", True)
return cthulhu.main()
if __name__ == "__main__":
sys.exit(main())

View File

@ -29,7 +29,6 @@ cthulhu_python_PYTHON = \
cmdnames.py \
colornames.py \
common_keyboardmap.py \
cthulhuVersion.py \
date_and_time_presenter.py \
debug.py \
desktop_keyboardmap.py \

View File

@ -1,28 +1,3 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Cthulhu Screen Reader"""
__copyright__ = "Copyright (c) 2005-2006 Sun Microsystems Inc."

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2008 Google Inc.
# Portions Copyright 2007-2008, Sun Microsystems, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -20,9 +18,6 @@
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""ACSS --- Aural CSS.
Class ACSS defines a simple wrapper for holding ACSS voice

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Module for performing accessible actions via a menu"""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Utilities for obtaining objects via the collection interface.
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""
Utilities for obtaining objects via the collection interface.

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2008 Sun Microsystems Inc.
# Copyright 2018-2023 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides support for synthesizing accessible input events."""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Utilities for obtaining information about accessible objects.
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""
Utilities for obtaining information about accessible objects.

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Utilities for obtaining information about containers supporting selection
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""
Utilities for obtaining information about containers supporting selection.

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Utilities for performing tasks related to accessibility inspection.
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""
Utilities for performing tasks related to accessibility inspection.

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Utilities for finding all objects that meet a certain criteria.
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""
Utilities for finding all objects that meet a certain criteria.

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Utilities for obtaining role-related information.
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""
Utilities for obtaining role-related information.

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Utilities for obtaining state-related information.
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""
Utilities for obtaining state-related information.

View File

@ -1,25 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca

View File

@ -1,9 +1,8 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2010-2011 Consorcio Fernando de los Rios.
# Author: Juanje Ojeda Croissier <jojeda@emergya.es>
# Author: Javier Hernandez Antunez <jhernandez@emergya.es>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +18,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""JSON backend for Cthulhu settings"""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2008 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides the default implementation for bookmarks in Cthulhu."""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2009 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""A very experimental approach to the refreshable Braille display. This
module treats each line of the display as a sequential set of regions, where

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2009 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Utilities for obtaining braille presentations for objects."""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2012 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Dictionary of abbreviated rolenames for use with braille."""

View File

@ -1,9 +1,8 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2006-2008 Sun Microsystems Inc.
# Copyright 2011 The Cthulhu Team.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +18,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides a graphical braille display, mainly for development tasks."""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2004-2009 Sun Microsystems Inc.
# Copyright 2010-2013 The Cthulhu Team
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Braille translation table names. These have been put in their own module
so that we can present them in the correct language when users change the

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2013-2015 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides an Cthulhu-controlled caret for text content."""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2010-2011 The Cthulhu Team
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Implements generic chat support."""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2004-2008 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides getCharacterName that maps punctuation marks and other
individual characters into localized words."""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2004-2009 Sun Microsystems Inc.
# Copyright 2010-2013 The Cthulhu Team
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Command names which Cthulhu presents in speech and/or braille. These
have been put in their own module so that we can present them in
@ -460,9 +455,6 @@ TOGGLE_SPEECH = _("Toggle the silencing of speech")
# This string describes that command.
TOGGLE_SPEECH_VERBOSITY = _("Toggle speech verbosity level")
# Translators: this string is associated with the keyboard shortcut to toggle sleepmode
TOGGLE_SLEEP_MODE = _("Toggle sleep mode")
# Translators: this string is associated with the keyboard shortcut to quit
# Cthulhu.
QUIT_CTHULHU = _("Quit the screen reader")

View File

@ -1,9 +1,8 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2014 Igalia, S.L.
#
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +18,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
__id__ = "$Id$"
__version__ = "$Revision$"

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2010-2011 The Cthulhu Team
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
""" A list of common keybindings and unbound keys
pulled out from default.py: getKeyBindings()
@ -57,9 +51,6 @@ keymap = (
("BackSpace", defaultModifierMask, CTHULHU_MODIFIER_MASK,
"bypassNextCommandHandler"),
("q", defaultModifierMask, CTHULHU_CTRL_ALT_MODIFIER_MASK, CTHULHU_SHIFT_MODIFIER_MASK,
"toggleSleepModeHandler"),
("q", defaultModifierMask, CTHULHU_MODIFIER_MASK,
"shutdownHandler"),

View File

@ -1,9 +1,8 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2004-2009 Sun Microsystems Inc.
# Copyright 2010-2011 The Cthulhu Team
# Copyright 2012 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +18,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""The main module for the Cthulhu screen reader."""
@ -34,108 +30,6 @@ __copyright__ = "Copyright (c) 2004-2009 Sun Microsystems Inc." \
__license__ = "LGPL"
import faulthandler
class APIHelper:
"""Helper class for plugin API interactions, including keybindings."""
def __init__(self, app):
"""Initialize the APIHelper.
Arguments:
- app: the Cthulhu application
"""
self.app = app
self._gestureBindings = {}
def registerGestureByString(self, function, name, gestureString,
inputEventType='default', normalizer='cthulhu',
learnModeEnabled=True, contextName=None):
"""Register a gesture by string.
Arguments:
- function: the function to call when the gesture is performed
- name: a human-readable name for this gesture
- gestureString: string representation of the gesture (e.g., 'kb:cthulhu+z')
- inputEventType: the type of input event
- normalizer: the normalizer to use
- learnModeEnabled: whether this should be available in learn mode
- contextName: the context for this gesture (e.g., plugin name)
Returns the binding ID or None if registration failed
"""
if not gestureString.startswith("kb:"):
return None
# Extract the key portion from the gesture string
key = gestureString.split(":", 1)[1]
# Handle Cthulhu modifier specially
if "cthulhu+" in key.lower():
from . import keybindings
key_parts = key.lower().split("+")
# Determine appropriate modifier mask
modifiers = keybindings.CTHULHU_MODIFIER_MASK
# Extract the final key (without modifiers)
final_key = key_parts[-1]
# Check for additional modifiers
if "shift" in key_parts:
modifiers = keybindings.CTHULHU_SHIFT_MODIFIER_MASK
elif "ctrl" in key_parts or "control" in key_parts:
modifiers = keybindings.CTHULHU_CTRL_MODIFIER_MASK
elif "alt" in key_parts:
modifiers = keybindings.CTHULHU_ALT_MODIFIER_MASK
# Create a keybinding handler
class GestureHandler:
def __init__(self, function, description):
self.function = function
self.description = description
def __call__(self, script, inputEvent):
return self.function(script, inputEvent)
handler = GestureHandler(function, name)
# Register the binding with the active script
from . import cthulhu_state
if cthulhu_state.activeScript:
bindings = cthulhu_state.activeScript.getKeyBindings()
binding = keybindings.KeyBinding(
final_key,
keybindings.defaultModifierMask,
modifiers,
handler)
bindings.add(binding)
# Store binding for later reference
if contextName not in self._gestureBindings:
self._gestureBindings[contextName] = []
self._gestureBindings[contextName].append(binding)
return binding
return None
def unregisterShortcut(self, binding, contextName=None):
"""Unregister a previously registered shortcut.
Arguments:
- binding: the binding to unregister
- contextName: the context for this gesture
"""
# Remove from script's keybindings
from . import cthulhu_state
if cthulhu_state.activeScript:
bindings = cthulhu_state.activeScript.getKeyBindings()
bindings.remove(binding)
# Remove from our tracking
if contextName in self._gestureBindings:
if binding in self._gestureBindings[contextName]:
self._gestureBindings[contextName].remove(binding)
import gi
import importlib
import os
@ -176,7 +70,7 @@ from .ax_object import AXObject
from .ax_utilities import AXUtilities
from .input_event import BrailleEvent
from . import cmdnames
from . import plugin_system_manager # This will now be your pluggy-based implementation
from . import plugin_system_manager
from . import guilabels
from . import acss
from . import text_attribute_names
@ -636,55 +530,6 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False):
return True
def _showPreferencesUI(script, prefs):
if cthulhu_state.cthulhuOS:
cthulhu_state.cthulhuOS.showGUI()
return
try:
module = importlib.import_module('.cthulhu_gui_prefs', 'cthulhu')
except Exception:
debug.printException(debug.LEVEL_SEVERE)
return
uiFile = os.path.join(cthulhu_platform.datadir,
cthulhu_platform.package,
"ui",
"cthulhu-setup.ui")
cthulhu_state.cthulhuOS = module.CthulhuSetupGUI(uiFile, "cthulhuSetupWindow", prefs)
cthulhu_state.cthulhuOS.init(script)
cthulhu_state.cthulhuOS.showGUI()
def showAppPreferencesGUI(script=None, inputEvent=None):
"""Displays the user interface to configure the settings for a
specific applications within Cthulhu and set up those app-specific
user preferences using a GUI.
Returns True to indicate the input event has been consumed.
"""
prefs = {}
for key in settings.userCustomizableSettings:
prefs[key] = _settingsManager.getSetting(key)
script = script or cthulhu_state.activeScript
_showPreferencesUI(script, prefs)
return True
def showPreferencesGUI(script=None, inputEvent=None):
"""Displays the user interface to configure Cthulhu and set up
user preferences using a GUI.
Returns True to indicate the input event has been consumed.
"""
prefs = _settingsManager.getGeneralSettings(_settingsManager.profile)
script = _scriptManager.getDefaultScript()
_showPreferencesUI(script, prefs)
return True
def addKeyGrab(binding):
""" Add a key grab for the given key binding."""
@ -1022,6 +867,7 @@ class Cthulhu(GObject.Object):
GObject.Object.__init__(self)
# add members
self.resourceManager = resource_manager.ResourceManager(self)
self.APIHelper = plugin_system_manager.APIHelper(self)
self.eventManager = _eventManager
self.settingsManager = _settingsManager
self.scriptManager = _scriptManager
@ -1029,11 +875,8 @@ class Cthulhu(GObject.Object):
self.dynamicApiManager = dynamic_api_manager.DynamicApiManager(self)
self.translationManager = translation_manager.TranslationManager(self)
self.debugManager = debug
self.APIHelper = APIHelper(self)
self.createCompatAPI()
self.pluginSystemManager = plugin_system_manager.PluginSystemManager(self)
# Scan for available plugins at startup
self.pluginSystemManager.rescanPlugins()
def getAPIHelper(self):
return self.APIHelper
def getPluginSystemManager(self):
@ -1091,7 +934,6 @@ class Cthulhu(GObject.Object):
# cthulhu lets say, special compat handling....
self.getDynamicApiManager().registerAPI('EmitRegionChanged', emitRegionChanged)
self.getDynamicApiManager().registerAPI('LoadUserSettings', loadUserSettings)
self.getDynamicApiManager().registerAPI('APIHelper', self.APIHelper)
cthulhuApp = Cthulhu()

View File

@ -1,27 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
version = "2025.04.04"
codeName = "testing"

View File

@ -1,9 +1,9 @@
#!@PYTHON@
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Cthulhu
#
# Copyright 2010-2012 The Cthulhu Team
# Copyright 2012 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +19,13 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
__id__ = "$Id$"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2010-2012 The Cthulhu Team" \
"Copyright (c) 2012 Igalia, S.L."
__license__ = "LGPL"
import argparse
import gi

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2009 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Displays a GUI for the user to quit Cthulhu."""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2009 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Displays a GUI for the Cthulhu Find window"""

View File

@ -1,9 +1,8 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2012 Igalia, S.L.
#
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +18,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Displays a GUI for Cthulhu navigation list dialogs"""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2009 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Displays a GUI for the user to set Cthulhu preferences."""
@ -193,7 +187,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
# ***** Key Bindings treeview initialization *****
self.keyBindView = self.get_widget("keyBindingsTreeview")
if self.keyBindView.get_columns():
for column in self.keyBindView.get_columns():
self.keyBindView.remove_column(column)
@ -337,7 +331,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
column.set_resizable(True)
column.set_sort_column_id(EDITABLE)
self.keyBindView.append_column(column)
# Populates the treeview with all the keybindings:
#
self._populateKeyBindings()
@ -582,7 +576,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self.get_widget("rateScale").set_value(rate)
else:
self.get_widget("rateScale").set_value(50.0)
pitch = self._getPitchForVoiceType(voiceType)
if pitch is not None:
self.get_widget("pitchScale").set_value(pitch)
@ -1150,7 +1144,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
grid = self.get_widget('flashMessageDurationGrid')
grid.set_sensitive(not checkbox.get_active())
self.prefsDict["flashIsPersistent"] = checkbox.get_active()
def textAttributeSpokenToggled(self, cell, path, model):
"""The user has toggled the state of one of the text attribute
checkboxes to be spoken. Update our model to reflect this, then
@ -1596,7 +1590,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
elif dateFormat == messages.DATE_FORMAT_ABBREVIATED_YMD:
indexdate = DATE_FORMAT_ABBREVIATED_YMD
combobox2.set_active (indexdate)
combobox3 = self.get_widget("timeFormatCombo")
self.populateComboBox(combobox3,
[sdtime(messages.TIME_FORMAT_LOCALE, ltime()),
@ -1757,7 +1751,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
prefs["enableEchoByWord"])
self.get_widget("enableEchoBySentenceCheckButton").set_active( \
prefs["enableEchoBySentence"])
# Text attributes pane.
#
self._createTextAttributesTreeView()
@ -1785,7 +1779,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self.get_widget("generalDesktopButton").set_active(True)
else:
self.get_widget("generalLaptopButton").set_active(True)
combobox = self.get_widget("sayAllStyle")
self.populateComboBox(combobox, [guilabels.SAY_ALL_STYLE_LINE,
guilabels.SAY_ALL_STYLE_SENTENCE])
@ -2748,7 +2742,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
elif dateFormatCombo == DATE_FORMAT_ABBREVIATED_YMD:
newFormat = messages.DATE_FORMAT_ABBREVIATED_YMD
self.prefsDict["presentDateFormat"] = newFormat
def timeFormatChanged(self, widget):
"""Signal handler for the "changed" signal for the timeFormat
GtkComboBox widget. Set the 'timeFormat' preference to the

View File

@ -1,9 +1,8 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2010 Consorcio Fernando de los Rios.
# Author: Javier Hernandez Antunez <jhernandez@emergya.es>
# Author: Alejandro Leiva <aleiva@emergya.es>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +18,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Displays the Save Profile As dialog."""

View File

@ -17,8 +17,6 @@
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
from cthulhu import cthulhuVersion
"""Holds platform-specific settings.
"""
@ -30,7 +28,7 @@ __license__ = "LGPL"
# $CTHULHU_VERSION
#
version = f"Cthulhu screen reader version {cthulhuVersion.version}-{cthulhuVersion.codeName}"
version = "@VERSION@"
# The revision if built from git; otherwise an empty string
#

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2009 Sun Microsystems Inc.
# Copyright 2010 Joanmarie Diggs, Mesar Hameed
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Holds state that is shared among many modules.
"""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2008 Sun Microsystems Inc.
# Copyright 2016-2023 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Module for date and time presentation"""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2008 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides debug utilities for Cthulhu. Debugging is managed by a debug
level, which is held in the debugLevel field. All other methods take

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2010 Joanmarie Diggs, Mesar Hameed.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
""" A list of common keybindings and unbound keys
pulled out from default.py: __getDesktopBindings()

View File

@ -1,28 +1,3 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
import gi
from gi.repository import GObject
@ -66,7 +41,7 @@ class DynamicApiManager():
def getAPI(self, key, application = '', fallback = True):
# get dynamic API
api = None
try:
api = self.api[application][key]
return api
@ -83,5 +58,5 @@ class DynamicApiManager():
api = self.api[application]['']
except:
print('API Key: "{}/{}" not found,'.format(application, key))
return api

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2011. Cthulhu Team.
# Author: Joanmarie Diggs <joanmarie.diggs@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
__id__ = "$Id$"
__version__ = "$Revision$"

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2006-2008 Sun Microsystems Inc.
# Copyright 2022 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides support for a flat review find."""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2008 Sun Microsystems Inc.
# Copyright 2016 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides the default implementation for flat review for Cthulhu."""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2008 Sun Microsystems Inc.
# Copyright 2016-2023 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Module for flat-review commands"""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2004-2009 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Manages the formatting settings for Cthulhu."""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2009 Sun Microsystems Inc.
# Copyright 2015-2016 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Superclass of classes used to generate presentations for objects."""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2004-2009 Sun Microsystems Inc.
# Copyright 2010-2013 The Cthulhu Team
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Labels for Cthulhu's GUIs. These have been put in their own module so that we
can present them in the correct language when users change the language on the

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Module for drawing highlights over an area of interest."""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2008 Sun Microsystems Inc.
# Copyright 2011-2016 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides support for handling input events."""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2008 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides support for defining keybindings and matching them to input
events."""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2006-2008 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Exposes a dictionary, keynames, that maps key events
into localized words."""

View File

@ -1,9 +1,8 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright (C) 2011-2013 Igalia, S.L.
#
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +18,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Heuristic means to infer the functional/displayed label of a widget."""

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2010 Joanmarie Diggs, Mesar Hameed.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
""" A list of common keybindings and unbound keys
pulled out from default.py: __getLaptopBindings()
@ -44,7 +38,7 @@ CTHULHU_SHIFT_MODIFIER_MASK = keybindings.CTHULHU_SHIFT_MODIFIER_MASK
CTHULHU_CTRL_MODIFIER_MASK = keybindings.CTHULHU_CTRL_MODIFIER_MASK
keymap = (
("9", defaultModifierMask, CTHULHU_MODIFIER_MASK,
"routePointerToItemHandler"),

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2005-2008 Sun Microsystems Inc.
# Copyright 2016-2023 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Module for learn mode"""

View File

@ -1,28 +1,3 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
import gi
gi.require_version("Atspi", "2.0")
from gi.repository import Atspi

View File

@ -1,9 +1,9 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2008 Sun Microsystems Inc.
# Copyright 2012 Igalia, S.L.
#
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +19,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Output logger for regression testing."""

View File

@ -1,9 +1,8 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2014 Igalia, S.L.
#
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +18,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
__id__ = "$Id$"
__version__ = "$Revision$"

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2004-2009 Sun Microsystems Inc.
# Copyright 2010-2013 The Cthulhu Team
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Messages which Cthulhu presents in speech and/or braille. These
have been put in their own module so that we can present them in
@ -2299,12 +2294,7 @@ SPOKEN_ELLIPSIS = _(" dot dot dot")
START_CTHULHU = _("Cthulhu welcomes you.")
# Translators: This message is presented to the user when Cthulhu is quit.
STOP_CTHULHU = _("Cthulhu lurks beneath the waves.")
# Sleep Mode
# Translators: This message is presented to the user when Cthulhu enters or leaves sleep mode.
SLEEP_MODE_ENABLED_FOR = _("Sleep mode enabled for %s")
SLEEP_MODE_DISABLED_FOR = _("Sleep mode disabled for %s")
STOP_CTHULHU = _("Cthulhu lerks beneath the waves.")
# Translators: This message means speech synthesis is not installed or working.
SPEECH_UNAVAILABLE = _("Speech is unavailable.")

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Mouse reviewer for Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2008 Eitan Isaacson
# Copyright 2016 Igalia, S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Mouse review mode."""

View File

@ -1,9 +1,10 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
# Based on the feature created by:
# Author: Jose Vilmar <vilmar@informal.com.br>
# Copyright 2010 Informal Informatica LTDA.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +20,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Module for notification messages"""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2023 The Cthulhu Team
# Author: Rynhardt Kruger <rynkruger@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides ability to navigate objects hierarchically."""

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2004-2009 Sun Microsystems Inc.
# Copyright 2010-2013 The Cthulhu Team
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +17,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Propeerties of accessible objects. These have been put in their own module
so that we can present them in the correct language when users change the

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
# Cthulhu
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# Copyright 2006-2008 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -19,9 +16,6 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Provides getPhoneticName method that maps each letter of the
alphabet into its localized phonetic equivalent."""

View File

@ -1,92 +1,160 @@
#!/usr/bin/env python3
# Copyright (c) 2024 Stormux
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
import os, inspect
import gi
from gi.repository import GObject
"""Base class for Cthulhu plugins using pluggy."""
class Plugin():
#__gtype_name__ = 'BasePlugin'
import os
import logging
# Import pluggy for hook specifications
try:
import pluggy
cthulhu_hookimpl = pluggy.HookimplMarker("cthulhu")
PLUGGY_AVAILABLE = True
logging.getLogger(__name__).info("Successfully imported pluggy")
except ImportError:
# Fallback if pluggy is not available
def cthulhu_hookimpl(func=None, **kwargs):
"""Fallback decorator when pluggy is not available.
This is a no-op decorator that returns the original function.
It allows the code to continue working without pluggy, though
plugins will be disabled.
"""
if func is None:
return lambda f: f
return func
PLUGGY_AVAILABLE = False
logging.getLogger(__name__).warning("Pluggy not available, plugins will be disabled")
import traceback
logging.getLogger(__name__).debug(traceback.format_exc())
logger = logging.getLogger(__name__)
class Plugin:
"""Base class for Cthulhu plugins."""
def __init__(self):
"""Initialize the plugin with default attributes."""
self.app = None
self.plugin_info = None
self.module_name = ''
def __init__(self, *args, **kwargs):
self.API = None
self.pluginInfo = None
self.moduleDir = ''
self.hidden = False
self.moduleName = ''
self.name = ''
self.version = ''
self.website = ''
self.authors = []
self.buildIn = False
self.description = ''
def set_app(self, app):
"""Set the application reference."""
self.iconName = ''
self.copyright = ''
self.dependencies = False
self.helpUri = ''
self.dataDir = ''
self.translationContext = None
def setApp(self, app):
self.app = app
self.dynamicApiManager = app.getDynamicApiManager()
self.signalManager = app.getSignalManager()
def set_plugin_info(self, plugin_info):
"""Set plugin information and extract relevant attributes."""
self.plugin_info = plugin_info
if plugin_info:
self.module_name = getattr(plugin_info, 'module_name', '')
self.name = getattr(plugin_info, 'name', '')
self.version = getattr(plugin_info, 'version', '')
self.description = getattr(plugin_info, 'description', '')
@cthulhu_hookimpl
def activate(self, plugin=None):
"""Activate the plugin. Override this in subclasses."""
if plugin is not None and plugin is not self:
def getApp(self):
return self.app
def setPluginInfo(self, pluginInfo):
self.pluginInfo = pluginInfo
self.updatePluginInfoAttributes()
def getPluginInfo(self):
return self.pluginInfo
def updatePluginInfoAttributes(self):
self.moduleDir = ''
self.hidden = False
self.moduleName = ''
self.name = ''
self.version = ''
self.website = ''
self.authors = []
self.buildIn = False
self.description = ''
self.iconName = ''
self.copyright = ''
self.dependencies = False
self.helpUri = ''
self.dataDir = ''
pluginInfo = self.getPluginInfo()
if pluginInfo == None:
return
logger.info(f"Activating plugin: {self.name}")
self.moduleName = self.getApp().getPluginSystemManager().getPluginModuleName(pluginInfo)
self.name = self.getApp().getPluginSystemManager().getPluginName(pluginInfo)
self.version = self.getApp().getPluginSystemManager().getPluginVersion(pluginInfo)
self.moduleDir = self.getApp().getPluginSystemManager().getPluginModuleDir(pluginInfo)
self.buildIn = self.getApp().getPluginSystemManager().isPluginBuildIn(pluginInfo)
self.description = self.getApp().getPluginSystemManager().getPluginDescription(pluginInfo)
self.hidden = self.getApp().getPluginSystemManager().isPluginHidden(pluginInfo)
self.website = self.getApp().getPluginSystemManager().getPluginWebsite(pluginInfo)
self.authors = self.getApp().getPluginSystemManager().getPluginAuthors(pluginInfo)
self.iconName = self.getApp().getPluginSystemManager().getPluginIconName(pluginInfo)
self.copyright = self.getApp().getPluginSystemManager().getPluginCopyright(pluginInfo)
self.dependencies = self.getApp().getPluginSystemManager().getPluginDependencies(pluginInfo)
@cthulhu_hookimpl
def deactivate(self, plugin=None):
"""Deactivate the plugin. Override this in subclasses."""
if plugin is not None and plugin is not self:
return
logger.info(f"Deactivating plugin: {self.name}")
#settings = self.getApp().getPluginSystemManager().getPluginSettings(pluginInfo)
#hasDependencies = self.getApp().getPluginSystemManager().hasPluginDependency(pluginInfo)
def registerGestureByString(self, function, name, gestureString, learnModeEnabled=True):
"""Register a gesture by string."""
if self.app:
api_helper = self.app.getAPIHelper()
if api_helper:
return api_helper.registerGestureByString(
function,
name,
gestureString,
'default',
'cthulhu',
learnModeEnabled,
contextName=self.module_name
)
#externalData = self.getApp().getPluginSystemManager().getPluginExternalData(pluginInfo)
self.helpUri = self.getApp().getPluginSystemManager().getPlugingetHelpUri(pluginInfo)
self.dataDir = self.getApp().getPluginSystemManager().getPluginDataDir(pluginInfo)
self.updateTranslationContext()
def updateTranslationContext(self, domain = None, localeDir = None, language = None, fallbackToCthulhuTranslation = True):
self.translationContext = None
useLocaleDir = '{}/locale/'.format(self.getModuleDir())
if localeDir:
if os.path.isdir(localeDir):
useLocaleDir = localeDir
useName = self.getModuleName()
useDomain = useName
if domain:
useDomain = domain
useLanguage = None
if language:
useLanguage = language
self.translationContext = self.getApp().getTranslationManager().initTranslation(useName, domain=useDomain, localeDir=useLocaleDir, language=useLanguage, fallbackToCthulhuTranslation=fallbackToCthulhuTranslation)
# Point _ to the translation object in the globals namespace of the caller frame
try:
callerFrame = inspect.currentframe().f_back
# Install our gettext and ngettext function to the upper frame
callerFrame.f_globals['_'] = self.translationContext.gettext
callerFrame.f_globals['ngettext'] = self.translationContext.ngettext
finally:
del callerFrame # Avoid reference problems with frames (per python docs)
def getTranslationContext(self):
return self.translationContext
def isPluginBuildIn(self):
return self.buildIn
def isPluginHidden(self):
return self.hidden
def getAuthors(self):
return self.authors
def getCopyright(self):
return self.copyright
def getDataDir(self):
return self.dataDir
def getDependencies(self):
return self.dependencies
def getDescription(self):
return self.description
def getgetHelpUri(self):
return self.helpUri
def getIconName(self):
return self.iconName
def getModuleDir(self):
return self.moduleDir
def getModuleName(self):
return self.moduleName
def getName(self):
return self.name
def getVersion(self):
return self.version
def getWebsite(self):
return self.website
def getSetting(key):
#self.getModuleName())
return None
def setSetting(key, value):
#self.getModuleName())
pass
def registerGestureByString(self, function, name, gestureString, learnModeEnabled = True):
keybinding = self.getApp().getAPIHelper().registerGestureByString(function, name, gestureString, 'default', 'cthulhu', learnModeEnabled, contextName = self.getModuleName())
return keybinding
def unregisterShortcut(self, function, name, gestureString, learnModeEnabled = True):
ok = self.getApp().getAPIHelper().unregisterShortcut(keybinding, contextName = self.getModuleName())
return ok
def registerSignal(self, signalName, signalFlag = GObject.SignalFlags.RUN_LAST, closure = GObject.TYPE_NONE, accumulator=()):
ok = self.signalManager.registerSignal(signalName, signalFlag, closure, accumulator, contextName = self.getModuleName())
return ok
def unregisterSignal(self, signalName):
# how to unregister?
pass
def connectSignal(self, signalName, function, param = None):
signalID = self.signalManager.connectSignal(signalName, function, param, contextName = self.getModuleName())
return signalID
def disconnectSignalByFunction(self, function):
# need get mapped function
mappedFunction = function
self.signalManager.disconnectSignalByFunction(mappedFunction, contextName = self.getModuleName())
def registerAPI(self, key, value, application = ''):
ok = self.dynamicApiManager.registerAPI(key, value, application = application, contextName = self.getModuleName())
return ok
def unregisterAPI(self, key, application = ''):
self.dynamicApiManager.unregisterAPI(key, application = application, contextName = self.getModuleName())

View File

@ -1,469 +1,520 @@
#!/usr/bin/env python3
# Copyright (c) 2024 Stormux
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#!/bin/python
"""PluginManager for loading cthulhu plugins."""
import os, inspect, sys, tarfile, shutil
"""Plugin System Manager for Cthulhu using pluggy."""
import os
import inspect
import importlib.util
import logging
from enum import IntEnum
# Import pluggy if available
try:
import pluggy
PLUGGY_AVAILABLE = True
except ImportError:
PLUGGY_AVAILABLE = False
logging.getLogger(__name__).info("Pluggy not available, plugins will be disabled")
version = sys.version[:3] # we only need major.minor version.
if version in ["3.3","3.4"]:
from importlib.machinery import SourceFileLoader
else: # Python 3.5+, no support for python < 3.3.
import importlib.util
# Set to True for more detailed plugin loading debug info
PLUGIN_DEBUG = True
import gi
gi.require_version('Peas', '1.0')
from gi.repository import GObject
from gi.repository import Peas
logger = logging.getLogger(__name__)
if PLUGIN_DEBUG:
logger.setLevel(logging.DEBUG)
gi.require_version('Atspi', '2.0')
from gi.repository import Atspi
from cthulhu import resource_manager
class API(GObject.GObject):
"""Interface that gives access to all the objects of Cthulhu."""
def __init__(self, app):
GObject.GObject.__init__(self)
self.app = app
class PluginType(IntEnum):
"""Types of plugins we support."""
"""Types of plugins we support, depending on their directory location."""
# pylint: disable=comparison-with-callable,inconsistent-return-statements,no-else-return
# SYSTEM: provides system wide plugins
SYSTEM = 1
# USER: provides per user plugin
USER = 2
def __str__(self):
if self.value == PluginType.SYSTEM:
return _("System plugin")
elif self.value == PluginType.USER:
return _("User plugin")
def get_root_dir(self):
"""Returns the directory where this type of plugins can be found."""
if self.value == PluginType.SYSTEM:
current_file = inspect.getfile(inspect.currentframe())
current_dir = os.path.dirname(os.path.realpath(os.path.abspath(current_file)))
return os.path.join(current_dir, 'plugins')
return os.path.dirname(os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))) + '/plugins'
elif self.value == PluginType.USER:
return os.path.expanduser('~/.local/share/cthulhu/plugins')
return os.path.expanduser('~') + '/.local/share/cthulhu/plugins'
class PluginInfo:
"""Information about a plugin."""
def __init__(self, name, module_name, module_dir, metadata=None):
self.name = name
self.module_name = module_name
self.module_dir = module_dir
self.metadata = metadata or {}
self.builtin = False
self.hidden = False
self.module = None
self.instance = None
self.loaded = False
def get_module_name(self):
return self.module_name
def get_name(self):
return self.metadata.get('name', self.name)
def get_version(self):
return self.metadata.get('version', '0.0.0')
def get_description(self):
return self.metadata.get('description', '')
def get_module_dir(self):
return self.module_dir
class PluginSystemManager:
"""Cthulhu Plugin Manager using pluggy."""
class PluginSystemManager():
"""Cthulhu Plugin Manager to handle a set of plugins.
Attributes:
DEFAULT_LOADERS (tuple): Default loaders used by the plugin manager. For
possible values see
https://developer.gnome.org/libpeas/stable/PeasEngine.html#peas-engine-enable-loader
"""
DEFAULT_LOADERS = ("python3", )
def __init__(self, app):
self.app = app
logger.info("Initializing PluginSystemManager")
self.engine = Peas.Engine.get_default()
# Initialize plugin manager
if PLUGGY_AVAILABLE:
logger.info("Pluggy is available, setting up plugin manager")
self.plugin_manager = pluggy.PluginManager("cthulhu")
for loader in self.DEFAULT_LOADERS:
self.engine.enable_loader(loader)
# Define hook specifications
hook_spec = pluggy.HookspecMarker("cthulhu")
class CthulhuHookSpecs:
@hook_spec
def activate(self, plugin=None):
"""Called when the plugin is activated."""
pass
@hook_spec
def deactivate(self, plugin=None):
"""Called when the plugin is deactivated."""
pass
logger.info("Adding hook specifications to plugin manager")
self.plugin_manager.add_hookspecs(CthulhuHookSpecs)
else:
logger.warning("Pluggy is not available, plugins will be disabled")
self.plugin_manager = None
# Plugin storage
self._plugins = {} # module_name -> PluginInfo
self._active_plugins = []
# Create plugin directories
self._setup_plugin_dirs()
self._setupPluginsDir()
self._setupExtensionSet()
# Log available plugins directory paths
logger.info(f"System plugins directory: {PluginType.SYSTEM.get_root_dir()}")
logger.info(f"User plugins directory: {PluginType.USER.get_root_dir()}")
def _setup_plugin_dirs(self):
"""Ensure plugin directories exist."""
os.makedirs(PluginType.SYSTEM.get_root_dir(), exist_ok=True)
os.makedirs(PluginType.USER.get_root_dir(), exist_ok=True)
if self.app:
self.gsettingsManager = self.app.getSettingsManager()
# settings else:
# settings self.gsettingsManager = gsettings_manager.getSettingsManager(self.app)
self._activePlugins = []
self._ignorePluginModulePath = []
@property
def plugins(self):
"""Get all available plugins."""
return list(self._plugins.values())
"""Gets the engine's plugin list."""
return self.engine.get_plugin_list()
@classmethod
def getPluginType(cls, pluginInfo):
"""Gets the PluginType for the specified Peas.PluginInfo."""
paths = [pluginInfo.get_data_dir(), PluginType.SYSTEM.get_root_dir()]
if os.path.commonprefix(paths) == PluginType.SYSTEM.get_root_dir():
return PluginType.SYSTEM
return PluginType.USER
def getExtension(self, pluginInfo):
if not pluginInfo:
return None
return self.extension_set.get_extension(pluginInfo)
def rescanPlugins(self):
self.engine.garbage_collect()
self.engine.rescan_plugins()
def getApp(self):
return self.app
def rescanPlugins(self):
"""Scan for plugins in the plugin directories."""
old_plugins = self._plugins.copy()
self._plugins = {}
# Scan system and user plugins
self._scan_plugins_in_directory(PluginType.SYSTEM.get_root_dir())
self._scan_plugins_in_directory(PluginType.USER.get_root_dir())
# Preserve state for already loaded plugins
for name, old_info in old_plugins.items():
if name in self._plugins and old_info.loaded:
self._plugins[name].loaded = True
self._plugins[name].instance = old_info.instance
self._plugins[name].module = old_info.module
def _scan_plugins_in_directory(self, directory):
"""Scan for plugins in a directory."""
if not os.path.exists(directory) or not os.path.isdir(directory):
logger.warning(f"Plugin directory not found or not a directory: {directory}")
return
logger.info(f"Scanning for plugins in directory: {directory}")
for item in os.listdir(directory):
plugin_dir = os.path.join(directory, item)
if not os.path.isdir(plugin_dir):
continue
# Check for the traditional structure first (plugin.py & plugin.info)
plugin_file = os.path.join(plugin_dir, "plugin.py")
metadata_file = os.path.join(plugin_dir, "plugin.info")
# Fall back to [PluginName].py if plugin.py doesn't exist
if not os.path.isfile(plugin_file):
alternative_plugin_file = os.path.join(plugin_dir, f"{item}.py")
if os.path.isfile(alternative_plugin_file):
plugin_file = alternative_plugin_file
logger.info(f"Using alternative plugin file: {alternative_plugin_file}")
# Check if we have any valid plugin file
if os.path.isfile(plugin_file):
# Extract plugin info
module_name = os.path.basename(plugin_dir)
logger.info(f"Found plugin: {module_name} in {plugin_dir}")
metadata = self._load_plugin_metadata(metadata_file)
plugin_info = PluginInfo(
metadata.get('name', module_name),
module_name,
plugin_dir,
metadata
)
# Check if it's a built-in or hidden plugin
plugin_info.builtin = metadata.get('builtin', 'false').lower() == 'true'
plugin_info.hidden = metadata.get('hidden', 'false').lower() == 'true'
logger.info(f"Adding plugin to registry: {module_name}")
self._plugins[module_name] = plugin_info
else:
logger.warning(f"No plugin file found in directory: {plugin_dir}")
def _load_plugin_metadata(self, metadata_file):
"""Load plugin metadata from a file."""
metadata = {}
if os.path.isfile(metadata_file):
try:
with open(metadata_file, 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
if '=' in line:
key, value = line.split('=', 1)
metadata[key.strip()] = value.strip()
except Exception as e:
logger.error(f"Error loading plugin metadata: {e}")
return metadata
def getPluginInfoByName(self, pluginName, pluginType=PluginType.USER):
"""Gets the plugin info for the specified plugin name.
Args:
pluginName (str): The name from the .plugin file of the module.
Returns:
Peas.PluginInfo: The plugin info if it exists. Otherwise, `None`.
"""
for pluginInfo in self.plugins:
if pluginInfo.get_module_name() == pluginName and PluginSystemManager.getPluginType(pluginInfo) == pluginType:
return pluginInfo
return None
def getActivePlugins(self):
"""Get the list of active plugin names."""
return self._active_plugins
return self._activePlugins
def setActivePlugins(self, activePlugins):
"""Set active plugins and sync their state."""
logger.info(f"Setting active plugins: {activePlugins}")
# Make sure we have scanned for plugins first
if not self._plugins:
logger.info("No plugins found, rescanning...")
self.rescanPlugins()
self._active_plugins = activePlugins
# Log active vs available plugins
available_plugins = [p.get_module_name() for p in self.plugins]
logger.info(f"Available plugins: {available_plugins}")
logger.info(f"Active plugins: {self._active_plugins}")
# Find missing plugins
missing_plugins = [p for p in self._active_plugins if p not in available_plugins]
if missing_plugins:
logger.warning(f"Active plugins not found: {missing_plugins}")
self._activePlugins = activePlugins
self.syncAllPluginsActive()
def isPluginBuildIn(self, pluginInfo):
return pluginInfo.is_builtin()
def isPluginHidden(self, pluginInfo):
return pluginInfo.is_hidden()
def getPluginAuthors(self, pluginInfo):
return pluginInfo.get_authors()
def getPluginCopyright(self, pluginInfo):
return pluginInfo.get_copyright()
def getPluginDataDir(self, pluginInfo):
return pluginInfo.get_data_dir()
def getPluginDependencies(self, pluginInfo):
return pluginInfo.get_dependencies()
def getPluginDescription(self, pluginInfo):
return pluginInfo.get_description()
def getPlugingetHelpUri(self, pluginInfo):
return pluginInfo.get_help_uri()
def getPluginIconName(self, pluginInfo):
return pluginInfo.get_icon_name()
def getPluginModuleDir(self, pluginInfo):
return pluginInfo.get_module_dir()
def getPluginModuleName(self, pluginInfo):
return pluginInfo.get_module_name()
def getPluginName(self, pluginInfo):
return pluginInfo.get_name()
def getPluginSettings(self, pluginInfo):
return pluginInfo.get_settings()
def getPluginVersion(self, pluginInfo):
return pluginInfo.get_version()
def getPluginWebsite(self, pluginInfo):
return pluginInfo.get_website()
# has_dependency and get_external_data seems broken-> takes exactly 2 arguments (1 given) but documentation doesnt say any parameter
#def hasPluginDependency(self, pluginInfo):
# return pluginInfo.has_dependency()
#def getPluginExternalData(self, pluginInfo):
# return pluginInfo.get_external_data()
def isPluginAvailable(self, pluginInfo):
try:
return pluginInfo.is_available()
except:
return False
def isPluginLoaded(self, pluginInfo):
try:
return pluginInfo.is_loaded()
except:
return False
def getIgnoredPlugins(self):
return self._ignorePluginModulePath
def setIgnoredPlugins(self, pluginModulePath, ignored):
if pluginModulePath.endswith('/'):
pluginModulePath = pluginModulePath[:-1]
if ignored:
if not pluginModulePath in self.getIgnoredPlugins():
self._ignorePluginModulePath.append(pluginModulePath)
else:
if pluginModulePath in self.getIgnoredPlugins():
self._ignorePluginModulePath.remove(pluginModulePath)
def setPluginActive(self, pluginInfo, active):
"""Set the active state of a plugin."""
if pluginInfo.builtin:
if self.isPluginBuildIn(pluginInfo):
active = True
pluginName = pluginInfo.get_module_name()
pluginName = self.getPluginModuleName(pluginInfo)
if active:
if pluginName not in self.getActivePlugins():
if not pluginName in self.getActivePlugins():
if self.loadPlugin(pluginInfo):
self._active_plugins.append(pluginName)
self._activePlugins.append(pluginName )
else:
if pluginName in self.getActivePlugins():
if pluginName in self.getActivePlugins():
if self.unloadPlugin(pluginInfo):
self._active_plugins.remove(pluginName)
self._activePlugins.remove(pluginName )
def isPluginActive(self, pluginInfo):
"""Check if a plugin is active."""
module_name = pluginInfo.get_module_name()
# Builtin plugins are always active
if pluginInfo.builtin:
logger.debug(f"Plugin {module_name} is builtin, active by default")
if self.isPluginBuildIn(pluginInfo):
return True
# If a plugin is already loaded, it's active
if pluginInfo.loaded:
logger.debug(f"Plugin {module_name} is already loaded, considered active")
if self.isPluginLoaded(pluginInfo):
return True
active_plugin_names = self.getActivePlugins()
return self.getPluginModuleName(pluginInfo) in active_plugin_names
def syncAllPluginsActive(self, ForceAllPlugins=False):
self.unloadAllPlugins(ForceAllPlugins)
self.loadAllPlugins(ForceAllPlugins)
# Check case-insensitive match in active plugins list
active_plugins = self.getActivePlugins()
# Try exact match first
if module_name in active_plugins:
logger.debug(f"Plugin {module_name} found in active plugins list")
return True
# Try case-insensitive match
module_name_lower = module_name.lower()
is_active = any(plugin.lower() == module_name_lower for plugin in active_plugins)
if is_active:
logger.debug(f"Plugin {module_name} found in active plugins list (case-insensitive match)")
else:
logger.debug(f"Plugin {module_name} not found in active plugins list")
return is_active
def syncAllPluginsActive(self):
"""Sync the active state of all plugins."""
logger.info("Syncing active state of all plugins")
# Log plugin status before syncing
if PLUGIN_DEBUG:
for pluginInfo in self.plugins:
is_active = self.isPluginActive(pluginInfo)
is_loaded = pluginInfo.loaded
logger.debug(f"Plugin {pluginInfo.get_module_name()}: active={is_active}, loaded={is_loaded}")
# First unload inactive plugins
def loadAllPlugins(self, ForceAllPlugins=False):
"""Loads plugins from settings."""
for pluginInfo in self.plugins:
if not self.isPluginActive(pluginInfo) and pluginInfo.loaded:
logger.info(f"Unloading inactive plugin: {pluginInfo.get_module_name()}")
self.unloadPlugin(pluginInfo)
# Then load active plugins
for pluginInfo in self.plugins:
if self.isPluginActive(pluginInfo) and not pluginInfo.loaded:
logger.info(f"Loading active plugin: {pluginInfo.get_module_name()}")
result = self.loadPlugin(pluginInfo)
logger.info(f"Plugin {pluginInfo.get_module_name()} load result: {result}")
# Log final plugin status
active_plugins = [p.get_module_name() for p in self.plugins if p.loaded]
logger.info(f"Active plugins after sync: {active_plugins}")
inactive_plugins = [p.get_module_name() for p in self.plugins if not p.loaded]
logger.info(f"Inactive plugins after sync: {inactive_plugins}")
if self.isPluginActive(pluginInfo) or ForceAllPlugins:
self.loadPlugin(pluginInfo)
def loadPlugin(self, pluginInfo):
"""Load a plugin."""
# Skip if pluggy is not available
if not PLUGGY_AVAILABLE:
logger.info(f"Skipping plugin {pluginInfo.get_name()}: pluggy not available")
return False
module_name = pluginInfo.get_module_name()
logger.info(f"Attempting to load plugin: {module_name}")
resourceManager = self.getApp().getResourceManager()
moduleName = pluginInfo.get_module_name()
try:
# Already loaded?
if pluginInfo.loaded:
logger.info(f"Plugin {module_name} already loaded, skipping")
return True
# Try to find the plugin file
module_name = pluginInfo.get_module_name()
plugin_dir = pluginInfo.get_module_dir()
# Check for plugin.py first (standard format)
plugin_file = os.path.join(plugin_dir, "plugin.py")
# Fall back to [PluginName].py if plugin.py doesn't exist
if not os.path.exists(plugin_file):
alternative_plugin_file = os.path.join(plugin_dir, f"{module_name}.py")
if os.path.exists(alternative_plugin_file):
plugin_file = alternative_plugin_file
logger.info(f"Using alternative plugin file: {alternative_plugin_file}")
if not os.path.exists(plugin_file):
logger.error(f"Plugin file not found: {plugin_file}")
if pluginInfo not in self.plugins:
print("Plugin missing: {}".format(moduleName))
return False
logger.info(f"Loading plugin from: {plugin_file}")
spec = importlib.util.spec_from_file_location(module_name, plugin_file)
if spec is None:
logger.error(f"Failed to create spec for plugin: {module_name}")
return False
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
pluginInfo.module = module
# Find Plugin class
plugin_class = None
for attr_name in dir(module):
attr = getattr(module, attr_name)
if (inspect.isclass(attr) and
attr.__module__ == module.__name__ and
hasattr(attr, 'activate')):
plugin_class = attr
logger.info(f"Found plugin class: {attr.__name__} in {module_name}")
break
if not plugin_class:
logger.error(f"No plugin class found in {module_name}")
return False
# Create and initialize plugin instance
logger.info(f"Creating instance of plugin class: {plugin_class.__name__}")
plugin_instance = plugin_class()
pluginInfo.instance = plugin_instance
# Ensure plugins have a reference to the app
plugin_instance.app = self.getApp()
logger.info(f"Set app reference for plugin: {module_name}")
if hasattr(plugin_instance, 'set_app'):
plugin_instance.set_app(self.getApp())
logger.info(f"Called set_app() for plugin: {module_name}")
if hasattr(plugin_instance, 'set_plugin_info'):
plugin_instance.set_plugin_info(pluginInfo)
logger.info(f"Called set_plugin_info() for plugin: {module_name}")
# Register with pluggy and activate
if self.plugin_manager is None:
logger.error(f"Plugin manager is None when loading {module_name}")
return False
logger.info(f"Registering plugin with pluggy: {module_name}")
self.plugin_manager.register(plugin_instance)
try:
logger.info(f"Activating plugin: {module_name}")
self.plugin_manager.hook.activate(plugin=plugin_instance)
except Exception as e:
logger.error(f"Error activating plugin {module_name}: {e}")
import traceback
logger.error(traceback.format_exc())
return False
pluginInfo.loaded = True
logger.info(f"Successfully loaded plugin: {module_name}")
return True
resourceManager.addResourceContext(moduleName)
self.engine.load_plugin(pluginInfo)
except Exception as e:
logger.error(f"Failed to load plugin {module_name}: {e}")
import traceback
logger.error(traceback.format_exc())
return False
def unloadPlugin(self, pluginInfo):
"""Unload a plugin."""
# Skip if pluggy is not available
if not PLUGGY_AVAILABLE:
return False
if pluginInfo.builtin:
return False
module_name = pluginInfo.get_module_name()
try:
# Not loaded?
if not pluginInfo.loaded:
return True
# Deactivate plugin
plugin_instance = pluginInfo.instance
if plugin_instance:
try:
self.plugin_manager.hook.deactivate(plugin=plugin_instance)
except Exception as e:
logger.error(f"Error deactivating plugin {module_name}: {e}")
# Unregister from pluggy
self.plugin_manager.unregister(plugin_instance)
# Clean up
pluginInfo.instance = None
pluginInfo.loaded = False
logger.info(f"Unloaded plugin: {module_name}")
return True
except Exception as e:
logger.error(f"Failed to unload plugin {module_name}: {e}")
print('loadPlugin:',e)
return False
return True
def unloadAllPlugins(self, ForceAllPlugins=False):
"""Unload all plugins."""
if not PLUGGY_AVAILABLE:
return
"""Loads plugins from settings."""
for pluginInfo in self.plugins:
if ForceAllPlugins or pluginInfo.loaded:
if not self.isPluginActive(pluginInfo) or ForceAllPlugins:
self.unloadPlugin(pluginInfo)
def unloadPlugin(self, pluginInfo):
resourceManager = self.getApp().getResourceManager()
moduleName = pluginInfo.get_module_name()
try:
if pluginInfo not in self.plugins:
print("Plugin missing: {}".format(moduleName))
return False
if self.isPluginBuildIn(pluginInfo):
return False
self.engine.unload_plugin(pluginInfo)
self.getApp().getResourceManager().removeResourceContext(moduleName)
self.engine.garbage_collect()
except Exception as e:
print('unloadPlugin:',e)
return False
return True
def installPlugin(self, pluginFilePath, pluginType=PluginType.USER):
if not self.isValidPluginFile(pluginFilePath):
return False
pluginFolder = pluginType.get_root_dir()
if not pluginFolder.endswith('/'):
pluginFolder += '/'
if not os.path.exists(pluginFolder):
os.mkdir(pluginFolder)
else:
if not os.path.isdir(pluginFolder):
return False
try:
with tarfile.open(pluginFilePath) as tar:
tar.extractall(path=pluginFolder)
except Exception as e:
print(e)
pluginModulePath = self.getModuleDirByPluginFile(pluginFilePath)
if pluginModulePath != '':
pluginModulePath = pluginFolder + pluginModulePath
self.setIgnoredPlugins(pluginModulePath[:-1], False) # without ending /
print('install', pluginFilePath)
self.callPackageTriggers(pluginModulePath, 'onPostInstall')
self.rescanPlugins()
return True
def getModuleDirByPluginFile(self, pluginFilePath):
if not isinstance(pluginFilePath, str):
return ''
if pluginFilePath == '':
return ''
if not os.path.exists(pluginFilePath):
return ''
try:
with tarfile.open(pluginFilePath) as tar:
tarMembers = tar.getmembers()
for tarMember in tarMembers:
if tarMember.isdir():
return tarMember.name
except Exception as e:
print(e)
return ''
def isValidPluginFile(self, pluginFilePath):
if not isinstance(pluginFilePath, str):
return False
if pluginFilePath == '':
return False
if not os.path.exists(pluginFilePath):
return False
pluginFolder = ''
pluginFileExists = False
packageFileExists = False
try:
with tarfile.open(pluginFilePath) as tar:
tarMembers = tar.getmembers()
for tarMember in tarMembers:
if tarMember.isdir():
if pluginFolder == '':
pluginFolder = tarMember.name
if tarMember.isfile():
if tarMember.name.endswith('.plugin'):
pluginFileExists = True
if tarMember.name.endswith('package.py'):
pluginFileExists = True
if not tarMember.name.startswith(pluginFolder):
return False
except Exception as e:
print(e)
return False
return pluginFileExists
def uninstallPlugin(self, pluginInfo):
if self.isPluginBuildIn(pluginInfo):
return False
# do we want to allow removing system plugins?
if PluginSystemManager.getPluginType(pluginInfo) == PluginType.SYSTEM:
return False
pluginFolder = pluginInfo.get_data_dir()
if not pluginFolder.endswith('/'):
pluginFolder += '/'
if not os.path.isdir(pluginFolder):
return False
if self.isPluginActive(pluginInfo):
self.setPluginActive(pluginInfo, False)
SettingsManager = self.app.getSettingsManager()
# TODO SettingsManager.set_settings_value_list('active-plugins', self.getActivePlugins())
self.callPackageTriggers(pluginFolder, 'onPreUninstall')
try:
shutil.rmtree(pluginFolder, ignore_errors=True)
except Exception as e:
print(e)
return False
self.setIgnoredPlugins(pluginFolder, True)
self.rescanPlugins()
return True
def callPackageTriggers(self, pluginPath, trigger):
if not os.path.exists(pluginPath):
return
if not pluginPath.endswith('/'):
pluginPath += '/'
packageModulePath = pluginPath + 'package.py'
if not os.path.isfile(packageModulePath):
return
if not os.access(packageModulePath, os.R_OK):
return
package = self.getApp().getAPIHelper().importModule('package', packageModulePath)
if trigger == 'onPostInstall':
try:
package.onPostInstall(pluginPath, self.getApp())
except Exception as e:
print(e)
elif trigger == 'onPreUninstall':
try:
package.onPreUninstall(pluginPath, self.getApp())
except Exception as e:
print(e)
def _setupExtensionSet(self):
plugin_iface = API(self.getApp())
self.extension_set = Peas.ExtensionSet.new(self.engine,
Peas.Activatable,
["object"],
[plugin_iface])
self.extension_set.connect("extension-removed",
self.__extensionRemoved)
self.extension_set.connect("extension-added",
self.__extensionAdded)
def _setupPluginsDir(self):
system_plugins_dir = PluginType.SYSTEM.get_root_dir()
user_plugins_dir = PluginType.USER.get_root_dir()
if os.path.exists(user_plugins_dir):
self.engine.add_search_path(user_plugins_dir)
if os.path.exists(system_plugins_dir):
self.engine.add_search_path(system_plugins_dir)
def __extensionRemoved(self, unusedSet, pluginInfo, extension):
extension.deactivate()
def __extensionAdded(self, unusedSet, pluginInfo, extension):
extension.setApp(self.getApp())
extension.setPluginInfo(pluginInfo)
extension.activate()
def __loadedPlugins(self, engine, unusedSet):
"""Handles the changing of the loaded plugin list."""
self.getApp().settings.ActivePlugins = engine.get_property("loaded-plugins")
class APIHelper():
def __init__(self, app):
self.app = app
self.cthulhuKeyBindings = None
'''
_pluginAPIManager.seCthulhuAPI('Logger', _logger)
_pluginAPIManager.setCthulhuAPI('SettingsManager', _settingsManager)
_pluginAPIManager.setCthulhuAPI('ScriptManager', _scriptManager)
_pluginAPIManager.setCthulhuAPI('EventManager', _eventManager)
_pluginAPIManager.setCthulhuAPI('Speech', speech)
_pluginAPIManager.setCthulhuAPI('Sound', sound)
_pluginAPIManager.setCthulhuAPI('Braille', braille)
_pluginAPIManager.setCthulhuAPI('Debug', debug)
_pluginAPIManager.setCthulhuAPI('Messages', messages)
_pluginAPIManager.setCthulhuAPI('MouseReview', mouse_review)
_pluginAPIManager.setCthulhuAPI('NotificationMessages', notification_messages)
_pluginAPIManager.setCthulhuAPI('CthulhuState', cthulhu_state)
_pluginAPIManager.setCthulhuAPI('CthulhuPlatform', cthulhu_platform)
_pluginAPIManager.setCthulhuAPI('Settings', settings)
_pluginAPIManager.setCthulhuAPI('Keybindings', keybindings)
'''
def outputMessage(self, Message, interrupt=False):
settings = self.app.getDynamicApiManager().getAPI('Settings')
braille = self.app.getDynamicApiManager().getAPI('Braille')
speech = self.app.getDynamicApiManager().getAPI('Speech')
if speech != None:
if (settings.enableSpeech):
if interrupt:
speech.cancel()
if Message != '':
speech.speak(Message)
if braille != None:
if (settings.enableBraille):
braille.displayMessage(Message)
def createInputEventHandler(self, function, name, learnModeEnabled=True):
EventManager = self.app.getDynamicApiManager().getAPI('EventManager')
newInputEventHandler = EventManager.input_event.InputEventHandler(function, name, learnModeEnabled)
return newInputEventHandler
def registerGestureByString(self, function, name, gestureString, profile, application, learnModeEnabled = True, contextName = None):
gestureList = gestureString.split(',')
registeredGestures = []
for gesture in gestureList:
if gesture.startswith('kb:'):
shortcutString = gesture[3:]
registuredGesture = self.registerShortcutByString(function, name, shortcutString, profile, application, learnModeEnabled, contextName=contextName)
if registuredGesture:
registeredGestures.append(registuredGesture)
return registeredGestures
def registerShortcutByString(self, function, name, shortcutString, profile, application, learnModeEnabled = True, contextName = None):
keybindings = self.app.getDynamicApiManager().getAPI('Keybindings')
settings = self.app.getDynamicApiManager().getAPI('Settings')
resourceManager = self.app.getResourceManager()
clickCount = 0
cthulhuKey = False
shiftKey = False
ctrlKey = False
altKey = False
key = ''
shortcutList = shortcutString.split('+')
for shortcutElement in shortcutList:
shortcutElementLower = shortcutElement.lower()
if shortcutElementLower == 'press':
clickCount += 1
elif shortcutElement == 'cthulhu':
cthulhuKey = True
elif shortcutElementLower == 'shift':
shiftKey = True
elif shortcutElementLower == 'control':
ctrlKey = True
elif shortcutElementLower == 'alt':
altKey = True
else:
key = shortcutElementLower
if clickCount == 0:
clickCount = 1
if self.cthulhuKeyBindings == None:
self.cthulhuKeyBindings = keybindings.KeyBindings()
tryFunction = resource_manager.TryFunction(function)
newInputEventHandler = self.createInputEventHandler(tryFunction.runInputEvent, name, learnModeEnabled)
currModifierMask = keybindings.NO_MODIFIER_MASK
if cthulhuKey:
currModifierMask = currModifierMask | 1 << keybindings.MODIFIER_CTHULHU
if shiftKey:
currModifierMask = currModifierMask | 1 << Atspi.ModifierType.SHIFT
if altKey:
currModifierMask = currModifierMask | 1 << Atspi.ModifierType.ALT
if ctrlKey:
currModifierMask = currModifierMask | 1 << Atspi.ModifierType.CONTROL
newKeyBinding = keybindings.KeyBinding(key, keybindings.defaultModifierMask, currModifierMask, newInputEventHandler, clickCount)
self.cthulhuKeyBindings.add(newKeyBinding)
settings.keyBindingsMap["default"] = self.cthulhuKeyBindings
if contextName:
resourceContext = resourceManager.getResourceContext(contextName)
if resourceContext:
resourceEntry = resource_manager.ResourceEntry('keyboard', newKeyBinding, function, tryFunction, shortcutString)
resourceContext.addGesture(profile, application, newKeyBinding, resourceEntry)
return newKeyBinding
def unregisterShortcut(self, KeyBindingToRemove, contextName = None):
ok = False
keybindings = self.app.getDynamicApiManager().getAPI('Keybindings')
settings = self.app.getDynamicApiManager().getAPI('Settings')
resourceManager = self.app.getResourceManager()
if self.cthulhuKeyBindings == None:
self.cthulhuKeyBindings = keybindings.KeyBindings()
try:
self.cthulhuKeyBindings.remove(KeyBindingToRemove)
settings.keyBindingsMap["default"] = self.cthulhuKeyBindings
ok = True
except KeyError:
pass
if contextName:
resourceContext = resourceManager.getResourceContext(contextName)
if resourceContext:
resourceContext.removeGesture(KeyBindingToRemove)
return ok
def importModule(self, moduleName, moduleLocation):
if version in ["3.3","3.4"]:
return SourceFileLoader(moduleName, moduleLocation).load_module()
else:
spec = importlib.util.spec_from_file_location(moduleName, moduleLocation)
driver_mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(driver_mod)
return driver_mod

View File

@ -0,0 +1,6 @@
[Plugin]
Module=ByeCthulhu
Loader=python3
Name=Stop announcement for cthulhu
Description=Test plugin for cthulhu
Authors=Chrys chrys@linux-a11y.org

View File

@ -0,0 +1,27 @@
from cthulhu import plugin
import gi
gi.require_version('Peas', '1.0')
from gi.repository import GObject
from gi.repository import Peas
import time
class ByeCthulhu(GObject.Object, Peas.Activatable, plugin.Plugin):
#__gtype_name__ = 'ByeCthulhu'
object = GObject.Property(type=GObject.Object)
def __init__(self):
plugin.Plugin.__init__(self)
def do_activate(self):
API = self.object
self.connectSignal("stop-application-completed", self.process)
def do_deactivate(self):
API = self.object
def do_update_state(self):
API = self.object
def process(self, app):
messages = app.getDynamicApiManager().getAPI('Messages')
activeScript = app.getDynamicApiManager().getAPI('CthulhuState').activeScript
activeScript.presentationInterrupt()
activeScript.presentMessage(messages.STOP_CTHULHU, resetStyles=False)

View File

@ -1,7 +1,7 @@
cthulhu_python_PYTHON = \
__init__.py \
plugin.info \
plugin.py
ByeCthulhu.plugin \
ByeCthulhu.py
cthulhu_pythondir=$(pkgpythondir)/plugins/ByeCthulhu

View File

@ -1,25 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca

View File

@ -1,8 +0,0 @@
name = Bye Cthulhu
version = 1.0.0
description = Says goodbye when Cthulhu is shutting down
authors = Stormux <storm_dragon@stormux.org>
website = https://stormux.org
copyright = Copyright 2025
builtin = false
hidden = false

View File

@ -1,75 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2025 Stormux
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
"""Bye Cthulhu plugin for Cthulhu."""
import logging
import time
from cthulhu.plugin import Plugin, cthulhu_hookimpl
logger = logging.getLogger(__name__)
class ByeCthulhu(Plugin):
"""Plugin that speaks a goodbye message when Cthulhu is shutting down."""
def __init__(self, *args, **kwargs):
"""Initialize the plugin."""
super().__init__(*args, **kwargs)
logger.info("ByeCthulhu plugin initialized")
self._signal_handler_id = None
@cthulhu_hookimpl
def activate(self, plugin=None):
"""Activate the plugin."""
# Skip if this activation call isn't for us
if plugin is not None and plugin is not self:
return
logger.info("Activating ByeCthulhu plugin")
try:
# Connect to the stop-application-completed signal
signal_manager = self.app.getSignalManager()
self._signal_handler_id = signal_manager.connectSignal(
"stop-application-completed",
self.process,
"default" # Add profile parameter
)
except Exception as e:
logger.error(f"Error activating ByeCthulhu plugin: {e}")
@cthulhu_hookimpl
def deactivate(self, plugin=None):
"""Deactivate the plugin."""
# Skip if this deactivation call isn't for us
if plugin is not None and plugin is not self:
return
logger.info("Deactivating ByeCthulhu plugin")
try:
# Disconnect signal if we have an ID
if self._signal_handler_id is not None:
signal_manager = self.app.getSignalManager()
# Use disconnectSignalByFunction instead since disconnectSignal doesn't exist
signal_manager.disconnectSignalByFunction(
self.process
)
self._signal_handler_id = None
except Exception as e:
logger.error(f"Error deactivating ByeCthulhu plugin: {e}")
def process(self, app):
"""Process the stop-application-completed signal."""
try:
messages = app.getDynamicApiManager().getAPI('Messages')
state = app.getDynamicApiManager().getAPI('CthulhuState')
if state.activeScript:
state.activeScript.presentationInterrupt()
state.activeScript.presentMessage(messages.STOP_CTHULHU, resetStyles=False)
except Exception as e:
logger.error(f"Error in ByeCthulhu process: {e}")

View File

@ -0,0 +1,6 @@
[Plugin]
Module=CapsLockHack
Loader=python3
Name=Caps Lock Hack
Description=Fix Capslock sometimes switch on / off when its used as modifier
Authors=Chrys chrys@linux-a11y.org

View File

@ -0,0 +1,119 @@
from cthulhu import plugin
import gi
gi.require_version('Peas', '1.0')
from gi.repository import GObject
from gi.repository import Peas
from threading import Thread, Lock
import subprocess, time, re, os
class CapsLockHack(GObject.Object, Peas.Activatable, plugin.Plugin):
__gtype_name__ = 'CapsLockHack'
object = GObject.Property(type=GObject.Object)
def __init__(self):
plugin.Plugin.__init__(self)
self.lock = Lock()
self.active = False
self.workerThread = Thread(target=self.worker)
def do_activate(self):
API = self.object
"""Enable or disable use of the caps lock key as an Cthulhu modifier key."""
self.interpretCapsLineProg = re.compile(
r'^\s*interpret\s+Caps[_+]Lock[_+]AnyOfOrNone\s*\(all\)\s*{\s*$', re.I)
self.normalCapsLineProg = re.compile(
r'^\s*action\s*=\s*LockMods\s*\(\s*modifiers\s*=\s*Lock\s*\)\s*;\s*$', re.I)
self.interpretShiftLineProg = re.compile(
r'^\s*interpret\s+Shift[_+]Lock[_+]AnyOf\s*\(\s*Shift\s*\+\s*Lock\s*\)\s*{\s*$', re.I)
self.normalShiftLineProg = re.compile(
r'^\s*action\s*=\s*LockMods\s*\(\s*modifiers\s*=\s*Shift\s*\)\s*;\s*$', re.I)
self.disabledModLineProg = re.compile(
r'^\s*action\s*=\s*NoAction\s*\(\s*\)\s*;\s*$', re.I)
self.normalCapsLine = ' action= LockMods(modifiers=Lock);'
self.normalShiftLine = ' action= LockMods(modifiers=Shift);'
self.disabledModLine = ' action= NoAction();'
self.activateWorker()
def do_deactivate(self):
API = self.object
self.deactivateWorker()
def do_update_state(self):
API = self.object
def deactivateWorker(self):
with self.lock:
self.active = False
self.workerThread.join()
def activateWorker(self):
with self.lock:
self.active = True
self.workerThread.start()
def isActive(self):
with self.lock:
return self.active
def worker(self):
"""Makes an Cthulhu-specific Xmodmap so that the keys behave as we
need them to do. This is especially the case for the Cthulhu modifier.
"""
API = self.object
capsLockCleared = False
settings = API.app.getDynamicApiManager().getAPI('Settings')
time.sleep(3)
while self.isActive():
if "Caps_Lock" in settings.cthulhuModifierKeys \
or "Shift_Lock" in settings.cthulhuModifierKeys:
self.setCapsLockAsCthulhuModifier(True)
capsLockCleared = True
elif capsLockCleared:
self.setCapsLockAsCthulhuModifier(False)
capsLockCleared = False
time.sleep(1)
def setCapsLockAsCthulhuModifier(self, enable):
originalXmodmap = None
lines = None
try:
originalXmodmap = subprocess.check_output(['xkbcomp', os.environ['DISPLAY'], '-'])
lines = originalXmodmap.decode('UTF-8').split('\n')
except:
return
foundCapsInterpretSection = False
foundShiftInterpretSection = False
modified = False
for i, line in enumerate(lines):
if not foundCapsInterpretSection and not foundShiftInterpretSection:
if self.interpretCapsLineProg.match(line):
foundCapsInterpretSection = True
elif self.interpretShiftLineProg.match(line):
foundShiftInterpretSection = True
elif foundCapsInterpretSection:
if enable:
if self.normalCapsLineProg.match(line):
lines[i] = self.disabledModLine
modified = True
else:
if self.disabledModLineProg.match(line):
lines[i] = self.normalCapsLine
modified = True
if line.find('}'):
foundCapsInterpretSection = False
else: # foundShiftInterpretSection
if enable:
if self.normalShiftLineProg.match(line):
lines[i] = self.disabledModLine
modified = True
else:
if self.disabledModLineProg.match(line):
lines[i] = self.normalShiftLine
modified = True
if line.find('}'):
foundShiftInterpretSection = False
if modified:
newXmodMap = bytes('\n'.join(lines), 'UTF-8')
self.setXmodmap(newXmodMap)
def setXmodmap(self, xkbmap):
"""Set the keyboard map using xkbcomp."""
try:
p = subprocess.Popen(['xkbcomp', '-w0', '-', os.environ['DISPLAY']],
stdin=subprocess.PIPE, stdout=None, stderr=None)
p.communicate(xkbmap)
except:
pass

View File

@ -0,0 +1,7 @@
cthulhu_python_PYTHON = \
__init__.py \
CapsLockHack.plugin \
CapsLockHack.py
cthulhu_pythondir=$(pkgpythondir)/plugins/CapsLockHack

View File

@ -0,0 +1,14 @@
[Plugin]
Module=ClassicPreferences
Loader=python3
Name=Classic Preferences UI
Description=The classic preferences dialog
Authors=Chrys chrys@linux-a11y.org
Website=
Version=1.0
Copyright=
Builtin=true
Hidden=true
Depends=
Icon=
Help=

View File

@ -0,0 +1,84 @@
from cthulhu import plugin
import gi, time
gi.require_version('Peas', '1.0')
from gi.repository import GObject
from gi.repository import Peas
import importlib, os
import cthulhu_gui_prefs
class ClassicPreferences(GObject.Object, Peas.Activatable, plugin.Plugin):
#__gtype_name__ = 'ClassicPreferences'
object = GObject.Property(type=GObject.Object)
def __init__(self):
plugin.Plugin.__init__(self)
def do_activate(self):
API = self.object
self.connectSignal("setup-inputeventhandlers-completed", self.setupCompatBinding)
#self.setupCompatBinding(API.app)
def setupCompatBinding(self, app):
cmdnames = app.getDynamicApiManager().getAPI('Cmdnames')
inputEventHandlers = app.getDynamicApiManager().getAPI('inputEventHandlers')
inputEventHandlers['preferencesSettingsHandler'] = app.getAPIHelper().createInputEventHandler(self.showPreferencesGUI, cmdnames.SHOW_PREFERENCES_GUI)
inputEventHandlers['appPreferencesSettingsHandler'] = app.getAPIHelper().createInputEventHandler(self.showAppPreferencesGUI, cmdnames.SHOW_APP_PREFERENCES_GUI)
def do_deactivate(self):
API = self.object
inputEventHandlers = API.app.getDynamicApiManager().getAPI('inputEventHandlers')
del inputEventHandlers['preferencesSettingsHandler']
del inputEventHandlers['appPreferencesSettingsHandler']
def do_update_state(self):
API = self.object
def showAppPreferencesGUI(self, script=None, inputEvent=None):
"""Displays the user interface to configure the settings for a
specific applications within Cthulhu and set up those app-specific
user preferences using a GUI.
Returns True to indicate the input event has been consumed.
"""
API = self.object
cthulhu_state = API.app.getDynamicApiManager().getAPI('CthulhuState')
settings = API.app.getDynamicApiManager().getAPI('Settings')
_settingsManager = API.app.getDynamicApiManager().getAPI('SettingsManager').getManager()
_scriptManager = API.app.getDynamicApiManager().getAPI('ScriptManager').getManager()
prefs = {}
for key in settings.userCustomizableSettings:
prefs[key] = _settingsManager.getSetting(key)
script = script or cthulhu_state.activeScript
self._showPreferencesUI(script, prefs)
return True
def showPreferencesGUI(self, script=None, inputEvent=None):
"""Displays the user interface to configure Cthulhu and set up
user preferences using a GUI.
Returns True to indicate the input event has been consumed.
"""
API = self.object
cthulhu_state = API.app.getDynamicApiManager().getAPI('CthulhuState')
settings = API.app.getDynamicApiManager().getAPI('Settings')
_settingsManager = API.app.getDynamicApiManager().getAPI('SettingsManager').getManager()
_scriptManager = API.app.getDynamicApiManager().getAPI('ScriptManager').getManager()
debug = API.app.getDynamicApiManager().getAPI('Debug')
prefs = _settingsManager.getGeneralSettings(_settingsManager.profile)
script = _scriptManager.getDefaultScript()
self._showPreferencesUI(script, prefs)
return True
def _showPreferencesUI(self, script, prefs):
API = self.object
cthulhu_state = API.app.getDynamicApiManager().getAPI('CthulhuState')
debug = API.app.getDynamicApiManager().getAPI('Debug')
cthulhu_platform = API.app.getDynamicApiManager().getAPI('CthulhuPlatform')
if cthulhu_state.cthulhuOS:
cthulhu_state.cthulhuOS.showGUI()
return
uiFile = os.path.join(self.getModuleDir(),
"cthulhu-setup.ui")
cthulhu_state.cthulhuOS = cthulhu_gui_prefs.CthulhuSetupGUI(uiFile, "cthulhuSetupWindow", prefs, API.app)
cthulhu_state.cthulhuOS.init(script)
cthulhu_state.cthulhuOS.setTranslationContext(self.getTranslationContext())
cthulhu_state.cthulhuOS.showGUI()

View File

@ -0,0 +1,10 @@
cthulhu_python_PYTHON = \
__init__.py \
ClassicPreferences.plugin \
ClassicPreferences.py \
cthulhu_gui_prefs.py \
cthulhu_gui_profile.py \
cthulhu-setup.ui
cthulhu_pythondir=$(pkgpythondir)/plugins/ClassicPreferences

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,163 @@
# Cthulhu
#
# Copyright 2010 Consorcio Fernando de los Rios.
# Author: Javier Hernandez Antunez <jhernandez@emergya.es>
# Author: Alejandro Leiva <aleiva@emergya.es>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
"""Displays the Save Profile As dialog."""
__id__ = "$Id$"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2010 Consorcio Fernando de los Rios."
__license__ = "LGPL"
import locale
import sys
from gi.repository import Gtk
cthulhu_state = None
guilabels = None
OS = None
newProfile = None
app = None
class CthulhuProfileGUI(Gtk.Dialog):
def __init__(self, app):
"""Initialize the Cthulhu profile configuration GUI."""
self.app = app
global guilabels
global cthulhu_state
guilabels = self.app.getDynamicApiManager().getAPI('GuiLabels')
cthulhu_state = self.app.getDynamicApiManager().getAPI('CthulhuState')
Gtk.Dialog.__init__(self)
self.set_title(guilabels.PROFILE_SAVE_AS_TITLE)
self.set_has_resize_grip(False)
self.add_button('gtk-cancel', Gtk.ResponseType.CANCEL)
self.add_button('gtk-save', Gtk.ResponseType.ACCEPT)
grid = Gtk.Grid()
grid.set_property('margin', 12)
grid.set_row_spacing(10)
grid.set_column_spacing(10)
# Right now the content area is a GtkBox. We'll need to update
# this once GtkBox is fully deprecated.
contentArea = self.get_content_area()
contentArea.pack_start(grid, True, True, 0)
self.profileEntry = Gtk.Entry()
self.profileEntry.set_property('hexpand', True)
self.profileEntry.set_activates_default(True)
grid.attach(self.profileEntry, 1, 0, 1, 1)
label = Gtk.Label(guilabels.PROFILE_NAME_LABEL)
label.set_use_underline(True)
label.set_mnemonic_widget(self.profileEntry)
grid.attach(label, 0, 0, 1, 1)
defaultButton = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
defaultButton.set_property('can-default', True)
defaultButton.set_property('has-default', True)
self.connect('response', self.onResponse)
self.connect('destroy', self.onDestroy)
self.searchString = None
self.profileString = None
self.prefsDialog = None
self.translationContext = None
def init(self):
self.profileString = ''
def showGUI(self, prefsDialog):
"""Show the Save Profile As dialog."""
self.show_all()
self.prefsDialog = prefsDialog
self.profileEntry.set_text(self.profileString)
ts = 0
try:
ts = cthulhu_state.lastInputEvent.timestamp
except:
pass
if ts == 0:
ts = Gtk.get_current_event_time()
self.present_with_time(ts)
def onResponse(self, widget, response):
"""Signal handler for the responses emitted by the dialog."""
if response in [Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT]:
self.hide()
return
if response == Gtk.ResponseType.ACCEPT:
global newProfile
newProfile = self.profileEntry.get_text()
if newProfile:
self.destroy()
if self.prefsDialog:
self.prefsDialog.saveProfile(newProfile)
def onDestroy(self, widget):
"""Signal handler for the 'destroy' signal of the dialog."""
global OS
OS = None
def setTranslationContext(newTranslationContext):
global _, translationContext
translationContext = newTranslationContext
_ = newTranslationContext.gettext
def setApp(newApp):
global app
app = newApp
def showProfileUI(prefsDialog=None):
global OS
global newProfile
newProfile = None
if not OS:
OS = CthulhuProfileGUI(app)
OS.init()
OS.showGUI(prefsDialog)
def main():
locale.setlocale(locale.LC_ALL, '')
showProfileUI()
Gtk.main()
sys.exit(0)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,6 @@
[Plugin]
Module=Clipboard
Loader=python3
Name=Clipboard
Description=Present the content of the current clipboard
Authors=Chrys chrys@linux-a11y.org

View File

@ -0,0 +1,76 @@
from cthulhu import plugin
import gi, os
gi.require_version('Peas', '1.0')
from gi.repository import GObject
from gi.repository import Peas
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk
class Clipboard(GObject.Object, Peas.Activatable, plugin.Plugin):
#__gtype_name__ = 'Clipboard'
object = GObject.Property(type=GObject.Object)
def __init__(self):
plugin.Plugin.__init__(self)
def do_activate(self):
API = self.object
self.registerGestureByString(self.speakClipboard, _('clipboard'), 'kb:cthulhu+c')
def do_deactivate(self):
API = self.object
def do_update_state(self):
API = self.object
def speakClipboard(self, script=None, inputEvent=None):
API = self.object
Message = self.getClipboard()
API.app.getDynamicApiManager().getAPI('CthulhuState').activeScript.presentMessage(Message, resetStyles=False)
return True
def getClipboard(self):
Message = ""
FoundClipboardContent = False
# Get Clipboard
ClipboardObj = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
ClipboardText = ClipboardObj.wait_for_text()
ClipboardImage = ClipboardObj.wait_for_image()
ClipboardURI = ClipboardObj.wait_for_uris()
if (ClipboardText != None):
FoundClipboardContent = True
if (ClipboardObj.wait_is_uris_available()):
noOfObjects = 0
noOfFolder = 0
noOfFiles = 0
noOfDisks = 0
noOfLinks = 0
for Uri in ClipboardURI:
if Uri == '':
continue
noOfObjects += 1
uriWithoutProtocoll = Uri[Uri.find('://') + 3:]
Message += " " + Uri[Uri.rfind('/') + 1:] + " "
if (os.path.isdir(uriWithoutProtocoll)):
noOfFolder += 1
Message = Message + _("Folder") #Folder
if (os.path.isfile(uriWithoutProtocoll)):
noOfFiles += 1
Message = Message + _("File") #File
if (os.path.ismount(uriWithoutProtocoll)):
noOfDisks += 1
Message = Message + _("Disk") #Mountpoint
if (os.path.islink(uriWithoutProtocoll)):
noOfLinks += 1
Message = Message + _("Link") #Link
if (noOfObjects > 1):
Message = str(noOfObjects) + _(" Objects in clipboard ") + Message # X Objects in Clipboard Object Object
else:
Message = str(noOfObjects) + _(" Object in clipboard ") + Message # 1 Object in Clipboard Object
else:
Message = _("Text in clipboard ") + ClipboardText # Text in Clipboard
if (ClipboardImage != None):
FoundClipboardContent = True
Message = _("The clipboard contains a image") # Image is in Clipboard
if (not FoundClipboardContent):
Message = _("The clipboard is empty")
return Message

View File

@ -1,7 +1,7 @@
cthulhu_python_PYTHON = \
__init__.py \
plugin.info \
plugin.py
Clipboard.plugin \
Clipboard.py
cthulhu_pythondir=$(pkgpythondir)/plugins/Clipboard

View File

@ -1,25 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca

View File

@ -1,7 +0,0 @@
[Plugin]
Name = Clipboard
Module = Clipboard
Description = Present the content of the current clipboard
Authors = Storm Dragon <storm_dragon@stormux.org>
Version = 1.0
Category = Utilities

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