File indexing completed on 2024-05-12 17:07:06
0001 /* 0002 SPDX-FileCopyrightText: 2000 Matthias Hölzer-Klüpfel <hoelzer@kde.org> 0003 SPDX-FileCopyrightText: 2014 Frederik Gladhorn <gladhorn@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "kcmaccess.h" 0009 0010 #include <math.h> 0011 #include <stdlib.h> 0012 0013 #include <QApplication> 0014 #include <QFileDialog> 0015 #include <QProcess> 0016 #include <QQuickItem> 0017 #include <QStandardPaths> 0018 #include <QWindow> 0019 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0020 #include <QX11Info> 0021 #else 0022 #include <QtGui/private/qtx11extras_p.h> 0023 #endif 0024 0025 #include <KConfigGroup> 0026 #include <KKeyServer> 0027 #include <KLocalizedString> 0028 #include <KNotifyConfigWidget> 0029 #include <KPluginFactory> 0030 #include <KSharedConfig> 0031 #include <X11/XKBlib.h> 0032 #include <X11/Xlib.h> 0033 0034 #define XK_MISCELLANY 0035 #define XK_XKB_KEYS 0036 #include <X11/keysymdef.h> 0037 0038 #include "kcmaccessibilitybell.h" 0039 #include "kcmaccessibilitydata.h" 0040 #include "kcmaccessibilitykeyboard.h" 0041 #include "kcmaccessibilitykeyboardfilters.h" 0042 #include "kcmaccessibilitymouse.h" 0043 #include "kcmaccessibilityscreenreader.h" 0044 0045 K_PLUGIN_FACTORY_WITH_JSON(KCMAccessFactory, "kcm_access.json", registerPlugin<KAccessConfig>(); registerPlugin<AccessibilityData>();) 0046 0047 QString mouseKeysShortcut(Display *display) 0048 { 0049 // Calculate the keycode 0050 KeySym sym = XK_MouseKeys_Enable; 0051 KeyCode code = XKeysymToKeycode(display, sym); 0052 if (code == 0) { 0053 sym = XK_Pointer_EnableKeys; 0054 code = XKeysymToKeycode(display, sym); 0055 if (code == 0) 0056 return QString(); // No shortcut available? 0057 } 0058 0059 // Calculate the modifiers by searching the keysym in the X keyboard mapping 0060 XkbDescPtr xkbdesc = XkbGetMap(display, XkbKeyTypesMask | XkbKeySymsMask, XkbUseCoreKbd); 0061 if (!xkbdesc) 0062 return QString(); // Failed to obtain the mapping from server 0063 0064 bool found = false; 0065 unsigned char modifiers = 0; 0066 int groups = XkbKeyNumGroups(xkbdesc, code); 0067 for (int grp = 0; grp < groups && !found; grp++) { 0068 int levels = XkbKeyGroupWidth(xkbdesc, code, grp); 0069 for (int level = 0; level < levels && !found; level++) { 0070 if (sym == XkbKeySymEntry(xkbdesc, code, level, grp)) { 0071 // keysym found => determine modifiers 0072 int typeIdx = xkbdesc->map->key_sym_map[code].kt_index[grp]; 0073 XkbKeyTypePtr type = &(xkbdesc->map->types[typeIdx]); 0074 for (int i = 0; i < type->map_count && !found; i++) { 0075 if (type->map[i].active && (type->map[i].level == level)) { 0076 modifiers = type->map[i].mods.mask; 0077 found = true; 0078 } 0079 } 0080 } 0081 } 0082 } 0083 XkbFreeClientMap(xkbdesc, 0, true); 0084 0085 if (!found) 0086 return QString(); // Somehow the keycode -> keysym mapping is flawed 0087 0088 XEvent ev; 0089 ev.type = KeyPress; 0090 ev.xkey.display = display; 0091 ev.xkey.keycode = code; 0092 ev.xkey.state = 0; 0093 int key; 0094 KKeyServer::xEventToQt(&ev, &key); 0095 QString keyname = QKeySequence(key).toString(); 0096 0097 unsigned int AltMask = KKeyServer::modXAlt(); 0098 unsigned int WinMask = KKeyServer::modXMeta(); 0099 unsigned int NumMask = KKeyServer::modXNumLock(); 0100 unsigned int ScrollMask = KKeyServer::modXScrollLock(); 0101 0102 unsigned int MetaMask = XkbKeysymToModifiers(display, XK_Meta_L); 0103 unsigned int SuperMask = XkbKeysymToModifiers(display, XK_Super_L); 0104 unsigned int HyperMask = XkbKeysymToModifiers(display, XK_Hyper_L); 0105 unsigned int AltGrMask = XkbKeysymToModifiers(display, XK_Mode_switch) | XkbKeysymToModifiers(display, XK_ISO_Level3_Shift) 0106 | XkbKeysymToModifiers(display, XK_ISO_Level3_Latch) | XkbKeysymToModifiers(display, XK_ISO_Level3_Lock); 0107 0108 unsigned int mods = ShiftMask | ControlMask | AltMask | WinMask | LockMask | NumMask | ScrollMask; 0109 0110 AltGrMask &= ~mods; 0111 MetaMask &= ~(mods | AltGrMask); 0112 SuperMask &= ~(mods | AltGrMask | MetaMask); 0113 HyperMask &= ~(mods | AltGrMask | MetaMask | SuperMask); 0114 0115 if ((modifiers & AltGrMask) != 0) 0116 keyname = i18n("AltGraph") + QLatin1Char('+') + keyname; 0117 if ((modifiers & HyperMask) != 0) 0118 keyname = i18n("Hyper") + QLatin1Char('+') + keyname; 0119 if ((modifiers & SuperMask) != 0) 0120 keyname = i18n("Super") + QLatin1Char('+') + keyname; 0121 if ((modifiers & WinMask) != 0) 0122 keyname = QKeySequence(Qt::META).toString() + QLatin1Char('+') + keyname; 0123 if ((modifiers & AltMask) != 0) 0124 keyname = QKeySequence(Qt::ALT).toString() + QLatin1Char('+') + keyname; 0125 if ((modifiers & ControlMask) != 0) 0126 keyname = QKeySequence(Qt::CTRL).toString() + QLatin1Char('+') + keyname; 0127 if ((modifiers & ShiftMask) != 0) 0128 keyname = QKeySequence(Qt::SHIFT).toString() + QLatin1Char('+') + keyname; 0129 0130 return modifiers & ScrollMask & LockMask & NumMask ? i18n("Press %1 while NumLock, CapsLock and ScrollLock are active", keyname) 0131 : modifiers & ScrollMask & LockMask ? i18n("Press %1 while CapsLock and ScrollLock are active", keyname) 0132 : modifiers & ScrollMask & NumMask ? i18n("Press %1 while NumLock and ScrollLock are active", keyname) 0133 : modifiers & ScrollMask ? i18n("Press %1 while ScrollLock is active", keyname) 0134 : modifiers & LockMask & NumMask ? i18n("Press %1 while NumLock and CapsLock are active", keyname) 0135 : modifiers & LockMask ? i18n("Press %1 while CapsLock is active", keyname) 0136 : modifiers & NumMask ? i18n("Press %1 while NumLock is active", keyname) 0137 : i18n("Press %1", keyname); 0138 } 0139 0140 KAccessConfig::KAccessConfig(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) 0141 : KQuickAddons::ManagedConfigModule(parent, metaData, args) 0142 , m_data(new AccessibilityData(this)) 0143 , m_desktopShortcutInfo(QX11Info::isPlatformX11() ? mouseKeysShortcut(QX11Info::display()) : QString()) 0144 { 0145 qmlRegisterAnonymousType<MouseSettings>("org.kde.plasma.access.kcm", 0); 0146 qmlRegisterAnonymousType<BellSettings>("org.kde.plasma.access.kcm", 0); 0147 qmlRegisterAnonymousType<KeyboardSettings>("org.kde.plasma.access.kcm", 0); 0148 qmlRegisterAnonymousType<KeyboardFiltersSettings>("org.kde.plasma.access.kcm", 0); 0149 qmlRegisterAnonymousType<ScreenReaderSettings>("org.kde.plasma.access.kcm", 0); 0150 0151 int tryOrcaRun = QProcess::execute(QStringLiteral("orca"), {QStringLiteral("--version")}); 0152 m_screenReaderInstalled = tryOrcaRun != -2; 0153 0154 setButtons(ConfigModule::Apply | ConfigModule::Default | ConfigModule::Help); 0155 0156 connect(m_data->bellSettings(), &BellSettings::configChanged, this, &KAccessConfig::bellIsDefaultsChanged); 0157 connect(m_data->mouseSettings(), &MouseSettings::configChanged, this, &KAccessConfig::mouseIsDefaultsChanged); 0158 connect(m_data->keyboardFiltersSettings(), &ScreenReaderSettings::configChanged, this, &KAccessConfig::keyboardFiltersIsDefaultsChanged); 0159 connect(m_data->keyboardSettings(), &ScreenReaderSettings::configChanged, this, &KAccessConfig::keyboardModifiersIsDefaultsChanged); 0160 connect(m_data->screenReaderSettings(), &ScreenReaderSettings::configChanged, this, &KAccessConfig::screenReaderIsDefaultsChanged); 0161 } 0162 0163 KAccessConfig::~KAccessConfig() 0164 { 0165 } 0166 0167 void KAccessConfig::configureKNotify() 0168 { 0169 KNotifyConfigWidget::configure(QApplication::activeWindow(), QStringLiteral("kaccess")); 0170 } 0171 0172 void KAccessConfig::launchOrcaConfiguration() 0173 { 0174 const QStringList gsettingArgs = {QStringLiteral("set"), 0175 QStringLiteral("org.gnome.desktop.a11y.applications"), 0176 QStringLiteral("screen-reader-enabled"), 0177 QStringLiteral("true")}; 0178 0179 int ret = QProcess::execute(QStringLiteral("gsettings"), gsettingArgs); 0180 if (ret) { 0181 const QString errorStr = QLatin1String("gsettings ") + gsettingArgs.join(QLatin1Char(' ')); 0182 setOrcaLaunchFeedback(i18n("Could not set gsettings for Orca: \"%1\" failed", errorStr)); 0183 return; 0184 } 0185 0186 qint64 pid = 0; 0187 bool started = QProcess::startDetached(QStringLiteral("orca"), {QStringLiteral("--setup")}, QString(), &pid); 0188 if (!started) { 0189 setOrcaLaunchFeedback(i18n("Error: Could not launch \"orca --setup\"")); 0190 } 0191 } 0192 0193 void KAccessConfig::save() 0194 { 0195 ManagedConfigModule::save(); 0196 0197 if (bellSettings()->systemBell() || bellSettings()->customBell() || bellSettings()->visibleBell()) { 0198 KConfig _cfg(QStringLiteral("kdeglobals"), KConfig::NoGlobals); 0199 KConfigGroup cfg(&_cfg, "General"); 0200 cfg.writeEntry("UseSystemBell", true); 0201 cfg.sync(); 0202 } 0203 0204 // make kaccess reread the configuration 0205 // turning a11y features off needs to be done by kaccess 0206 // so run it to clear any enabled features and it will exit if it should 0207 QProcess::startDetached(QStringLiteral("kaccess"), {}); 0208 } 0209 0210 QString KAccessConfig::orcaLaunchFeedback() const 0211 { 0212 return m_orcaLaunchFeedback; 0213 } 0214 0215 void KAccessConfig::setOrcaLaunchFeedback(const QString &value) 0216 { 0217 if (m_orcaLaunchFeedback != value) { 0218 m_orcaLaunchFeedback = value; 0219 Q_EMIT orcaLaunchFeedbackChanged(); 0220 } 0221 } 0222 0223 bool KAccessConfig::orcaInstalled() 0224 { 0225 int tryOrcaRun = QProcess::execute(QStringLiteral("orca"), {QStringLiteral("--version")}); 0226 // If the process cannot be started, -2 is returned. 0227 return tryOrcaRun != -2; 0228 } 0229 0230 MouseSettings *KAccessConfig::mouseSettings() const 0231 { 0232 return m_data->mouseSettings(); 0233 } 0234 0235 BellSettings *KAccessConfig::bellSettings() const 0236 { 0237 return m_data->bellSettings(); 0238 } 0239 0240 KeyboardSettings *KAccessConfig::keyboardSettings() const 0241 { 0242 return m_data->keyboardSettings(); 0243 } 0244 0245 KeyboardFiltersSettings *KAccessConfig::keyboardFiltersSettings() const 0246 { 0247 return m_data->keyboardFiltersSettings(); 0248 } 0249 0250 ScreenReaderSettings *KAccessConfig::screenReaderSettings() const 0251 { 0252 return m_data->screenReaderSettings(); 0253 } 0254 0255 bool KAccessConfig::bellIsDefaults() const 0256 { 0257 return bellSettings()->isDefaults(); 0258 } 0259 0260 bool KAccessConfig::mouseIsDefaults() const 0261 { 0262 return mouseSettings()->isDefaults(); 0263 } 0264 0265 bool KAccessConfig::keyboardFiltersIsDefaults() const 0266 { 0267 return keyboardFiltersSettings()->isDefaults(); 0268 } 0269 0270 bool KAccessConfig::keyboardModifiersIsDefaults() const 0271 { 0272 return keyboardSettings()->isDefaults(); 0273 } 0274 0275 bool KAccessConfig::screenReaderIsDefaults() const 0276 { 0277 return screenReaderSettings()->isDefaults(); 0278 } 0279 0280 #include "kcmaccess.moc" 0281 #include "moc_kcmaccess.cpp"