Add KWin UI tab for license details
This commit is contained in:
parent
449fda2e9e
commit
b266a20949
|
|
@ -5,3 +5,5 @@ gschemas.compiled
|
|||
out/
|
||||
*.po~
|
||||
kwin/src/xrdriveripc/xrdriveripc.py
|
||||
kwin/VERSION
|
||||
kwin/build-test/
|
||||
|
|
@ -62,6 +62,7 @@ cp $XR_DRIVER_DIR/bin/xr_driver_setup $PACKAGE_DIR/bin
|
|||
# alternative to symlinking, since the Docker build can't resolve to the parent directory
|
||||
# this file is in .gitignore so it doesn't get duplicated
|
||||
cp ui/modules/PyXRLinuxDriverIPC/xrdriveripc.py $KWIN_DIR/src/xrdriveripc/xrdriveripc.py
|
||||
cp VERSION $KWIN_DIR
|
||||
|
||||
pushd $KWIN_DIR
|
||||
docker-build/init.sh
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
add_subdirectory(xrdriveripc)
|
||||
|
||||
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/../VERSION" BREEZY_DESKTOP_VERSION_RAW)
|
||||
if(NOT BREEZY_DESKTOP_VERSION_RAW)
|
||||
set(BREEZY_DESKTOP_VERSION_RAW "dev")
|
||||
endif()
|
||||
string(STRIP "${BREEZY_DESKTOP_VERSION_RAW}" BREEZY_DESKTOP_VERSION)
|
||||
|
||||
add_subdirectory(kcm)
|
||||
|
||||
kcoreaddons_add_plugin(breezy_desktop INSTALL_NAMESPACE "kwin/effects/plugins/")
|
||||
|
|
@ -32,11 +39,12 @@ math(EXPR KWIN_VERSION_ENCODED "${KWIN_VERSION_MAJOR} * 10000 + ${KWIN_VERSION_M
|
|||
|
||||
# Export as compile definitions. Keep the original string macro as well.
|
||||
target_compile_definitions(breezy_desktop PRIVATE
|
||||
KWIN_VERSION_STR=\"${KWin_VERSION}\"
|
||||
KWIN_VERSION_MAJOR=${KWIN_VERSION_MAJOR}
|
||||
KWIN_VERSION_MINOR=${KWIN_VERSION_MINOR}
|
||||
KWIN_VERSION_PATCH=${KWIN_VERSION_PATCH}
|
||||
KWIN_VERSION_ENCODED=${KWIN_VERSION_ENCODED}
|
||||
KWIN_VERSION_STR=\"${KWin_VERSION}\"
|
||||
KWIN_VERSION_MAJOR=${KWIN_VERSION_MAJOR}
|
||||
KWIN_VERSION_MINOR=${KWIN_VERSION_MINOR}
|
||||
KWIN_VERSION_PATCH=${KWIN_VERSION_PATCH}
|
||||
KWIN_VERSION_ENCODED=${KWIN_VERSION_ENCODED}
|
||||
BREEZY_DESKTOP_VERSION_STR=\"${BREEZY_DESKTOP_VERSION}\"
|
||||
)
|
||||
target_include_directories(breezy_desktop PRIVATE /usr/include/kwin)
|
||||
target_include_directories(breezy_desktop PRIVATE xrdriveripc)
|
||||
|
|
|
|||
|
|
@ -221,11 +221,11 @@ void BreezyDesktopEffect::deactivate()
|
|||
void BreezyDesktopEffect::enableDriver()
|
||||
{
|
||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - enableDriver";
|
||||
XRDriverIPC::instance().writeConfig({
|
||||
{"disabled", false},
|
||||
{"output_mode", "external_only"},
|
||||
{"external_mode", "breezy_desktop"}
|
||||
});
|
||||
QJsonObject obj;
|
||||
obj.insert(QStringLiteral("disabled"), false);
|
||||
obj.insert(QStringLiteral("output_mode"), QStringLiteral("external_only"));
|
||||
obj.insert(QStringLiteral("external_mode"), QStringLiteral("breezy_desktop"));
|
||||
XRDriverIPC::instance().writeConfig(obj);
|
||||
}
|
||||
|
||||
void BreezyDesktopEffect::realDeactivate()
|
||||
|
|
|
|||
|
|
@ -16,3 +16,8 @@ target_link_libraries(breezy_desktop_config
|
|||
|
||||
xr_driver_ipc
|
||||
)
|
||||
|
||||
# Ensure the version macro is available to the KCM as well (defined in parent CMakeLists)
|
||||
if(BREEZY_DESKTOP_VERSION)
|
||||
target_compile_definitions(breezy_desktop_config PRIVATE BREEZY_DESKTOP_VERSION_STR=\"${BREEZY_DESKTOP_VERSION}\")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -14,8 +14,17 @@
|
|||
#include <KPluginFactory>
|
||||
|
||||
#include <QAction>
|
||||
#include <QTableWidget>
|
||||
#include <QHeaderView>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QProgressBar>
|
||||
#include <QLabel>
|
||||
#include <QJsonValue>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QKeyEvent>
|
||||
|
||||
Q_LOGGING_CATEGORY(KWIN_XR, "kwin.xr")
|
||||
|
||||
|
|
@ -72,6 +81,44 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu
|
|||
connect(ui.kcfg_FocusedDisplayDistance, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save);
|
||||
connect(ui.kcfg_AllDisplaysDistance, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save);
|
||||
connect(ui.kcfg_DisplaySpacing, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save);
|
||||
|
||||
if (auto label = widget()->findChild<QLabel*>("labelAppNameVersion")) {
|
||||
label->setText(QStringLiteral("Breezy Desktop - v%1").arg(QLatin1String(BREEZY_DESKTOP_VERSION_STR)));
|
||||
}
|
||||
|
||||
if (auto btnEmail = widget()->findChild<QPushButton*>("buttonSubmitEmail")) {
|
||||
connect(btnEmail, &QPushButton::clicked, this, [this]() {
|
||||
auto edit = widget()->findChild<QLineEdit*>("lineEditLicenseEmail");
|
||||
auto labelStatus = widget()->findChild<QLabel*>("labelEmailStatus");
|
||||
if (!edit || edit->text().trimmed().isEmpty() || !labelStatus) return;
|
||||
setRequestInProgress({edit, sender()}, true);
|
||||
labelStatus->setVisible(false);
|
||||
bool success = XRDriverIPC::instance().requestToken(edit->text().trimmed().toStdString());
|
||||
showStatus(labelStatus, success, success ? tr("Request sent. Check your email for instructions.") : tr("Failed to send request."));
|
||||
setRequestInProgress({edit, sender()}, false);
|
||||
});
|
||||
if (auto emailEdit = widget()->findChild<QLineEdit*>("lineEditLicenseEmail")) {
|
||||
emailEdit->installEventFilter(this);
|
||||
}
|
||||
}
|
||||
if (auto btnToken = widget()->findChild<QPushButton*>("buttonSubmitToken")) {
|
||||
connect(btnToken, &QPushButton::clicked, this, [this]() {
|
||||
auto edit = widget()->findChild<QLineEdit*>("lineEditLicenseToken");
|
||||
auto labelStatus = widget()->findChild<QLabel*>("labelTokenStatus");
|
||||
if (!edit || edit->text().trimmed().isEmpty() || !labelStatus) return;
|
||||
setRequestInProgress({edit, sender()}, true);
|
||||
labelStatus->setVisible(false);
|
||||
bool success = XRDriverIPC::instance().verifyToken(edit->text().trimmed().toStdString());
|
||||
if (success) {
|
||||
XRDriverIPC::instance().writeControlFlags({{"refresh_device_license", true}});
|
||||
}
|
||||
showStatus(labelStatus, success, success ? tr("Your license has been refreshed.") : tr("Invalid or expired token."));
|
||||
setRequestInProgress({edit, sender()}, false);
|
||||
});
|
||||
if (auto tokenEdit = widget()->findChild<QLineEdit*>("lineEditLicenseToken")) {
|
||||
tokenEdit->installEventFilter(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BreezyDesktopEffectConfig::~BreezyDesktopEffectConfig()
|
||||
|
|
@ -133,11 +180,12 @@ void BreezyDesktopEffectConfig::updateUnmanagedState()
|
|||
void BreezyDesktopEffectConfig::pollDriverState()
|
||||
{
|
||||
auto &bridge = XRDriverIPC::instance();
|
||||
auto stateOpt = bridge.retrieveDriverState();
|
||||
const XRDict &state = stateOpt.value();
|
||||
auto stateJsonOpt = bridge.retrieveDriverState();
|
||||
if (!stateJsonOpt) return;
|
||||
auto stateJson = stateJsonOpt.value();
|
||||
|
||||
m_connectedDeviceBrand = QString::fromStdString(XRDriverIPC::string(state, XRStateEntry::ConnectedDeviceBrand));
|
||||
m_connectedDeviceModel = QString::fromStdString(XRDriverIPC::string(state, XRStateEntry::ConnectedDeviceModel));
|
||||
m_connectedDeviceBrand = stateJson.value(QStringLiteral("connected_device_brand")).toString();
|
||||
m_connectedDeviceModel = stateJson.value(QStringLiteral("connected_device_model")).toString();
|
||||
|
||||
const bool wasDeviceConnected = m_deviceConnected;
|
||||
m_deviceConnected = !m_connectedDeviceBrand.isEmpty() && !m_connectedDeviceModel.isEmpty();
|
||||
|
|
@ -146,6 +194,137 @@ void BreezyDesktopEffectConfig::pollDriverState()
|
|||
QStringLiteral("%1 %2 connected").arg(m_connectedDeviceBrand, m_connectedDeviceModel) :
|
||||
QStringLiteral("No device connected"));
|
||||
}
|
||||
|
||||
refreshLicenseUi(stateJson);
|
||||
}
|
||||
|
||||
void BreezyDesktopEffectConfig::showStatus(QLabel *label, bool success, const QString &message) {
|
||||
if (!label) return;
|
||||
QPalette pal = label->palette();
|
||||
pal.setColor(QPalette::WindowText, success ? QColor(Qt::darkGreen) : QColor(Qt::red));
|
||||
label->setPalette(pal);
|
||||
label->setText(message);
|
||||
label->setVisible(true);
|
||||
}
|
||||
|
||||
void BreezyDesktopEffectConfig::setRequestInProgress(std::initializer_list<QObject*> widgets, bool inProgress) {
|
||||
for (auto *obj : widgets) {
|
||||
if (auto *w = qobject_cast<QWidget*>(obj)) {
|
||||
w->setEnabled(!inProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BreezyDesktopEffectConfig::eventFilter(QObject *watched, QEvent *event) {
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
auto *ke = static_cast<QKeyEvent*>(event);
|
||||
if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) {
|
||||
if (auto *edit = qobject_cast<QLineEdit*>(watched)) {
|
||||
// Determine which button to invoke
|
||||
QString objName = edit->objectName();
|
||||
QString buttonName;
|
||||
if (objName == QLatin1String("lineEditLicenseEmail")) buttonName = QStringLiteral("buttonSubmitEmail");
|
||||
else if (objName == QLatin1String("lineEditLicenseToken")) buttonName = QStringLiteral("buttonSubmitToken");
|
||||
if (!buttonName.isEmpty()) {
|
||||
if (auto btn = widget()->findChild<QPushButton*>(buttonName)) {
|
||||
// Trigger click but stop further propagation so dialog doesn't accept/close
|
||||
QMetaObject::invokeMethod(btn, "click", Qt::QueuedConnection);
|
||||
event->accept();
|
||||
return true; // eat event
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return KCModule::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
static QString secondsToRemainingString(qint64 secs) {
|
||||
if (secs <= 0) return {};
|
||||
|
||||
if (secs / 60 < 60) {
|
||||
return QObject::tr("less than an hour");
|
||||
}
|
||||
if (secs / 3600 < 24) {
|
||||
qint64 hours = secs / 3600;
|
||||
if (hours == 1) return QObject::tr("1 hour");
|
||||
return QObject::tr("%1 hours").arg(hours);
|
||||
}
|
||||
if ((secs / 86400) < 30 ) {
|
||||
qint64 days = secs / 86400;
|
||||
if (days == 1) return QObject::tr("1 day");
|
||||
return QObject::tr("%1 days").arg(days);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void BreezyDesktopEffectConfig::refreshLicenseUi(const QJsonObject &rootObj) {
|
||||
auto tab = widget()->findChild<QWidget*>("tabLicenseDetails");
|
||||
if (!tab) return;
|
||||
auto labelSummary = tab->findChild<QLabel*>("labelLicenseSummary");
|
||||
if (!labelSummary) return;
|
||||
|
||||
QString status = tr("disabled");
|
||||
QString renewalDescriptor = QStringLiteral("");
|
||||
auto uiView = rootObj.value(QStringLiteral("ui_view")).toObject();
|
||||
auto license = uiView.value(QStringLiteral("license")).toObject();
|
||||
bool warningState = true;
|
||||
if (!license.isEmpty()) {
|
||||
auto tiers = license.value(QStringLiteral("tiers")).toObject();
|
||||
QJsonValue prodTier = tiers.value(QStringLiteral("subscriber"));
|
||||
QJsonObject prodTierObj = prodTier.isUndefined() ? QJsonObject() : prodTier.toObject();
|
||||
|
||||
auto features = license.value(QStringLiteral("features")).toObject();
|
||||
QJsonValue prodFeature = features.value(QStringLiteral("productivity_basic"));
|
||||
QJsonObject prodFeatureObj = prodFeature.isUndefined() ? QJsonObject() : prodFeature.toObject();
|
||||
if (!prodTierObj.isEmpty() && !prodFeatureObj.isEmpty()) {
|
||||
const QString activePeriod = prodTierObj.value(QStringLiteral("active_period")).toString();
|
||||
const bool isActive = !activePeriod.isEmpty();
|
||||
if (isActive) {
|
||||
status = tr("active");
|
||||
|
||||
QString periodDescriptor = activePeriod.contains(QStringLiteral("lifetime"), Qt::CaseInsensitive) ?
|
||||
tr("lifetime") :
|
||||
tr("%1 license").arg(activePeriod);
|
||||
|
||||
QString timeDescriptor;
|
||||
auto secsVal = prodTierObj.value(QStringLiteral("funds_needed_in_seconds"));
|
||||
if (secsVal.isDouble()) {
|
||||
qint64 secs = static_cast<qint64>(secsVal.toDouble());
|
||||
QString remaining = secondsToRemainingString(secs);
|
||||
if (!remaining.isEmpty()) {
|
||||
timeDescriptor = tr("%1 remaining").arg(remaining);
|
||||
}
|
||||
}
|
||||
renewalDescriptor = tr(" (%1)").arg(periodDescriptor);
|
||||
warningState = !timeDescriptor.isEmpty();
|
||||
if (warningState) {
|
||||
auto fundsNeeded = prodTierObj.value(QStringLiteral("funds_needed_by_period")).toObject().value(activePeriod).toDouble();
|
||||
if (fundsNeeded > 0.0) {
|
||||
QString fundsNeededDescriptor = tr("$%1 USD to renew").arg(fundsNeeded);
|
||||
renewalDescriptor = tr(" (%1, %2, %3)").arg(periodDescriptor, fundsNeededDescriptor, timeDescriptor);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QJsonValue isEnabled = prodFeatureObj.value(QStringLiteral("is_enabled"));
|
||||
QJsonValue isTrial = prodFeatureObj.value(QStringLiteral("is_trial"));
|
||||
if (isEnabled.toBool() && isTrial.toBool()) {
|
||||
status = tr("in trial");
|
||||
auto secsVal = prodFeatureObj.value(QStringLiteral("funds_needed_in_seconds"));
|
||||
if (secsVal.isDouble()) {
|
||||
qint64 secs = static_cast<qint64>(secsVal.toDouble());
|
||||
QString remaining = secondsToRemainingString(secs);
|
||||
warningState = !remaining.isEmpty();
|
||||
if (warningState) {
|
||||
QString timeDescriptor = tr("%1 remaining").arg(remaining);
|
||||
renewalDescriptor = tr(" (%1)").arg(timeDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
labelSummary->setText(tr("Productivity Tier features are %1%2").arg(status, renewalDescriptor));
|
||||
}
|
||||
|
||||
#include "breezydesktopeffectkcm.moc"
|
||||
|
|
@ -30,6 +30,10 @@ private:
|
|||
void updateConfigFromUi();
|
||||
void updateUnmanagedState();
|
||||
void pollDriverState();
|
||||
void refreshLicenseUi(const QJsonObject &rootObj);
|
||||
void showStatus(QLabel *label, bool success, const QString &message);
|
||||
void setRequestInProgress(std::initializer_list<QObject*> widgets, bool inProgress);
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
::Ui::BreezyDesktopEffectConfig ui;
|
||||
|
||||
|
|
@ -39,4 +43,5 @@ private:
|
|||
QString m_connectedDeviceBrand;
|
||||
QString m_connectedDeviceModel;
|
||||
QTimer m_statePollTimer; // periodic driver state polling
|
||||
bool m_licenseLoading = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -141,6 +141,167 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabLicenseDetails">
|
||||
<attribute name="title">
|
||||
<string>&License Details</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayoutLicense">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelLicenseSummary">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBoxEmail">
|
||||
<property name="title">
|
||||
<string>Request a token</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayoutEmail">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLineEdit" name="lineEditLicenseEmail">
|
||||
<property name="placeholderText">
|
||||
<string>you@example.com</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="buttonSubmitEmail">
|
||||
<property name="text">
|
||||
<string>Submit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="labelEmailStatus">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBoxToken">
|
||||
<property name="title">
|
||||
<string>Verify token</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayoutToken">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLineEdit" name="lineEditLicenseToken"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="buttonSubmitToken">
|
||||
<property name="text">
|
||||
<string>Verify</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="labelTokenStatus">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacerLicense">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabAbout">
|
||||
<attribute name="title">
|
||||
<string>&About</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayoutAbout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelAppNameVersion">
|
||||
<property name="text">
|
||||
<string>Breezy Desktop Effect - v0.0.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignHCenter|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Author: Wayne Heaney <wayne@xronlinux.com></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignHCenter|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>License: GPL-3.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignHCenter|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacerAbout">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
|||
|
|
@ -27,14 +27,6 @@ XRDriverIPC &XRDriverIPC::instance() {
|
|||
return inst;
|
||||
}
|
||||
|
||||
std::string XRDriverIPC::string(const XRDict &dict, const std::string &key) {
|
||||
auto it = dict.find(key);
|
||||
if (it != dict.end() && std::holds_alternative<std::string>(it->second)) {
|
||||
return std::get<std::string>(it->second);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string XRDriverIPC::configHome() const {
|
||||
QString configHome = QString::fromUtf8(qgetenv("XDG_CONFIG_HOME"));
|
||||
if (configHome.isEmpty()) {
|
||||
|
|
@ -75,47 +67,24 @@ QByteArray XRDriverIPC::invokePython(const QString &method,
|
|||
return proc.readAllStandardOutput().trimmed();
|
||||
}
|
||||
|
||||
static XRDict jsonToXRDict(const QJsonObject &obj) {
|
||||
XRDict out;
|
||||
for (auto it = obj.begin(); it != obj.end(); ++it) {
|
||||
const QString &k = it.key();
|
||||
const QJsonValue &v = it.value();
|
||||
if (v.isBool()) out[k.toStdString()] = v.toBool();
|
||||
else if (v.isDouble() && std::floor(v.toDouble()) == v.toDouble())
|
||||
out[k.toStdString()] = (int)v.toDouble();
|
||||
else if (v.isDouble()) out[k.toStdString()] = v.toDouble();
|
||||
else if (v.isString()) out[k.toStdString()] = v.toString().toStdString();
|
||||
else out[k.toStdString()] = std::monostate{};
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<XRDict> XRDriverIPC::retrieveConfig() {
|
||||
std::optional<QJsonObject> XRDriverIPC::retrieveConfig() {
|
||||
QByteArray out = invokePython(QStringLiteral("retrieve_config"), {}, QStringLiteral("1"));
|
||||
if (out.isEmpty()) return std::nullopt;
|
||||
QJsonParseError err; auto doc = QJsonDocument::fromJson(out, &err);
|
||||
if (err.error != QJsonParseError::NoError || !doc.isObject()) return std::nullopt;
|
||||
return jsonToXRDict(doc.object());
|
||||
return doc.object();
|
||||
}
|
||||
|
||||
std::optional<XRDict> XRDriverIPC::retrieveDriverState() {
|
||||
std::optional<QJsonObject> XRDriverIPC::retrieveDriverState() {
|
||||
QByteArray out = invokePython(QStringLiteral("retrieve_driver_state"), {}, {});
|
||||
if (out.isEmpty()) return std::nullopt;
|
||||
QJsonParseError err; auto doc = QJsonDocument::fromJson(out, &err);
|
||||
if (err.error != QJsonParseError::NoError || !doc.isObject()) return std::nullopt;
|
||||
return jsonToXRDict(doc.object());
|
||||
return doc.object();
|
||||
}
|
||||
|
||||
bool XRDriverIPC::writeConfig(const XRDict &configUpdate) {
|
||||
QJsonObject obj;
|
||||
for (const auto &kv : configUpdate) {
|
||||
const std::string &k = kv.first; const XRValue &v = kv.second;
|
||||
if (std::holds_alternative<bool>(v)) obj.insert(QString::fromStdString(k), std::get<bool>(v));
|
||||
else if (std::holds_alternative<int>(v)) obj.insert(QString::fromStdString(k), std::get<int>(v));
|
||||
else if (std::holds_alternative<double>(v)) obj.insert(QString::fromStdString(k), std::get<double>(v));
|
||||
else if (std::holds_alternative<std::string>(v)) obj.insert(QString::fromStdString(k), QString::fromStdString(std::get<std::string>(v)));
|
||||
}
|
||||
QByteArray payload = QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||
bool XRDriverIPC::writeConfig(const QJsonObject &configUpdate) {
|
||||
QByteArray payload = QJsonDocument(configUpdate).toJson(QJsonDocument::Compact);
|
||||
QByteArray out = invokePython(QStringLiteral("write_config"), payload, {});
|
||||
return !out.isEmpty();
|
||||
}
|
||||
|
|
@ -130,13 +99,13 @@ bool XRDriverIPC::writeControlFlags(const std::map<std::string, bool> &flags) {
|
|||
bool XRDriverIPC::requestToken(const std::string &email) {
|
||||
QByteArray out = invokePython(QStringLiteral("request_token"), {}, QString::fromStdString(email));
|
||||
if (out.isEmpty()) return false;
|
||||
auto value = QJsonValue(QString::fromStdString(out.toStdString()));
|
||||
return value.isBool() ? value.toBool() : false;
|
||||
QString result = QString::fromUtf8(out).trimmed().toLower();
|
||||
return result == QStringLiteral("true");
|
||||
}
|
||||
|
||||
bool XRDriverIPC::verifyToken(const std::string &token) {
|
||||
QByteArray out = invokePython(QStringLiteral("verify_token"), {}, QString::fromStdString(token));
|
||||
if (out.isEmpty()) return false;
|
||||
auto value = QJsonValue(QString::fromStdString(out.toStdString()));
|
||||
return value.isBool() ? value.toBool() : false;
|
||||
QString result = QString::fromUtf8(out).trimmed().toLower();
|
||||
return result == QStringLiteral("true");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
// C++ bridge now invoking xrdriveripc via external python process
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QJsonObject>
|
||||
#include <optional>
|
||||
|
||||
// Export header generated by CMake (GenerateExportHeader)
|
||||
#ifdef __has_include
|
||||
|
|
@ -78,18 +75,13 @@ namespace XRConfigEntry {
|
|||
inline constexpr const char *Debug = "debug";
|
||||
}
|
||||
|
||||
// Simple variant type for config/state key values we care about
|
||||
using XRValue = std::variant<std::monostate, bool, int, double, std::string>;
|
||||
using XRDict = std::map<std::string, XRValue>;
|
||||
|
||||
class XR_DRIVER_IPC_EXPORT XRDriverIPC {
|
||||
public:
|
||||
static XRDriverIPC &instance();
|
||||
static std::string string(const XRDict &dict, const std::string &key);
|
||||
|
||||
std::optional<XRDict> retrieveConfig();
|
||||
std::optional<XRDict> retrieveDriverState();
|
||||
bool writeConfig(const XRDict &configUpdate);
|
||||
std::optional<QJsonObject> retrieveConfig();
|
||||
std::optional<QJsonObject> retrieveDriverState();
|
||||
bool writeConfig(const QJsonObject &configUpdate);
|
||||
bool writeControlFlags(const std::map<std::string, bool> &flags);
|
||||
bool requestToken(const std::string &email);
|
||||
bool verifyToken(const std::string &token);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@ import os
|
|||
import sys
|
||||
import traceback
|
||||
|
||||
class Logger:
|
||||
def info(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def error(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
def main() -> int:
|
||||
# Ensure the current directory (where xrdriveripc.py lives) is in sys.path
|
||||
|
|
@ -32,7 +39,7 @@ def main() -> int:
|
|||
return 2
|
||||
|
||||
config_home = os.environ.get("BREEZY_CONFIG_HOME")
|
||||
inst = xrdriveripc.XRDriverIPC(config_home=config_home)
|
||||
inst = xrdriveripc.XRDriverIPC(logger=Logger(), config_home=config_home)
|
||||
|
||||
arg = os.environ.get("BREEZY_ARG")
|
||||
payload_raw = os.environ.get("BREEZY_PAYLOAD")
|
||||
|
|
|
|||
|
|
@ -26,12 +26,9 @@ class LicenseFeatureRow(Adw.ActionRow):
|
|||
self.set_subtitle(f"{status}{details}")
|
||||
|
||||
def _feature_name(self, feature):
|
||||
print(f"Translating feature: {feature}")
|
||||
print(f"_ is: {_}")
|
||||
feature_names = {
|
||||
'sbs': lambda: gettext.gettext('Side-by-side mode (gaming)'),
|
||||
'sbs': lambda: _('Side-by-side mode (gaming)'),
|
||||
'smooth_follow': lambda: _('Smooth Follow (gaming)'),
|
||||
'productivity_basic': lambda: _('Breezy Desktop (productivity)')
|
||||
}
|
||||
print(f"Translated string: {feature_names[feature]()}")
|
||||
return feature_names[feature]()
|
||||
Loading…
Reference in New Issue