Desparately try to re-use the tried-and-true IPC Python script for communications with the driver
Creates a wrapper script around the existing IPC library that makes for simpler invocations
This commit is contained in:
parent
0d8fb02388
commit
843f7907e7
|
|
@ -4,3 +4,4 @@ __pycache__
|
||||||
gschemas.compiled
|
gschemas.compiled
|
||||||
out/
|
out/
|
||||||
*.po~
|
*.po~
|
||||||
|
kwin/src/xrdriveripc/xrdriveripc.py
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,10 @@ rm -rf $XR_DRIVER_TMP_DIR
|
||||||
cp $XR_DRIVER_BINARY $PACKAGE_DIR/xrDriver.tar.gz
|
cp $XR_DRIVER_BINARY $PACKAGE_DIR/xrDriver.tar.gz
|
||||||
cp $XR_DRIVER_DIR/bin/xr_driver_setup $PACKAGE_DIR/bin
|
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
|
||||||
|
|
||||||
pushd $KWIN_DIR
|
pushd $KWIN_DIR
|
||||||
docker-build/init.sh
|
docker-build/init.sh
|
||||||
docker-build/run-build.sh $BUILD_ARCH
|
docker-build/run-build.sh $BUILD_ARCH
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ if [ -n "\$QT_PLUGIN_PATH" ]; then
|
||||||
else
|
else
|
||||||
export QT_PLUGIN_PATH="$QT_PLUGIN_DIR"
|
export QT_PLUGIN_PATH="$QT_PLUGIN_DIR"
|
||||||
fi
|
fi
|
||||||
|
export QT_DEBUG_PLUGINS=1
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@ RUN pacman -Sy --noconfirm --needed \
|
||||||
kwindowsystem \
|
kwindowsystem \
|
||||||
kwin \
|
kwin \
|
||||||
&& pacman -Scc --noconfirm
|
&& pacman -Scc --noconfirm
|
||||||
|
RUN pacman -Sy --noconfirm --needed \
|
||||||
|
python \
|
||||||
|
&& pacman -Scc --noconfirm
|
||||||
|
|
||||||
WORKDIR /source
|
WORKDIR /source
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ RUN pacman -Sy --noconfirm --needed \
|
||||||
kwindowsystem \
|
kwindowsystem \
|
||||||
kwin \
|
kwin \
|
||||||
&& pacman -Scc --noconfirm
|
&& pacman -Scc --noconfirm
|
||||||
|
RUN pacman -Sy --noconfirm --needed \
|
||||||
|
python \
|
||||||
|
&& pacman -Scc --noconfirm
|
||||||
|
|
||||||
WORKDIR /source
|
WORKDIR /source
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
add_subdirectory(xrdriveripc)
|
||||||
add_subdirectory(kcm)
|
add_subdirectory(kcm)
|
||||||
|
|
||||||
kcoreaddons_add_plugin(breezy_desktop INSTALL_NAMESPACE "kwin/effects/plugins/")
|
kcoreaddons_add_plugin(breezy_desktop INSTALL_NAMESPACE "kwin/effects/plugins/")
|
||||||
|
|
@ -38,6 +39,7 @@ target_compile_definitions(breezy_desktop PRIVATE
|
||||||
KWIN_VERSION_ENCODED=${KWIN_VERSION_ENCODED}
|
KWIN_VERSION_ENCODED=${KWIN_VERSION_ENCODED}
|
||||||
)
|
)
|
||||||
target_include_directories(breezy_desktop PRIVATE /usr/include/kwin)
|
target_include_directories(breezy_desktop PRIVATE /usr/include/kwin)
|
||||||
|
target_include_directories(breezy_desktop PRIVATE xrdriveripc)
|
||||||
target_link_libraries(breezy_desktop
|
target_link_libraries(breezy_desktop
|
||||||
Qt6::Core
|
Qt6::Core
|
||||||
Qt6::Gui
|
Qt6::Gui
|
||||||
|
|
@ -51,6 +53,9 @@ target_link_libraries(breezy_desktop
|
||||||
KF6::WindowSystem
|
KF6::WindowSystem
|
||||||
|
|
||||||
KWin::kwin
|
KWin::kwin
|
||||||
|
|
||||||
|
xr_driver_ipc
|
||||||
)
|
)
|
||||||
|
|
||||||
install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/breezy_desktop)
|
|
||||||
|
install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/breezy_desktop)
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
|
|
||||||
|
#include "core/rendertarget.h"
|
||||||
|
#include "core/renderviewport.h"
|
||||||
#include "kcm/shortcuts.h"
|
#include "kcm/shortcuts.h"
|
||||||
#include "breezydesktopeffect.h"
|
#include "breezydesktopeffect.h"
|
||||||
#include "breezydesktopconfig.h"
|
#include "breezydesktopconfig.h"
|
||||||
#include "effect/effect.h"
|
#include "effect/effect.h"
|
||||||
#include "effect/effecthandler.h"
|
#include "effect/effecthandler.h"
|
||||||
#include "opengl/glutils.h"
|
#include "opengl/glutils.h"
|
||||||
#include "core/rendertarget.h"
|
#include "xrdriveripc.h"
|
||||||
#include "core/renderviewport.h"
|
|
||||||
|
|
||||||
#include <kwin/main.h>
|
#include <kwin/main.h>
|
||||||
#include <core/outputbackend.h>
|
#include <core/outputbackend.h>
|
||||||
|
|
@ -80,11 +82,17 @@ BreezyDesktopEffect::BreezyDesktopEffect()
|
||||||
);
|
);
|
||||||
setupGlobalShortcut(
|
setupGlobalShortcut(
|
||||||
BreezyShortcuts::RECENTER,
|
BreezyShortcuts::RECENTER,
|
||||||
[this]() { this->recenter(); }
|
[this]() {
|
||||||
|
XRDriverIPCBridge::instance().writeControlFlags({
|
||||||
|
{"recenter_screen", true}
|
||||||
|
});
|
||||||
|
}
|
||||||
);
|
);
|
||||||
setupGlobalShortcut(
|
setupGlobalShortcut(
|
||||||
BreezyShortcuts::TOGGLE_ZOOM_ON_FOCUS,
|
BreezyShortcuts::TOGGLE_ZOOM_ON_FOCUS,
|
||||||
[this]() { this->toggleZoomOnFocus(); }
|
[this]() {
|
||||||
|
this->setZoomOnFocusEnabled(!m_zoomOnFocusEnabled);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
connect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage);
|
connect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage);
|
||||||
|
|
@ -209,60 +217,11 @@ void BreezyDesktopEffect::deactivate()
|
||||||
void BreezyDesktopEffect::enableDriver()
|
void BreezyDesktopEffect::enableDriver()
|
||||||
{
|
{
|
||||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - enableDriver";
|
qCCritical(KWIN_XR) << "\t\t\tBreezy - enableDriver";
|
||||||
QByteArray homeEnv = qgetenv("HOME");
|
XRDriverIPCBridge::instance().writeConfig({
|
||||||
QString program = QString::fromUtf8(homeEnv) + QStringLiteral("/.local/bin/xr_driver_cli");
|
{"disabled", false},
|
||||||
|
{"output_mode", "external_only"},
|
||||||
// Helper lambda to start the second call
|
{"external_mode", "breezy_desktop"}
|
||||||
auto setBreezyDesktopMode = [this, program]() {
|
|
||||||
QProcess *proc2 = new QProcess(this);
|
|
||||||
proc2->setProgram(program);
|
|
||||||
proc2->setArguments({QStringLiteral("-bd")}); // change the mode to Breezy Desktop
|
|
||||||
proc2->setProcessChannelMode(QProcess::MergedChannels);
|
|
||||||
|
|
||||||
connect(proc2, &QProcess::readyReadStandardOutput, this, [proc2]() {
|
|
||||||
const QByteArray out = proc2->readAllStandardOutput();
|
|
||||||
if (!out.isEmpty()) {
|
|
||||||
qCInfo(KWIN_XR) << "xr_driver_cli -bd:" << out;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
connect(proc2, &QProcess::errorOccurred, this, [proc2](QProcess::ProcessError err) {
|
|
||||||
qCCritical(KWIN_XR) << "xr_driver_cli -bd error" << err << proc2->errorString();
|
|
||||||
});
|
|
||||||
connect(proc2, QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished),
|
|
||||||
this, [this, proc2](int code, QProcess::ExitStatus status) {
|
|
||||||
qCInfo(KWIN_XR) << "xr_driver_cli -bd exited" << code << "status" << status;
|
|
||||||
proc2->deleteLater();
|
|
||||||
});
|
|
||||||
|
|
||||||
proc2->start();
|
|
||||||
};
|
|
||||||
|
|
||||||
QProcess *proc1 = new QProcess(this);
|
|
||||||
proc1->setProgram(program);
|
|
||||||
proc1->setArguments({QStringLiteral("-e")}); // enable flag
|
|
||||||
proc1->setProcessChannelMode(QProcess::MergedChannels);
|
|
||||||
|
|
||||||
connect(proc1, &QProcess::readyReadStandardOutput, this, [proc1]() {
|
|
||||||
const QByteArray out = proc1->readAllStandardOutput();
|
|
||||||
if (!out.isEmpty()) {
|
|
||||||
qCInfo(KWIN_XR) << "xr_driver_cli -e:" << out;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
connect(proc1, &QProcess::errorOccurred, this, [proc1](QProcess::ProcessError err) {
|
|
||||||
qCCritical(KWIN_XR) << "xr_driver_cli -e error" << err << proc1->errorString();
|
|
||||||
});
|
|
||||||
connect(proc1, QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished),
|
|
||||||
this, [proc1, setBreezyDesktopMode](int code, QProcess::ExitStatus status) {
|
|
||||||
qCInfo(KWIN_XR) << "xr_driver_cli -e exited" << code << "status" << status;
|
|
||||||
proc1->deleteLater();
|
|
||||||
if (status == QProcess::NormalExit && code == 0) {
|
|
||||||
setBreezyDesktopMode();
|
|
||||||
} else {
|
|
||||||
qCCritical(KWIN_XR) << "First call failed; not starting second.";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
proc1->start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BreezyDesktopEffect::realDeactivate()
|
void BreezyDesktopEffect::realDeactivate()
|
||||||
|
|
@ -271,20 +230,6 @@ void BreezyDesktopEffect::realDeactivate()
|
||||||
setRunning(false);
|
setRunning(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BreezyDesktopEffect::recenter()
|
|
||||||
{
|
|
||||||
QFile controlFile(QStringLiteral("/dev/shm/xr_driver_control"));
|
|
||||||
if (controlFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
|
||||||
controlFile.write("recenter_screen=true\n");
|
|
||||||
controlFile.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BreezyDesktopEffect::toggleZoomOnFocus()
|
|
||||||
{
|
|
||||||
setZoomOnFocusEnabled(!m_zoomOnFocusEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BreezyDesktopEffect::addVirtualDisplay(QSize size)
|
void BreezyDesktopEffect::addVirtualDisplay(QSize size)
|
||||||
{
|
{
|
||||||
// QSize size(2560, 1440);
|
// QSize size(2560, 1440);
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,6 @@ namespace KWin
|
||||||
void deactivate();
|
void deactivate();
|
||||||
void enableDriver();
|
void enableDriver();
|
||||||
void toggle();
|
void toggle();
|
||||||
void recenter();
|
|
||||||
void toggleZoomOnFocus();
|
|
||||||
void addVirtualDisplay(QSize size);
|
void addVirtualDisplay(QSize size);
|
||||||
void updateImuRotation();
|
void updateImuRotation();
|
||||||
void updateCursorImage();
|
void updateCursorImage();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
# SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
set(breezy_desktop_config_SOURCES breezydesktopeffectkcm.cpp)
|
set(breezy_desktop_config_SOURCES breezydesktopeffectkcm.cpp)
|
||||||
ki18n_wrap_ui(breezy_desktop_config_SOURCES breezydesktopeffectkcm.ui)
|
ki18n_wrap_ui(breezy_desktop_config_SOURCES breezydesktopeffectkcm.ui)
|
||||||
qt_add_dbus_interface(breezy_desktop_config_SOURCES ${KWIN_EFFECTS_INTERFACE} kwineffects_interface)
|
qt_add_dbus_interface(breezy_desktop_config_SOURCES ${KWIN_EFFECTS_INTERFACE} kwineffects_interface)
|
||||||
|
|
@ -17,4 +13,6 @@ target_link_libraries(breezy_desktop_config
|
||||||
KF6::I18n
|
KF6::I18n
|
||||||
KF6::KCMUtils
|
KF6::KCMUtils
|
||||||
KF6::XmlGui
|
KF6::XmlGui
|
||||||
|
|
||||||
|
xr_driver_ipc
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
add_library(xr_driver_ipc STATIC
|
||||||
|
xrdriveripc.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure position independent code so the static archive can link into the KWin effect plugin (a shared module)
|
||||||
|
set_target_properties(xr_driver_ipc PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
|
# Generate an export header so symbols can be visible outside the shared lib
|
||||||
|
include(GenerateExportHeader)
|
||||||
|
generate_export_header(xr_driver_ipc EXPORT_FILE_NAME xr_driver_ipc_export.h)
|
||||||
|
|
||||||
|
target_include_directories(xr_driver_ipc
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR} # for generated export header
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(xr_driver_ipc PRIVATE
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:/EHsc>
|
||||||
|
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-fexceptions>
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(xr_driver_ipc
|
||||||
|
PRIVATE Qt6::Core
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES xrdriveripc.py xrdriveripc_runner.py DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/breezy_desktop)
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
// New implementation using QProcess to call python
|
||||||
|
#include "xrdriveripc.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <cmath>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
|
||||||
|
XRDriverIPCBridge &XRDriverIPCBridge::instance() {
|
||||||
|
static XRDriverIPCBridge inst;
|
||||||
|
if (!inst.m_initialized) {
|
||||||
|
QString installedFile = QStandardPaths::locate(
|
||||||
|
QStandardPaths::GenericDataLocation,
|
||||||
|
QStringLiteral("kwin/effects/breezy_desktop/xrdriveripc.py"),
|
||||||
|
QStandardPaths::LocateFile);
|
||||||
|
if (installedFile.isEmpty()) {
|
||||||
|
throw std::runtime_error("Cannot locate kwin/effects/breezy_desktop/xrdriveripc.py");
|
||||||
|
}
|
||||||
|
inst.m_pythonDir = QFileInfo(installedFile).path();
|
||||||
|
inst.m_initialized = true;
|
||||||
|
}
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string XRDriverIPCBridge::configHome() const {
|
||||||
|
QString configHome = QString::fromUtf8(qgetenv("XDG_CONFIG_HOME"));
|
||||||
|
if (configHome.isEmpty()) {
|
||||||
|
QString homeDir = QString::fromUtf8(qgetenv("HOME"));
|
||||||
|
configHome = homeDir + QStringLiteral("/.config");
|
||||||
|
}
|
||||||
|
return configHome.toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray XRDriverIPCBridge::invokePython(const QString &method,
|
||||||
|
const QByteArray &payloadJson,
|
||||||
|
const QString &singleArg) const {
|
||||||
|
QProcess proc;
|
||||||
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||||
|
env.insert(QStringLiteral("BREEZY_METHOD"), method);
|
||||||
|
env.insert(QStringLiteral("BREEZY_CONFIG_HOME"), QString::fromStdString(configHome()));
|
||||||
|
if (!singleArg.isEmpty()) env.insert(QStringLiteral("BREEZY_ARG"), singleArg);
|
||||||
|
if (!payloadJson.isEmpty()) env.insert(QStringLiteral("BREEZY_PAYLOAD"), QString::fromUtf8(payloadJson));
|
||||||
|
proc.setProcessEnvironment(env);
|
||||||
|
// Expect xrdriveripc_runner.py to reside in the same directory as xrdriveripc.py (m_pythonDir)
|
||||||
|
QString wrapperPath = m_pythonDir + QStringLiteral("/xrdriveripc_runner.py");
|
||||||
|
proc.start(QStringLiteral("python3"), QStringList() << wrapperPath);
|
||||||
|
if (!proc.waitForStarted(5000)) {
|
||||||
|
std::cerr << "Failed to start python process" << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
proc.closeWriteChannel();
|
||||||
|
if (!proc.waitForFinished(15000)) {
|
||||||
|
proc.kill();
|
||||||
|
std::cerr << "Python process timeout" << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (proc.exitStatus() != QProcess::NormalExit || proc.exitCode() != 0) {
|
||||||
|
std::cerr << "Python process failed (" << proc.exitCode() << "):\n"
|
||||||
|
<< proc.readAllStandardError().toStdString() << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
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> XRDriverIPCBridge::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());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<XRDict> XRDriverIPCBridge::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());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XRDriverIPCBridge::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);
|
||||||
|
QByteArray out = invokePython(QStringLiteral("write_config"), payload, {});
|
||||||
|
return !out.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XRDriverIPCBridge::writeControlFlags(const std::map<std::string, bool> &flags) {
|
||||||
|
QJsonObject obj; for (const auto &kv : flags) obj.insert(QString::fromStdString(kv.first), kv.second);
|
||||||
|
QByteArray payload = QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||||
|
QByteArray out = invokePython(QStringLiteral("write_control_flags"), payload, {});
|
||||||
|
return !out.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XRDriverIPCBridge::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XRDriverIPCBridge::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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// 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>
|
||||||
|
|
||||||
|
// Export header generated by CMake (GenerateExportHeader)
|
||||||
|
#ifdef __has_include
|
||||||
|
# if __has_include("xr_driver_ipc_export.h")
|
||||||
|
# include "xr_driver_ipc_export.h"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef XR_DRIVER_IPC_EXPORT
|
||||||
|
# define XR_DRIVER_IPC_EXPORT __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 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 XRDriverIPCBridge {
|
||||||
|
public:
|
||||||
|
static XRDriverIPCBridge &instance();
|
||||||
|
|
||||||
|
std::optional<XRDict> retrieveConfig();
|
||||||
|
std::optional<XRDict> retrieveDriverState();
|
||||||
|
bool writeConfig(const XRDict &configUpdate);
|
||||||
|
bool writeControlFlags(const std::map<std::string, bool> &flags);
|
||||||
|
bool requestToken(const std::string &email);
|
||||||
|
bool verifyToken(const std::string &token);
|
||||||
|
|
||||||
|
private:
|
||||||
|
XRDriverIPCBridge() = default;
|
||||||
|
~XRDriverIPCBridge() = default;
|
||||||
|
XRDriverIPCBridge(const XRDriverIPCBridge&) = delete;
|
||||||
|
XRDriverIPCBridge& operator=(const XRDriverIPCBridge&) = delete;
|
||||||
|
|
||||||
|
std::string configHome() const;
|
||||||
|
QByteArray invokePython(const QString &method,
|
||||||
|
const QByteArray &payloadJson,
|
||||||
|
const QString &singleArg) const;
|
||||||
|
|
||||||
|
bool m_initialized = false;
|
||||||
|
QString m_pythonDir; // directory containing xrdriveripc.py
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Wrapper script invoked by xrdriveripc.cpp via QProcess.
|
||||||
|
|
||||||
|
It reads environment variables to determine which XRDriverIPC method to call
|
||||||
|
and prints the JSON-serialized result to stdout, mirroring the prior inline
|
||||||
|
python one-liner implementation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
# Ensure the current directory (where xrdriveripc.py lives) is in sys.path
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
if script_dir not in sys.path:
|
||||||
|
sys.path.insert(0, script_dir)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import xrdriveripc # type: ignore
|
||||||
|
except Exception as e: # pragma: no cover - import failure path
|
||||||
|
print("Failed to import xrdriveripc: %s" % e, file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
method = os.environ.get("BREEZY_METHOD")
|
||||||
|
if not method:
|
||||||
|
print("BREEZY_METHOD not set", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
config_home = os.environ.get("BREEZY_CONFIG_HOME")
|
||||||
|
inst = xrdriveripc.XRDriverIPC(config_home=config_home)
|
||||||
|
|
||||||
|
arg = os.environ.get("BREEZY_ARG")
|
||||||
|
payload_raw = os.environ.get("BREEZY_PAYLOAD")
|
||||||
|
|
||||||
|
# Dispatch replicating previous inline logic
|
||||||
|
try:
|
||||||
|
if method == "retrieve_config":
|
||||||
|
res = getattr(inst, method)(int(arg) if arg else 1)
|
||||||
|
elif method in ("write_config", "write_control_flags") and payload_raw:
|
||||||
|
res = getattr(inst, method)(json.loads(payload_raw))
|
||||||
|
elif method in ("request_token", "verify_token") and arg:
|
||||||
|
res = getattr(inst, method)(arg)
|
||||||
|
else:
|
||||||
|
res = getattr(inst, method)()
|
||||||
|
except Exception: # pragma: no cover - runtime failure path
|
||||||
|
traceback.print_exc()
|
||||||
|
return 3
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(json.dumps(res))
|
||||||
|
except Exception: # pragma: no cover
|
||||||
|
traceback.print_exc()
|
||||||
|
return 3
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": # pragma: no cover
|
||||||
|
sys.exit(main())
|
||||||
Loading…
Reference in New Issue