#include #include "GraphEditorWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int captureWindow(QMainWindow *window, const QString &path) { QPixmap pixmap = window->grab(); if (pixmap.isNull()) { QScreen *screen = window->screen(); if (screen) { pixmap = screen->grabWindow(window->winId()); } } if (pixmap.isNull()) { std::cerr << "warppipe: screenshot capture failed\n"; return 3; } QFileInfo fi(path); QDir dir = fi.absoluteDir(); if (!dir.exists()) { dir.mkpath("."); } if (!pixmap.save(path)) { std::cerr << "warppipe: failed to write screenshot to " << path.toStdString() << "\n"; return 3; } return 0; } int main(int argc, char *argv[]) { for (int i = 1; i < argc; ++i) { if (std::strcmp(argv[i], "--offscreen") == 0) { qputenv("QT_QPA_PLATFORM", "offscreen"); } } QApplication app(argc, argv); QCoreApplication::setApplicationName("Warppipe"); QCoreApplication::setApplicationVersion("0.1.0"); QCommandLineParser parser; parser.setApplicationDescription("Warppipe — PipeWire Audio Router GUI"); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption screenshotOpt( QStringList() << "s" << "screenshot", "Capture window to PNG and exit.", "path"); QCommandLineOption delayOpt( "screenshot-delay", "Delay in ms before capture (default 800).", "ms", "800"); QCommandLineOption debugDirOpt( "debug-screenshot-dir", "Save timestamped screenshot on every graph state change.", "dir"); QCommandLineOption offscreenOpt( "offscreen", "Run with QT_QPA_PLATFORM=offscreen (headless)."); parser.addOption(screenshotOpt); parser.addOption(delayOpt); parser.addOption(debugDirOpt); parser.addOption(offscreenOpt); parser.process(app); warppipe::ConnectionOptions opts; opts.application_name = "warppipe-gui"; auto result = warppipe::Client::Create(opts); if (!result.ok()) { std::cerr << "warppipe: failed to connect: " << result.status.message << "\n"; return 1; } auto &client = result.value; QMainWindow window; window.setWindowTitle("Warppipe \u2014 Audio Router"); auto *editor = new GraphEditorWidget(client.get(), &window); window.setCentralWidget(editor); window.resize(1280, 720); auto *statusLabel = new QLabel(&window); window.statusBar()->addWidget(statusLabel); auto *statusTimer = new QTimer(&window); QObject::connect(statusTimer, &QTimer::timeout, [&]() { int nodes = editor->nodeCount(); int links = editor->linkCount(); statusLabel->setText( QString("PipeWire connected | %1 nodes | %2 links") .arg(nodes) .arg(links)); }); statusTimer->start(500); if (parser.isSet(debugDirOpt)) { editor->setDebugScreenshotDir(parser.value(debugDirOpt)); } if (parser.isSet(screenshotOpt)) { QString screenshotPath = parser.value(screenshotOpt); int delay = parser.value(delayOpt).toInt(); if (delay <= 0) { delay = 800; } bool captured = false; auto doCapture = [&]() { if (captured) return; captured = true; QTimer::singleShot(100, [&]() { int code = captureWindow(&window, screenshotPath); app.exit(code); }); }; auto *fallbackTimer = new QTimer(&window); fallbackTimer->setSingleShot(true); QObject::connect(fallbackTimer, &QTimer::timeout, doCapture); QObject::connect(editor, &GraphEditorWidget::graphReady, [&]() { fallbackTimer->stop(); doCapture(); }); fallbackTimer->start(delay); } auto *f12Action = new QAction(&window); f12Action->setShortcut(Qt::Key_F12); QObject::connect(f12Action, &QAction::triggered, [&]() { QString picDir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) + "/warppipe"; QDir().mkpath(picDir); QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss"); QString path = picDir + "/warppipe_" + timestamp + ".png"; int code = captureWindow(&window, path); if (code == 0) { window.statusBar()->showMessage("Screenshot saved: " + path, 3000); } }); window.addAction(f12Action); auto *closeAction = new QAction(&window); closeAction->setShortcut(QKeySequence::Quit); QObject::connect(closeAction, &QAction::triggered, &window, &QMainWindow::close); window.addAction(closeAction); window.show(); return app.exec(); }