File indexing completed on 2024-05-19 05:36:13
0001 /* 0002 SPDX-FileCopyrightText: 2010 Andriy Rysin <rysin@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "xkb_helper.h" 0008 #include "debug.h" 0009 0010 #include <QDebug> 0011 #include <QDir> 0012 #include <QElapsedTimer> 0013 #include <QFile> 0014 #include <QProcess> 0015 #include <QStandardPaths> 0016 #include <QString> 0017 #include <QTime> 0018 #include <QtGui/private/qtx11extras_p.h> 0019 0020 #include "keyboard_config.h" 0021 #include "keyboardsettings.h" 0022 0023 static const char SETXKBMAP_EXEC[] = "setxkbmap"; 0024 static const char XMODMAP_EXEC[] = "xmodmap"; 0025 0026 static bool setxkbmapNotFound = false; 0027 static QString setxkbmapExe; 0028 0029 static bool xmodmapNotFound = false; 0030 static QString xmodmapExe; 0031 0032 static const QString COMMAND_OPTIONS_SEPARATOR(QStringLiteral(",")); 0033 0034 static QString getSetxkbmapExe() 0035 { 0036 if (setxkbmapNotFound) 0037 return QString(); 0038 0039 if (setxkbmapExe.isEmpty()) { 0040 setxkbmapExe = QStandardPaths::findExecutable(SETXKBMAP_EXEC); 0041 if (setxkbmapExe.isEmpty()) { 0042 setxkbmapNotFound = true; 0043 qCCritical(KCM_KEYBOARD) << "Can't find" << SETXKBMAP_EXEC << "- keyboard layouts won't be configured"; 0044 return QString(); 0045 } 0046 } 0047 return setxkbmapExe; 0048 } 0049 0050 static void executeXmodmap(const QString &configFileName) 0051 { 0052 if (xmodmapNotFound) 0053 return; 0054 0055 if (QFile(configFileName).exists()) { 0056 if (xmodmapExe.isEmpty()) { 0057 xmodmapExe = QStandardPaths::findExecutable(XMODMAP_EXEC); 0058 if (xmodmapExe.isEmpty()) { 0059 xmodmapNotFound = true; 0060 qCCritical(KCM_KEYBOARD) << "Can't find" << XMODMAP_EXEC << "- xmodmap files won't be run"; 0061 return; 0062 } 0063 } 0064 0065 qCDebug(KCM_KEYBOARD) << "Executing" << xmodmapExe << configFileName; 0066 const int res = QProcess::execute(xmodmapExe, QStringList{configFileName}); 0067 if (res != 0) { 0068 qCCritical(KCM_KEYBOARD) << "Failed with return code:" << res; 0069 } 0070 } 0071 } 0072 0073 static void restoreXmodmap() 0074 { 0075 // TODO: is just home .Xmodmap enough or should system be involved too? 0076 // QString configFileName = QDir("/etc/X11/xinit").filePath(".Xmodmap"); 0077 // executeXmodmap(configFileName); 0078 QString configFileName = QDir::home().filePath(QStringLiteral(".Xmodmap")); 0079 executeXmodmap(configFileName); 0080 } 0081 0082 // TODO: make private 0083 bool XkbHelper::runConfigLayoutCommand(const QStringList &setxkbmapCommandArguments) 0084 { 0085 QElapsedTimer timer; 0086 timer.start(); 0087 0088 const auto setxkbmapExe = getSetxkbmapExe(); 0089 if (setxkbmapExe.isEmpty()) { 0090 return false; 0091 } 0092 0093 qCDebug(KCM_KEYBOARD) << "Running" << setxkbmapExe << setxkbmapCommandArguments.join(QLatin1Char(' ')); 0094 0095 const int res = QProcess::execute(setxkbmapExe, setxkbmapCommandArguments); 0096 if (res != 0) { 0097 qCCritical(KCM_KEYBOARD) << "Failed with return code:" << res; 0098 return false; 0099 } 0100 0101 // restore Xmodmap mapping reset by setxkbmap 0102 qCDebug(KCM_KEYBOARD) << "Executed successfully in " << timer.elapsed() << "ms"; 0103 restoreXmodmap(); 0104 qCDebug(KCM_KEYBOARD) << "\t and with xmodmap" << timer.elapsed() << "ms"; 0105 return true; 0106 } 0107 0108 bool XkbHelper::initializeKeyboardLayouts(const QList<LayoutUnit> &layoutUnits) 0109 { 0110 QStringList layouts; 0111 QStringList variants; 0112 for (const auto &layoutUnit : layoutUnits) { 0113 layouts.append(layoutUnit.layout()); 0114 variants.append(layoutUnit.variant()); 0115 } 0116 0117 QStringList setxkbmapCommandArguments; 0118 setxkbmapCommandArguments.append(QStringLiteral("-layout")); 0119 setxkbmapCommandArguments.append(layouts.join(COMMAND_OPTIONS_SEPARATOR)); 0120 if (!variants.join(QString()).isEmpty()) { 0121 setxkbmapCommandArguments.append(QStringLiteral("-variant")); 0122 setxkbmapCommandArguments.append(variants.join(COMMAND_OPTIONS_SEPARATOR)); 0123 } 0124 0125 return runConfigLayoutCommand(setxkbmapCommandArguments); 0126 } 0127 0128 bool XkbHelper::initializeKeyboardLayouts(KeyboardConfig &config) 0129 { 0130 QStringList setxkbmapCommandArguments; 0131 if (!config.keyboardModel().isEmpty()) { 0132 XkbConfig xkbConfig; 0133 X11Helper::getGroupNames(QX11Info::display(), &xkbConfig, X11Helper::MODEL_ONLY); 0134 if (xkbConfig.keyboardModel != config.keyboardModel()) { 0135 setxkbmapCommandArguments.append(QStringLiteral("-model")); 0136 setxkbmapCommandArguments.append(config.keyboardModel()); 0137 } 0138 } 0139 if (config.configureLayouts()) { 0140 QStringList layouts; 0141 QStringList variants; 0142 const QList<LayoutUnit> defaultLayouts = config.getDefaultLayouts(); 0143 for (const auto &layoutUnit : defaultLayouts) { 0144 layouts.append(layoutUnit.layout()); 0145 variants.append(layoutUnit.variant()); 0146 } 0147 0148 setxkbmapCommandArguments.append(QStringLiteral("-layout")); 0149 setxkbmapCommandArguments.append(layouts.join(COMMAND_OPTIONS_SEPARATOR)); 0150 if (!variants.join(QString()).isEmpty()) { 0151 setxkbmapCommandArguments.append(QStringLiteral("-variant")); 0152 setxkbmapCommandArguments.append(variants.join(COMMAND_OPTIONS_SEPARATOR)); 0153 } 0154 } 0155 if (config.resetOldXkbOptions()) { 0156 // Pass -option "" to clear previously set options 0157 setxkbmapCommandArguments.append(QStringLiteral("-option")); 0158 setxkbmapCommandArguments.append(QStringLiteral("")); 0159 } 0160 const QStringList xkbOpts = config.xkbOptions(); 0161 for (const auto &option : xkbOpts) { 0162 setxkbmapCommandArguments.append(QStringLiteral("-option")); 0163 setxkbmapCommandArguments.append(option); 0164 } 0165 0166 if (!setxkbmapCommandArguments.isEmpty()) { 0167 return runConfigLayoutCommand(setxkbmapCommandArguments); 0168 if (config.configureLayouts()) { 0169 X11Helper::setDefaultLayout(); 0170 } 0171 } 0172 return false; 0173 }