Compare commits

...

11 Commits

Author SHA1 Message Date
Malte Jürgens
a10cfda56d fix usage of application.process.binary 2023-01-26 01:19:38 +01:00
Malte Jürgens
2a809e163e fix class names for tray settings 2023-01-26 00:45:14 +01:00
Malte Jürgens
bc317d5531 use application.process.binary if application.name is not available 2023-01-25 18:47:08 +01:00
Malte Jürgens
b4db987217 fix tray settings 2023-01-23 18:59:25 +01:00
Malte Jürgens
9f46e710a9 fix tray bug 2023-01-14 16:28:23 +01:00
Malte Jürgens
d6641a7a6e Merge pull request #88 from maltejur/tray
Implement Tray Icon
2023-01-14 14:51:46 +00:00
Malte Jürgens
b836be6530 Implement Tray Icon 2023-01-13 23:33:47 +01:00
Malte Jürgens
bfb0714b13 fix keybinds query selector 2023-01-09 22:20:11 +01:00
Malte Jürgens
374b854261 Update README 2023-01-09 22:19:45 +01:00
Malte Jürgens
1f6105f76b allow for building with qt6 2022-12-27 19:00:10 +01:00
Malte Jürgens
3071159332 Update screenshot 2022-12-11 17:55:04 +00:00
7 changed files with 246 additions and 40 deletions

View File

@@ -13,24 +13,27 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
string(TIMESTAMP TIMESTAMP %s) string(TIMESTAMP TIMESTAMP %s)
# set(CMAKE_AUTOUIC ON) # set(CMAKE_AUTOUIC ON)
find_package(Qt5 CONFIG REQUIRED COMPONENTS find_package(Qt5 COMPONENTS Widgets)
Widgets if (Qt5_FOUND)
WebEngineWidgets find_package(Qt5 CONFIG REQUIRED COMPONENTS Widgets WebEngineWidgets)
)
find_package(KF5Notifications) find_package(KF5Notifications)
if(KF5Notifications_FOUND) if(KF5Notifications_FOUND)
add_definitions( -DKNOTIFICATIONS ) add_definitions( -DKNOTIFICATIONS )
endif() endif()
find_package(KF5XmlGui) find_package(KF5XmlGui)
if(KF5XmlGui_FOUND) if(KF5XmlGui_FOUND)
add_definitions( -DKXMLGUI ) add_definitions( -DKXMLGUI )
endif() endif()
find_package(KF5GlobalAccel) find_package(KF5GlobalAccel)
if(KF5GlobalAccel_FOUND) if(KF5GlobalAccel_FOUND)
add_definitions( -DKGLOBALACCEL ) 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() endif()
set(discord-screenaudio_SRC set(discord-screenaudio_SRC
@@ -66,7 +69,7 @@ add_subdirectory(submodules/rohrkabel)
add_executable(discord-screenaudio ${discord-screenaudio_SRC}) 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) if(KF5Notifications_FOUND)
target_link_libraries(discord-screenaudio KF5::Notifications) target_link_libraries(discord-screenaudio KF5::Notifications)

View File

@@ -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 screenshare and not passed to the user microphone
([see explanation](#how-it-works)). ([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 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 Discord client. Rather, it should be used in addition to the original client in
@@ -50,6 +50,8 @@ You have multiple options:
### Requirements ### Requirements
- Basic building tools - Basic building tools
- An up-to-date system (I can't guarantee that it works on Debian or Ubuntu
20/21)
- CMake - CMake
- Qt5 and QtWebEngine - Qt5 and QtWebEngine
- **PipeWire** (it currently doesn't work with PulseAudio) - **PipeWire** (it currently doesn't work with PulseAudio)
@@ -57,7 +59,7 @@ You have multiple options:
- _Kf5Notifications (optional, for better notifications)_ - _Kf5Notifications (optional, for better notifications)_
- _KXMLGui and KGlobalAccel (optional, for keybinds)_ - _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` `apt install -y build-essential cmake qtbase5-dev qtwebengine5-dev libkf5notifications-dev libkf5xmlgui-dev libkf5globalaccel-dev pkg-config libpipewire-0.3-dev git`
### Building ### Building

View File

@@ -75,6 +75,52 @@ const clonedElements = [];
const hiddenElements = []; const hiddenElements = [];
let wasStreamActive = false; 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-NnoUIp";
const label = document.createElement("label");
label.innerText = text;
label.className = "title-2yADjX";
container.appendChild(label);
const svg = document.createElement("div");
container.appendChild(svg);
function setSvgDisabled() {
svg.innerHTML = `<div class="container-1QtPKm default-colors disabled-3_3z1m" style="opacity: 0.3; background-color: rgb(114, 118, 125);"><svg class="slider-HJFN2i" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" aria-hidden="true" style="left: -3px;"><rect fill="white" x="4" y="0" height="20" width="20" rx="10"></rect><svg viewBox="0 0 20 20" fill="none"><path fill="rgba(114, 118, 125, 1)" d="M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z"></path><path fill="rgba(114, 118, 125, 1)" d="M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z"></path></svg></svg><input id="uid_75" type="checkbox" class="input-125oad" tabindex="-1" disabled=""></div>`;
}
function setSvgEnabled() {
svg.innerHTML = `<div class="container-1QtPKm default-colors checked-16gMAN" style="opacity: 1; background-color: rgb(59, 165, 92);"><svg class="slider-HJFN2i" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" aria-hidden="true" style="left: 12px;"><rect fill="white" x="4" y="0" height="20" width="20" rx="10"></rect><svg viewBox="0 0 20 20" fill="none"><path fill="rgba(59, 165, 92, 1)" d="M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z"></path><path fill="rgba(59, 165, 92, 1)" d="M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z"></path></svg></svg><input id="uid_74" type="checkbox" class="input-125oad" tabindex="0" checked=""></div>`;
}
function updateSvg() {
if (enabled) setSvgEnabled();
else setSvgDisabled();
}
container.addEventListener("click", () => {
enabled = !enabled;
updateSvg();
onClick(enabled);
});
updateSvg();
return container;
}
setInterval(() => { setInterval(() => {
const streamActive = const streamActive =
document.getElementsByClassName("panel-2ZFCRb activityPanel-9icbyU") document.getElementsByClassName("panel-2ZFCRb activityPanel-9icbyU")
@@ -171,7 +217,7 @@ setInterval(() => {
document document
.getElementById("keybinds-tab") .getElementById("keybinds-tab")
?.getElementsByClassName( ?.getElementsByClassName(
"container-3jbRo5 info-1hMolH fontSize16-3zr6Io browserNotice-1u-Y5o" "container-3jbRo5 info-1hMolH browserNotice-1u-Y5o"
).length ).length
) { ) {
const el = document const el = document
@@ -179,14 +225,11 @@ setInterval(() => {
.getElementsByClassName("children-1xdcWE")[0]; .getElementsByClassName("children-1xdcWE")[0];
const div = document.createElement("div"); const div = document.createElement("div");
div.style.marginBottom = "50px"; div.style.marginBottom = "50px";
const button = document.createElement("button"); div.appendChild(
button.classList = createButton("Edit Global Keybinds", () => {
"button-f2h6uQ lookFilled-yCfaCM colorBrand-I6CyqQ sizeSmall-wU2dO- grow-2sR_-F"; console.log("!discord-screenaudio-keybinds");
button.innerText = "Edit Global Keybinds"; })
button.addEventListener("click", () => { );
console.log("!discord-screenaudio-keybinds");
});
div.appendChild(button);
el.innerHTML = ""; el.innerHTML = "";
el.appendChild(div); el.appendChild(div);
} }
@@ -221,6 +264,52 @@ setInterval(() => {
el.innerHTML = window.discordScreenaudioResolutionString; 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}`);
}
)
);
const divider = document.createElement("div");
divider.className = "divider-3nqZNm marginTop40-Q4o1tS";
firstDivider.after(section);
section.after(divider);
}
}
}
}, 500); }, 500);
// Fix for broken discord notifications after restart // Fix for broken discord notifications after restart

View File

@@ -55,9 +55,14 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
injectScriptFile("userscript.js", ":/assets/userscript.js"); injectScriptFile("userscript.js", ":/assets/userscript.js");
injectScriptText("version.js", injectScriptText("vars.js",
QString("window.discordScreenaudioVersion = '%1';") QString("window.discordScreenaudioVersion = '%1'; "
.arg(QApplication::applicationVersion())); "window.discordScreenaudioTrayEnabled = %2;")
.arg(QApplication::applicationVersion())
.arg(MainWindow::instance()
->settings()
->value("trayIcon", false)
.toBool()));
#ifdef KXMLGUI #ifdef KXMLGUI
injectScriptText("xmlgui.js", "window.discordScreenaudioKXMLGUI = true;"); injectScriptText("xmlgui.js", "window.discordScreenaudioKXMLGUI = true;");
@@ -221,6 +226,10 @@ void DiscordPage::javaScriptConsoleMessage(
"(KXmlGui and KGlobalAccel are not available).", "(KXmlGui and KGlobalAccel are not available).",
QMessageBox::Ok); QMessageBox::Ok);
#endif #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: ")) { } else if (message.startsWith("dsa: ")) {
qDebug(userscriptLog) << message.mid(5).toUtf8().constData(); qDebug(userscriptLog) << message.mid(5).toUtf8().constData();
} else { } else {

View File

@@ -29,7 +29,9 @@ MainWindow::MainWindow(bool useNotifySend, QWidget *parent)
assert(MainWindow::m_instance == nullptr); assert(MainWindow::m_instance == nullptr);
MainWindow::m_instance = this; MainWindow::m_instance = this;
m_useNotifySend = useNotifySend; m_useNotifySend = useNotifySend;
setupSettings();
setupWebView(); setupWebView();
setupTrayIcon();
resize(1000, 700); resize(1000, 700);
showMaximized(); showMaximized();
} }
@@ -66,6 +68,7 @@ void MainWindow::setupWebView() {
connect(notification, &KNotification::defaultActivated, connect(notification, &KNotification::defaultActivated,
[&, notificationInfo = std::move(notificationInfo)]() { [&, notificationInfo = std::move(notificationInfo)]() {
notificationInfo->click(); notificationInfo->click();
show();
activateWindow(); activateWindow();
}); });
notification->sendEvent(); notification->sendEvent();
@@ -87,6 +90,72 @@ 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() {
if (m_trayIcon == nullptr)
return;
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; } MainWindow *MainWindow::instance() { return m_instance; }

View File

@@ -3,8 +3,11 @@
#include "discordpage.h" #include "discordpage.h"
#include <QMainWindow> #include <QMainWindow>
#include <QMenu>
#include <QScopedPointer> #include <QScopedPointer>
#include <QSettings>
#include <QString> #include <QString>
#include <QSystemTrayIcon>
#include <QVector> #include <QVector>
#include <QWebEnginePage> #include <QWebEnginePage>
#include <QWebEngineProfile> #include <QWebEngineProfile>
@@ -16,13 +19,20 @@ class MainWindow : public QMainWindow {
public: public:
explicit MainWindow(bool useNotifySend = false, QWidget *parent = nullptr); explicit MainWindow(bool useNotifySend = false, QWidget *parent = nullptr);
static MainWindow *instance(); static MainWindow *instance();
QSettings *settings() const;
private: private:
void setupWebView(); void setupWebView();
void setupTrayIcon();
void cleanTrayIcon();
void setupSettings();
QWebEngineView *m_webView; QWebEngineView *m_webView;
QWebEngineProfile *prepareProfile(); QWebEngineProfile *prepareProfile();
DiscordPage *m_discordPage; DiscordPage *m_discordPage;
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
QSystemTrayIcon *m_trayIcon = nullptr;
QMenu *m_trayIconMenu;
QSettings *m_settings;
bool m_wasMaximized; bool m_wasMaximized;
static MainWindow *m_instance; static MainWindow *m_instance;
bool m_useNotifySend; bool m_useNotifySend;
@@ -32,6 +42,9 @@ private:
bool m_useKF5Notifications = false; bool m_useKF5Notifications = false;
#endif #endif
public Q_SLOTS:
void setTrayIcon(bool enabled);
private Q_SLOTS: private Q_SLOTS:
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest); void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);
}; };

View File

@@ -22,7 +22,13 @@ QVector<QString> getTargets() {
if (global.type == pipewire::node::type) { if (global.type == pipewire::node::type) {
auto node = reg.bind<pipewire::node>(global.id); auto node = reg.bind<pipewire::node>(global.id);
auto info = node.info(); auto info = node.info();
auto name = QString::fromStdString(info.props["application.name"]); QString name;
if (info.props.count("application.name") &&
info.props["application.name"] != "")
name = QString::fromStdString(info.props["application.name"]);
else
name = QString::fromStdString(
info.props["application.process.binary"]);
if (name != "" && !EXCLUDE_TARGETS.contains(name) && if (name != "" && !EXCLUDE_TARGETS.contains(name) &&
!targets.contains(name)) { !targets.contains(name)) {
@@ -67,7 +73,12 @@ void start(QString _target) {
continue; continue;
auto &parent = nodes.at(parent_id); auto &parent = nodes.at(parent_id);
auto name = parent.props["application.name"]; std::string name;
if (parent.props.count("application.name") &&
parent.props["application.name"] != "")
name = parent.props["application.name"];
else
name = parent.props["application.process.binary"];
if (name == target || if (name == target ||
(target == "[All Desktop Audio]" && (target == "[All Desktop Audio]" &&
@@ -78,8 +89,7 @@ void start(QString _target) {
core.create<pipewire::link_factory>( core.create<pipewire::link_factory>(
{fl ? virt_fl->info().id : virt_fr->info().id, port_id})); {fl ? virt_fl->info().id : virt_fr->info().id, port_id}));
qDebug(virtmicLog) << QString("Link: %1:%2 -> %3") qDebug(virtmicLog) << QString("Link: %1:%2 -> %3")
.arg(QString::fromStdString( .arg(QString::fromStdString(name))
parent.props["application.name"]))
.arg(port_id) .arg(port_id)
.arg(fl ? virt_fl->info().id .arg(fl ? virt_fl->info().id
: virt_fr->info().id) : virt_fr->info().id)
@@ -112,11 +122,17 @@ void start(QString _target) {
[&](const pipewire::global &global) { [&](const pipewire::global &global) {
if (global.type == pipewire::node::type) { if (global.type == pipewire::node::type) {
auto node = reg.bind<pipewire::node>(global.id); auto node = reg.bind<pipewire::node>(global.id);
if (!node.info().props.count("application.name")) auto info = node.info();
std::string name;
if (info.props.count("application.name") &&
info.props["application.name"] != "")
name = info.props["application.name"];
else if (info.props.count("application.process.binary")) {
name = info.props["application.process.binary"];
} else
return; return;
qDebug(virtmicLog) << QString("Added: %1") qDebug(virtmicLog) << QString("Added: %1")
.arg(QString::fromStdString( .arg(QString::fromStdString(name))
node.info().props["application.name"]))
.toUtf8() .toUtf8()
.data(); .data();
@@ -152,9 +168,14 @@ void start(QString _target) {
[&](const std::uint32_t id) { [&](const std::uint32_t id) {
if (nodes.count(id)) { if (nodes.count(id)) {
auto info = nodes.at(id); auto info = nodes.at(id);
std::string name;
if (info.props.count("application.name") &&
info.props["application.name"] != "")
name = info.props["application.name"];
else
name = info.props["application.process.binary"];
qDebug(virtmicLog) << QString("Removed: %1") qDebug(virtmicLog) << QString("Removed: %1")
.arg(QString::fromStdString( .arg(QString::fromStdString(name))
info.props["application.name"].data()))
.toUtf8() .toUtf8()
.data(); .data();
nodes.erase(id); nodes.erase(id);