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