Implement Keybinds (#57)

This commit is contained in:
Malte Jürgens 2022-10-10 19:50:26 +00:00 committed by GitHub
parent 3740553aba
commit dd2beed4eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 212 additions and 33 deletions

View File

@ -23,6 +23,16 @@ if(KF5Notifications_FOUND)
add_definitions( -DKNOTIFICATIONS )
endif()
find_package(KF5XmlGui)
if(KF5XmlGui_FOUND)
add_definitions( -DKXMLGUI )
endif()
find_package(KF5GlobalAccel)
if(KF5GlobalAccel_FOUND)
add_definitions( -DKGLOBALACCEL )
endif()
set(discord-screenaudio_SRC
src/main.cpp
src/mainwindow.cpp
@ -62,6 +72,12 @@ if(KF5Notifications_FOUND)
target_link_libraries(discord-screenaudio KF5::Notifications)
install(FILES assets/discord-screenaudio.notifyrc DESTINATION ${CMAKE_INSTALL_PREFIX}/share/knotifications5)
endif()
if(KF5XmlGui_FOUND)
target_link_libraries(discord-screenaudio KF5::XmlGui)
endif()
if(KF5GlobalAccel_FOUND)
target_link_libraries(discord-screenaudio KF5::GlobalAccel)
endif()
install(TARGETS discord-screenaudio DESTINATION bin)
install(FILES assets/de.shorsh.discord-screenaudio.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps)

View File

@ -51,12 +51,14 @@ You have multiple options:
- Basic building tools
- CMake
- Qt5, QtWebEngine and Kf5Notifications
- Qt5 and QtWebEngine
- **PipeWire** (it currently doesn't work with PulseAudio)
- Git
- _Kf5Notifications (optional, for better notifications)_
- _KXMLGui and KGlobalAccel (optional, for keybinds)_
On Debian:
`apt install -y build-essential cmake qtbase5-dev qtwebengine5-dev libkf5notifications-dev pkg-config libpipewire-0.3-dev git`
`apt install -y build-essential cmake qtbase5-dev qtwebengine5-dev libkf5notifications-dev libkf5xmlgui-dev libkf5globalaccel-dev pkg-config libpipewire-0.3-dev git`
### Building

View File

@ -135,12 +135,21 @@ setInterval(() => {
document.getElementsByClassName("dirscordScreenaudioAboutText").length == 0
) {
for (const el of document.getElementsByClassName("info-3pQQBb")) {
const aboutEl = document.createElement("div");
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);
}
}
@ -149,6 +158,40 @@ setInterval(() => {
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 fontSize16-3zr6Io browserNotice-1u-Y5o"
).length
) {
const el = document
.getElementById("keybinds-tab")
.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);
el.innerHTML = "";
el.appendChild(div);
}
const muteBtn = document.getElementsByClassName(
"button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F"
)[0];
window.discordScreenaudioToggleMute = () => muteBtn.click();
const deafenBtn = document.getElementsByClassName(
"button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F"
)[1];
window.discordScreenaudioToggleDeafen = () => deafenBtn.click();
if (window.discordScreenaudioResolutionString) {
for (const el of document.getElementsByClassName(
"qualityIndicator-39wQDy"

View File

@ -1,10 +1,25 @@
#include "discordpage.h"
#include "log.h"
#include "mainwindow.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 <QDesktopServices>
#include <QFile>
#include <QMessageBox>
#include <QTimer>
#include <QWebChannel>
#include <QWebEngineScript>
@ -36,16 +51,82 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
setUrl(QUrl("https://discord.com/app"));
injectScript(":/assets/userscript.js");
injectVersion(QApplication::applicationVersion());
injectScriptFile("userscript.js", ":/assets/userscript.js");
injectScriptText("version.js",
QString("window.discordScreenaudioVersion = '%1';")
.arg(QApplication::applicationVersion()));
#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::injectScript(QString source) {
qDebug(mainLog) << "Injecting " << source;
void DiscordPage::injectScriptText(QString name, QString content) {
qDebug(mainLog) << "Injecting " << name;
QWebEngineScript script;
script.setSourceCode(content);
script.setName(name);
script.setWorldId(QWebEngineScript::MainWorld);
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
script.setRunsOnSubFrames(false);
scripts().insert(script);
}
void DiscordPage::injectScriptFile(QString name, QString source) {
QFile userscript(source);
if (!userscript.open(QIODevice::ReadOnly)) {
@ -53,33 +134,10 @@ void DiscordPage::injectScript(QString source) {
userscript.errorString().toLatin1().constData());
} else {
QByteArray userscriptJs = userscript.readAll();
QWebEngineScript script;
script.setSourceCode(userscriptJs);
script.setName("userscript.js");
script.setWorldId(QWebEngineScript::MainWorld);
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
script.setRunsOnSubFrames(false);
scripts().insert(script);
injectScriptText(name, userscriptJs);
}
}
void DiscordPage::injectVersion(QString version) {
QWebEngineScript script;
auto code = QString("window.discordScreenaudioVersion = '%1';").arg(version);
script.setSourceCode(code);
script.setName("version.js");
script.setWorldId(QWebEngineScript::MainWorld);
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
script.setRunsOnSubFrames(false);
scripts().insert(script);
}
void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
QWebEnginePage::Feature feature) {
// Allow every permission asked
@ -144,6 +202,26 @@ void DiscordPage::javaScriptConsoleMessage(
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.startsWith("dsa: ")) {
qDebug(userscriptLog) << message.mid(5).toUtf8().constData();
} else {
@ -163,3 +241,13 @@ void DiscordPage::startStream(QString target, uint width, uint height,
.arg(frameRate));
});
}
void DiscordPage::toggleMute() {
qDebug(shortcutLog) << "Toggling mute";
runJavaScript("window.discordScreenaudioToggleMute();");
}
void DiscordPage::toggleDeafen() {
qDebug(shortcutLog) << "Toggling deafen";
runJavaScript("window.discordScreenaudioToggleDeafen();");
}

View File

@ -3,6 +3,12 @@
#include "streamdialog.h"
#include "virtmic.h"
#ifdef KXMLGUI
#include <KActionCollection>
#include <KHelpMenu>
#include <KShortcutsDialog>
#endif
#include <QProcess>
#include <QWebEngineFullScreenRequest>
#include <QWebEnginePage>
@ -16,6 +22,13 @@ public:
private:
StreamDialog m_streamDialog;
QProcess m_virtmicProcess;
#ifdef KXMLGUI
KHelpMenu *m_helpMenu;
#ifdef KGLOBALACCEL
KActionCollection *m_actionCollection;
KShortcutsDialog *m_shortcutsDialog;
#endif
#endif
bool acceptNavigationRequest(const QUrl &url,
QWebEnginePage::NavigationType type,
bool isMainFrame) override;
@ -24,10 +37,12 @@ private:
javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level,
const QString &message, int lineNumber,
const QString &sourceID) override;
void injectScript(QString source);
void injectVersion(QString version);
void injectScriptText(QString name, QString source);
void injectScriptFile(QString name, QString content);
void stopVirtmic();
void startVirtmic(QString target);
void toggleMute();
void toggleDeafen();
private Q_SLOTS:
void featurePermissionRequested(const QUrl &securityOrigin,

View File

@ -4,3 +4,4 @@ Q_LOGGING_CATEGORY(mainLog, "main");
Q_LOGGING_CATEGORY(discordLog, "discord");
Q_LOGGING_CATEGORY(userscriptLog, "userscript");
Q_LOGGING_CATEGORY(virtmicLog, "virtmic");
Q_LOGGING_CATEGORY(shortcutLog, "shortcut");

View File

@ -6,3 +6,4 @@ Q_DECLARE_LOGGING_CATEGORY(mainLog);
Q_DECLARE_LOGGING_CATEGORY(discordLog);
Q_DECLARE_LOGGING_CATEGORY(userscriptLog);
Q_DECLARE_LOGGING_CATEGORY(virtmicLog);
Q_DECLARE_LOGGING_CATEGORY(shortcutLog);

View File

@ -1,6 +1,10 @@
#include "mainwindow.h"
#include "virtmic.h"
#ifdef KXMLGUI
#include <KAboutData>
#endif
#include <QApplication>
#include <QCommandLineParser>
#include <QLoggingCategory>
@ -26,6 +30,7 @@ int main(int argc, char *argv[]) {
QCommandLineOption degubOption("remote-debugging",
"Open Chromium Remote Debugging on port 9222");
parser.addOption(degubOption);
parser.process(app);
if (parser.isSet(virtmicOption)) {

View File

@ -22,7 +22,11 @@
#include <QWebEngineSettings>
#include <QWidget>
MainWindow *MainWindow::m_instance = nullptr;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
assert(MainWindow::m_instance == nullptr);
MainWindow::m_instance = this;
setupWebView();
resize(1000, 700);
showMaximized();
@ -68,3 +72,5 @@ void MainWindow::fullScreenRequested(
}
void MainWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); }
MainWindow *MainWindow::instance() { return m_instance; }

View File

@ -15,6 +15,7 @@ class MainWindow : public QMainWindow {
public:
explicit MainWindow(QWidget *parent = nullptr);
static MainWindow *instance();
private:
void setupWebView();
@ -23,6 +24,7 @@ private:
DiscordPage *m_discordPage;
void closeEvent(QCloseEvent *event) override;
bool m_wasMaximized;
static MainWindow *m_instance;
private Q_SLOTS:
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);