File indexing completed on 2024-11-24 05:00:24
0001 /* 0002 SPDX-FileCopyrightText: 2014 Weng Xuetian <wengxt@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 #include "xkblayoutmanager.h" 0007 #include <QDir> 0008 #include <QProcess> 0009 #include <QStringList> 0010 0011 const char *XKB_COMMAND = "setxkbmap"; 0012 const char *XKB_QUERY_ARG = "-query"; 0013 const char *XKB_LAYOUT_ARG = "-layout"; 0014 const char *XMODMAP_COMMAND = "xmodmap"; 0015 const char *XMODMAP_KNOWN_FILES[] = {".xmodmap", ".xmodmaprc", ".Xmodmap", ".Xmodmaprc"}; 0016 0017 XkbLayoutManager::XkbLayoutManager() 0018 : m_useXkbModmap(false) 0019 { 0020 } 0021 0022 void XkbLayoutManager::setLatinLayouts(const gchar **variants, gsize length) 0023 { 0024 m_latinLayouts.clear(); 0025 for (gsize i = 0; i < length; i++) { 0026 m_latinLayouts.insert(QString::fromUtf8(variants[i])); 0027 } 0028 } 0029 0030 void XkbLayoutManager::getLayout() 0031 { 0032 QProcess process; 0033 process.start(QString::fromLatin1(XKB_COMMAND), QStringList(QString::fromLatin1(XKB_QUERY_ARG))); 0034 process.waitForFinished(); 0035 const QByteArray output = process.readAllStandardOutput(); 0036 const QList<QByteArray> lines = output.split('\n'); 0037 for (const QByteArray &line : lines) { 0038 QByteArray element("layout:"); 0039 if (line.startsWith(element)) { 0040 m_defaultLayout = QString::fromLatin1(line.mid(element.length(), line.length())).trimmed(); 0041 } 0042 0043 element = "variant:"; 0044 if (line.startsWith(element)) { 0045 m_defaultVariant = QString::fromLatin1(line.mid(element.length(), line.length())).trimmed(); 0046 } 0047 0048 element = "options:"; 0049 if (line.startsWith(element)) { 0050 m_defaultOption = QString::fromLatin1(line.mid(element.length(), line.length())).trimmed(); 0051 } 0052 } 0053 } 0054 0055 void XkbLayoutManager::setLayout(IBusEngineDesc *desc) 0056 { 0057 const gchar *clayout = ibus_engine_desc_get_layout(desc); 0058 const gchar *cvariant = ibus_engine_desc_get_layout_variant(desc); 0059 const gchar *coption = ibus_engine_desc_get_layout_option(desc); 0060 0061 QString layout = QString::fromUtf8(clayout ? clayout : ""); 0062 QString variant = QString::fromUtf8(cvariant ? cvariant : ""); 0063 QString option = QString::fromUtf8(coption ? coption : ""); 0064 0065 if (layout == QLatin1String{"default"} && (variant.isEmpty() || variant == QLatin1String{"default"}) 0066 && (option.isEmpty() && option == QLatin1String{"default"})) { 0067 return; 0068 } 0069 0070 bool need_us_layout = false; 0071 if (variant != QLatin1String{"eng"}) { 0072 need_us_layout = m_latinLayouts.contains(layout); 0073 } 0074 if (!variant.isEmpty() && !need_us_layout) { 0075 need_us_layout = m_latinLayouts.contains(QLatin1String("%1(%2)").arg(layout, variant)); 0076 } 0077 0078 if (m_defaultLayout.isEmpty()) { 0079 getLayout(); 0080 } 0081 0082 if (layout.isEmpty() || layout == QLatin1String{"default"}) { 0083 layout = m_defaultLayout; 0084 variant = m_defaultVariant; 0085 } 0086 0087 if (layout.isEmpty()) { 0088 g_warning("Could not get the correct layout"); 0089 return; 0090 } 0091 0092 if (option.isEmpty() || option == QLatin1String{"default"}) { 0093 option = m_defaultOption; 0094 } else { 0095 if (!(m_defaultOption.split(QLatin1Char{','}).contains(option))) { 0096 option = QLatin1String("%1,%2").arg(m_defaultOption, option); 0097 } else { 0098 option = m_defaultOption; 0099 } 0100 } 0101 0102 if (need_us_layout) { 0103 layout += QLatin1String{",us"}; 0104 if (!variant.isEmpty()) { 0105 variant += QLatin1Char{','}; 0106 } 0107 } 0108 0109 QStringList args; 0110 args << QString::fromLatin1(XKB_LAYOUT_ARG); 0111 args << layout; 0112 if (!variant.isEmpty() && variant != QLatin1String("default")) { 0113 args << QStringLiteral("-variant"); 0114 args << variant; 0115 } 0116 if (!option.isEmpty() && option != QLatin1String("default")) { 0117 /*TODO: Need to get the session XKB options */ 0118 args << QStringLiteral("-option"); 0119 args << option; 0120 } 0121 0122 int exit_status = QProcess::execute(QString::fromLatin1(XKB_COMMAND), args); 0123 0124 if (exit_status != 0) { 0125 g_warning("Execute setxkbmap failed."); 0126 } 0127 0128 runXmodmap(); 0129 } 0130 0131 void XkbLayoutManager::runXmodmap() 0132 { 0133 if (!m_useXkbModmap) { 0134 return; 0135 } 0136 0137 QDir home = QDir::home(); 0138 for (size_t i = 0; i < sizeof(XMODMAP_KNOWN_FILES) / sizeof(XMODMAP_KNOWN_FILES[0]); i++) { 0139 if (home.exists(QString::fromLatin1(XMODMAP_KNOWN_FILES[i]))) { 0140 QProcess::startDetached(QString::fromLatin1(XMODMAP_COMMAND), QStringList{home.filePath(QString::fromLatin1(XMODMAP_KNOWN_FILES[i]))}); 0141 break; 0142 } 0143 } 0144 } 0145 0146 void XkbLayoutManager::setUseXkbModmap(bool use) 0147 { 0148 m_useXkbModmap = use; 0149 }