From 68473d04d8b4851c790157cd3482c9270fa19886 Mon Sep 17 00:00:00 2001 From: Kacper Herchel Date: Tue, 25 Oct 2022 17:01:10 +0200 Subject: [PATCH 01/29] Fix mute and deafen keybinds disconnecting from the call instead of performing their appropriate action --- assets/userscript.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/assets/userscript.js b/assets/userscript.js index cf00169..88d33e8 100644 --- a/assets/userscript.js +++ b/assets/userscript.js @@ -183,14 +183,21 @@ setInterval(() => { el.appendChild(div); } - const muteBtn = document.getElementsByClassName( + const buttonContainer = document.getElementsByClassName("container-YkUktl")[0]; + if (!buttonContainer) { + console.log('dsa: Cannot locate Mute/Deafen/Settings button container, please report this on GitHub'); + } + + const muteBtn = buttonContainer ? buttonContainer.getElementsByClassName( "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" - )[0]; - window.discordScreenaudioToggleMute = () => muteBtn.click(); - const deafenBtn = document.getElementsByClassName( + )[0] : null; + window.discordScreenaudioToggleMute = () => muteBtn && muteBtn.click(); + + const deafenBtn = buttonContainer ? buttonContainer.getElementsByClassName( "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" - )[1]; - window.discordScreenaudioToggleDeafen = () => deafenBtn.click(); + )[1] : null; + + window.discordScreenaudioToggleDeafen = () => deafenBtn && deafenBtn.click(); if (window.discordScreenaudioResolutionString) { for (const el of document.getElementsByClassName( From 3c48621427597064d3671dd1c17d875800f27560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Fri, 28 Oct 2022 21:57:32 +0200 Subject: [PATCH 02/29] add developer field to metainfo --- assets/de.shorsh.discord-screenaudio.metainfo.xml.in | 1 + submodules/Vencord | 1 + 2 files changed, 2 insertions(+) create mode 160000 submodules/Vencord diff --git a/assets/de.shorsh.discord-screenaudio.metainfo.xml.in b/assets/de.shorsh.discord-screenaudio.metainfo.xml.in index bd81780..7d459dc 100644 --- a/assets/de.shorsh.discord-screenaudio.metainfo.xml.in +++ b/assets/de.shorsh.discord-screenaudio.metainfo.xml.in @@ -4,6 +4,7 @@ CC0-1.0 GPL-3.0+ discord-screenaudio + Malte Jürgens diff --git a/submodules/Vencord b/submodules/Vencord new file mode 160000 index 0000000..35d2b8d --- /dev/null +++ b/submodules/Vencord @@ -0,0 +1 @@ +Subproject commit 35d2b8d1cfaad8ff92b6a812238d62b029a217a0 From a0a2924796a338f5f852caad7b66d16e5dd8ba1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Fri, 4 Nov 2022 20:45:55 +0000 Subject: [PATCH 03/29] remove Vencord folder --- submodules/Vencord | 1 - 1 file changed, 1 deletion(-) delete mode 160000 submodules/Vencord diff --git a/submodules/Vencord b/submodules/Vencord deleted file mode 160000 index 35d2b8d..0000000 --- a/submodules/Vencord +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 35d2b8d1cfaad8ff92b6a812238d62b029a217a0 From 150fd4364e68f953df0adbadfb8f546d7976b032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Fri, 4 Nov 2022 23:16:49 +0100 Subject: [PATCH 04/29] add `--notify-send` option --- src/main.cpp | 5 ++++- src/mainwindow.cpp | 48 ++++++++++++++++++++++++++++++---------------- src/mainwindow.h | 8 +++++++- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index e2cd74b..df1fec8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,6 +30,9 @@ int main(int argc, char *argv[]) { QCommandLineOption degubOption("remote-debugging", "Open Chromium Remote Debugging on port 9222"); parser.addOption(degubOption); + QCommandLineOption notifySendOption( + "notify-send", "Use notify-send instead of QT/KF5 notifications"); + parser.addOption(notifySendOption); parser.process(app); @@ -46,7 +49,7 @@ int main(int argc, char *argv[]) { "--remote-debugging-port=9222 " + qgetenv("QTWEBENGINE_CHROMIUM_FLAGS")); - MainWindow w; + MainWindow w(parser.isSet(notifySendOption)); w.show(); return app.exec(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index bcf8f57..c43582a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -24,9 +24,11 @@ MainWindow *MainWindow::m_instance = nullptr; -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { +MainWindow::MainWindow(bool useNotifySend, QWidget *parent) + : QMainWindow(parent) { assert(MainWindow::m_instance == nullptr); MainWindow::m_instance = this; + m_useNotifySend = useNotifySend; setupWebView(); resize(1000, 700); showMaximized(); @@ -40,22 +42,34 @@ void MainWindow::setupWebView() { m_webView = new QWebEngineView(this); m_webView->setPage(page); -#ifdef KNOTIFICATIONS - QWebEngineProfile::defaultProfile()->setNotificationPresenter( - [&](std::unique_ptr notificationInfo) { - KNotification *notification = new KNotification("discordNotification"); - notification->setTitle(notificationInfo->title()); - notification->setText(notificationInfo->message()); - notification->setPixmap(QPixmap::fromImage(notificationInfo->icon())); - notification->setDefaultAction("View"); - connect(notification, &KNotification::defaultActivated, - [&, notificationInfo = std::move(notificationInfo)]() { - notificationInfo->click(); - activateWindow(); - }); - notification->sendEvent(); - }); -#endif + if (m_useKF5Notifications || m_useNotifySend) + QWebEngineProfile::defaultProfile()->setNotificationPresenter( + [&](std::unique_ptr notificationInfo) { + if (m_useNotifySend) { + auto title = notificationInfo->title(); + auto message = notificationInfo->message(); + auto image_path = + QString("/tmp/discord-screenaudio-%1.png").arg(title); + notificationInfo->icon().save(image_path); + QProcess::execute("notify-send", + {"--icon", image_path, "--app-name", + "discord-screenaudio", title, message}); + } else if (m_useKF5Notifications) { + KNotification *notification = + new KNotification("discordNotification"); + notification->setTitle(notificationInfo->title()); + notification->setText(notificationInfo->message()); + notification->setPixmap( + QPixmap::fromImage(notificationInfo->icon())); + notification->setDefaultAction("View"); + connect(notification, &KNotification::defaultActivated, + [&, notificationInfo = std::move(notificationInfo)]() { + notificationInfo->click(); + activateWindow(); + }); + notification->sendEvent(); + } + }); setCentralWidget(m_webView); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 39721c8..e1b991a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -14,7 +14,7 @@ class MainWindow : public QMainWindow { Q_OBJECT public: - explicit MainWindow(QWidget *parent = nullptr); + explicit MainWindow(bool useNotifySend = false, QWidget *parent = nullptr); static MainWindow *instance(); private: @@ -25,6 +25,12 @@ private: void closeEvent(QCloseEvent *event) override; bool m_wasMaximized; static MainWindow *m_instance; + bool m_useNotifySend; +#ifdef KNOTIFICATIONS + bool m_useKF5Notifications = true; +#else + bool m_useKF5Notifications = false; +#endif private Q_SLOTS: void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest); From b0a8815bb8e252d7cafdb22736b1b28442fc45ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Fri, 4 Nov 2022 23:20:46 +0100 Subject: [PATCH 05/29] add clipboard permission --- src/discordpage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/discordpage.cpp b/src/discordpage.cpp index 6821a39..073641c 100644 --- a/src/discordpage.cpp +++ b/src/discordpage.cpp @@ -50,6 +50,8 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) { false); settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false); settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true); + settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, + true); setUrl(QUrl("https://discord.com/app")); From e41af697f76d3f02a940a2e581615423dff9f36d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Sat, 5 Nov 2022 00:08:25 +0100 Subject: [PATCH 06/29] fix error --- src/mainwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c43582a..9072bcf 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -55,6 +55,7 @@ void MainWindow::setupWebView() { {"--icon", image_path, "--app-name", "discord-screenaudio", title, message}); } else if (m_useKF5Notifications) { +#ifdef KNOTIFICATIONS KNotification *notification = new KNotification("discordNotification"); notification->setTitle(notificationInfo->title()); @@ -68,6 +69,7 @@ void MainWindow::setupWebView() { activateWindow(); }); notification->sendEvent(); +#endif } }); From d693535d539c143b309917ba2bde7cabb5552468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Wed, 9 Nov 2022 22:13:34 +0100 Subject: [PATCH 07/29] Revert "add clipboard permission" This reverts commit b0a8815bb8e252d7cafdb22736b1b28442fc45ba. --- src/discordpage.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/discordpage.cpp b/src/discordpage.cpp index 073641c..6821a39 100644 --- a/src/discordpage.cpp +++ b/src/discordpage.cpp @@ -50,8 +50,6 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) { false); settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false); settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true); - settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, - true); setUrl(QUrl("https://discord.com/app")); From f4a60f281df817967c8eec8d8b3996355b0f6fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Tue, 22 Nov 2022 16:35:47 +0100 Subject: [PATCH 08/29] reworked stream dialog --- assets/userscript.js | 45 ++++++++++------ src/discordpage.cpp | 24 ++++----- src/discordpage.h | 3 +- src/streamdialog.cpp | 123 ++++++++++++++++++++++++++----------------- src/streamdialog.h | 11 ++-- 5 files changed, 125 insertions(+), 81 deletions(-) diff --git a/assets/userscript.js b/assets/userscript.js index 88d33e8..8e22d3f 100644 --- a/assets/userscript.js +++ b/assets/userscript.js @@ -25,7 +25,7 @@ const getAudioDevice = async (nameOfAudioDevice) => { return audioDevice; }; -function setGetDisplayMedia(overrideArgs = undefined) { +function setGetDisplayMedia(video = true, overrideArgs = undefined) { const getDisplayMedia = async (...args) => { var id; try { @@ -63,6 +63,7 @@ function setGetDisplayMedia(overrideArgs = undefined) { : args || [{ video: true, audio: true }]) ); gdm.addTrack(track); + if (!video) for (const track of gdm.getVideoTracks()) track.enabled = false; return gdm; }; navigator.mediaDevices.getDisplayMedia = getDisplayMedia; @@ -111,9 +112,16 @@ setInterval(() => { const initialDisplay = el.style.display; - window.discordScreenaudioStartStream = (width, height, frameRate) => { - window.discordScreenaudioResolutionString = `${height}p ${frameRate}FPS`; - setGetDisplayMedia({ + window.discordScreenaudioStartStream = ( + video, + width, + height, + frameRate + ) => { + window.discordScreenaudioResolutionString = video + ? `${height}p ${frameRate}FPS` + : "Audio Only"; + setGetDisplayMedia(video, { audio: true, video: { width, height, frameRate }, }); @@ -183,20 +191,27 @@ setInterval(() => { el.appendChild(div); } - const buttonContainer = document.getElementsByClassName("container-YkUktl")[0]; + const buttonContainer = + document.getElementsByClassName("container-YkUktl")[0]; if (!buttonContainer) { - console.log('dsa: Cannot locate Mute/Deafen/Settings button container, please report this on GitHub'); + console.log( + "dsa: Cannot locate Mute/Deafen/Settings button container, please report this on GitHub" + ); } - - const muteBtn = buttonContainer ? buttonContainer.getElementsByClassName( - "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" - )[0] : null; + + const muteBtn = buttonContainer + ? buttonContainer.getElementsByClassName( + "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" + )[0] + : null; window.discordScreenaudioToggleMute = () => muteBtn && muteBtn.click(); - - const deafenBtn = buttonContainer ? buttonContainer.getElementsByClassName( - "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" - )[1] : null; - + + const deafenBtn = buttonContainer + ? buttonContainer.getElementsByClassName( + "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" + )[1] + : null; + window.discordScreenaudioToggleDeafen = () => deafenBtn && deafenBtn.click(); if (window.discordScreenaudioResolutionString) { diff --git a/src/discordpage.cpp b/src/discordpage.cpp index 6821a39..8f42f01 100644 --- a/src/discordpage.cpp +++ b/src/discordpage.cpp @@ -186,10 +186,8 @@ void DiscordPage::stopVirtmic() { } void DiscordPage::startVirtmic(QString target) { - if (target != "None") { - qDebug(virtmicLog) << "Starting Virtmic with target" << target; - m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target}); - } + qDebug(virtmicLog) << "Starting Virtmic with target" << target; + m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target}); } void DiscordPage::javaScriptConsoleMessage( @@ -230,16 +228,18 @@ void DiscordPage::javaScriptConsoleMessage( } } -void DiscordPage::startStream(QString target, uint width, uint height, - uint frameRate) { +void DiscordPage::startStream(bool video, bool audio, uint width, uint height, + uint frameRate, QString target) { stopVirtmic(); - startVirtmic(target); + startVirtmic(audio ? target : "[None]"); // Wait a bit for the virtmic to start - QTimer::singleShot(target == "None" ? 0 : 200, [=]() { - runJavaScript(QString("window.discordScreenaudioStartStream(%1, %2, %3);") - .arg(width) - .arg(height) - .arg(frameRate)); + QTimer::singleShot(200, [=]() { + runJavaScript( + QString("window.discordScreenaudioStartStream(%1, %2, %3, %4);") + .arg(video) + .arg(video ? width : 32) + .arg(video ? height : 16) + .arg(video ? frameRate : 1)); }); } diff --git a/src/discordpage.h b/src/discordpage.h index ee1b2ba..3668b2c 100644 --- a/src/discordpage.h +++ b/src/discordpage.h @@ -47,7 +47,8 @@ private: private Q_SLOTS: void featurePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature); - void startStream(QString target, uint width, uint height, uint frameRate); + void startStream(bool video, bool audio, uint width, uint height, + uint frameRate, QString target); }; // Will immediately get destroyed again but is needed for navigation to diff --git a/src/streamdialog.cpp b/src/streamdialog.cpp index 5a151fb..cdba4a3 100644 --- a/src/streamdialog.cpp +++ b/src/streamdialog.cpp @@ -12,70 +12,96 @@ StreamDialog::StreamDialog() : QWidget() { setAttribute(Qt::WA_QuitOnClose, false); - auto layout = new QVBoxLayout(this); - layout->setSizeConstraint(QLayout::SetFixedSize); + { + auto layout = new QVBoxLayout(this); + layout->setSizeConstraint(QLayout::SetFixedSize); - auto targetLabel = new QLabel(this); - targetLabel->setText("Which app do you want to stream sound from?"); - layout->addWidget(targetLabel); + m_videoGroupBox = new QGroupBox(this); + m_videoGroupBox->setTitle("Video"); + m_videoGroupBox->setCheckable(true); + layout->addWidget(m_videoGroupBox); - auto targetHBox = new QHBoxLayout(this); - layout->addLayout(targetHBox); + { + auto videoLayout = new QVBoxLayout(this); + m_videoGroupBox->setLayout(videoLayout); - m_targetComboBox = new QComboBox(this); - updateTargets(); - targetHBox->addWidget(m_targetComboBox); + auto resolutionLabel = new QLabel(this); + resolutionLabel->setText("Stream Resolution"); + videoLayout->addWidget(resolutionLabel); - auto refreshTargetsButton = new QPushButton(this); - refreshTargetsButton->setFixedSize(30, 30); - refreshTargetsButton->setIcon(QIcon::fromTheme("view-refresh")); - connect(refreshTargetsButton, &QPushButton::clicked, this, - &StreamDialog::updateTargets); - targetHBox->addWidget(refreshTargetsButton); + m_resolutionComboBox = new QComboBox(this); + m_resolutionComboBox->addItem("2160p", "3840x2160"); + m_resolutionComboBox->addItem("1440p", "2560x1440"); + m_resolutionComboBox->addItem("1080p", "1920x1080"); + m_resolutionComboBox->addItem("720p", "1280x720"); + m_resolutionComboBox->addItem("480p", "854x480"); + m_resolutionComboBox->addItem("360p", "640x360"); + m_resolutionComboBox->addItem("240p", "426x240"); + m_resolutionComboBox->setCurrentText("720p"); + videoLayout->addWidget(m_resolutionComboBox); - auto qualityLabel = new QLabel(this); - qualityLabel->setText("Stream Quality"); - layout->addWidget(qualityLabel); + auto framerateLabel = new QLabel(this); + framerateLabel->setText("Stream Framerate"); + videoLayout->addWidget(framerateLabel); - auto qualityHBox = new QHBoxLayout(this); - layout->addLayout(qualityHBox); + m_framerateComboBox = new QComboBox(this); + m_framerateComboBox->addItem("144 FPS", 144); + m_framerateComboBox->addItem("60 FPS", 60); + m_framerateComboBox->addItem("30 FPS", 30); + m_framerateComboBox->addItem("15 FPS", 15); + m_framerateComboBox->addItem("5 FPS", 5); + m_framerateComboBox->setCurrentText("30 FPS"); + videoLayout->addWidget(m_framerateComboBox); + } - m_qualityResolutionComboBox = new QComboBox(this); - m_qualityResolutionComboBox->addItem("2160p", "3840x2160"); - m_qualityResolutionComboBox->addItem("1440p", "2560x1440"); - m_qualityResolutionComboBox->addItem("1080p", "1920x1080"); - m_qualityResolutionComboBox->addItem("720p", "1280x720"); - m_qualityResolutionComboBox->addItem("480p", "854x480"); - m_qualityResolutionComboBox->addItem("360p", "640x360"); - m_qualityResolutionComboBox->addItem("240p", "426x240"); - m_qualityResolutionComboBox->setCurrentText("720p"); - qualityHBox->addWidget(m_qualityResolutionComboBox); + m_audioGroupBox = new QGroupBox(this); + m_audioGroupBox->setCheckable(true); + m_audioGroupBox->setTitle("Audio"); + layout->addWidget(m_audioGroupBox); - m_qualityFPSComboBox = new QComboBox(this); - m_qualityFPSComboBox->addItem("144 FPS", 144); - m_qualityFPSComboBox->addItem("60 FPS", 60); - m_qualityFPSComboBox->addItem("30 FPS", 30); - m_qualityFPSComboBox->addItem("15 FPS", 15); - m_qualityFPSComboBox->addItem("5 FPS", 5); - m_qualityFPSComboBox->setCurrentText("30 FPS"); - qualityHBox->addWidget(m_qualityFPSComboBox); + { + auto audioLayout = new QVBoxLayout(this); + m_audioGroupBox->setLayout(audioLayout); - auto button = new QPushButton(this); - button->setText("Start Stream"); - connect(button, &QPushButton::clicked, this, &StreamDialog::startStream); - layout->addWidget(button, Qt::AlignRight | Qt::AlignBottom); + auto targetLabel = new QLabel(this); + targetLabel->setText("Audio Source"); + audioLayout->addWidget(targetLabel); - setLayout(layout); + { + auto targetLayout = new QHBoxLayout(this); + audioLayout->addLayout(targetLayout); + + m_targetComboBox = new QComboBox(this); + updateTargets(); + targetLayout->addWidget(m_targetComboBox); + + auto refreshTargetsButton = new QPushButton(this); + refreshTargetsButton->setFixedSize(30, 30); + refreshTargetsButton->setIcon(QIcon::fromTheme("view-refresh")); + connect(refreshTargetsButton, &QPushButton::clicked, this, + &StreamDialog::updateTargets); + targetLayout->addWidget(refreshTargetsButton); + } + } + + auto button = new QPushButton(this); + button->setText("Start Stream"); + connect(button, &QPushButton::clicked, this, &StreamDialog::startStream); + layout->addWidget(button, Qt::AlignRight | Qt::AlignBottom); + + setLayout(layout); + } setWindowTitle("discord-screenaudio Stream Dialog"); } void StreamDialog::startStream() { - auto resolution = - m_qualityResolutionComboBox->currentData().toString().split('x'); - emit requestedStreamStart(m_targetComboBox->currentText(), + auto resolution = m_resolutionComboBox->currentData().toString().split('x'); + emit requestedStreamStart(m_videoGroupBox->isChecked(), + m_audioGroupBox->isChecked(), resolution[0].toUInt(), resolution[1].toUInt(), - m_qualityFPSComboBox->currentData().toUInt()); + m_framerateComboBox->currentData().toUInt(), + m_targetComboBox->currentText()); setHidden(true); } @@ -83,7 +109,6 @@ void StreamDialog::updateTargets() { auto lastTarget = m_targetComboBox->currentText(); m_targetComboBox->clear(); - m_targetComboBox->addItem("[None]"); m_targetComboBox->addItem("[All Desktop Audio]"); for (auto target : Virtmic::getTargets()) { m_targetComboBox->addItem(target); diff --git a/src/streamdialog.h b/src/streamdialog.h index c2f833c..8fab813 100644 --- a/src/streamdialog.h +++ b/src/streamdialog.h @@ -2,6 +2,7 @@ #include #include +#include #include class StreamDialog : public QWidget { @@ -12,12 +13,14 @@ public: private: QComboBox *m_targetComboBox; - QComboBox *m_qualityResolutionComboBox; - QComboBox *m_qualityFPSComboBox; + QComboBox *m_resolutionComboBox; + QComboBox *m_framerateComboBox; + QGroupBox *m_videoGroupBox; + QGroupBox *m_audioGroupBox; Q_SIGNALS: - void requestedStreamStart(QString target, uint width, uint height, - uint frameRate); + void requestedStreamStart(bool video, bool audio, uint width, uint height, + uint frameRate, QString target); public Q_SLOTS: void updateTargets(); From f2de080e1b0b080e78306a4f14ab94dbd4d9f40c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Tue, 22 Nov 2022 16:39:58 +0100 Subject: [PATCH 09/29] update labels --- src/streamdialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/streamdialog.cpp b/src/streamdialog.cpp index cdba4a3..ba25f58 100644 --- a/src/streamdialog.cpp +++ b/src/streamdialog.cpp @@ -26,7 +26,7 @@ StreamDialog::StreamDialog() : QWidget() { m_videoGroupBox->setLayout(videoLayout); auto resolutionLabel = new QLabel(this); - resolutionLabel->setText("Stream Resolution"); + resolutionLabel->setText("Resolution"); videoLayout->addWidget(resolutionLabel); m_resolutionComboBox = new QComboBox(this); @@ -41,7 +41,7 @@ StreamDialog::StreamDialog() : QWidget() { videoLayout->addWidget(m_resolutionComboBox); auto framerateLabel = new QLabel(this); - framerateLabel->setText("Stream Framerate"); + framerateLabel->setText("Framerate"); videoLayout->addWidget(framerateLabel); m_framerateComboBox = new QComboBox(this); From 30711593329be4d35744f61db99a6499fab890df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Sun, 11 Dec 2022 17:55:04 +0000 Subject: [PATCH 10/29] Update screenshot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ee83eaa..1af6c6d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Unlike a lot of other solutions, the audio here is directly fed into the screenshare and not passed to the user microphone ([see explanation](#how-it-works)). -![Screenshot_20220925_112945](https://user-images.githubusercontent.com/48161361/192137080-33466cf7-8c56-4373-90c6-01ea74b6fb83.png) +![Screenshot_20221211_185028](https://user-images.githubusercontent.com/48161361/206920213-58a8091a-d8f9-4bb7-ae3d-3f8581b84d24.png) The purpose of this project is **not** to provide an alternative to the original Discord client. Rather, it should be used in addition to the original client in From 1f6105f76b32dc1c0266f54f42cdf783379498b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Tue, 27 Dec 2022 19:00:10 +0100 Subject: [PATCH 11/29] allow for building with qt6 --- CMakeLists.txt | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 833258e..18b3df7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,24 +13,27 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") string(TIMESTAMP TIMESTAMP %s) # set(CMAKE_AUTOUIC ON) -find_package(Qt5 CONFIG REQUIRED COMPONENTS - Widgets - WebEngineWidgets -) +find_package(Qt5 COMPONENTS Widgets) +if (Qt5_FOUND) + find_package(Qt5 CONFIG REQUIRED COMPONENTS Widgets WebEngineWidgets) -find_package(KF5Notifications) -if(KF5Notifications_FOUND) - add_definitions( -DKNOTIFICATIONS ) -endif() + find_package(KF5Notifications) + if(KF5Notifications_FOUND) + add_definitions( -DKNOTIFICATIONS ) + endif() -find_package(KF5XmlGui) -if(KF5XmlGui_FOUND) - add_definitions( -DKXMLGUI ) -endif() + find_package(KF5XmlGui) + if(KF5XmlGui_FOUND) + add_definitions( -DKXMLGUI ) + endif() -find_package(KF5GlobalAccel) -if(KF5GlobalAccel_FOUND) - add_definitions( -DKGLOBALACCEL ) + find_package(KF5GlobalAccel) + if(KF5GlobalAccel_FOUND) + add_definitions( -DKGLOBALACCEL ) + endif() +else() + message(WARNING "Qt 5 was not found on your system and Qt 6 will be used. You will not be able to use any features using KDE Frameworks.") + find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets WebEngineWidgets) endif() set(discord-screenaudio_SRC @@ -66,7 +69,7 @@ add_subdirectory(submodules/rohrkabel) add_executable(discord-screenaudio ${discord-screenaudio_SRC}) -target_link_libraries(discord-screenaudio Qt5::Widgets Qt5::WebEngineWidgets rohrkabel) +target_link_libraries(discord-screenaudio Qt::Widgets Qt::WebEngineWidgets rohrkabel) if(KF5Notifications_FOUND) target_link_libraries(discord-screenaudio KF5::Notifications) From 374b8542616ff9f671ea1f3e138d79adae227603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Mon, 9 Jan 2023 22:19:45 +0100 Subject: [PATCH 12/29] Update README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1af6c6d..3bc0b96 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ You have multiple options: ### Requirements - Basic building tools +- An up-to-date system (I can't guarantee that it works on Debian or Ubuntu + 20/21) - CMake - Qt5 and QtWebEngine - **PipeWire** (it currently doesn't work with PulseAudio) @@ -57,7 +59,7 @@ You have multiple options: - _Kf5Notifications (optional, for better notifications)_ - _KXMLGui and KGlobalAccel (optional, for keybinds)_ -On Debian: +With apt: `apt install -y build-essential cmake qtbase5-dev qtwebengine5-dev libkf5notifications-dev libkf5xmlgui-dev libkf5globalaccel-dev pkg-config libpipewire-0.3-dev git` ### Building From bfb0714b1350b452edcdd39eb360a41444faf118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Mon, 9 Jan 2023 22:20:11 +0100 Subject: [PATCH 13/29] fix keybinds query selector --- assets/userscript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/userscript.js b/assets/userscript.js index 8e22d3f..55e5e84 100644 --- a/assets/userscript.js +++ b/assets/userscript.js @@ -171,7 +171,7 @@ setInterval(() => { document .getElementById("keybinds-tab") ?.getElementsByClassName( - "container-3jbRo5 info-1hMolH fontSize16-3zr6Io browserNotice-1u-Y5o" + "container-3jbRo5 info-1hMolH browserNotice-1u-Y5o" ).length ) { const el = document From b836be653070cbfddef1cf376a2c2bd0d6be6ba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Fri, 13 Jan 2023 23:33:47 +0100 Subject: [PATCH 14/29] Implement Tray Icon --- assets/userscript.js | 104 +++++++++++++++++++++++++++++++++++++++---- src/discordpage.cpp | 15 +++++-- src/mainwindow.cpp | 69 +++++++++++++++++++++++++++- src/mainwindow.h | 13 ++++++ 4 files changed, 189 insertions(+), 12 deletions(-) diff --git a/assets/userscript.js b/assets/userscript.js index 55e5e84..de7affb 100644 --- a/assets/userscript.js +++ b/assets/userscript.js @@ -75,6 +75,52 @@ const clonedElements = []; const hiddenElements = []; let wasStreamActive = false; +function createButton(text, onClick) { + const button = document.createElement("button"); + button.style.marginBottom = "20px"; + button.classList = + "button-f2h6uQ lookFilled-yCfaCM colorBrand-I6CyqQ sizeSmall-wU2dO- grow-2sR_-F"; + button.innerText = text; + button.addEventListener("click", onClick); + return button; +} + +function createSwitch(text, enabled, onClick) { + const container = document.createElement("div"); + container.style.marginBottom = "20px"; + container.className = "labelRow-2jl9gK"; + + const label = document.createElement("label"); + label.innerText = text; + label.className = "title-2dsDLn"; + container.appendChild(label); + + const svg = document.createElement("div"); + container.appendChild(svg); + + function setSvgDisabled() { + svg.innerHTML = `
`; + } + + function setSvgEnabled() { + svg.innerHTML = `
`; + } + + function updateSvg() { + if (enabled) setSvgEnabled(); + else setSvgDisabled(); + } + + container.addEventListener("click", () => { + enabled = !enabled; + updateSvg(); + onClick(enabled); + }); + updateSvg(); + + return container; +} + setInterval(() => { const streamActive = document.getElementsByClassName("panel-2ZFCRb activityPanel-9icbyU") @@ -179,14 +225,11 @@ setInterval(() => { .getElementsByClassName("children-1xdcWE")[0]; const div = document.createElement("div"); div.style.marginBottom = "50px"; - const button = document.createElement("button"); - button.classList = - "button-f2h6uQ lookFilled-yCfaCM colorBrand-I6CyqQ sizeSmall-wU2dO- grow-2sR_-F"; - button.innerText = "Edit Global Keybinds"; - button.addEventListener("click", () => { - console.log("!discord-screenaudio-keybinds"); - }); - div.appendChild(button); + div.appendChild( + createButton("Edit Global Keybinds", () => { + console.log("!discord-screenaudio-keybinds"); + }) + ); el.innerHTML = ""; el.appendChild(div); } @@ -221,6 +264,51 @@ setInterval(() => { el.innerHTML = window.discordScreenaudioResolutionString; } } + + const accountTab = document.getElementById("my-account-tab"); + if (accountTab) { + const discordScreenaudioSettings = document.getElementById( + "discord-screenaudio-settings" + ); + if (!discordScreenaudioSettings) { + const firstDivider = accountTab.getElementsByClassName( + "divider-_0um2u marginTop40-Q4o1tS" + )[0]; + if (firstDivider) { + const section = document.createElement("div"); + section.className = "marginTop40-Q4o1tS"; + section.id = "discord-screenaudio-settings"; + + const title = document.createElement("h2"); + title.className = + "h1-34Txb0 title-3hptVQ defaultColor-2cKwKo defaultMarginh1-EURXsm"; + title.innerText = "discord-screenaudio"; + section.appendChild(title); + + section.appendChild( + createButton("Edit Global Keybinds", () => { + console.log("!discord-screenaudio-keybinds"); + }) + ); + + section.appendChild( + createSwitch( + "Move discord-screenaudio to the system tray instead of closing", + window.discordScreenaudioTrayEnabled, + (enabled) => { + console.log(`!discord-screenaudio-tray-${enabled}`); + } + ) + ); + + const divider = document.createElement("div"); + divider.className = "divider-_0um2u marginTop40-Q4o1tS"; + + firstDivider.after(section); + section.after(divider); + } + } + } }, 500); // Fix for broken discord notifications after restart diff --git a/src/discordpage.cpp b/src/discordpage.cpp index 8f42f01..3b63111 100644 --- a/src/discordpage.cpp +++ b/src/discordpage.cpp @@ -55,9 +55,14 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) { injectScriptFile("userscript.js", ":/assets/userscript.js"); - injectScriptText("version.js", - QString("window.discordScreenaudioVersion = '%1';") - .arg(QApplication::applicationVersion())); + injectScriptText("vars.js", + QString("window.discordScreenaudioVersion = '%1'; " + "window.discordScreenaudioTrayEnabled = %2;") + .arg(QApplication::applicationVersion()) + .arg(MainWindow::instance() + ->settings() + ->value("trayIcon", false) + .toBool())); #ifdef KXMLGUI injectScriptText("xmlgui.js", "window.discordScreenaudioKXMLGUI = true;"); @@ -221,6 +226,10 @@ void DiscordPage::javaScriptConsoleMessage( "(KXmlGui and KGlobalAccel are not available).", QMessageBox::Ok); #endif + } else if (message == "!discord-screenaudio-tray-true") { + MainWindow::instance()->setTrayIcon(true); + } else if (message == "!discord-screenaudio-tray-false") { + MainWindow::instance()->setTrayIcon(false); } else if (message.startsWith("dsa: ")) { qDebug(userscriptLog) << message.mid(5).toUtf8().constData(); } else { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9072bcf..fa9a47f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -29,7 +29,9 @@ MainWindow::MainWindow(bool useNotifySend, QWidget *parent) assert(MainWindow::m_instance == nullptr); MainWindow::m_instance = this; m_useNotifySend = useNotifySend; + setupSettings(); setupWebView(); + setupTrayIcon(); resize(1000, 700); showMaximized(); } @@ -66,6 +68,7 @@ void MainWindow::setupWebView() { connect(notification, &KNotification::defaultActivated, [&, notificationInfo = std::move(notificationInfo)]() { notificationInfo->click(); + show(); activateWindow(); }); notification->sendEvent(); @@ -87,6 +90,70 @@ void MainWindow::fullScreenRequested( } } -void MainWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); } +void MainWindow::setupTrayIcon() { + if (m_settings->value("trayIcon", false).toBool() == false || + m_trayIcon != nullptr) + return; + + auto aboutAction = new QAction( + "discord-screenaudio v" + QString(DISCORD_SCEENAUDIO_VERSION_FULL), this); + aboutAction->setIcon(QIcon(":assets/de.shorsh.discord-screenaudio.png")); + aboutAction->setEnabled(false); + + auto exitAction = new QAction("Exit", this); + connect(exitAction, &QAction::triggered, []() { QApplication::quit(); }); + + m_trayIconMenu = new QMenu(this); + m_trayIconMenu->addAction(aboutAction); + m_trayIconMenu->addAction(exitAction); + + m_trayIcon = new QSystemTrayIcon(this); + m_trayIcon->setContextMenu(m_trayIconMenu); + m_trayIcon->setIcon(QIcon(":assets/de.shorsh.discord-screenaudio.png")); + m_trayIcon->show(); + + connect(m_trayIcon, &QSystemTrayIcon::activated, [this](auto reason) { + if (reason == QSystemTrayIcon::Trigger) { + if (isVisible()) { + hide(); + } else { + show(); + activateWindow(); + } + } + }); +} + +void MainWindow::cleanTrayIcon() { + m_trayIcon->hide(); + m_trayIconMenu->deleteLater(); + m_trayIcon->deleteLater(); + m_trayIconMenu = nullptr; + m_trayIcon = nullptr; +} + +void MainWindow::setupSettings() { + m_settings = new QSettings("maltejur", "discord-screenaudio", this); + m_settings->beginGroup("settings"); + m_settings->endGroup(); +} + +QSettings *MainWindow::settings() const { return m_settings; } + +void MainWindow::setTrayIcon(bool enabled) { + m_settings->setValue("trayIcon", enabled); + if (enabled) { + setupTrayIcon(); + } else { + cleanTrayIcon(); + } +} + +void MainWindow::closeEvent(QCloseEvent *event) { + if (m_settings->value("trayIcon", false).toBool()) { + hide(); + } else + QApplication::quit(); +} MainWindow *MainWindow::instance() { return m_instance; } diff --git a/src/mainwindow.h b/src/mainwindow.h index e1b991a..3b5ed4a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -3,8 +3,11 @@ #include "discordpage.h" #include +#include #include +#include #include +#include #include #include #include @@ -16,13 +19,20 @@ class MainWindow : public QMainWindow { public: explicit MainWindow(bool useNotifySend = false, QWidget *parent = nullptr); static MainWindow *instance(); + QSettings *settings() const; private: void setupWebView(); + void setupTrayIcon(); + void cleanTrayIcon(); + void setupSettings(); QWebEngineView *m_webView; QWebEngineProfile *prepareProfile(); DiscordPage *m_discordPage; void closeEvent(QCloseEvent *event) override; + QSystemTrayIcon *m_trayIcon; + QMenu *m_trayIconMenu; + QSettings *m_settings; bool m_wasMaximized; static MainWindow *m_instance; bool m_useNotifySend; @@ -32,6 +42,9 @@ private: bool m_useKF5Notifications = false; #endif +public Q_SLOTS: + void setTrayIcon(bool enabled); + private Q_SLOTS: void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest); }; From 9f46e710a9a4f71c887659b1c2686b0d073433be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Sat, 14 Jan 2023 16:28:23 +0100 Subject: [PATCH 15/29] fix tray bug --- src/mainwindow.cpp | 2 ++ src/mainwindow.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index fa9a47f..f60df7c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -125,6 +125,8 @@ void MainWindow::setupTrayIcon() { } void MainWindow::cleanTrayIcon() { + if (m_trayIcon == nullptr) + return; m_trayIcon->hide(); m_trayIconMenu->deleteLater(); m_trayIcon->deleteLater(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 3b5ed4a..0a50d62 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -30,7 +30,7 @@ private: QWebEngineProfile *prepareProfile(); DiscordPage *m_discordPage; void closeEvent(QCloseEvent *event) override; - QSystemTrayIcon *m_trayIcon; + QSystemTrayIcon *m_trayIcon = nullptr; QMenu *m_trayIconMenu; QSettings *m_settings; bool m_wasMaximized; From b4db987217dedd06f7467a85171bbed47fa8868f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Mon, 23 Jan 2023 18:59:25 +0100 Subject: [PATCH 16/29] fix tray settings --- assets/userscript.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/userscript.js b/assets/userscript.js index de7affb..5a5616f 100644 --- a/assets/userscript.js +++ b/assets/userscript.js @@ -296,6 +296,7 @@ setInterval(() => { "Move discord-screenaudio to the system tray instead of closing", window.discordScreenaudioTrayEnabled, (enabled) => { + window.discordScreenaudioTrayEnabled = enabled; console.log(`!discord-screenaudio-tray-${enabled}`); } ) From bc317d5531ce3b2c4e5372ec19b24a32be09a9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Wed, 25 Jan 2023 18:47:08 +0100 Subject: [PATCH 17/29] use `application.process.binary` if `application.name` is not available --- src/virtmic.cpp | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/virtmic.cpp b/src/virtmic.cpp index ba6d1fc..5ec2e5a 100644 --- a/src/virtmic.cpp +++ b/src/virtmic.cpp @@ -22,7 +22,12 @@ QVector getTargets() { if (global.type == pipewire::node::type) { auto node = reg.bind(global.id); auto info = node.info(); - auto name = QString::fromStdString(info.props["application.name"]); + QString name; + if (info.props.count("application.name")) + name = QString::fromStdString(info.props["application.name"]); + else + name = QString::fromStdString( + info.props["application.process.binary"]); if (name != "" && !EXCLUDE_TARGETS.contains(name) && !targets.contains(name)) { @@ -67,7 +72,11 @@ void start(QString _target) { continue; auto &parent = nodes.at(parent_id); - auto name = parent.props["application.name"]; + std::string name; + if (parent.props.count("application.name")) + name = parent.props["application.name"]; + else + name = parent.props["application.process.binary"]; if (name == target || (target == "[All Desktop Audio]" && @@ -78,8 +87,7 @@ void start(QString _target) { core.create( {fl ? virt_fl->info().id : virt_fr->info().id, port_id})); qDebug(virtmicLog) << QString("Link: %1:%2 -> %3") - .arg(QString::fromStdString( - parent.props["application.name"])) + .arg(QString::fromStdString(name)) .arg(port_id) .arg(fl ? virt_fl->info().id : virt_fr->info().id) @@ -112,11 +120,16 @@ void start(QString _target) { [&](const pipewire::global &global) { if (global.type == pipewire::node::type) { auto node = reg.bind(global.id); - if (!node.info().props.count("application.name")) + auto info = node.info(); + std::string name; + if (info.props.count("application.name")) + name = info.props["application.name"]; + else if (info.props.count("application.process.binary")) + name = info.props["application.process.binary"]; + else return; qDebug(virtmicLog) << QString("Added: %1") - .arg(QString::fromStdString( - node.info().props["application.name"])) + .arg(QString::fromStdString(name)) .toUtf8() .data(); @@ -152,9 +165,13 @@ void start(QString _target) { [&](const std::uint32_t id) { if (nodes.count(id)) { auto info = nodes.at(id); + std::string name; + if (info.props.count("application.name")) + name = info.props["application.name"]; + else + name = info.props["application.process.binary"]; qDebug(virtmicLog) << QString("Removed: %1") - .arg(QString::fromStdString( - info.props["application.name"].data())) + .arg(QString::fromStdString(name)) .toUtf8() .data(); nodes.erase(id); From 2a809e163e78e3c89241b0dd0593a665cc63cde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Thu, 26 Jan 2023 00:45:14 +0100 Subject: [PATCH 18/29] fix class names for tray settings --- assets/userscript.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/assets/userscript.js b/assets/userscript.js index 5a5616f..1b2cd8f 100644 --- a/assets/userscript.js +++ b/assets/userscript.js @@ -88,22 +88,22 @@ function createButton(text, onClick) { function createSwitch(text, enabled, onClick) { const container = document.createElement("div"); container.style.marginBottom = "20px"; - container.className = "labelRow-2jl9gK"; + container.className = "labelRow-NnoUIp"; const label = document.createElement("label"); label.innerText = text; - label.className = "title-2dsDLn"; + label.className = "title-2yADjX"; container.appendChild(label); const svg = document.createElement("div"); container.appendChild(svg); function setSvgDisabled() { - svg.innerHTML = `
`; + svg.innerHTML = `
`; } function setSvgEnabled() { - svg.innerHTML = `
`; + svg.innerHTML = `
`; } function updateSvg() { @@ -272,7 +272,7 @@ setInterval(() => { ); if (!discordScreenaudioSettings) { const firstDivider = accountTab.getElementsByClassName( - "divider-_0um2u marginTop40-Q4o1tS" + "divider-3nqZNm marginTop40-Q4o1tS" )[0]; if (firstDivider) { const section = document.createElement("div"); @@ -281,7 +281,7 @@ setInterval(() => { const title = document.createElement("h2"); title.className = - "h1-34Txb0 title-3hptVQ defaultColor-2cKwKo defaultMarginh1-EURXsm"; + "h1-3iMExa title-lXcL8p defaultColor-3Olr-9 defaultMarginh1-1UYutH"; title.innerText = "discord-screenaudio"; section.appendChild(title); @@ -303,7 +303,7 @@ setInterval(() => { ); const divider = document.createElement("div"); - divider.className = "divider-_0um2u marginTop40-Q4o1tS"; + divider.className = "divider-3nqZNm marginTop40-Q4o1tS"; firstDivider.after(section); section.after(divider); From a10cfda56d6a31acee4871527540992db7337d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Thu, 26 Jan 2023 01:19:38 +0100 Subject: [PATCH 19/29] fix usage of `application.process.binary` --- src/virtmic.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/virtmic.cpp b/src/virtmic.cpp index 5ec2e5a..2c0ba4f 100644 --- a/src/virtmic.cpp +++ b/src/virtmic.cpp @@ -23,7 +23,8 @@ QVector getTargets() { auto node = reg.bind(global.id); auto info = node.info(); QString name; - if (info.props.count("application.name")) + if (info.props.count("application.name") && + info.props["application.name"] != "") name = QString::fromStdString(info.props["application.name"]); else name = QString::fromStdString( @@ -73,7 +74,8 @@ void start(QString _target) { auto &parent = nodes.at(parent_id); std::string name; - if (parent.props.count("application.name")) + if (parent.props.count("application.name") && + parent.props["application.name"] != "") name = parent.props["application.name"]; else name = parent.props["application.process.binary"]; @@ -122,11 +124,12 @@ void start(QString _target) { auto node = reg.bind(global.id); auto info = node.info(); std::string name; - if (info.props.count("application.name")) + if (info.props.count("application.name") && + info.props["application.name"] != "") name = info.props["application.name"]; - else if (info.props.count("application.process.binary")) + else if (info.props.count("application.process.binary")) { name = info.props["application.process.binary"]; - else + } else return; qDebug(virtmicLog) << QString("Added: %1") .arg(QString::fromStdString(name)) @@ -166,7 +169,8 @@ void start(QString _target) { if (nodes.count(id)) { auto info = nodes.at(id); std::string name; - if (info.props.count("application.name")) + if (info.props.count("application.name") && + info.props["application.name"] != "") name = info.props["application.name"]; else name = info.props["application.process.binary"]; From 0b12487dfd9595d17093dd91e325a28b3a0e8ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Tue, 7 Feb 2023 11:53:10 +0100 Subject: [PATCH 20/29] add setting to start hidden to tray --- assets/userscript.js | 13 ++++++++++++- src/discordpage.cpp | 11 ++++++++++- src/mainwindow.cpp | 6 ++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/assets/userscript.js b/assets/userscript.js index 1b2cd8f..28aaa7e 100644 --- a/assets/userscript.js +++ b/assets/userscript.js @@ -99,7 +99,7 @@ function createSwitch(text, enabled, onClick) { container.appendChild(svg); function setSvgDisabled() { - svg.innerHTML = `
`; + svg.innerHTML = `
`; } function setSvgEnabled() { @@ -302,6 +302,17 @@ setInterval(() => { ) ); + section.appendChild( + createSwitch( + "Start discord-screenaudio hidden to tray", + window.discordScreenaudioStartHidden, + (hidden) => { + window.discordScreenaudioStartHidden = hidden; + console.log(`!discord-screenaudio-starthidden-${hidden}`); + } + ) + ); + const divider = document.createElement("div"); divider.className = "divider-3nqZNm marginTop40-Q4o1tS"; diff --git a/src/discordpage.cpp b/src/discordpage.cpp index 3b63111..01a699e 100644 --- a/src/discordpage.cpp +++ b/src/discordpage.cpp @@ -57,11 +57,16 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) { injectScriptText("vars.js", QString("window.discordScreenaudioVersion = '%1'; " - "window.discordScreenaudioTrayEnabled = %2;") + "window.discordScreenaudioTrayEnabled = %2; " + "window.discordScreenaudioStartHidden = %3;") .arg(QApplication::applicationVersion()) .arg(MainWindow::instance() ->settings() ->value("trayIcon", false) + .toBool()) + .arg(MainWindow::instance() + ->settings() + ->value("startHidden", false) .toBool())); #ifdef KXMLGUI @@ -230,6 +235,10 @@ void DiscordPage::javaScriptConsoleMessage( MainWindow::instance()->setTrayIcon(true); } else if (message == "!discord-screenaudio-tray-false") { MainWindow::instance()->setTrayIcon(false); + } else if (message == "!discord-screenaudio-starthidden-true") { + MainWindow::instance()->settings()->setValue("startHidden", true); + } else if (message == "!discord-screenaudio-starthidden-false") { + MainWindow::instance()->settings()->setValue("startHidden", false); } else if (message.startsWith("dsa: ")) { qDebug(userscriptLog) << message.mid(5).toUtf8().constData(); } else { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f60df7c..092ec18 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,11 @@ MainWindow::MainWindow(bool useNotifySend, QWidget *parent) setupTrayIcon(); resize(1000, 700); showMaximized(); + if (m_settings->value("trayIcon", false).toBool() && + m_settings->value("startHidden", false).toBool()) { + hide(); + QTimer::singleShot(0, [=]() { hide(); }); + } } void MainWindow::setupWebView() { From 1477a9d4c043bbab3308a333d91afa539bdb922d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Sun, 12 Feb 2023 17:20:33 +0100 Subject: [PATCH 21/29] use qtwebchannel to communicate with userscript --- CMakeLists.txt | 1 + assets/userscript.js | 427 ++++++++++++++++++++++--------------------- src/discordpage.cpp | 172 +---------------- src/discordpage.h | 26 +-- src/streamdialog.cpp | 6 +- src/streamdialog.h | 4 +- src/userscript.cpp | 164 +++++++++++++++++ src/userscript.h | 69 +++++++ 8 files changed, 469 insertions(+), 400 deletions(-) create mode 100644 src/userscript.cpp create mode 100644 src/userscript.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 18b3df7..9feccc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ set(discord-screenaudio_SRC src/discordpage.cpp src/streamdialog.cpp src/log.cpp + src/userscript.cpp resources.qrc ) diff --git a/assets/userscript.js b/assets/userscript.js index 28aaa7e..254ae6d 100644 --- a/assets/userscript.js +++ b/assets/userscript.js @@ -1,5 +1,3 @@ -// From v0.4 - navigator.mediaDevices.chromiumGetDisplayMedia = navigator.mediaDevices.getDisplayMedia; @@ -16,12 +14,12 @@ const getAudioDevice = async (nameOfAudioDevice) => { let devices = await navigator.mediaDevices.enumerateDevices(); audioDevice = devices.find(({ label }) => label === nameOfAudioDevice); if (!audioDevice) - console.log( - `dsa: Did not find '${nameOfAudioDevice}', trying again in 100ms` + userscript.log( + `Did not find '${nameOfAudioDevice}', trying again in 100ms` ); await sleep(100); } - console.log(`dsa: Found '${nameOfAudioDevice}'`); + userscript.log(`Found '${nameOfAudioDevice}'`); return audioDevice; }; @@ -71,6 +69,13 @@ function setGetDisplayMedia(video = true, overrideArgs = undefined) { setGetDisplayMedia(); +let userscript; +let muteBtn; +let deafenBtn; +let streamStartBtn; +let streamStartBtnInitialDisplay; +let streamStartBtnClone; +let resolutionString; const clonedElements = []; const hiddenElements = []; let wasStreamActive = false; @@ -121,208 +126,216 @@ function createSwitch(text, enabled, onClick) { return container; } -setInterval(() => { - const streamActive = - document.getElementsByClassName("panel-2ZFCRb activityPanel-9icbyU") - .length > 0; - - if (!streamActive && wasStreamActive) - console.log("!discord-screenaudio-stream-stopped"); - wasStreamActive = streamActive; - - if (streamActive) { - clonedElements.forEach((el) => { - el.remove(); - }); - clonedElements.length = 0; - - hiddenElements.forEach((el) => { - el.style.display = "block"; - }); - hiddenElements.length = 0; - } else { - for (const el of [ - document.getElementsByClassName("actionButtons-2vEOUh")?.[0]?.children[1], - document.querySelector( - ".wrapper-3t3Yqv > div > div > div > div > .controlButton-2PMNom" - ), - ]) { - if (!el) continue; - if (el.classList.contains("discord-screenaudio-cloned")) continue; - el.classList.add("discord-screenaudio-cloned"); - elClone = el.cloneNode(true); - elClone.title = "Share Your Screen with Audio"; - elClone.addEventListener("click", () => { - console.log("!discord-screenaudio-start-stream"); - }); - - const initialDisplay = el.style.display; - - window.discordScreenaudioStartStream = ( - video, - width, - height, - frameRate - ) => { - window.discordScreenaudioResolutionString = video - ? `${height}p ${frameRate}FPS` - : "Audio Only"; - setGetDisplayMedia(video, { - audio: true, - video: { width, height, frameRate }, - }); - el.click(); - el.style.display = initialDisplay; - elClone.remove(); - }; - - el.style.display = "none"; - el.parentNode.insertBefore(elClone, el); - - clonedElements.push(elClone); - hiddenElements.push(el); - } - } - - // Add about text in settings - if ( - document.getElementsByClassName("dirscordScreenaudioAboutText").length == 0 - ) { - for (const el of document.getElementsByClassName("info-3pQQBb")) { - let aboutEl; - if (window.discordScreenaudioKXMLGUI) { - aboutEl = document.createElement("a"); - aboutEl.addEventListener("click", () => { - console.log("!discord-screenaudio-about"); - }); - } else { - aboutEl = document.createElement("div"); - } - aboutEl.innerText = `discord-screenaudio ${window.discordScreenaudioVersion}`; - aboutEl.style.fontSize = "12px"; - aboutEl.style.color = "var(--text-muted)"; - aboutEl.style.textTransform = "none"; - aboutEl.classList.add("dirscordScreenaudioAboutText"); - aboutEl.style.cursor = "pointer"; - el.appendChild(aboutEl); - } - } - - // Remove stream settings if stream is active - document.getElementById("manage-streams-change-windows")?.remove(); - document.querySelector(`[aria-label="Stream Settings"]`)?.remove(); - - // Add event listener for keybind tab - if ( - document - .getElementById("keybinds-tab") - ?.getElementsByClassName( - "container-3jbRo5 info-1hMolH browserNotice-1u-Y5o" - ).length - ) { - const el = document - .getElementById("keybinds-tab") - .getElementsByClassName("children-1xdcWE")[0]; - const div = document.createElement("div"); - div.style.marginBottom = "50px"; - div.appendChild( - createButton("Edit Global Keybinds", () => { - console.log("!discord-screenaudio-keybinds"); - }) - ); - el.innerHTML = ""; - el.appendChild(div); - } - - const buttonContainer = - document.getElementsByClassName("container-YkUktl")[0]; - if (!buttonContainer) { - console.log( - "dsa: Cannot locate Mute/Deafen/Settings button container, please report this on GitHub" - ); - } - - const muteBtn = buttonContainer - ? buttonContainer.getElementsByClassName( - "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" - )[0] - : null; - window.discordScreenaudioToggleMute = () => muteBtn && muteBtn.click(); - - const deafenBtn = buttonContainer - ? buttonContainer.getElementsByClassName( - "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" - )[1] - : null; - - window.discordScreenaudioToggleDeafen = () => deafenBtn && deafenBtn.click(); - - if (window.discordScreenaudioResolutionString) { - for (const el of document.getElementsByClassName( - "qualityIndicator-39wQDy" - )) { - el.innerHTML = window.discordScreenaudioResolutionString; - } - } - - const accountTab = document.getElementById("my-account-tab"); - if (accountTab) { - const discordScreenaudioSettings = document.getElementById( - "discord-screenaudio-settings" - ); - if (!discordScreenaudioSettings) { - const firstDivider = accountTab.getElementsByClassName( - "divider-3nqZNm marginTop40-Q4o1tS" - )[0]; - if (firstDivider) { - const section = document.createElement("div"); - section.className = "marginTop40-Q4o1tS"; - section.id = "discord-screenaudio-settings"; - - const title = document.createElement("h2"); - title.className = - "h1-3iMExa title-lXcL8p defaultColor-3Olr-9 defaultMarginh1-1UYutH"; - title.innerText = "discord-screenaudio"; - section.appendChild(title); - - section.appendChild( - createButton("Edit Global Keybinds", () => { - console.log("!discord-screenaudio-keybinds"); - }) - ); - - section.appendChild( - createSwitch( - "Move discord-screenaudio to the system tray instead of closing", - window.discordScreenaudioTrayEnabled, - (enabled) => { - window.discordScreenaudioTrayEnabled = enabled; - console.log(`!discord-screenaudio-tray-${enabled}`); - } - ) - ); - - section.appendChild( - createSwitch( - "Start discord-screenaudio hidden to tray", - window.discordScreenaudioStartHidden, - (hidden) => { - window.discordScreenaudioStartHidden = hidden; - console.log(`!discord-screenaudio-starthidden-${hidden}`); - } - ) - ); - - const divider = document.createElement("div"); - divider.className = "divider-3nqZNm marginTop40-Q4o1tS"; - - firstDivider.after(section); - section.after(divider); - } - } - } -}, 500); - // Fix for broken discord notifications after restart // (https://github.com/maltejur/discord-screenaudio/issues/17) Notification.requestPermission(); + +setTimeout(() => { + new QWebChannel(qt.webChannelTransport, (channel) => { + userscript = channel.objects.userscript; + main(); + }); +}); + +function main() { + userscript.muteToggled.connect(() => { + muteBtn && muteBtn.click(); + }); + + userscript.deafenToggled.connect(() => { + deafenBtn && deafenBtn.click(); + }); + + userscript.streamStarted.connect((video, width, height, frameRate) => { + resolutionString = video ? `${height}p ${frameRate}FPS` : "Audio Only"; + setGetDisplayMedia(video, { + audio: true, + video: { width, height, frameRate }, + }); + streamStartBtn.click(); + streamStartBtn.style.display = streamStartBtnInitialDisplay; + streamStartBtnClone.remove(); + }); + + setInterval(async () => { + const streamActive = + document.getElementsByClassName("panel-2ZFCRb activityPanel-9icbyU") + .length > 0; + + if (!streamActive && wasStreamActive) userscript.stopVirtmic(); + wasStreamActive = streamActive; + + if (streamActive) { + clonedElements.forEach((el) => { + el.remove(); + }); + clonedElements.length = 0; + + hiddenElements.forEach((el) => { + el.style.display = "block"; + }); + hiddenElements.length = 0; + } else { + for (const el of [ + document.getElementsByClassName("actionButtons-2vEOUh")?.[0] + ?.children[1], + document.querySelector( + ".wrapper-3t3Yqv > div > div > div > div > .controlButton-2PMNom" + ), + ]) { + if (!el) continue; + if (el.classList.contains("discord-screenaudio-cloned")) continue; + streamStartBtn = el; + streamStartBtn.classList.add("discord-screenaudio-cloned"); + + streamStartBtnClone = streamStartBtn.cloneNode(true); + streamStartBtnClone.title = "Share Your Screen with Audio"; + streamStartBtnClone.addEventListener("click", () => { + userscript.showStreamDialog(); + }); + + streamStartBtnInitialDisplay = streamStartBtn.style.display; + + streamStartBtn.style.display = "none"; + streamStartBtn.parentNode.insertBefore(streamStartBtnClone, el); + + clonedElements.push(streamStartBtnClone); + hiddenElements.push(streamStartBtn); + } + } + + // Add about text in settings + if ( + document.getElementsByClassName("dirscordScreenaudioAboutText").length == + 0 + ) { + for (const el of document.getElementsByClassName("info-3pQQBb")) { + let aboutEl; + if (userscript.kxmlgui) { + aboutEl = document.createElement("a"); + aboutEl.addEventListener("click", () => { + userscript.showHelpMenu(); + }); + } else { + aboutEl = document.createElement("div"); + } + aboutEl.innerText = `discord-screenaudio ${userscript.version}`; + aboutEl.style.fontSize = "12px"; + aboutEl.style.color = "var(--text-muted)"; + aboutEl.style.textTransform = "none"; + aboutEl.classList.add("dirscordScreenaudioAboutText"); + aboutEl.style.cursor = "pointer"; + el.appendChild(aboutEl); + } + } + + // Remove stream settings if stream is active + document.getElementById("manage-streams-change-windows")?.remove(); + document.querySelector(`[aria-label="Stream Settings"]`)?.remove(); + + // Add event listener for keybind tab + if ( + document + .getElementById("keybinds-tab") + ?.getElementsByClassName( + "container-3jbRo5 info-1hMolH browserNotice-1u-Y5o" + ).length + ) { + const el = document + .getElementById("keybinds-tab") + .getElementsByClassName("children-1xdcWE")[0]; + const div = document.createElement("div"); + div.style.marginBottom = "50px"; + div.appendChild( + createButton("Edit Global Keybinds", () => { + userscript.log("!discord-screenaudio-keybinds"); + }) + ); + el.innerHTML = ""; + el.appendChild(div); + } + + const buttonContainer = + document.getElementsByClassName("container-YkUktl")[0]; + if (!buttonContainer) { + userscript.log( + "Cannot locate Mute/Deafen/Settings button container, please report this on GitHub" + ); + } + + muteBtn = buttonContainer + ? buttonContainer.getElementsByClassName( + "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" + )[0] + : null; + + deafenBtn = buttonContainer + ? buttonContainer.getElementsByClassName( + "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" + )[1] + : null; + + if (resolutionString) { + for (const el of document.getElementsByClassName( + "qualityIndicator-39wQDy" + )) { + el.innerHTML = resolutionString; + } + } + + const accountTab = document.getElementById("my-account-tab"); + if (accountTab) { + const discordScreenaudioSettings = document.getElementById( + "discord-screenaudio-settings" + ); + if (!discordScreenaudioSettings) { + const firstDivider = accountTab.getElementsByClassName( + "divider-3nqZNm marginTop40-Q4o1tS" + )[0]; + if (firstDivider) { + const section = document.createElement("div"); + section.className = "marginTop40-Q4o1tS"; + section.id = "discord-screenaudio-settings"; + + const title = document.createElement("h2"); + title.className = + "h1-3iMExa title-lXcL8p defaultColor-3Olr-9 defaultMarginh1-1UYutH"; + title.innerText = "discord-screenaudio"; + section.appendChild(title); + + section.appendChild( + createButton("Edit Global Keybinds", () => { + userscript.log("!discord-screenaudio-keybinds"); + }) + ); + + section.appendChild( + createSwitch( + "Move discord-screenaudio to the system tray instead of closing", + await userscript.getBoolPref("trayIcon", false), + (enabled) => { + userscript.setTrayIcon(enabled); + } + ) + ); + + section.appendChild( + createSwitch( + "Start discord-screenaudio hidden to tray", + await userscript.getPref("startHidden", false), + (hidden) => { + userscript.setPref("startHidden", hidden); + } + ) + ); + + const divider = document.createElement("div"); + divider.className = "divider-3nqZNm marginTop40-Q4o1tS"; + + firstDivider.after(section); + section.after(divider); + } + } + } + }, 500); +} diff --git a/src/discordpage.cpp b/src/discordpage.cpp index 01a699e..2739a19 100644 --- a/src/discordpage.cpp +++ b/src/discordpage.cpp @@ -3,19 +3,6 @@ #include "mainwindow.h" #include "virtmic.h" -#ifdef KXMLGUI -#include -#include -#include -#include -#include - -#ifdef KGLOBALACCEL -#include -#endif - -#endif - #include #include #include @@ -29,7 +16,6 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) { setBackgroundColor(QColor("#202225")); - m_virtmicProcess.setProcessChannelMode(QProcess::ForwardedChannels); connect(this, &QWebEnginePage::featurePermissionRequested, this, &DiscordPage::featurePermissionRequested); @@ -51,77 +37,14 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) { settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false); settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true); + injectScriptFile("qwebchannel.js", ":/qtwebchannel/qwebchannel.js"); + setUrl(QUrl("https://discord.com/app")); + setWebChannel(new QWebChannel(this)); + webChannel()->registerObject("userscript", &m_userScript); + injectScriptFile("userscript.js", ":/assets/userscript.js"); - - injectScriptText("vars.js", - QString("window.discordScreenaudioVersion = '%1'; " - "window.discordScreenaudioTrayEnabled = %2; " - "window.discordScreenaudioStartHidden = %3;") - .arg(QApplication::applicationVersion()) - .arg(MainWindow::instance() - ->settings() - ->value("trayIcon", false) - .toBool()) - .arg(MainWindow::instance() - ->settings() - ->value("startHidden", false) - .toBool())); - -#ifdef KXMLGUI - injectScriptText("xmlgui.js", "window.discordScreenaudioKXMLGUI = true;"); - - KAboutData aboutData( - "discord-screenaudio", "discord-screenaudio", - QApplication::applicationVersion(), - "Custom Discord client with the ability to stream audio on Linux", - KAboutLicense::GPL_V3, "Copyright 2022 (C) Malte Jürgens"); - aboutData.setBugAddress("https://github.com/maltejur/discord-screenaudio"); - aboutData.addAuthor("Malte Jürgens", "Author", "maltejur@dismail.de", - "https://github.com/maltejur"); - aboutData.addCredit("edisionnano", - "For creating and documenting the approach for streaming " - "audio in Discord used in this project.", - QString(), - "https://github.com/edisionnano/" - "Screenshare-with-audio-on-Discord-with-Linux"); - aboutData.addCredit( - "Curve", "For creating the Rohrkabel library used in this project.", - QString(), "https://github.com/Curve"); - aboutData.addComponent("Rohrkabel", "A C++ RAII Pipewire-API Wrapper", "1.3", - "https://github.com/Soundux/rohrkabel"); - m_helpMenu = new KHelpMenu(parent, aboutData); - -#ifdef KGLOBALACCEL - injectScriptText("kglobalaccel.js", - "window.discordScreenaudioKGLOBALACCEL = true;"); - - auto toggleMuteAction = new QAction(this); - toggleMuteAction->setText("Toggle Mute"); - toggleMuteAction->setIcon(QIcon::fromTheme("microphone-sensitivity-muted")); - connect(toggleMuteAction, &QAction::triggered, this, - &DiscordPage::toggleMute); - - auto toggleDeafenAction = new QAction(this); - toggleDeafenAction->setText("Toggle Deafen"); - toggleDeafenAction->setIcon(QIcon::fromTheme("audio-volume-muted")); - connect(toggleDeafenAction, &QAction::triggered, this, - &DiscordPage::toggleDeafen); - - m_actionCollection = new KActionCollection(this); - m_actionCollection->addAction("toggleMute", toggleMuteAction); - KGlobalAccel::setGlobalShortcut(toggleMuteAction, QList{}); - m_actionCollection->addAction("toggleDeafen", toggleDeafenAction); - KGlobalAccel::setGlobalShortcut(toggleDeafenAction, QList{}); - - m_shortcutsDialog = new KShortcutsDialog(KShortcutsEditor::GlobalAction); - m_shortcutsDialog->addCollection(m_actionCollection); -#endif -#endif - - connect(&m_streamDialog, &StreamDialog::requestedStreamStart, this, - &DiscordPage::startStream); } void DiscordPage::injectScriptText(QString name, QString content) { @@ -156,11 +79,10 @@ void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::PermissionGrantedByUser); if (feature == QWebEnginePage::Feature::MediaAudioCapture) { - if (m_virtmicProcess.state() == QProcess::NotRunning) { + if (!m_userScript.isVirtmicRunning()) { qDebug(virtmicLog) << "Starting Virtmic with no target to make sure " "Discord can find all the audio devices"; - m_virtmicProcess.start(QApplication::arguments()[0], - {"--virtmic", "None"}); + m_userScript.startVirtmic("None"); } } } @@ -187,86 +109,8 @@ QWebEnginePage *DiscordPage::createWindow(QWebEnginePage::WebWindowType type) { return new ExternalPage; } -void DiscordPage::stopVirtmic() { - if (m_virtmicProcess.state() == QProcess::Running) { - qDebug(virtmicLog) << "Stopping Virtmic"; - m_virtmicProcess.kill(); - m_virtmicProcess.waitForFinished(); - } -} - -void DiscordPage::startVirtmic(QString target) { - qDebug(virtmicLog) << "Starting Virtmic with target" << target; - m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target}); -} - void DiscordPage::javaScriptConsoleMessage( QWebEnginePage::JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) { - if (message == "!discord-screenaudio-start-stream") { - if (m_streamDialog.isHidden()) - m_streamDialog.setHidden(false); - else - m_streamDialog.activateWindow(); - m_streamDialog.updateTargets(); - } else if (message == "!discord-screenaudio-stream-stopped") { - stopVirtmic(); - } else if (message == "!discord-screenaudio-about") { -#ifdef KXMLGUI - m_helpMenu->aboutApplication(); -#endif - } else if (message == "!discord-screenaudio-keybinds") { -#ifdef KXMLGUI -#ifdef KGLOBALACCEL - m_shortcutsDialog->show(); -#else - QMessageBox::information(MainWindow::instance(), "discord-screenaudio", - "Keybinds are not supported on this platform " - "(KGlobalAccel is not available).", - QMessageBox::Ok); -#endif -#else - QMessageBox::information(MainWindow::instance(), "discord-screenaudio", - "Keybinds are not supported on this platform " - "(KXmlGui and KGlobalAccel are not available).", - QMessageBox::Ok); -#endif - } else if (message == "!discord-screenaudio-tray-true") { - MainWindow::instance()->setTrayIcon(true); - } else if (message == "!discord-screenaudio-tray-false") { - MainWindow::instance()->setTrayIcon(false); - } else if (message == "!discord-screenaudio-starthidden-true") { - MainWindow::instance()->settings()->setValue("startHidden", true); - } else if (message == "!discord-screenaudio-starthidden-false") { - MainWindow::instance()->settings()->setValue("startHidden", false); - } else if (message.startsWith("dsa: ")) { - qDebug(userscriptLog) << message.mid(5).toUtf8().constData(); - } else { - qDebug(discordLog) << message; - } -} - -void DiscordPage::startStream(bool video, bool audio, uint width, uint height, - uint frameRate, QString target) { - stopVirtmic(); - startVirtmic(audio ? target : "[None]"); - // Wait a bit for the virtmic to start - QTimer::singleShot(200, [=]() { - runJavaScript( - QString("window.discordScreenaudioStartStream(%1, %2, %3, %4);") - .arg(video) - .arg(video ? width : 32) - .arg(video ? height : 16) - .arg(video ? frameRate : 1)); - }); -} - -void DiscordPage::toggleMute() { - qDebug(shortcutLog) << "Toggling mute"; - runJavaScript("window.discordScreenaudioToggleMute();"); -} - -void DiscordPage::toggleDeafen() { - qDebug(shortcutLog) << "Toggling deafen"; - runJavaScript("window.discordScreenaudioToggleDeafen();"); + qDebug(discordLog) << message; } diff --git a/src/discordpage.h b/src/discordpage.h index 3668b2c..cc0da53 100644 --- a/src/discordpage.h +++ b/src/discordpage.h @@ -1,15 +1,7 @@ #pragma once -#include "streamdialog.h" -#include "virtmic.h" +#include "userscript.h" -#ifdef KXMLGUI -#include -#include -#include -#endif - -#include #include #include @@ -20,15 +12,7 @@ public: explicit DiscordPage(QWidget *parent = nullptr); private: - StreamDialog m_streamDialog; - QProcess m_virtmicProcess; -#ifdef KXMLGUI - KHelpMenu *m_helpMenu; -#ifdef KGLOBALACCEL - KActionCollection *m_actionCollection; - KShortcutsDialog *m_shortcutsDialog; -#endif -#endif + UserScript m_userScript; bool acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) override; @@ -39,16 +23,10 @@ private: const QString &sourceID) override; void injectScriptText(QString name, QString content); void injectScriptFile(QString name, QString source); - void stopVirtmic(); - void startVirtmic(QString target); - void toggleMute(); - void toggleDeafen(); private Q_SLOTS: void featurePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature); - void startStream(bool video, bool audio, uint width, uint height, - uint frameRate, QString target); }; // Will immediately get destroyed again but is needed for navigation to diff --git a/src/streamdialog.cpp b/src/streamdialog.cpp index ba25f58..b1ebc2b 100644 --- a/src/streamdialog.cpp +++ b/src/streamdialog.cpp @@ -98,9 +98,9 @@ StreamDialog::StreamDialog() : QWidget() { void StreamDialog::startStream() { auto resolution = m_resolutionComboBox->currentData().toString().split('x'); emit requestedStreamStart(m_videoGroupBox->isChecked(), - m_audioGroupBox->isChecked(), - resolution[0].toUInt(), resolution[1].toUInt(), - m_framerateComboBox->currentData().toUInt(), + m_audioGroupBox->isChecked(), resolution[0].toInt(), + resolution[1].toInt(), + m_framerateComboBox->currentData().toInt(), m_targetComboBox->currentText()); setHidden(true); } diff --git a/src/streamdialog.h b/src/streamdialog.h index 8fab813..0154a8d 100644 --- a/src/streamdialog.h +++ b/src/streamdialog.h @@ -19,8 +19,8 @@ private: QGroupBox *m_audioGroupBox; Q_SIGNALS: - void requestedStreamStart(bool video, bool audio, uint width, uint height, - uint frameRate, QString target); + void requestedStreamStart(bool video, bool audio, int width, int height, + int frameRate, QString target); public Q_SLOTS: void updateTargets(); diff --git a/src/userscript.cpp b/src/userscript.cpp new file mode 100644 index 0000000..e6fc764 --- /dev/null +++ b/src/userscript.cpp @@ -0,0 +1,164 @@ +#include "userscript.h" +#include "log.h" +#include "mainwindow.h" + +#include +#include +#include + +#ifdef KXMLGUI +#include +#endif + +UserScript::UserScript() : QObject() { + setupHelpMenu(); + setupShortcutsDialog(); + setupStreamDialog(); + setupVirtmic(); +} + +void UserScript::setupHelpMenu() { +#ifdef KXMLGUI + m_kxmlgui = true; + + KAboutData aboutData( + "discord-screenaudio", "discord-screenaudio", + QApplication::applicationVersion(), + "Custom Discord client with the ability to stream audio on Linux", + KAboutLicense::GPL_V3, "Copyright 2022 (C) Malte Jürgens"); + aboutData.setBugAddress("https://github.com/maltejur/discord-screenaudio"); + aboutData.addAuthor("Malte Jürgens", "Author", "maltejur@dismail.de", + "https://github.com/maltejur"); + aboutData.addCredit("edisionnano", + "For creating and documenting the approach for streaming " + "audio in Discord used in this project.", + QString(), + "https://github.com/edisionnano/" + "Screenshare-with-audio-on-Discord-with-Linux"); + aboutData.addCredit( + "Curve", "For creating the Rohrkabel library used in this project.", + QString(), "https://github.com/Curve"); + aboutData.addComponent("Rohrkabel", "A C++ RAII Pipewire-API Wrapper", "1.3", + "https://github.com/Soundux/rohrkabel"); + m_helpMenu = new KHelpMenu(MainWindow::instance(), aboutData); +#endif +} + +void UserScript::setupShortcutsDialog() { +#ifdef KXMLGUI +#ifdef KGLOBALACCEL + m_kglobalaccel = true; + + auto toggleMuteAction = new QAction(this); + toggleMuteAction->setText("Toggle Mute"); + toggleMuteAction->setIcon(QIcon::fromTheme("microphone-sensitivity-muted")); + connect(toggleMuteAction, &QAction::triggered, this, + &UserScript::muteToggled); + + auto toggleDeafenAction = new QAction(this); + toggleDeafenAction->setText("Toggle Deafen"); + toggleDeafenAction->setIcon(QIcon::fromTheme("audio-volume-muted")); + connect(toggleMuteAction, &QAction::triggered, this, + &UserScript::deafenToggled); + + m_actionCollection = new KActionCollection(this); + m_actionCollection->addAction("toggleMute", toggleMuteAction); + KGlobalAccel::setGlobalShortcut(toggleMuteAction, QList{}); + m_actionCollection->addAction("toggleDeafen", toggleDeafenAction); + KGlobalAccel::setGlobalShortcut(toggleDeafenAction, QList{}); + + m_shortcutsDialog = new KShortcutsDialog(KShortcutsEditor::GlobalAction); + m_shortcutsDialog->addCollection(m_actionCollection); +#endif +#endif +} + +void UserScript::setupStreamDialog() { + connect(&m_streamDialog, &StreamDialog::requestedStreamStart, this, + &UserScript::startStream); +} + +void UserScript::setupVirtmic() { + m_virtmicProcess.setProcessChannelMode(QProcess::ForwardedChannels); +} + +bool UserScript::isVirtmicRunning() { + return m_virtmicProcess.state() != QProcess::NotRunning; +} + +QString UserScript::version() { return QApplication::applicationVersion(); } + +QVariant UserScript::getPref(QString name, QVariant fallback) { + return MainWindow::instance()->settings()->value(name, fallback); +} + +bool UserScript::getBoolPref(QString name, bool fallback) { + return getPref(name, fallback).toBool(); +} + +void UserScript::setPref(QString name, QVariant value) { + return MainWindow::instance()->settings()->setValue(name, value); +} + +void UserScript::setTrayIcon(bool value) { + setPref("trayIcon", value); + MainWindow::instance()->setTrayIcon(value); +} + +void UserScript::log(QString message) { + qDebug(userscriptLog) << message.toUtf8().constData(); +} + +void UserScript::showShortcutsDialog() { +#ifdef KXMLGUI +#ifdef KGLOBALACCEL + m_shortcutsDialog->show(); +#else + QMessageBox::information(MainWindow::instance(), "discord-screenaudio", + "Keybinds are not supported on this platform " + "(KGlobalAccel is not available).", + QMessageBox::Ok); +#endif +#else + QMessageBox::information(MainWindow::instance(), "discord-screenaudio", + "Keybinds are not supported on this platform " + "(KXmlGui and KGlobalAccel are not available).", + QMessageBox::Ok); +#endif +} + +void UserScript::showHelpMenu() { +#ifdef KXMLGUI + m_helpMenu->aboutApplication(); +#endif +} + +void UserScript::stopVirtmic() { + if (m_virtmicProcess.state() == QProcess::Running) { + qDebug(virtmicLog) << "Stopping Virtmic"; + m_virtmicProcess.kill(); + m_virtmicProcess.waitForFinished(); + } +} + +void UserScript::startVirtmic(QString target) { + qDebug(virtmicLog) << "Starting Virtmic with target" << target; + m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target}); +} + +void UserScript::startStream(bool video, bool audio, int width, int height, + int frameRate, QString target) { + stopVirtmic(); + startVirtmic(audio ? target : "[None]"); + // Wait a bit for the virtmic to start + QTimer::singleShot( + 200, [=]() { emit streamStarted(video, width, height, frameRate); }); +} + +void UserScript::showStreamDialog() { + if (m_streamDialog.isHidden()) + m_streamDialog.setHidden(false); + else + m_streamDialog.activateWindow(); + m_streamDialog.updateTargets(); +} \ No newline at end of file diff --git a/src/userscript.h b/src/userscript.h new file mode 100644 index 0000000..bb1ca5e --- /dev/null +++ b/src/userscript.h @@ -0,0 +1,69 @@ +#pragma once + +#include "streamdialog.h" + +#include +#include + +#ifdef KXMLGUI +#include +#include +#include +#include +#include + +#ifdef KGLOBALACCEL +#include +#endif + +#endif + +class UserScript : public QObject { + Q_OBJECT + +public: + UserScript(); + bool isVirtmicRunning(); + Q_PROPERTY(QString version READ version); + Q_PROPERTY(bool kxmlgui MEMBER m_kxmlgui); + Q_PROPERTY(bool kglobalaccel MEMBER m_kglobalaccel); + +private: + QProcess m_virtmicProcess; + StreamDialog m_streamDialog; + bool m_kxmlgui = false; + bool m_kglobalaccel = false; +#ifdef KXMLGUI + KHelpMenu *m_helpMenu; +#ifdef KGLOBALACCEL + KActionCollection *m_actionCollection; + KShortcutsDialog *m_shortcutsDialog; +#endif +#endif + void setupHelpMenu(); + void setupShortcutsDialog(); + void setupStreamDialog(); + void setupVirtmic(); + +Q_SIGNALS: + void muteToggled(); + void deafenToggled(); + void streamStarted(bool video, int width, int height, int frameRate); + +public Q_SLOTS: + void log(QString message); + QString version(); + QVariant getPref(QString name, QVariant fallback); + bool getBoolPref(QString name, bool fallback); + void setPref(QString name, QVariant value); + void setTrayIcon(bool value); + void showShortcutsDialog(); + void showHelpMenu(); + void showStreamDialog(); + void stopVirtmic(); + void startVirtmic(QString target); + +private Q_SLOTS: + void startStream(bool video, bool audio, int width, int height, int frameRate, + QString target); +}; From 5df24629e6005d4c8625868ba0644e6f50c49d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Sun, 12 Feb 2023 17:21:10 +0100 Subject: [PATCH 22/29] add faq about drag and drop in flatpak --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3bc0b96..5832abc 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,9 @@ And then to optionally install it, run: sudo cmake --install build ``` -## How it works +## FAQ + +### How does this work? This whole project is based on [this](https://github.com/edisionnano/Screenshare-with-audio-on-Discord-with-Linux) @@ -93,6 +95,14 @@ Discord. Basically: a virtual microphone is created which captures the application audio, and this microphone is then fed to the Discord stream by intercepting a API call of Discord. +### Drag and drop doesn't work in the Flatpak + +This is due to sandboxing limitations of Flatpak. The main Discord Flatpak has +the same problem. If you still want to use drag and drop, you can disable most +of Flatpak's sandboxing by installing +[Flatseal](https://flathub.org/apps/details/com.github.tchx84.Flatseal) and +allowing access to "All system files" under the "Filesystem" section. + ## License Copyright (C) 2022 Malte Jürgens From fc0dbb5b344a50d2361e453fd6620eef77095e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Wed, 15 Feb 2023 00:33:52 +0100 Subject: [PATCH 23/29] make userscript properties constant --- src/userscript.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/userscript.h b/src/userscript.h index bb1ca5e..ab4ba37 100644 --- a/src/userscript.h +++ b/src/userscript.h @@ -24,9 +24,9 @@ class UserScript : public QObject { public: UserScript(); bool isVirtmicRunning(); - Q_PROPERTY(QString version READ version); - Q_PROPERTY(bool kxmlgui MEMBER m_kxmlgui); - Q_PROPERTY(bool kglobalaccel MEMBER m_kglobalaccel); + Q_PROPERTY(QString version READ version CONSTANT); + Q_PROPERTY(bool kxmlgui MEMBER m_kxmlgui CONSTANT); + Q_PROPERTY(bool kglobalaccel MEMBER m_kglobalaccel CONSTANT); private: QProcess m_virtmicProcess; From ac71e9bbcb0cc6eaaca9dabbe0ad6c30e51841da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Wed, 15 Feb 2023 01:30:22 +0100 Subject: [PATCH 24/29] make discord log look better --- src/discordpage.cpp | 73 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/src/discordpage.cpp b/src/discordpage.cpp index 2739a19..65596f3 100644 --- a/src/discordpage.cpp +++ b/src/discordpage.cpp @@ -109,8 +109,79 @@ QWebEnginePage *DiscordPage::createWindow(QWebEnginePage::WebWindowType type) { return new ExternalPage; } +const QMap cssAnsiColorMap = {{"black", "30"}, + {"red", "31"}, + {"green", "32"}, + {"yellow", "33"}, + {"blue", "34"}, + {"magenta", "35"}, + {"cyan", "36"}, + {"white", "37"}, + {"gray", "90"}, + {"bright-red", "91"}, + {"bright-green", "92"}, + {"bright-yellow", "93"}, + {"bright-blue", "94"}, + {"bright-magenta", "95"}, + {"bright-cyan", "96"}, + {"bright-white", "97"}, + {"orange", "38;5;208"}, + {"pink", "38;5;205"}, + {"brown", "38;5;94"}, + {"light-gray", "38;5;251"}, + {"dark-gray", "38;5;239"}, + {"light-red", "38;5;203"}, + {"light-green", "38;5;83"}, + {"light-yellow", "38;5;227"}, + {"light-blue", "38;5;75"}, + {"light-magenta", "38;5;207"}, + {"light-cyan", "38;5;87"}, + {"turquoise", "38;5;80"}, + {"violet", "38;5;92"}, + {"purple", "38;5;127"}, + {"lavender", "38;5;183"}, + {"maroon", "38;5;124"}, + {"beige", "38;5;230"}, + {"olive", "38;5;142"}, + {"indigo", "38;5;54"}, + {"teal", "38;5;30"}, + {"gold", "38;5;220"}, + {"silver", "38;5;7"}, + {"navy", "38;5;17"}, + {"steel", "38;5;188"}, + {"salmon", "38;5;173"}, + {"peach", "38;5;217"}, + {"khaki", "38;5;179"}, + {"coral", "38;5;209"}, + {"crimson", "38;5;160"}}; + void DiscordPage::javaScriptConsoleMessage( QWebEnginePage::JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) { - qDebug(discordLog) << message; + auto colorSegments = message.split("%c"); + for (auto segment : colorSegments.mid(1)) { + auto lines = segment.split("\n"); + QString ansi; + uint endOfStyles = lines.length(); + for (size_t line = 1; line < lines.length(); line++) { + if (!lines[line].endsWith(";")) { + endOfStyles = line; + break; + } + if (lines[line] == "font-weight: bold;") + ansi += "\033[1m"; + else if (lines[line].startsWith("color: ")) { + auto color = lines[line].mid(7).chopped(1); + if (cssAnsiColorMap.find(color) != cssAnsiColorMap.end()) + ansi += "\033[" + cssAnsiColorMap[color] + "m"; + } + } + qDebug(discordLog) << (ansi + lines[0].trimmed() + "\033[0m " + + lines[endOfStyles].trimmed()) + .toUtf8() + .constData(); + for (auto line : lines.mid(endOfStyles + 1)) { + qDebug(discordLog) << line.toUtf8().constData(); + } + } } From 8c4fae341046cd686af974b9a738a6c4c670f32f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Wed, 15 Feb 2023 01:35:23 +0100 Subject: [PATCH 25/29] fix keybind button --- assets/userscript.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/userscript.js b/assets/userscript.js index 254ae6d..4b58d2c 100644 --- a/assets/userscript.js +++ b/assets/userscript.js @@ -84,7 +84,7 @@ function createButton(text, onClick) { const button = document.createElement("button"); button.style.marginBottom = "20px"; button.classList = - "button-f2h6uQ lookFilled-yCfaCM colorBrand-I6CyqQ sizeSmall-wU2dO- grow-2sR_-F"; + "button-ejjZWC lookFilled-1H2Jvj colorBrand-2M3O3N sizeSmall-3R2P2p grow-2T4nbg"; button.innerText = text; button.addEventListener("click", onClick); return button; @@ -248,7 +248,7 @@ function main() { div.style.marginBottom = "50px"; div.appendChild( createButton("Edit Global Keybinds", () => { - userscript.log("!discord-screenaudio-keybinds"); + userscript.showShortcutsDialog(); }) ); el.innerHTML = ""; @@ -305,7 +305,7 @@ function main() { section.appendChild( createButton("Edit Global Keybinds", () => { - userscript.log("!discord-screenaudio-keybinds"); + userscript.showShortcutsDialog(); }) ); From c52daab420ffb72c1614a67d4a85dab5514a68ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Wed, 15 Feb 2023 01:59:22 +0100 Subject: [PATCH 26/29] allow for custom user styles --- src/discordpage.cpp | 67 +++++++++++++++++++++++++++++++++------------ src/discordpage.h | 11 ++++++-- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/src/discordpage.cpp b/src/discordpage.cpp index 65596f3..bcb4ed7 100644 --- a/src/discordpage.cpp +++ b/src/discordpage.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -20,11 +21,23 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) { connect(this, &QWebEnginePage::featurePermissionRequested, this, &DiscordPage::featurePermissionRequested); - connect(this, &QWebEnginePage::loadStarted, [=]() { - runJavaScript(QString("window.discordScreenaudioVersion = '%1';") - .arg(QApplication::applicationVersion())); - }); + setupPermissions(); + injectFile(&DiscordPage::injectScript, "qwebchannel.js", + ":/qtwebchannel/qwebchannel.js"); + + setUrl(QUrl("https://discord.com/app")); + + setWebChannel(new QWebChannel(this)); + webChannel()->registerObject("userscript", &m_userScript); + + injectFile(&DiscordPage::injectScript, "userscript.js", + ":/assets/userscript.js"); + + setupUserStyles(); +} + +void DiscordPage::setupPermissions() { settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, @@ -36,18 +49,21 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) { false); settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false); settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true); - - injectScriptFile("qwebchannel.js", ":/qtwebchannel/qwebchannel.js"); - - setUrl(QUrl("https://discord.com/app")); - - setWebChannel(new QWebChannel(this)); - webChannel()->registerObject("userscript", &m_userScript); - - injectScriptFile("userscript.js", ":/assets/userscript.js"); } -void DiscordPage::injectScriptText(QString name, QString content) { +void DiscordPage::setupUserStyles() { + QString file = + QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + + "/userstyles.css"; + if (QFileInfo(file).exists()) { + qDebug(mainLog) << "Found userstyles:" << file; + injectFile(&DiscordPage::injectStylesheet, "userstyles.js", file); + } +} + +void DiscordPage::injectScript( + QString name, QString content, + QWebEngineScript::InjectionPoint injectionPoint) { qDebug(mainLog) << "Injecting " << name; QWebEngineScript script; @@ -55,20 +71,37 @@ void DiscordPage::injectScriptText(QString name, QString content) { script.setSourceCode(content); script.setName(name); script.setWorldId(QWebEngineScript::MainWorld); - script.setInjectionPoint(QWebEngineScript::DocumentCreation); + script.setInjectionPoint(injectionPoint); script.setRunsOnSubFrames(false); scripts().insert(script); } -void DiscordPage::injectScriptFile(QString name, QString source) { +void DiscordPage::injectScript(QString name, QString content) { + injectScript(name, content, QWebEngineScript::DocumentCreation); +} + +void DiscordPage::injectStylesheet(QString name, QString content) { + auto script = QString(R"(const stylesheet = document.createElement("style"); +stylesheet.type = "text/css"; +stylesheet.id = "%1"; +stylesheet.innerText = `%2`; +document.head.appendChild(stylesheet); +)") + .arg(name) + .arg(content); + injectScript(name, script, QWebEngineScript::DocumentReady); +} + +void DiscordPage::injectFile(void (DiscordPage::*inject)(QString, QString), + QString name, QString source) { QFile file(source); if (!file.open(QIODevice::ReadOnly)) { qFatal("Failed to load %s with error: %s", source.toLatin1().constData(), file.errorString().toLatin1().constData()); } else { - injectScriptText(name, file.readAll()); + (this->*inject)(name, file.readAll()); } } diff --git a/src/discordpage.h b/src/discordpage.h index cc0da53..2b0f58f 100644 --- a/src/discordpage.h +++ b/src/discordpage.h @@ -4,6 +4,7 @@ #include #include +#include class DiscordPage : public QWebEnginePage { Q_OBJECT @@ -13,6 +14,8 @@ public: private: UserScript m_userScript; + void setupPermissions(); + void setupUserStyles(); bool acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) override; @@ -21,8 +24,12 @@ private: javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) override; - void injectScriptText(QString name, QString content); - void injectScriptFile(QString name, QString source); + void injectScript(QString name, QString content, + QWebEngineScript::InjectionPoint injectionPoint); + void injectScript(QString name, QString content); + void injectStylesheet(QString name, QString content); + void injectFile(void (DiscordPage::*inject)(QString, QString), QString name, + QString source); private Q_SLOTS: void featurePermissionRequested(const QUrl &securityOrigin, From 36c8cfedb76409fe1e21cd76f76da82f5845c0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Wed, 15 Feb 2023 02:21:41 +0100 Subject: [PATCH 27/29] fix qlayout error --- src/streamdialog.cpp | 12 ++++-------- src/streamdialog.h | 4 ++-- src/userscript.cpp | 11 ++++++----- src/userscript.h | 2 +- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/streamdialog.cpp b/src/streamdialog.cpp index b1ebc2b..e383dea 100644 --- a/src/streamdialog.cpp +++ b/src/streamdialog.cpp @@ -9,7 +9,7 @@ #include #include -StreamDialog::StreamDialog() : QWidget() { +StreamDialog::StreamDialog(QWidget *parent) : QDialog(parent) { setAttribute(Qt::WA_QuitOnClose, false); { @@ -22,8 +22,7 @@ StreamDialog::StreamDialog() : QWidget() { layout->addWidget(m_videoGroupBox); { - auto videoLayout = new QVBoxLayout(this); - m_videoGroupBox->setLayout(videoLayout); + auto videoLayout = new QVBoxLayout(m_videoGroupBox); auto resolutionLabel = new QLabel(this); resolutionLabel->setText("Resolution"); @@ -60,15 +59,14 @@ StreamDialog::StreamDialog() : QWidget() { layout->addWidget(m_audioGroupBox); { - auto audioLayout = new QVBoxLayout(this); - m_audioGroupBox->setLayout(audioLayout); + auto audioLayout = new QVBoxLayout(m_audioGroupBox); auto targetLabel = new QLabel(this); targetLabel->setText("Audio Source"); audioLayout->addWidget(targetLabel); { - auto targetLayout = new QHBoxLayout(this); + auto targetLayout = new QHBoxLayout(); audioLayout->addLayout(targetLayout); m_targetComboBox = new QComboBox(this); @@ -88,8 +86,6 @@ StreamDialog::StreamDialog() : QWidget() { button->setText("Start Stream"); connect(button, &QPushButton::clicked, this, &StreamDialog::startStream); layout->addWidget(button, Qt::AlignRight | Qt::AlignBottom); - - setLayout(layout); } setWindowTitle("discord-screenaudio Stream Dialog"); diff --git a/src/streamdialog.h b/src/streamdialog.h index 0154a8d..4631ea3 100644 --- a/src/streamdialog.h +++ b/src/streamdialog.h @@ -5,11 +5,11 @@ #include #include -class StreamDialog : public QWidget { +class StreamDialog : public QDialog { Q_OBJECT public: - explicit StreamDialog(); + explicit StreamDialog(QWidget *parent = nullptr); private: QComboBox *m_targetComboBox; diff --git a/src/userscript.cpp b/src/userscript.cpp index e6fc764..18e3a28 100644 --- a/src/userscript.cpp +++ b/src/userscript.cpp @@ -74,7 +74,8 @@ void UserScript::setupShortcutsDialog() { } void UserScript::setupStreamDialog() { - connect(&m_streamDialog, &StreamDialog::requestedStreamStart, this, + m_streamDialog = new StreamDialog(MainWindow::instance()); + connect(m_streamDialog, &StreamDialog::requestedStreamStart, this, &UserScript::startStream); } @@ -156,9 +157,9 @@ void UserScript::startStream(bool video, bool audio, int width, int height, } void UserScript::showStreamDialog() { - if (m_streamDialog.isHidden()) - m_streamDialog.setHidden(false); + if (m_streamDialog->isHidden()) + m_streamDialog->setHidden(false); else - m_streamDialog.activateWindow(); - m_streamDialog.updateTargets(); + m_streamDialog->activateWindow(); + m_streamDialog->updateTargets(); } \ No newline at end of file diff --git a/src/userscript.h b/src/userscript.h index ab4ba37..2eacc1d 100644 --- a/src/userscript.h +++ b/src/userscript.h @@ -30,7 +30,7 @@ public: private: QProcess m_virtmicProcess; - StreamDialog m_streamDialog; + StreamDialog *m_streamDialog; bool m_kxmlgui = false; bool m_kglobalaccel = false; #ifdef KXMLGUI From d9e3704176b076f85a98b7211fbb916f4f33cf33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Wed, 15 Feb 2023 02:27:23 +0100 Subject: [PATCH 28/29] fix about styles --- assets/userscript.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/userscript.js b/assets/userscript.js index 4b58d2c..b8605a6 100644 --- a/assets/userscript.js +++ b/assets/userscript.js @@ -223,6 +223,8 @@ function main() { aboutEl.style.fontSize = "12px"; aboutEl.style.color = "var(--text-muted)"; aboutEl.style.textTransform = "none"; + aboutEl.style.display = "inline-block"; + aboutEl.style.width = "100%"; aboutEl.classList.add("dirscordScreenaudioAboutText"); aboutEl.style.cursor = "pointer"; el.appendChild(aboutEl); From 02fdc62fbea5967dfdf0c2b4ca3f8d60bce32fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Wed, 15 Feb 2023 02:35:48 +0100 Subject: [PATCH 29/29] add faq about `userstyles.css` --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 5832abc..65dbe21 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,13 @@ of Flatpak's sandboxing by installing [Flatseal](https://flathub.org/apps/details/com.github.tchx84.Flatseal) and allowing access to "All system files" under the "Filesystem" section. +### Is there any way to add custom CSS / a theme? + +Yes, you can add all your styles into +`~/.config/discord-screenaudio/userstyles.css`. But please note that due to +QtWebEngine limitations concerning content security policies, you can't use any +external files (like `@import` or `url()`). + ## License Copyright (C) 2022 Malte Jürgens