SPDX-FileCopyrightText: 2019 Harald Sitter <sitter@kde.org>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include <QCommandLineParser>
#include <QDebug>
#include <QProcess>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QtGui/private/qtx11extras_p.h>
#include <KAboutData>
#include <KLocalizedString>
#include <memory>
#include "application.h"
#include "config-workspace.h"
#include "doodad.h"
#include "geometry.h"
static QString getRulesName()
{
XkbRF_VarDefsRec vd;
char *tmp = nullptr;
if (XkbRF_GetNamesProp(QX11Info::display(), &tmp, &vd) && tmp != nullptr) {
const QString name(tmp);
XFree(tmp);
return name;
}
return QStringLiteral("evdev");
}
static QString findXkbRulesFile()
{
const QString rulesName = getRulesName();
return QStringLiteral("%1/rules/%2").arg(XKBDIR, rulesName);
}
int main(int argc, char *argv[])
{
setenv("QT_QPA_PLATFORM", "xcb", 1);
Application app(argc, argv);
Q_ASSERT(app.platformName() == QStringLiteral("xcb"));
KAboutData aboutData(QStringLiteral("tastenbrett"),
i18nc("app display name", "Keyboard Preview"),
QStringLiteral("1.0"),
i18nc("app description", "Keyboard layout visualization"),
KAboutLicense::GPL);
KAboutData::setApplicationData(aboutData);
QCommandLineParser parser;
aboutData.setupCommandLine(&parser);
QCommandLineOption modelOption(QStringList{"m", "model"}, {}, QStringLiteral("MODEL"));
parser.addOption(modelOption);
QCommandLineOption layoutOption(QStringList{"l", "layout"}, {}, QStringLiteral("LAYOUT"));
parser.addOption(layoutOption);
QCommandLineOption variantOption(QStringList{"a", "variant"}, {}, QStringLiteral("VARIANT"));
parser.addOption(variantOption);
QCommandLineOption optionsOption(QStringList{"o", "options"}, {}, QStringLiteral("OPTIONS"));
parser.addOption(optionsOption);
parser.process(app);
aboutData.processCommandLine(&parser);
XkbRF_VarDefsRec varDefs;
memset(&varDefs, 0, sizeof(XkbRF_VarDefsRec));
QString model = parser.value(modelOption);
const QString layout = parser.value(layoutOption);
const QString variant = parser.value(variantOption);
const QString options = parser.value(optionsOption);
QByteArray modelArray = model.toUtf8();
QByteArray layoutArray = layout.toUtf8();
QByteArray variantArray = variant.toUtf8();
QByteArray optionsArray = options.toUtf8();
varDefs.model = modelArray.data();
varDefs.layout = layoutArray.data();
varDefs.variant = variantArray.data();
varDefs.options = optionsArray.data();
XkbRF_RulesPtr rules = XkbRF_Load(findXkbRulesFile().toUtf8().data(),
qgetenv("LOCALE").data(),
True,
True);
Q_ASSERT(rules);
std::unique_ptr<XkbRF_RulesRec, std::function<void(XkbRF_RulesPtr)>> rulesCleanup(rules, [](XkbRF_RulesPtr obj) {
XkbRF_Free(obj, True);
});
XkbComponentNamesRec componentNames;
memset(&componentNames, 0, sizeof(XkbComponentNamesRec));
XkbRF_GetComponents(rules, &varDefs, &componentNames);
QString errorDescription;
QString errorDetails;
std::unique_ptr<Geometry> geometry;
XkbDescPtr xkb =
XkbGetKeyboardByName(QX11Info::display(),
XkbUseCoreKbd,
&componentNames,
0,
XkbGBN_GeometryMask | XkbGBN_KeyNamesMask | XkbGBN_OtherNamesMask | XkbGBN_ClientSymbolsMask | XkbGBN_IndicatorMapMask,
false);
if (!xkb) {
QProcess setxkbmap;
QProcess xkbcomp;
setxkbmap.setStandardOutputProcess(&xkbcomp);
xkbcomp.setProcessChannelMode(QProcess::MergedChannels);
setxkbmap.start(QStringLiteral("setxkbmap"),
{QStringLiteral("-print"),
QStringLiteral("-model"),
model,
QStringLiteral("-layout"),
layout,
QStringLiteral("-variant"),
variant,
QStringLiteral("-option"),
options});
xkbcomp.start(QStringLiteral("xkbcomp"), {QStringLiteral("-")});
setxkbmap.waitForFinished();
xkbcomp.waitForFinished();
errorDescription = i18nc("@label",
"The keyboard geometry failed to load."
" This often indicates that the selected model does not support a specific layout"
" or layout variant."
" This problem will likely also present when you try to use this combination of model, layout and variant."
"\nPreviewing layouts that are defined outside your systems xkb directory is not supported and "
" will also trigger this message. These might still work fine if applied");
errorDetails = xkbcomp.readAllStandardOutput();
} else {
Q_ASSERT(xkb);
geometry.reset(new Geometry(xkb->geom, xkb));
}
const char uri[] = "org.kde.tastenbrett.private";
qmlRegisterUncreatableType<TextDoodad>(uri, 1, 0, "TextDoodad", QString());
qmlRegisterUncreatableType<LogoDoodad>(uri, 1, 0, "LogoDoodad", QString());
qmlRegisterUncreatableType<ShapeDoodad>(uri, 1, 0, "ShapeDoodad", QString());
qmlRegisterUncreatableType<IndicatorDoodad>(uri, 1, 0, "IndicatorDoodad", QString());
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/qml/main.qml"));
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreated,
&app,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
engine.rootContext()->setContextProperty("geometry", geometry.get());
engine.rootContext()->setContextProperty("errorDescription", errorDescription);
engine.rootContext()->setContextProperty("errorDetails", errorDetails);
engine.load(url);
return app.exec();
}