File indexing completed on 2024-05-26 05:37:03
0001 /* 0002 SPDX-FileCopyrightText: 2019 Harald Sitter <sitter@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include <QCommandLineParser> 0008 #include <QDebug> 0009 #include <QProcess> 0010 #include <QQmlApplicationEngine> 0011 #include <QQmlContext> 0012 #include <QtGui/private/qtx11extras_p.h> 0013 0014 #include <KAboutData> 0015 #include <KLocalizedString> 0016 0017 #include <memory> 0018 0019 #include "application.h" 0020 #include "config-workspace.h" 0021 #include "doodad.h" 0022 #include "geometry.h" 0023 0024 // kind-of copy from xkb_rules.cpp (less complicated) 0025 static QString getRulesName() 0026 { 0027 XkbRF_VarDefsRec vd; 0028 char *tmp = nullptr; 0029 0030 if (XkbRF_GetNamesProp(QX11Info::display(), &tmp, &vd) && tmp != nullptr) { 0031 const QString name(tmp); 0032 XFree(tmp); 0033 return name; 0034 } 0035 0036 return QStringLiteral("evdev"); // default to evdev 0037 } 0038 0039 static QString findXkbRulesFile() 0040 { 0041 const QString rulesName = getRulesName(); 0042 return QStringLiteral("%1/rules/%2").arg(XKBDIR, rulesName); 0043 } 0044 0045 int main(int argc, char *argv[]) 0046 { 0047 setenv("QT_QPA_PLATFORM", "xcb", 1); 0048 Application app(argc, argv); 0049 Q_ASSERT(app.platformName() == QStringLiteral("xcb")); 0050 0051 KAboutData aboutData(QStringLiteral("tastenbrett"), 0052 i18nc("app display name", "Keyboard Preview"), 0053 QStringLiteral("1.0"), 0054 i18nc("app description", "Keyboard layout visualization"), 0055 KAboutLicense::GPL); 0056 KAboutData::setApplicationData(aboutData); 0057 0058 QCommandLineParser parser; 0059 aboutData.setupCommandLine(&parser); 0060 0061 QCommandLineOption modelOption(QStringList{"m", "model"}, {}, QStringLiteral("MODEL")); 0062 parser.addOption(modelOption); 0063 QCommandLineOption layoutOption(QStringList{"l", "layout"}, {}, QStringLiteral("LAYOUT")); 0064 parser.addOption(layoutOption); 0065 QCommandLineOption variantOption(QStringList{"a", "variant"}, {}, QStringLiteral("VARIANT")); 0066 parser.addOption(variantOption); 0067 QCommandLineOption optionsOption(QStringList{"o", "options"}, {}, QStringLiteral("OPTIONS")); 0068 parser.addOption(optionsOption); 0069 parser.process(app); 0070 aboutData.processCommandLine(&parser); 0071 0072 XkbRF_VarDefsRec varDefs; 0073 memset(&varDefs, 0, sizeof(XkbRF_VarDefsRec)); 0074 0075 // Models worth testing for obvious mistakes: 0076 // pc104, tm2020 (fancy), kinesis (fancy) 0077 QString model = parser.value(modelOption); 0078 const QString layout = parser.value(layoutOption); 0079 const QString variant = parser.value(variantOption); 0080 const QString options = parser.value(optionsOption); 0081 0082 // Hold these so so we can pass data into xkb getter. 0083 QByteArray modelArray = model.toUtf8(); 0084 QByteArray layoutArray = layout.toUtf8(); 0085 QByteArray variantArray = variant.toUtf8(); 0086 QByteArray optionsArray = options.toUtf8(); 0087 0088 varDefs.model = modelArray.data(); 0089 varDefs.layout = layoutArray.data(); 0090 varDefs.variant = variantArray.data(); 0091 varDefs.options = optionsArray.data(); 0092 0093 XkbRF_RulesPtr rules = XkbRF_Load(findXkbRulesFile().toUtf8().data(), // needs to be non-const! 0094 qgetenv("LOCALE").data(), 0095 True, 0096 True); 0097 0098 Q_ASSERT(rules); 0099 std::unique_ptr<XkbRF_RulesRec, std::function<void(XkbRF_RulesPtr)>> rulesCleanup(rules, [](XkbRF_RulesPtr obj) { 0100 XkbRF_Free(obj, True); 0101 }); 0102 0103 XkbComponentNamesRec componentNames; 0104 memset(&componentNames, 0, sizeof(XkbComponentNamesRec)); 0105 XkbRF_GetComponents(rules, &varDefs, &componentNames); 0106 0107 QString errorDescription; 0108 QString errorDetails; 0109 std::unique_ptr<Geometry> geometry; 0110 0111 XkbDescPtr xkb = 0112 XkbGetKeyboardByName(QX11Info::display(), 0113 XkbUseCoreKbd, 0114 &componentNames, 0115 0, 0116 XkbGBN_GeometryMask | XkbGBN_KeyNamesMask | XkbGBN_OtherNamesMask | XkbGBN_ClientSymbolsMask | XkbGBN_IndicatorMapMask, 0117 false); 0118 if (!xkb) { 0119 QProcess setxkbmap; 0120 QProcess xkbcomp; 0121 0122 setxkbmap.setStandardOutputProcess(&xkbcomp); 0123 xkbcomp.setProcessChannelMode(QProcess::MergedChannels); // combine in single channel 0124 setxkbmap.start(QStringLiteral("setxkbmap"), 0125 {QStringLiteral("-print"), 0126 QStringLiteral("-model"), 0127 model, 0128 QStringLiteral("-layout"), 0129 layout, 0130 QStringLiteral("-variant"), 0131 variant, 0132 QStringLiteral("-option"), 0133 options}); 0134 xkbcomp.start(QStringLiteral("xkbcomp"), {QStringLiteral("-")}); 0135 setxkbmap.waitForFinished(); 0136 xkbcomp.waitForFinished(); 0137 0138 errorDescription = i18nc("@label", 0139 "The keyboard geometry failed to load." 0140 " This often indicates that the selected model does not support a specific layout" 0141 " or layout variant." 0142 " This problem will likely also present when you try to use this combination of model, layout and variant."); 0143 errorDetails = xkbcomp.readAllStandardOutput(); 0144 } else { 0145 Q_ASSERT(xkb); 0146 geometry.reset(new Geometry(xkb->geom, xkb)); 0147 } 0148 0149 // Register the doodads so we can perform easy type checks with them 0150 // and determine how to render the individual object. 0151 const char uri[] = "org.kde.tastenbrett.private"; 0152 qmlRegisterUncreatableType<TextDoodad>(uri, 1, 0, "TextDoodad", QString()); 0153 qmlRegisterUncreatableType<LogoDoodad>(uri, 1, 0, "LogoDoodad", QString()); 0154 qmlRegisterUncreatableType<ShapeDoodad>(uri, 1, 0, "ShapeDoodad", QString()); 0155 qmlRegisterUncreatableType<IndicatorDoodad>(uri, 1, 0, "IndicatorDoodad", QString()); 0156 0157 // The way this is currently written we need the engine after 0158 // we have a geometry, lest geometry is dtor'd before the engine 0159 // causing exhaustive error spam on shutdown. 0160 // Also, the above stuff is blocking, but optimizing it is hardly 0161 // worth the effort. The Xkb calls altogether take ~8ms (I am not 0162 // certain putting xkb into a qfuture is thread-safe or even 0163 // faster). Constructing our QObjects takes 1ms. 0164 QQmlApplicationEngine engine; 0165 const QUrl url(QStringLiteral("qrc:/qml/main.qml")); 0166 QObject::connect( 0167 &engine, 0168 &QQmlApplicationEngine::objectCreated, 0169 &app, 0170 [url](QObject *obj, const QUrl &objUrl) { 0171 if (!obj && url == objUrl) 0172 QCoreApplication::exit(-1); 0173 }, 0174 Qt::QueuedConnection); 0175 engine.rootContext()->setContextProperty("geometry", geometry.get()); 0176 engine.rootContext()->setContextProperty("errorDescription", errorDescription); 0177 engine.rootContext()->setContextProperty("errorDetails", errorDetails); 0178 engine.load(url); 0179 0180 return app.exec(); 0181 } 0182 0183 #include "main.moc"