use qtwebchannel to communicate with userscript

This commit is contained in:
Malte Jürgens 2023-02-12 17:20:33 +01:00
parent 0b12487dfd
commit 1477a9d4c0
No known key found for this signature in database
GPG Key ID: D29FBD5F93C0CFC3
8 changed files with 469 additions and 400 deletions

View File

@ -43,6 +43,7 @@ set(discord-screenaudio_SRC
src/discordpage.cpp src/discordpage.cpp
src/streamdialog.cpp src/streamdialog.cpp
src/log.cpp src/log.cpp
src/userscript.cpp
resources.qrc resources.qrc
) )

View File

@ -1,5 +1,3 @@
// From v0.4
navigator.mediaDevices.chromiumGetDisplayMedia = navigator.mediaDevices.chromiumGetDisplayMedia =
navigator.mediaDevices.getDisplayMedia; navigator.mediaDevices.getDisplayMedia;
@ -16,12 +14,12 @@ const getAudioDevice = async (nameOfAudioDevice) => {
let devices = await navigator.mediaDevices.enumerateDevices(); let devices = await navigator.mediaDevices.enumerateDevices();
audioDevice = devices.find(({ label }) => label === nameOfAudioDevice); audioDevice = devices.find(({ label }) => label === nameOfAudioDevice);
if (!audioDevice) if (!audioDevice)
console.log( userscript.log(
`dsa: Did not find '${nameOfAudioDevice}', trying again in 100ms` `Did not find '${nameOfAudioDevice}', trying again in 100ms`
); );
await sleep(100); await sleep(100);
} }
console.log(`dsa: Found '${nameOfAudioDevice}'`); userscript.log(`Found '${nameOfAudioDevice}'`);
return audioDevice; return audioDevice;
}; };
@ -71,6 +69,13 @@ function setGetDisplayMedia(video = true, overrideArgs = undefined) {
setGetDisplayMedia(); setGetDisplayMedia();
let userscript;
let muteBtn;
let deafenBtn;
let streamStartBtn;
let streamStartBtnInitialDisplay;
let streamStartBtnClone;
let resolutionString;
const clonedElements = []; const clonedElements = [];
const hiddenElements = []; const hiddenElements = [];
let wasStreamActive = false; let wasStreamActive = false;
@ -121,13 +126,43 @@ function createSwitch(text, enabled, onClick) {
return container; return container;
} }
setInterval(() => { // 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 = const streamActive =
document.getElementsByClassName("panel-2ZFCRb activityPanel-9icbyU") document.getElementsByClassName("panel-2ZFCRb activityPanel-9icbyU")
.length > 0; .length > 0;
if (!streamActive && wasStreamActive) if (!streamActive && wasStreamActive) userscript.stopVirtmic();
console.log("!discord-screenaudio-stream-stopped");
wasStreamActive = streamActive; wasStreamActive = streamActive;
if (streamActive) { if (streamActive) {
@ -142,63 +177,49 @@ setInterval(() => {
hiddenElements.length = 0; hiddenElements.length = 0;
} else { } else {
for (const el of [ for (const el of [
document.getElementsByClassName("actionButtons-2vEOUh")?.[0]?.children[1], document.getElementsByClassName("actionButtons-2vEOUh")?.[0]
?.children[1],
document.querySelector( document.querySelector(
".wrapper-3t3Yqv > div > div > div > div > .controlButton-2PMNom" ".wrapper-3t3Yqv > div > div > div > div > .controlButton-2PMNom"
), ),
]) { ]) {
if (!el) continue; if (!el) continue;
if (el.classList.contains("discord-screenaudio-cloned")) continue; if (el.classList.contains("discord-screenaudio-cloned")) continue;
el.classList.add("discord-screenaudio-cloned"); streamStartBtn = el;
elClone = el.cloneNode(true); streamStartBtn.classList.add("discord-screenaudio-cloned");
elClone.title = "Share Your Screen with Audio";
elClone.addEventListener("click", () => { streamStartBtnClone = streamStartBtn.cloneNode(true);
console.log("!discord-screenaudio-start-stream"); streamStartBtnClone.title = "Share Your Screen with Audio";
streamStartBtnClone.addEventListener("click", () => {
userscript.showStreamDialog();
}); });
const initialDisplay = el.style.display; streamStartBtnInitialDisplay = streamStartBtn.style.display;
window.discordScreenaudioStartStream = ( streamStartBtn.style.display = "none";
video, streamStartBtn.parentNode.insertBefore(streamStartBtnClone, el);
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"; clonedElements.push(streamStartBtnClone);
el.parentNode.insertBefore(elClone, el); hiddenElements.push(streamStartBtn);
clonedElements.push(elClone);
hiddenElements.push(el);
} }
} }
// Add about text in settings // Add about text in settings
if ( if (
document.getElementsByClassName("dirscordScreenaudioAboutText").length == 0 document.getElementsByClassName("dirscordScreenaudioAboutText").length ==
0
) { ) {
for (const el of document.getElementsByClassName("info-3pQQBb")) { for (const el of document.getElementsByClassName("info-3pQQBb")) {
let aboutEl; let aboutEl;
if (window.discordScreenaudioKXMLGUI) { if (userscript.kxmlgui) {
aboutEl = document.createElement("a"); aboutEl = document.createElement("a");
aboutEl.addEventListener("click", () => { aboutEl.addEventListener("click", () => {
console.log("!discord-screenaudio-about"); userscript.showHelpMenu();
}); });
} else { } else {
aboutEl = document.createElement("div"); aboutEl = document.createElement("div");
} }
aboutEl.innerText = `discord-screenaudio ${window.discordScreenaudioVersion}`; aboutEl.innerText = `discord-screenaudio ${userscript.version}`;
aboutEl.style.fontSize = "12px"; aboutEl.style.fontSize = "12px";
aboutEl.style.color = "var(--text-muted)"; aboutEl.style.color = "var(--text-muted)";
aboutEl.style.textTransform = "none"; aboutEl.style.textTransform = "none";
@ -227,7 +248,7 @@ setInterval(() => {
div.style.marginBottom = "50px"; div.style.marginBottom = "50px";
div.appendChild( div.appendChild(
createButton("Edit Global Keybinds", () => { createButton("Edit Global Keybinds", () => {
console.log("!discord-screenaudio-keybinds"); userscript.log("!discord-screenaudio-keybinds");
}) })
); );
el.innerHTML = ""; el.innerHTML = "";
@ -237,31 +258,28 @@ setInterval(() => {
const buttonContainer = const buttonContainer =
document.getElementsByClassName("container-YkUktl")[0]; document.getElementsByClassName("container-YkUktl")[0];
if (!buttonContainer) { if (!buttonContainer) {
console.log( userscript.log(
"dsa: Cannot locate Mute/Deafen/Settings button container, please report this on GitHub" "Cannot locate Mute/Deafen/Settings button container, please report this on GitHub"
); );
} }
const muteBtn = buttonContainer muteBtn = buttonContainer
? buttonContainer.getElementsByClassName( ? buttonContainer.getElementsByClassName(
"button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F"
)[0] )[0]
: null; : null;
window.discordScreenaudioToggleMute = () => muteBtn && muteBtn.click();
const deafenBtn = buttonContainer deafenBtn = buttonContainer
? buttonContainer.getElementsByClassName( ? buttonContainer.getElementsByClassName(
"button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F"
)[1] )[1]
: null; : null;
window.discordScreenaudioToggleDeafen = () => deafenBtn && deafenBtn.click(); if (resolutionString) {
if (window.discordScreenaudioResolutionString) {
for (const el of document.getElementsByClassName( for (const el of document.getElementsByClassName(
"qualityIndicator-39wQDy" "qualityIndicator-39wQDy"
)) { )) {
el.innerHTML = window.discordScreenaudioResolutionString; el.innerHTML = resolutionString;
} }
} }
@ -287,17 +305,16 @@ setInterval(() => {
section.appendChild( section.appendChild(
createButton("Edit Global Keybinds", () => { createButton("Edit Global Keybinds", () => {
console.log("!discord-screenaudio-keybinds"); userscript.log("!discord-screenaudio-keybinds");
}) })
); );
section.appendChild( section.appendChild(
createSwitch( createSwitch(
"Move discord-screenaudio to the system tray instead of closing", "Move discord-screenaudio to the system tray instead of closing",
window.discordScreenaudioTrayEnabled, await userscript.getBoolPref("trayIcon", false),
(enabled) => { (enabled) => {
window.discordScreenaudioTrayEnabled = enabled; userscript.setTrayIcon(enabled);
console.log(`!discord-screenaudio-tray-${enabled}`);
} }
) )
); );
@ -305,10 +322,9 @@ setInterval(() => {
section.appendChild( section.appendChild(
createSwitch( createSwitch(
"Start discord-screenaudio hidden to tray", "Start discord-screenaudio hidden to tray",
window.discordScreenaudioStartHidden, await userscript.getPref("startHidden", false),
(hidden) => { (hidden) => {
window.discordScreenaudioStartHidden = hidden; userscript.setPref("startHidden", hidden);
console.log(`!discord-screenaudio-starthidden-${hidden}`);
} }
) )
); );
@ -322,7 +338,4 @@ setInterval(() => {
} }
} }
}, 500); }, 500);
}
// Fix for broken discord notifications after restart
// (https://github.com/maltejur/discord-screenaudio/issues/17)
Notification.requestPermission();

View File

@ -3,19 +3,6 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "virtmic.h" #include "virtmic.h"
#ifdef KXMLGUI
#include <KAboutData>
#include <KHelpMenu>
#include <KShortcutsDialog>
#include <KXmlGuiWindow>
#include <QAction>
#ifdef KGLOBALACCEL
#include <KGlobalAccel>
#endif
#endif
#include <QApplication> #include <QApplication>
#include <QDesktopServices> #include <QDesktopServices>
#include <QFile> #include <QFile>
@ -29,7 +16,6 @@
DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) { DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
setBackgroundColor(QColor("#202225")); setBackgroundColor(QColor("#202225"));
m_virtmicProcess.setProcessChannelMode(QProcess::ForwardedChannels);
connect(this, &QWebEnginePage::featurePermissionRequested, this, connect(this, &QWebEnginePage::featurePermissionRequested, this,
&DiscordPage::featurePermissionRequested); &DiscordPage::featurePermissionRequested);
@ -51,77 +37,14 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false); settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true); settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true);
injectScriptFile("qwebchannel.js", ":/qtwebchannel/qwebchannel.js");
setUrl(QUrl("https://discord.com/app")); setUrl(QUrl("https://discord.com/app"));
setWebChannel(new QWebChannel(this));
webChannel()->registerObject("userscript", &m_userScript);
injectScriptFile("userscript.js", ":/assets/userscript.js"); 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<QKeySequence>{});
m_actionCollection->addAction("toggleDeafen", toggleDeafenAction);
KGlobalAccel::setGlobalShortcut(toggleDeafenAction, QList<QKeySequence>{});
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) { void DiscordPage::injectScriptText(QString name, QString content) {
@ -156,11 +79,10 @@ void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
QWebEnginePage::PermissionGrantedByUser); QWebEnginePage::PermissionGrantedByUser);
if (feature == QWebEnginePage::Feature::MediaAudioCapture) { 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 " qDebug(virtmicLog) << "Starting Virtmic with no target to make sure "
"Discord can find all the audio devices"; "Discord can find all the audio devices";
m_virtmicProcess.start(QApplication::arguments()[0], m_userScript.startVirtmic("None");
{"--virtmic", "None"});
} }
} }
} }
@ -187,86 +109,8 @@ QWebEnginePage *DiscordPage::createWindow(QWebEnginePage::WebWindowType type) {
return new ExternalPage; 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( void DiscordPage::javaScriptConsoleMessage(
QWebEnginePage::JavaScriptConsoleMessageLevel level, const QString &message, QWebEnginePage::JavaScriptConsoleMessageLevel level, const QString &message,
int lineNumber, const QString &sourceID) { 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; 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();");
}

View File

@ -1,15 +1,7 @@
#pragma once #pragma once
#include "streamdialog.h" #include "userscript.h"
#include "virtmic.h"
#ifdef KXMLGUI
#include <KActionCollection>
#include <KHelpMenu>
#include <KShortcutsDialog>
#endif
#include <QProcess>
#include <QWebEngineFullScreenRequest> #include <QWebEngineFullScreenRequest>
#include <QWebEnginePage> #include <QWebEnginePage>
@ -20,15 +12,7 @@ public:
explicit DiscordPage(QWidget *parent = nullptr); explicit DiscordPage(QWidget *parent = nullptr);
private: private:
StreamDialog m_streamDialog; UserScript m_userScript;
QProcess m_virtmicProcess;
#ifdef KXMLGUI
KHelpMenu *m_helpMenu;
#ifdef KGLOBALACCEL
KActionCollection *m_actionCollection;
KShortcutsDialog *m_shortcutsDialog;
#endif
#endif
bool acceptNavigationRequest(const QUrl &url, bool acceptNavigationRequest(const QUrl &url,
QWebEnginePage::NavigationType type, QWebEnginePage::NavigationType type,
bool isMainFrame) override; bool isMainFrame) override;
@ -39,16 +23,10 @@ private:
const QString &sourceID) override; const QString &sourceID) override;
void injectScriptText(QString name, QString content); void injectScriptText(QString name, QString content);
void injectScriptFile(QString name, QString source); void injectScriptFile(QString name, QString source);
void stopVirtmic();
void startVirtmic(QString target);
void toggleMute();
void toggleDeafen();
private Q_SLOTS: private Q_SLOTS:
void featurePermissionRequested(const QUrl &securityOrigin, void featurePermissionRequested(const QUrl &securityOrigin,
QWebEnginePage::Feature feature); 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 // Will immediately get destroyed again but is needed for navigation to

View File

@ -98,9 +98,9 @@ StreamDialog::StreamDialog() : QWidget() {
void StreamDialog::startStream() { void StreamDialog::startStream() {
auto resolution = m_resolutionComboBox->currentData().toString().split('x'); auto resolution = m_resolutionComboBox->currentData().toString().split('x');
emit requestedStreamStart(m_videoGroupBox->isChecked(), emit requestedStreamStart(m_videoGroupBox->isChecked(),
m_audioGroupBox->isChecked(), m_audioGroupBox->isChecked(), resolution[0].toInt(),
resolution[0].toUInt(), resolution[1].toUInt(), resolution[1].toInt(),
m_framerateComboBox->currentData().toUInt(), m_framerateComboBox->currentData().toInt(),
m_targetComboBox->currentText()); m_targetComboBox->currentText());
setHidden(true); setHidden(true);
} }

View File

@ -19,8 +19,8 @@ private:
QGroupBox *m_audioGroupBox; QGroupBox *m_audioGroupBox;
Q_SIGNALS: Q_SIGNALS:
void requestedStreamStart(bool video, bool audio, uint width, uint height, void requestedStreamStart(bool video, bool audio, int width, int height,
uint frameRate, QString target); int frameRate, QString target);
public Q_SLOTS: public Q_SLOTS:
void updateTargets(); void updateTargets();

164
src/userscript.cpp Normal file
View File

@ -0,0 +1,164 @@
#include "userscript.h"
#include "log.h"
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include <QTimer>
#ifdef KXMLGUI
#include <KActionCollection>
#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<QKeySequence>{});
m_actionCollection->addAction("toggleDeafen", toggleDeafenAction);
KGlobalAccel::setGlobalShortcut(toggleDeafenAction, QList<QKeySequence>{});
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();
}

69
src/userscript.h Normal file
View File

@ -0,0 +1,69 @@
#pragma once
#include "streamdialog.h"
#include <QObject>
#include <QProcess>
#ifdef KXMLGUI
#include <KAboutData>
#include <KHelpMenu>
#include <KShortcutsDialog>
#include <KXmlGuiWindow>
#include <QAction>
#ifdef KGLOBALACCEL
#include <KGlobalAccel>
#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);
};