Compare commits
3 Commits
master
...
virtmic-re
Author | SHA1 | Date |
---|---|---|
Malte Jürgens | e6449ad807 | |
Malte Jürgens | e919e3ea25 | |
Malte Jürgens | f31901073e |
|
@ -1,20 +1,3 @@
|
|||
# Ignore build and output directories
|
||||
/build/
|
||||
/.flatpak-builder/
|
||||
|
||||
# Ignore flatpak-builder cache
|
||||
/.flatpak-builder-cache/
|
||||
|
||||
# Ignore flatpak-builder log files
|
||||
/*.log
|
||||
|
||||
# Ignore flatpak-builder generated files
|
||||
/*.flatpak
|
||||
/*.flatpak-builder
|
||||
/*.flatpak-origin
|
||||
|
||||
# Ignore specific files or directories
|
||||
/override.json
|
||||
/metadata/
|
||||
/manifest.json
|
||||
/CMakeLists.txt.user
|
||||
/build
|
||||
.vscode
|
||||
/submodules/arrpc
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[submodule "submodules/rohrkabel"]
|
||||
path = submodules/rohrkabel
|
||||
url = https://github.com/Soundux/rohrkabel
|
||||
[submodule "submodules/channel"]
|
||||
path = submodules/channel
|
||||
url = https://github.com/Soundux/channel.git
|
||||
|
|
|
@ -45,15 +45,6 @@ set(discord-screenaudio_SRC
|
|||
src/log.cpp
|
||||
src/userscript.cpp
|
||||
src/centralwidget.cpp
|
||||
src/localserver.cpp
|
||||
src/centralwidget.h
|
||||
src/discordpage.h
|
||||
src/localserver.h
|
||||
src/log.h
|
||||
src/mainwindow.h
|
||||
src/streamdialog.h
|
||||
src/userscript.h
|
||||
src/virtmic.h
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
|
@ -72,15 +63,20 @@ if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/submodules/rohrkabel/CMakeLists.txt")
|
||||
message(FATAL_ERROR "Rohrkabel was not found since you are not in a Git checkout or have GIT_SUBMODULE disabled. Please provide rohrkabel manually to `./submodules/rohrkabel`.")
|
||||
endif()
|
||||
function(add_git_subdirectory SUBMODULE)
|
||||
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/submodules/${SUBMODULE}/CMakeLists.txt")
|
||||
message(FATAL_ERROR "Submodule ${SUBMODULE} was not found since you are not in a Git checkout or have GIT_SUBMODULE disabled. Please provide ${SUBMODULE} manually to `./submodules/${SUBMODULE}`.")
|
||||
endif()
|
||||
|
||||
add_subdirectory(submodules/rohrkabel)
|
||||
add_subdirectory(submodules/${SUBMODULE})
|
||||
endfunction()
|
||||
|
||||
add_git_subdirectory(rohrkabel)
|
||||
add_git_subdirectory(channel)
|
||||
|
||||
add_executable(discord-screenaudio ${discord-screenaudio_SRC})
|
||||
|
||||
target_link_libraries(discord-screenaudio Qt::Widgets Qt::WebEngineWidgets rohrkabel)
|
||||
target_link_libraries(discord-screenaudio Qt::Widgets Qt::WebEngineWidgets rohrkabel channel)
|
||||
|
||||
if(KF5Notifications_FOUND)
|
||||
target_link_libraries(discord-screenaudio KF5::Notifications)
|
||||
|
|
|
@ -62,9 +62,6 @@ You have multiple options:
|
|||
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`
|
||||
|
||||
With dnf:
|
||||
`dnf install @development-tools cmake qt5-qtbase-devel qt5-qtwebengine-devel kf5-knotifications-devel kf5-kxmlgui-devel kf5-kglobalaccel-devel pkgconfig pipewire-devel git`
|
||||
|
||||
### Building
|
||||
|
||||
First, clone the repository:
|
||||
|
|
|
@ -28,7 +28,7 @@ function setGetDisplayMedia(video = true, overrideArgs = undefined) {
|
|||
var id;
|
||||
try {
|
||||
let myDiscordAudioSink = await getAudioDevice(
|
||||
"discord-awesomeaudio-virtmic"
|
||||
"discord-screenaudio-virtmic"
|
||||
);
|
||||
id = myDiscordAudioSink.deviceId;
|
||||
} catch (error) {
|
||||
|
@ -240,7 +240,7 @@ function main() {
|
|||
} else {
|
||||
aboutEl = document.createElement("div");
|
||||
}
|
||||
aboutEl.innerText = `discord-awesomeaudio ${userscript.version}`;
|
||||
aboutEl.innerText = `discord-screenaudio ${userscript.version}`;
|
||||
aboutEl.style.fontSize = "12px";
|
||||
aboutEl.style.color = "var(--text-muted)";
|
||||
aboutEl.style.textTransform = "none";
|
||||
|
@ -319,7 +319,7 @@ function main() {
|
|||
const title = document.createElement("h2");
|
||||
title.className =
|
||||
"h1-3iMExa title-lXcL8p defaultColor-3Olr-9 defaultMarginh1-1UYutH";
|
||||
title.innerText = "discord-awesomeaudio";
|
||||
title.innerText = "discord-screenaudio";
|
||||
section.appendChild(title);
|
||||
|
||||
section.appendChild(
|
||||
|
@ -340,19 +340,9 @@ function main() {
|
|||
// })
|
||||
// );
|
||||
|
||||
section.appendChild(
|
||||
section.appendChild(
|
||||
createSwitch(
|
||||
"Allow audio from your mic (and audio you pass through) to become stereo",
|
||||
await userscript.getBoolPref("stereoMic", false),
|
||||
(enabled) => {
|
||||
userscript.setStereoMic(enabled);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
section.appendChild(
|
||||
createSwitch(
|
||||
"Move discord-awesomeaudio to the system tray instead of closing",
|
||||
"Move discord-screenaudio to the system tray instead of closing",
|
||||
await userscript.getBoolPref("trayIcon", false),
|
||||
(enabled) => {
|
||||
userscript.setTrayIcon(enabled);
|
||||
|
@ -362,7 +352,7 @@ function main() {
|
|||
|
||||
section.appendChild(
|
||||
createSwitch(
|
||||
"Start discord-awesomeaudio hidden to tray",
|
||||
"Start discord-screenaudio hidden to tray",
|
||||
await userscript.getPref("startHidden", false),
|
||||
(hidden) => {
|
||||
userscript.setPref("startHidden", hidden);
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/// <reference path="../src/modules.d.ts" />
|
||||
/// <reference path="../src/globals.d.ts" />
|
||||
|
||||
import monacoHtml from "~fileContent/../src/components/monacoWin.html";
|
||||
import * as DataStore from "../src/api/DataStore";
|
||||
import { debounce } from "../src/utils";
|
||||
import { getTheme, Theme } from "../src/utils/discord";
|
||||
|
||||
// Discord deletes this so need to store in variable
|
||||
const { localStorage } = window;
|
||||
|
||||
// listeners for ipc.on
|
||||
const cssListeners = new Set<(css: string) => void>();
|
||||
const NOOP = () => { };
|
||||
const NOOP_ASYNC = async () => { };
|
||||
|
||||
const setCssDebounced = debounce((css: string) => VencordNative.quickCss.set(css));
|
||||
|
||||
// probably should make this less cursed at some point
|
||||
window.VencordNative = {
|
||||
native: {
|
||||
getVersions: () => ({}),
|
||||
openExternal: async (url) => void open(url, "_blank")
|
||||
},
|
||||
|
||||
updater: {
|
||||
getRepo: async () => ({ ok: true, value: "https://github.com/Vendicated/Vencord" }),
|
||||
getUpdates: async () => ({ ok: true, value: [] }),
|
||||
update: async () => ({ ok: true, value: false }),
|
||||
rebuild: async () => ({ ok: true, value: true }),
|
||||
},
|
||||
|
||||
quickCss: {
|
||||
get: () => DataStore.get("VencordQuickCss").then(s => s ?? ""),
|
||||
set: async (css: string) => {
|
||||
await DataStore.set("VencordQuickCss", css);
|
||||
cssListeners.forEach(l => l(css));
|
||||
},
|
||||
addChangeListener(cb) {
|
||||
cssListeners.add(cb);
|
||||
},
|
||||
openFile: NOOP_ASYNC,
|
||||
async openEditor() {
|
||||
const features = `popup,width=${Math.min(window.innerWidth, 1000)},height=${Math.min(window.innerHeight, 1000)}`;
|
||||
const win = open("about:blank", "VencordQuickCss", features);
|
||||
if (!win) {
|
||||
alert("Failed to open QuickCSS popup. Make sure to allow popups!");
|
||||
return;
|
||||
}
|
||||
|
||||
win.setCss = setCssDebounced;
|
||||
win.getCurrentCss = () => VencordNative.quickCss.get();
|
||||
win.getTheme = () =>
|
||||
getTheme() === Theme.Light
|
||||
? "vs-light"
|
||||
: "vs-dark";
|
||||
|
||||
win.document.write(monacoHtml);
|
||||
},
|
||||
},
|
||||
|
||||
settings: {
|
||||
get: () => localStorage.getItem("VencordSettings") || "{}",
|
||||
set: async (s: string) => localStorage.setItem("VencordSettings", s),
|
||||
getSettingsDir: async () => "LocalStorage"
|
||||
}
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
import definePlugin from "../utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "discord-awesomeaudio",
|
||||
authors: [
|
||||
{
|
||||
name: "retard",
|
||||
id: 205966226709676099n,
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
description: "UI patches for discord-screenaudio.",
|
||||
patches: [],
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
--- a/src/components/VencordSettings/VencordTab.tsx
|
||||
+++ b/src/components/VencordSettings/VencordTab.tsx
|
||||
@@ -87,10 +87,10 @@ function VencordSettings() {
|
||||
<Card className={cl("quick-actions-card")}>
|
||||
{IS_WEB ? (
|
||||
<Button
|
||||
- onClick={() => require("../Monaco").launchMonacoEditor()}
|
||||
+ onClick={() => VencordNative.ipc.send(IpcEvents.OPEN_EXTERNAL, settingsDir)}
|
||||
size={Button.Sizes.SMALL}
|
||||
disabled={settingsDir === "Loading..."}>
|
||||
- Open QuickCSS File
|
||||
+ Launch Directory
|
||||
</Button>
|
||||
) : (
|
||||
<React.Fragment>
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 48 KiB |
42
install.sh
42
install.sh
|
@ -1,42 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
is_package_installed() {
|
||||
if command -v "$1" >/dev/null 2>&1; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
if [[ "$ID" == "ubuntu" || "$ID_LIKE" == "ubuntu" ]]; then
|
||||
if ! is_package_installed flatpak-builder; then
|
||||
sudo apt update
|
||||
sudo apt install -y flatpak-builder
|
||||
fi
|
||||
elif [[ "$ID" == "fedora" || "$ID_LIKE" == "fedora" ]]; then
|
||||
if ! is_package_installed flatpak-builder; then
|
||||
sudo dnf install -y flatpak-builder
|
||||
fi
|
||||
elif [[ "$ID" == "void" ]]; then
|
||||
if ! is_package_installed flatpak-builder; then
|
||||
sudo xbps-install -Sy flatpak-builder
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
flatpak install flathub org.kde.Sdk//5.15-22.08
|
||||
|
||||
flatpak install flathub io.qt.qtwebengine.BaseApp//5.15-22.08
|
||||
|
||||
flatpak-builder build-dir lol.deadzone.discord-awesomeaudio.json --install --user --force-clean
|
||||
|
||||
echo "[Desktop Entry]
|
||||
Name=Discord Awesome Audio
|
||||
Exec=flatpak run lol.deadzone.discord-awesomeaudio
|
||||
Icon=/path/to/application/icon.png
|
||||
Type=Application
|
||||
Categories=AudioVideo;Network;" > ~/.local/share/applications/discord-awesomeaudio.desktop
|
||||
|
||||
chmod +x ~/.local/share/applications/discord-awesomeaudio.desktop
|
|
@ -1,44 +0,0 @@
|
|||
{
|
||||
"app-id": "lol.deadzone.discord-awesomeaudio",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "5.15-22.08",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"base": "io.qt.qtwebengine.BaseApp",
|
||||
"base-version": "5.15-22.08",
|
||||
"command": "discord-screenaudio",
|
||||
"finish-args": [
|
||||
"--share=ipc",
|
||||
"--share=network",
|
||||
"--socket=wayland",
|
||||
"--socket=fallback-x11",
|
||||
"--socket=pulseaudio",
|
||||
"--filesystem=xdg-videos:ro",
|
||||
"--filesystem=xdg-pictures:ro",
|
||||
"--filesystem=xdg-download",
|
||||
"--filesystem=xdg-run/pipewire-0",
|
||||
"--device=all",
|
||||
"--talk-name=org.kde.StatusNotifierWatcher",
|
||||
"--env=QTWEBENGINEPROCESS_PATH=/app/bin/QtWebEngineProcess",
|
||||
"--talk-name=org.freedesktop.Notifications",
|
||||
"--talk-name=org.kde.kglobalaccel"
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "discord-screenaudio",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [
|
||||
"-DCMAKE_BUILD_TYPE=RelWithDebInfo",
|
||||
"-DGIT_SUBMODULE=OFF"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"finish-install": {
|
||||
"desktop-file": "[Desktop Entry]\nName=Discord Awesome Audio\nExec=flatpak run lol.deadzone.discord-awesomeaudio\nIcon=/path/to/application/icon.png\nType=Application\nCategories=AudioVideo;Network;"
|
||||
}
|
||||
}
|
|
@ -1,12 +1,9 @@
|
|||
<!DOCTYPE RCC>
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>assets/userscript.js</file>
|
||||
<file>assets/arrpc_bridge_mod.js</file>
|
||||
<file>assets/arrpc.js</file>
|
||||
<file>assets/de.shorsh.discord-screenaudio.png</file>
|
||||
<file>assets/vencord/vencord.js</file>
|
||||
<file>assets/vencord/settings.patch</file>
|
||||
<file>assets/vencord/plugin.js</file>
|
||||
<file>assets/vencord/VencordNativeStub.ts</file>
|
||||
</qresource>
|
||||
<qresource>
|
||||
<file>assets/userscript.js</file>
|
||||
<file>assets/arrpc_bridge_mod.js</file>
|
||||
<file>assets/arrpc.js</file>
|
||||
<file>assets/de.shorsh.discord-screenaudio.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -1,39 +0,0 @@
|
|||
#!/usr/bin/bash
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/../submodules"
|
||||
|
||||
echo_status() {
|
||||
echo
|
||||
echo
|
||||
echo "-> $1..."
|
||||
}
|
||||
|
||||
if [ ! -d "Vencord" ]; then
|
||||
echo_status "Cloning Vencord"
|
||||
git clone https://github.com/Vendicated/Vencord.git
|
||||
cd Vencord
|
||||
else
|
||||
echo_status "Fetching Vencord changes"
|
||||
cd Vencord
|
||||
git fetch
|
||||
fi
|
||||
|
||||
echo_status "Checking out latest commit"
|
||||
git reset --hard HEAD
|
||||
git checkout main
|
||||
#git reset --hard devbuild
|
||||
|
||||
echo_status "Installing dependencies"
|
||||
pnpm i
|
||||
|
||||
echo_status "Patching Vencord"
|
||||
cp -v ../../assets/vencord/plugin.js ./src/plugins/discord-screenaudio.js
|
||||
cp -v ../../assets/vencord/VencordNativeStub.ts ./browser/VencordNativeStub.ts
|
||||
patch -p1 -i ../../assets/vencord/settings.patch
|
||||
|
||||
echo_status "Building Vencord"
|
||||
pnpm run buildWeb
|
||||
|
||||
echo_status "Copying built file"
|
||||
cp -v ./dist/browser.js ../../assets/vencord/vencord.js
|
|
@ -7,7 +7,6 @@
|
|||
#include <QWebEngineScript>
|
||||
#include <QWebEngineScriptCollection>
|
||||
#include <QWebEngineSettings>
|
||||
#include <QWebEngineView>
|
||||
|
||||
CentralWidget::CentralWidget(QWidget *parent) : QWidget(parent) {
|
||||
setStyleSheet("background-color:#313338;");
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#ifndef CENTRALWIDGET_H
|
||||
#define CENTRALWIDGET_H
|
||||
#pragma once
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
@ -8,28 +7,23 @@
|
|||
#include <QWebEngineView>
|
||||
#include <QWidget>
|
||||
|
||||
#ifdef KNOTIFICATIONS
|
||||
constexpr bool USE_KF5_NOTIFICATIONS = true;
|
||||
#else
|
||||
constexpr bool USE_KF5_NOTIFICATIONS = false;
|
||||
#endif
|
||||
|
||||
class CentralWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CentralWidget(QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void setLoadingIndicator(QString text);
|
||||
|
||||
private:
|
||||
void setupWebView();
|
||||
|
||||
QVBoxLayout *m_layout;
|
||||
QWebEngineView *m_webView;
|
||||
QLabel *m_loadingLabel;
|
||||
bool m_useKF5Notifications;
|
||||
};
|
||||
#ifdef KNOTIFICATIONS
|
||||
bool m_useKF5Notifications = true;
|
||||
#else
|
||||
bool m_useKF5Notifications = false;
|
||||
#endif
|
||||
QLabel *m_loadingLabel = nullptr;
|
||||
|
||||
#endif // CENTRALWIDGET_H
|
||||
public Q_SLOTS:
|
||||
void setLoadingIndicator(QString text);
|
||||
};
|
||||
|
|
|
@ -36,16 +36,7 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
|
|||
|
||||
injectFile(&DiscordPage::injectScript, "userscript.js",
|
||||
":/assets/userscript.js");
|
||||
QFile vencord(":/assets/vencord/vencord.js");
|
||||
if (!vencord.open(QIODevice::ReadOnly))
|
||||
qFatal("Failed to load vencord source with error: %s",
|
||||
vencord.errorString().toLatin1().constData());
|
||||
injectScript(
|
||||
"vencord.js",
|
||||
QString("window.discordScreenaudioVencordSettings = `%1`; %2")
|
||||
.arg(m_userScript.vencordSend("VencordGetSettings", {}).toString(),
|
||||
vencord.readAll()));
|
||||
vencord.close();
|
||||
|
||||
setupUserStyles();
|
||||
setupArrpc();
|
||||
}
|
||||
|
@ -291,11 +282,10 @@ void DiscordPage::javaScriptConsoleMessage(
|
|||
ansi += "\033[" + cssAnsiColorMap[color] + "m";
|
||||
}
|
||||
}
|
||||
if (endOfStyles < lines.length())
|
||||
qDebug(discordLog) << (ansi + lines[0].trimmed() + "\033[0m " +
|
||||
lines[endOfStyles].trimmed())
|
||||
.toUtf8()
|
||||
.constData();
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
#include "localserver.h"
|
||||
|
||||
bool isProgramRunning(const QString &program_name) {
|
||||
QLocalSocket socket;
|
||||
socket.connectToServer(program_name);
|
||||
if (socket.waitForConnected()) {
|
||||
return true; // program is already running
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void showErrorMessage(const char *text) {
|
||||
QMessageBox msgBox;
|
||||
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setText(text);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.setWindowIcon(QIcon(":assets/de.shorsh.discord-screenaudio.png"));
|
||||
|
||||
msgBox.exec();
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#pragma once
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QMessageBox>
|
||||
|
||||
bool isProgramRunning(const QString &program_name);
|
||||
void showErrorMessage(const char *text);
|
30
src/main.cpp
30
src/main.cpp
|
@ -1,4 +1,3 @@
|
|||
#include "localserver.h"
|
||||
#include "mainwindow.h"
|
||||
#include "virtmic.h"
|
||||
|
||||
|
@ -8,19 +7,15 @@
|
|||
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMessageBox>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QApplication::setApplicationName("discord-awesomeaudio");
|
||||
QApplication::setApplicationName("discord-screenaudio");
|
||||
QApplication::setWindowIcon(
|
||||
QIcon(":assets/de.shorsh.discord-screenaudio.png"));
|
||||
QApplication::setApplicationVersion(DISCORD_SCEENAUDIO_VERSION_FULL);
|
||||
QApplication::setDesktopFileName("lol.deadzone.discord-awesomeaudio");
|
||||
QApplication::setDesktopFileName("de.shorsh.discord-screenaudio");
|
||||
|
||||
qSetMessagePattern("[%{category}] %{message}");
|
||||
|
||||
|
@ -29,9 +24,6 @@ int main(int argc, char *argv[]) {
|
|||
"Custom Discord client with the ability to stream audio on Linux");
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
QCommandLineOption virtmicOption("virtmic", "Start the Virtual Microphone",
|
||||
"target");
|
||||
parser.addOption(virtmicOption);
|
||||
QCommandLineOption degubOption("remote-debugging",
|
||||
"Open Chromium Remote Debugging on port 9222");
|
||||
parser.addOption(degubOption);
|
||||
|
@ -41,10 +33,6 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
parser.process(app);
|
||||
|
||||
if (parser.isSet(virtmicOption)) {
|
||||
Virtmic::start(parser.value(virtmicOption));
|
||||
}
|
||||
|
||||
qputenv("QTWEBENGINE_CHROMIUM_FLAGS",
|
||||
"--enable-features=WebRTCPipeWireCapturer " +
|
||||
qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"));
|
||||
|
@ -55,20 +43,6 @@ int main(int argc, char *argv[]) {
|
|||
qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"));
|
||||
|
||||
MainWindow w(parser.isSet(notifySendOption));
|
||||
|
||||
// Check if discord is already running
|
||||
QString program_name = "discord-awesomeaudio";
|
||||
if (isProgramRunning(program_name)) {
|
||||
// if running show error message
|
||||
showErrorMessage("discord-awesomeaudio is already running");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// open server so we can check if discord is running
|
||||
QLocalServer server;
|
||||
server.listen(program_name);
|
||||
QObject::connect(&server, &QLocalServer::newConnection, []() {});
|
||||
|
||||
w.show();
|
||||
|
||||
return app.exec();
|
||||
|
|
|
@ -12,154 +12,121 @@
|
|||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QShortcut>
|
||||
#include <QSpacerItem>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QWebEngineFullScreenRequest>
|
||||
#include <QWebEngineProfile>
|
||||
#include <QWebEngineUrlRequestInterceptor>
|
||||
#include <QWidget>
|
||||
|
||||
// Custom network interceptor class
|
||||
class NetworkInterceptor : public QWebEngineUrlRequestInterceptor {
|
||||
public:
|
||||
NetworkInterceptor(QObject* parent = nullptr) : QWebEngineUrlRequestInterceptor(parent) {}
|
||||
|
||||
void interceptRequest(QWebEngineUrlRequestInfo& info) override {
|
||||
// Modify the request headers here as needed
|
||||
info.setHttpHeader("Access-Control-Allow-Origin", "*");
|
||||
info.setHttpHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
||||
}
|
||||
};
|
||||
|
||||
MainWindow *MainWindow::m_instance = nullptr;
|
||||
|
||||
MainWindow::MainWindow(bool useNotifySend, QWidget *parent)
|
||||
: QMainWindow(parent) {
|
||||
assert(MainWindow::m_instance == nullptr);
|
||||
MainWindow::m_instance = this;
|
||||
setupSettings();
|
||||
m_settings->setValue("useNotifySend", useNotifySend);
|
||||
m_centralWidget = new CentralWidget(this);
|
||||
setCentralWidget(m_centralWidget);
|
||||
|
||||
// Create and install the network interceptor
|
||||
NetworkInterceptor* networkInterceptor = new NetworkInterceptor(this);
|
||||
QWebEngineProfile::defaultProfile()->setRequestInterceptor(networkInterceptor);
|
||||
|
||||
setupTrayIcon();
|
||||
setMinimumSize(800, 300);
|
||||
connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this),
|
||||
&QShortcut::activated, this, &MainWindow::toggleOrCloseWindow);
|
||||
if (m_settings->contains("geometry")) {
|
||||
restoreGeometry(m_settings->value("geometry").toByteArray());
|
||||
} else {
|
||||
resize(1000, 700);
|
||||
showMaximized();
|
||||
}
|
||||
if (m_settings->value("trayIcon", false).toBool() &&
|
||||
m_settings->value("startHidden", false).toBool()) {
|
||||
hide();
|
||||
QTimer::singleShot(0, [=]() { hide(); });
|
||||
}
|
||||
assert(MainWindow::m_instance == nullptr);
|
||||
MainWindow::m_instance = this;
|
||||
setupSettings();
|
||||
m_settings->setValue("useNotifySend", useNotifySend);
|
||||
m_centralWidget = new CentralWidget(this);
|
||||
setCentralWidget(m_centralWidget);
|
||||
setupTrayIcon();
|
||||
if (m_settings->contains("geometry")) {
|
||||
restoreGeometry(m_settings->value("geometry").toByteArray());
|
||||
} else {
|
||||
resize(1000, 700);
|
||||
showMaximized();
|
||||
}
|
||||
if (m_settings->value("trayIcon", false).toBool() &&
|
||||
m_settings->value("startHidden", false).toBool()) {
|
||||
hide();
|
||||
QTimer::singleShot(0, [=]() { hide(); });
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::fullScreenRequested(
|
||||
QWebEngineFullScreenRequest fullScreenRequest) {
|
||||
fullScreenRequest.accept();
|
||||
if (fullScreenRequest.toggleOn()) {
|
||||
m_wasMaximized = isMaximized();
|
||||
if (!m_wasMaximized) {
|
||||
showNormal();
|
||||
}
|
||||
showFullScreen();
|
||||
} else {
|
||||
m_wasMaximized ? showMaximized() : showNormal();
|
||||
}
|
||||
QWebEngineFullScreenRequest fullScreenRequest) {
|
||||
fullScreenRequest.accept();
|
||||
if (fullScreenRequest.toggleOn()) {
|
||||
m_wasMaximized = isMaximized();
|
||||
showFullScreen();
|
||||
} else {
|
||||
m_wasMaximized ? showMaximized() : showNormal();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setupTrayIcon() {
|
||||
if (m_settings->value("trayIcon", false).toBool() == false ||
|
||||
m_trayIcon != nullptr)
|
||||
return;
|
||||
if (m_settings->value("trayIcon", false).toBool() == false ||
|
||||
m_trayIcon != nullptr)
|
||||
return;
|
||||
|
||||
auto aboutAction = new QAction(
|
||||
"discord-awesomeaudio v" + QString(DISCORD_SCEENAUDIO_VERSION_FULL), this);
|
||||
aboutAction->setIcon(QIcon(":assets/de.shorsh.discord-screenaudio.png"));
|
||||
aboutAction->setEnabled(false);
|
||||
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(); });
|
||||
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_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();
|
||||
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) {
|
||||
toggleOrCloseWindow();
|
||||
}
|
||||
});
|
||||
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;
|
||||
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("discord-awesomeaudio", "discord-awesomeaudio", this);
|
||||
m_settings->beginGroup("settings");
|
||||
m_settings->endGroup();
|
||||
m_settings =
|
||||
new QSettings("discord-screenaudio", "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();
|
||||
}
|
||||
m_settings->setValue("trayIcon", enabled);
|
||||
if (enabled) {
|
||||
setupTrayIcon();
|
||||
} else {
|
||||
cleanTrayIcon();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event) {
|
||||
if (m_settings->value("trayIcon", false).toBool()) {
|
||||
hide();
|
||||
} else {
|
||||
m_settings->setValue("geometry", saveGeometry());
|
||||
QApplication::quit();
|
||||
}
|
||||
if (m_settings->value("trayIcon", false).toBool()) {
|
||||
hide();
|
||||
} else {
|
||||
m_settings->setValue("geometry", saveGeometry());
|
||||
QApplication::quit();
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow *MainWindow::instance() { return m_instance; }
|
||||
|
||||
CentralWidget *MainWindow::centralWidget() {
|
||||
return instance()->m_centralWidget;
|
||||
return instance()->m_centralWidget;
|
||||
};
|
||||
|
||||
void MainWindow::toggleOrCloseWindow() {
|
||||
if (isVisible()) {
|
||||
if (m_trayIcon == nullptr)
|
||||
QApplication::quit();
|
||||
else
|
||||
hide();
|
||||
} else {
|
||||
show();
|
||||
activateWindow();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "centralwidget.h"
|
||||
#include "virtmic.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMenu>
|
||||
|
@ -10,31 +11,30 @@
|
|||
#include <QSystemTrayIcon>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVector>
|
||||
#include <QWebEngineUrlRequestInterceptor>
|
||||
|
||||
class MainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(bool useNotifySend = false, QWidget *parent = nullptr);
|
||||
static MainWindow *instance();
|
||||
QSettings *settings() const;
|
||||
static CentralWidget *centralWidget();
|
||||
explicit MainWindow(bool useNotifySend = false, QWidget *parent = nullptr);
|
||||
static MainWindow *instance();
|
||||
QSettings *settings() const;
|
||||
static CentralWidget *centralWidget();
|
||||
|
||||
private:
|
||||
void setupTrayIcon();
|
||||
void cleanTrayIcon();
|
||||
void setupSettings();
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
QSystemTrayIcon *m_trayIcon = nullptr;
|
||||
QMenu *m_trayIconMenu;
|
||||
QSettings *m_settings;
|
||||
bool m_wasMaximized;
|
||||
static MainWindow *m_instance;
|
||||
CentralWidget *m_centralWidget;
|
||||
void setupTrayIcon();
|
||||
void cleanTrayIcon();
|
||||
void setupSettings();
|
||||
QWebEngineProfile *prepareProfile();
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
QSystemTrayIcon *m_trayIcon = nullptr;
|
||||
QMenu *m_trayIconMenu;
|
||||
QSettings *m_settings;
|
||||
bool m_wasMaximized;
|
||||
static MainWindow *m_instance;
|
||||
CentralWidget *m_centralWidget;
|
||||
|
||||
public Q_SLOTS:
|
||||
void setTrayIcon(bool enabled);
|
||||
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);
|
||||
void toggleOrCloseWindow();
|
||||
void setTrayIcon(bool enabled);
|
||||
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "streamdialog.h"
|
||||
#include "mainwindow.h"
|
||||
#include "virtmic.h"
|
||||
|
||||
#include <QComboBox>
|
||||
|
|
|
@ -21,7 +21,6 @@ UserScript::UserScript() : QObject() {
|
|||
setupVirtmic();
|
||||
}
|
||||
|
||||
|
||||
void UserScript::setupHelpMenu() {
|
||||
#ifdef KXMLGUI
|
||||
m_kxmlgui = true;
|
||||
|
@ -43,12 +42,12 @@ void UserScript::setupHelpMenu() {
|
|||
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",
|
||||
aboutData.addComponent("Rohrkabel", "A C++ RAII Pipewire-API Wrapper", "1.5",
|
||||
"https://github.com/Soundux/rohrkabel");
|
||||
aboutData.addComponent("arRPC",
|
||||
"An open implementation of Discord's local RPC "
|
||||
"servers<br>Copyright (c) 2022 OpenAsar",
|
||||
nullptr, "https://github.com/OpenAsar/arrpc");
|
||||
aboutData.addComponent(
|
||||
"Soundux/channel ",
|
||||
"A C++ implementation of Rust's std::sync::mpsc::channel", nullptr,
|
||||
"https://github.com/Soundux/channel");
|
||||
m_helpMenu = new KHelpMenu(MainWindow::instance(), aboutData);
|
||||
#endif
|
||||
}
|
||||
|
@ -84,7 +83,8 @@ void UserScript::setupShortcutsDialog() {
|
|||
|
||||
void UserScript::setupStreamDialog() {
|
||||
m_streamDialog = new StreamDialog(MainWindow::instance());
|
||||
connect(m_streamDialog, &StreamDialog::requestedStreamStart, this, &UserScript::startStream);
|
||||
connect(m_streamDialog, &StreamDialog::requestedStreamStart, this,
|
||||
&UserScript::startStream);
|
||||
}
|
||||
|
||||
void UserScript::setupVirtmic() {
|
||||
|
@ -155,13 +155,13 @@ void UserScript::startVirtmic(QString target) {
|
|||
m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target});
|
||||
}
|
||||
|
||||
void UserScript::startStream(bool video, bool audio, int width, int height, int frameRate, QString 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);
|
||||
});
|
||||
QTimer::singleShot(
|
||||
200, [=]() { emit streamStarted(video, width, height, frameRate); });
|
||||
}
|
||||
|
||||
void UserScript::showStreamDialog() {
|
||||
|
@ -173,7 +173,8 @@ void UserScript::showStreamDialog() {
|
|||
}
|
||||
|
||||
void UserScript::showThemeDialog() {
|
||||
auto url = QInputDialog::getText(MainWindow::instance(), "Theme Installation", "Please enter the URL of the Theme");
|
||||
auto url = QInputDialog::getText(MainWindow::instance(), "Theme Installation",
|
||||
"Please enter the URL of the Theme");
|
||||
if (url != "")
|
||||
emit shouldInstallUserStyles(url);
|
||||
}
|
||||
|
@ -181,68 +182,3 @@ void UserScript::showThemeDialog() {
|
|||
void UserScript::installUserStyles(QString url) {
|
||||
emit shouldInstallUserStyles(url);
|
||||
}
|
||||
|
||||
QVariant UserScript::vencordSend(QString event, QVariantList args) {
|
||||
QString configFolder =
|
||||
QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) +
|
||||
"/vencord";
|
||||
QString quickCssFile = configFolder + "/quickCss.css";
|
||||
QString settingsFile = configFolder + "/settings.json";
|
||||
|
||||
if (!QDir().exists(configFolder))
|
||||
QDir().mkpath(configFolder);
|
||||
|
||||
if (event == "VencordGetRepo") {
|
||||
return true;
|
||||
}
|
||||
if (event == "VencordGetSettingsDir") {
|
||||
return configFolder;
|
||||
}
|
||||
if (event == "VencordGetQuickCss") {
|
||||
if (QFile::exists(quickCssFile)) {
|
||||
QFile file(quickCssFile);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
qFatal("Failed to load %s with error: %s",
|
||||
quickCssFile.toLatin1().constData(),
|
||||
file.errorString().toLatin1().constData());
|
||||
auto content = file.readAll();
|
||||
file.close();
|
||||
return QString(content);
|
||||
} else
|
||||
return "";
|
||||
}
|
||||
if (event == "VencordGetSettings") {
|
||||
if (QFile::exists(settingsFile)) {
|
||||
QFile file(settingsFile);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
qFatal("Failed to load %s with error: %s",
|
||||
settingsFile.toLatin1().constData(),
|
||||
file.errorString().toLatin1().constData());
|
||||
auto content = file.readAll();
|
||||
file.close();
|
||||
return QString(content);
|
||||
} else
|
||||
return "{}";
|
||||
}
|
||||
if (event == "VencordSetSettings") {
|
||||
QFile file(settingsFile);
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
qFatal("Failed to load %s with error: %s",
|
||||
settingsFile.toLatin1().constData(),
|
||||
file.errorString().toLatin1().constData());
|
||||
file.write(args[0].toString().toUtf8());
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
if (event == "VencordGetUpdates") {
|
||||
return QVariantMap{{"ok", true}, {"value", QVariantList()}};
|
||||
}
|
||||
if (event == "VencordOpenExternal") {
|
||||
QDesktopServices::openUrl(QUrl(args[0].toString()));
|
||||
return true;
|
||||
}
|
||||
if (event == "VencordOpenQuickCss") {
|
||||
return true;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "streamdialog.h"
|
||||
#include <QProcess>
|
||||
|
||||
#include <QObject>
|
||||
#include <QDir>
|
||||
#include <QDesktopServices>
|
||||
#include <QProcess>
|
||||
|
||||
#ifdef KXMLGUI
|
||||
#include <KAboutData>
|
||||
|
@ -24,66 +23,61 @@
|
|||
#endif
|
||||
|
||||
class UserScript : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
UserScript();
|
||||
bool isVirtmicRunning();
|
||||
Q_PROPERTY(QString version READ version CONSTANT)
|
||||
Q_PROPERTY(bool kxmlgui MEMBER m_kxmlgui CONSTANT)
|
||||
Q_PROPERTY(bool kglobalaccel MEMBER m_kglobalaccel CONSTANT)
|
||||
Q_PROPERTY(QString userstyles MEMBER m_userstyles NOTIFY userstylesChanged)
|
||||
Q_PROPERTY(QString loadingMessage MEMBER m_loadingMessage NOTIFY loadingMessageChanged)
|
||||
UserScript();
|
||||
bool isVirtmicRunning();
|
||||
Q_PROPERTY(QString version READ version CONSTANT);
|
||||
Q_PROPERTY(bool kxmlgui MEMBER m_kxmlgui CONSTANT);
|
||||
Q_PROPERTY(bool kglobalaccel MEMBER m_kglobalaccel CONSTANT);
|
||||
Q_PROPERTY(QString userstyles MEMBER m_userstyles NOTIFY userstylesChanged);
|
||||
Q_PROPERTY(QString loadingMessage MEMBER m_loadingMessage NOTIFY
|
||||
loadingMessageChanged);
|
||||
|
||||
private:
|
||||
QProcess m_virtmicProcess;
|
||||
StreamDialog *m_streamDialog;
|
||||
bool m_kxmlgui = false;
|
||||
bool m_kglobalaccel = false;
|
||||
QString m_userstyles;
|
||||
QString m_loadingMessage;
|
||||
|
||||
QString m_vencordSettings;
|
||||
|
||||
void setupHelpMenu();
|
||||
void setupShortcutsDialog();
|
||||
void setupStreamDialog();
|
||||
void setupVirtmic();
|
||||
|
||||
QProcess m_virtmicProcess;
|
||||
StreamDialog *m_streamDialog;
|
||||
bool m_kxmlgui = false;
|
||||
bool m_kglobalaccel = false;
|
||||
QString m_userstyles;
|
||||
QString m_loadingMessage;
|
||||
#ifdef KXMLGUI
|
||||
KHelpMenu *m_helpMenu;
|
||||
KHelpMenu *m_helpMenu;
|
||||
#ifdef KGLOBALACCEL
|
||||
KActionCollection *m_actionCollection;
|
||||
KShortcutsDialog *m_shortcutsDialog;
|
||||
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);
|
||||
void userstylesChanged();
|
||||
void loadingMessageChanged(QString message);
|
||||
void shouldInstallUserStyles(QString url);
|
||||
void muteToggled();
|
||||
void deafenToggled();
|
||||
void streamStarted(bool video, int width, int height, int frameRate);
|
||||
void userstylesChanged();
|
||||
void loadingMessageChanged(QString message);
|
||||
void shouldInstallUserStyles(QString url);
|
||||
|
||||
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);
|
||||
void showThemeDialog();
|
||||
void installUserStyles(QString url);
|
||||
QVariant vencordSend(QString event, QVariantList args);
|
||||
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);
|
||||
void showThemeDialog();
|
||||
void installUserStyles(QString url);
|
||||
|
||||
private Q_SLOTS:
|
||||
void startStream(bool video, bool audio, int width, int height, int frameRate, QString target);
|
||||
void startStream(bool video, bool audio, int width, int height, int frameRate,
|
||||
QString target);
|
||||
};
|
||||
|
||||
|
|
346
src/virtmic.cpp
346
src/virtmic.cpp
|
@ -1,200 +1,174 @@
|
|||
#include "virtmic.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <rohrkabel/loop/main.hpp>
|
||||
#include <rohrkabel/registry/registry.hpp>
|
||||
QThread virtmicThread;
|
||||
std::unique_ptr<pipewire::sender<Virtmic::set_target, Virtmic::terminate>>
|
||||
senderr;
|
||||
std::unique_ptr<cr::receiver<Virtmic::new_targets>> receiverr;
|
||||
|
||||
namespace Virtmic {
|
||||
|
||||
const QStringList EXCLUDE_TARGETS{"Chromium input", "discord-awesomeaudio", "pavucontrol", "PulseAudio Volume Control", "OBS", "OBS-Monitor", "OBS/OBS-MONITOR"};
|
||||
|
||||
const std::string nullstr = "";
|
||||
const std::string &getTarget(const pipewire::spa::dict &props) {
|
||||
if (props.count("media.class") &&
|
||||
props.at("media.class") == "Stream/Output/Audio") {
|
||||
if (props.count("application.name") && props.at("application.name") != "")
|
||||
return props.at("application.name");
|
||||
else if (props.count("application.process.binary") &&
|
||||
props.at("application.process.binary") != "")
|
||||
return props.at("application.process.binary");
|
||||
else
|
||||
return props.at("node.name");
|
||||
} else
|
||||
return nullstr;
|
||||
}
|
||||
|
||||
QString qGetTarget(const pipewire::spa::dict &props) {
|
||||
return QString::fromStdString(getTarget(props));
|
||||
}
|
||||
|
||||
QVector<QString> getTargets() {
|
||||
auto main_loop = pipewire::main_loop();
|
||||
auto context = pipewire::context(main_loop);
|
||||
auto core = pipewire::core(context);
|
||||
auto reg = pipewire::registry(core);
|
||||
|
||||
QVector<QString> targets;
|
||||
|
||||
auto reg_listener = reg.listen<pipewire::registry_listener>();
|
||||
reg_listener.on<pipewire::registry_event::global>(
|
||||
[&](const pipewire::global &global) {
|
||||
if (global.type == pipewire::node::type) {
|
||||
auto node = reg.bind<pipewire::node>(global.id);
|
||||
auto info = node.info();
|
||||
QString name = qGetTarget(info.props);
|
||||
if (name != "" && !EXCLUDE_TARGETS.contains(name) &&
|
||||
!targets.contains(name)) {
|
||||
targets.append(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
core.update();
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
void start(QString _target) {
|
||||
std::map<std::uint32_t, pipewire::port> ports;
|
||||
std::unique_ptr<pipewire::port> virt_fl, virt_fr;
|
||||
|
||||
std::map<std::uint32_t, pipewire::node_info> nodes;
|
||||
std::map<std::uint32_t, pipewire::link_factory> links;
|
||||
|
||||
auto main_loop = pipewire::main_loop();
|
||||
auto context = pipewire::context(main_loop);
|
||||
auto core = pipewire::core(context);
|
||||
auto reg = pipewire::registry(core);
|
||||
|
||||
auto link = [&](const std::string &target, pipewire::core &core) {
|
||||
for (const auto &[port_id, port] : ports) {
|
||||
if (!virt_fl || !virt_fr)
|
||||
continue;
|
||||
|
||||
if (links.count(port_id))
|
||||
continue;
|
||||
|
||||
if (port.info().direction == pipewire::port_direction::input)
|
||||
continue;
|
||||
|
||||
if (!port.info().props.count("node.id"))
|
||||
continue;
|
||||
|
||||
auto parent_id = std::stoul(port.info().props["node.id"]);
|
||||
|
||||
if (!nodes.count(parent_id))
|
||||
continue;
|
||||
|
||||
auto &parent = nodes.at(parent_id);
|
||||
std::string name = getTarget(parent.props);
|
||||
if (EXCLUDE_TARGETS.contains(QString::fromStdString(name)))
|
||||
continue;
|
||||
|
||||
if (parent.props.count("media.class") && (parent.props.at("media.class") == "Audio/Source" || parent.props.at("media.class") == "Audio/Source/Virtual"))
|
||||
continue;
|
||||
|
||||
if (name == target ||
|
||||
(target == "[All Desktop Audio]" &&
|
||||
!EXCLUDE_TARGETS.contains(QString::fromStdString(name)))) {
|
||||
auto fl = port.info().props["audio.channel"] == "FL";
|
||||
links.emplace(
|
||||
port_id,
|
||||
core.create<pipewire::link_factory>(
|
||||
{fl ? virt_fl->info().id : virt_fr->info().id, port_id}));
|
||||
qDebug(virtmicLog) << QString("Link: %1:%2 -> %3")
|
||||
.arg(QString::fromStdString(name))
|
||||
.arg(port_id)
|
||||
.arg(fl ? virt_fl->info().id
|
||||
: virt_fr->info().id)
|
||||
.toUtf8()
|
||||
.data();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::string target = _target.toUtf8().toStdString();
|
||||
|
||||
auto virtual_mic = core.create("adapter",
|
||||
{{"node.name", "discord-awesomeaudio-virtmic"},
|
||||
{"media.class", "Audio/Source/Virtual"},
|
||||
{"factory.name", "support.null-audio-sink"},
|
||||
{"audio.channels", "2"},
|
||||
{"audio.position", "FL,FR"}},
|
||||
pipewire::node::type, pipewire::node::version,
|
||||
pipewire::update_strategy::none);
|
||||
|
||||
if (target == "[None]") {
|
||||
while (true) {
|
||||
main_loop.run();
|
||||
}
|
||||
return;
|
||||
void Virtmic::instance() {
|
||||
if (!virtmicThread.isRunning()) {
|
||||
auto [main_sender, main_receiver] = cr::channel<new_targets>();
|
||||
auto [pw_sender, pw_receiver] = pipewire::channel<set_target, terminate>();
|
||||
Virtmic *virtmic =
|
||||
new Virtmic(std::move(pw_receiver), std::move(main_sender));
|
||||
virtmic->moveToThread(&virtmicThread);
|
||||
virtmicThread.start();
|
||||
QMetaObject::invokeMethod(virtmic, "run");
|
||||
receiverr = std::make_unique<cr::receiver<Virtmic::new_targets>>(
|
||||
std::move(main_receiver));
|
||||
senderr = std::make_unique<
|
||||
pipewire::sender<Virtmic::set_target, Virtmic::terminate>>(
|
||||
std::move(pw_sender));
|
||||
}
|
||||
}
|
||||
|
||||
auto reg_events = reg.listen<pipewire::registry_listener>();
|
||||
reg_events.on<pipewire::registry_event::global>(
|
||||
[&](const pipewire::global &global) {
|
||||
if (global.type == pipewire::node::type) {
|
||||
auto node = reg.bind<pipewire::node>(global.id);
|
||||
auto info = node.info();
|
||||
std::string name = getTarget(info.props);
|
||||
if (name == nullstr)
|
||||
return;
|
||||
qDebug(virtmicLog) << QString("Added: %1")
|
||||
.arg(QString::fromStdString(name))
|
||||
.toUtf8()
|
||||
.data();
|
||||
void Virtmic::setTarget(QString target) {
|
||||
senderr.get()->send<Virtmic::set_target>({target});
|
||||
}
|
||||
|
||||
if (!nodes.count(global.id)) {
|
||||
nodes.emplace(global.id, node.info());
|
||||
link(target, core);
|
||||
}
|
||||
}
|
||||
if (global.type == pipewire::port::type) {
|
||||
auto port = reg.bind<pipewire::port>(global.id);
|
||||
auto info = port.info();
|
||||
void Virtmic::getTargets() { senderr.get()->send<Virtmic::get_targets>(); }
|
||||
|
||||
if (info.props.count("node.id")) {
|
||||
auto node_id = std::stoul(info.props["node.id"]);
|
||||
|
||||
if (node_id == virtual_mic.id() &&
|
||||
info.direction == pipewire::port_direction::input) {
|
||||
if (info.props["audio.channel"] == "FL") {
|
||||
virt_fl = std::make_unique<pipewire::port>(std::move(port));
|
||||
} else {
|
||||
virt_fr = std::make_unique<pipewire::port>(std::move(port));
|
||||
}
|
||||
} else {
|
||||
ports.emplace(global.id, std::move(port));
|
||||
}
|
||||
|
||||
link(target, core);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
reg_events.on<pipewire::registry_event::global_removed>(
|
||||
[&](const std::uint32_t id) {
|
||||
if (nodes.count(id)) {
|
||||
auto info = nodes.at(id);
|
||||
std::string name = getTarget(info.props);
|
||||
if (name == nullstr)
|
||||
return;
|
||||
qDebug(virtmicLog) << QString("Removed: %1")
|
||||
.arg(QString::fromStdString(name))
|
||||
.toUtf8()
|
||||
.data();
|
||||
nodes.erase(id);
|
||||
}
|
||||
if (ports.count(id)) {
|
||||
ports.erase(id);
|
||||
}
|
||||
if (links.count(id)) {
|
||||
links.erase(id);
|
||||
}
|
||||
});
|
||||
Virtmic::Virtmic(pipewire::receiver<set_target, terminate> receiver,
|
||||
cr::sender<new_targets> sender) {
|
||||
m_receiver = std::make_unique<pipewire::receiver<set_target, terminate>>(
|
||||
std::move(receiver));
|
||||
m_sender = std::make_unique<cr::sender<new_targets>>(std::move(sender));
|
||||
virtual_mic = std::make_unique<pipewire::proxy>(
|
||||
std::move(*core.create("adapter",
|
||||
{{"node.name", "discord-screenaudio-virtmic"},
|
||||
{"media.class", "Audio/Source/Virtual"},
|
||||
{"factory.name", "support.null-audio-sink"},
|
||||
{"audio.channels", "2"},
|
||||
{"audio.position", "FL,FR"}},
|
||||
pipewire::node::type, pipewire::node::version,
|
||||
pipewire::update_strategy::none)
|
||||
.get()));
|
||||
metadata_listener.on<pipewire::registry_event::global>(
|
||||
[&](const auto &global) { globalEvent(global); });
|
||||
metadata_listener.on<pipewire::registry_event::global_removed>(
|
||||
[&](const std::uint32_t id) { globalRemovedEvent(id); });
|
||||
}
|
||||
|
||||
void Virtmic::run() {
|
||||
while (true) {
|
||||
main_loop.run();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Virtmic
|
||||
void Virtmic::link() {
|
||||
for (const auto &[port_id, port] : ports) {
|
||||
auto port_info = port.info();
|
||||
if (!virt_fl || !virt_fr)
|
||||
continue;
|
||||
|
||||
if (links.count(port_id))
|
||||
continue;
|
||||
|
||||
if (port_info.direction == pipewire::port_direction::input)
|
||||
continue;
|
||||
|
||||
if (!port_info.props.count("node.id"))
|
||||
continue;
|
||||
|
||||
auto parent_id = std::stoul(port_info.props["node.id"]);
|
||||
|
||||
if (!nodes.count(parent_id))
|
||||
continue;
|
||||
|
||||
auto &parent = nodes[parent_id];
|
||||
QString name;
|
||||
if (parent.props.count("application.name") &&
|
||||
parent.props["application.name"] != "")
|
||||
name = QString::fromStdString(parent.props["application.name"]);
|
||||
else
|
||||
name = QString::fromStdString(parent.props["application.process.binary"]);
|
||||
|
||||
if (name == target ||
|
||||
(target == "[All Desktop Audio]" && !EXCLUDE_TARGETS.contains(name))) {
|
||||
auto fl = port_info.props["audio.channel"] == "FL";
|
||||
links.emplace(port_id,
|
||||
*core.create_simple<pipewire::link>(fl ? virt_fl->info().id
|
||||
: virt_fr->info().id,
|
||||
port_id)
|
||||
.get());
|
||||
qDebug(virtmicLog) << QString("Link: %1:%2 -> %3")
|
||||
.arg(name)
|
||||
.arg(port_id)
|
||||
.arg(fl ? virt_fl->info().id
|
||||
: virt_fr->info().id)
|
||||
.toUtf8()
|
||||
.data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Virtmic::unlink() { links.clear(); }
|
||||
|
||||
void Virtmic::globalEvent(const pipewire::global &global) {
|
||||
if (global.type == pipewire::node::type) {
|
||||
auto node = *reg.bind<pipewire::node>(global.id).get();
|
||||
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;
|
||||
qDebug(virtmicLog) << QString("Added: %1")
|
||||
.arg(QString::fromStdString(name))
|
||||
.toUtf8()
|
||||
.data();
|
||||
|
||||
if (!nodes.count(global.id)) {
|
||||
nodes.emplace(global.id, node.info());
|
||||
link();
|
||||
}
|
||||
}
|
||||
if (global.type == pipewire::port::type) {
|
||||
auto port = *reg.bind<pipewire::port>(global.id).get();
|
||||
auto info = port.info();
|
||||
|
||||
if (info.props.count("node.id")) {
|
||||
auto node_id = std::stoul(info.props["node.id"]);
|
||||
|
||||
if (node_id == virtual_mic.get()->id() &&
|
||||
info.direction == pipewire::port_direction::input) {
|
||||
if (info.props["audio.channel"] == "FL") {
|
||||
virt_fl = std::make_unique<pipewire::port>(std::move(port));
|
||||
} else {
|
||||
virt_fr = std::make_unique<pipewire::port>(std::move(port));
|
||||
}
|
||||
} else {
|
||||
ports.emplace(global.id, std::move(port));
|
||||
}
|
||||
|
||||
link();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Virtmic::globalRemovedEvent(const std::uint32_t id) {
|
||||
if (nodes.count(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")
|
||||
.arg(QString::fromStdString(name))
|
||||
.toUtf8()
|
||||
.data();
|
||||
nodes.erase(id);
|
||||
}
|
||||
if (ports.count(id)) {
|
||||
ports.erase(id);
|
||||
}
|
||||
if (links.count(id)) {
|
||||
links.erase(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <cr/channel.hpp>
|
||||
#include <iostream>
|
||||
#include <rohrkabel/channel/channel.hpp>
|
||||
#include <rohrkabel/main_loop.hpp>
|
||||
#include <rohrkabel/registry/registry.hpp>
|
||||
|
||||
namespace Virtmic {
|
||||
#include <QMap>
|
||||
#include <QScopedPointer>
|
||||
#include <QStringList>
|
||||
#include <QThread>
|
||||
|
||||
QVector<QString> getTargets();
|
||||
void start(QString _target);
|
||||
class Virtmic : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static void setTarget(QString target);
|
||||
static void getTargets();
|
||||
|
||||
} // namespace Virtmic
|
||||
public:
|
||||
struct set_target {
|
||||
QString name;
|
||||
};
|
||||
struct get_targets {};
|
||||
struct terminate {};
|
||||
struct new_targets {
|
||||
QStringList targets;
|
||||
};
|
||||
|
||||
protected:
|
||||
static void instance();
|
||||
|
||||
protected:
|
||||
Virtmic(pipewire::receiver<set_target, terminate> receiver,
|
||||
cr::sender<new_targets> sender);
|
||||
void run();
|
||||
|
||||
private:
|
||||
std::unique_ptr<pipewire::receiver<set_target, terminate>> m_receiver;
|
||||
std::unique_ptr<cr::sender<new_targets>> m_sender;
|
||||
|
||||
const QStringList EXCLUDE_TARGETS{"Chromium input", "discord-screenaudio"};
|
||||
QString target;
|
||||
|
||||
pipewire::main_loop main_loop = pipewire::main_loop();
|
||||
pipewire::context context = pipewire::context(main_loop);
|
||||
pipewire::core core = pipewire::core(context);
|
||||
pipewire::registry reg = pipewire::registry(core);
|
||||
|
||||
pipewire::registry_listener metadata_listener =
|
||||
reg.listen<pipewire::registry_listener>();
|
||||
std::unique_ptr<pipewire::proxy> virtual_mic;
|
||||
|
||||
std::map<uint32_t, pipewire::port> ports;
|
||||
std::unique_ptr<pipewire::port> virt_fl, virt_fr;
|
||||
std::map<uint32_t, pipewire::node_info> nodes;
|
||||
std::map<uint32_t, pipewire::link> links;
|
||||
|
||||
void link();
|
||||
void unlink();
|
||||
void globalEvent(const pipewire::global &global);
|
||||
void globalRemovedEvent(const std::uint32_t id);
|
||||
};
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 62b2acebe6806c7b0e2ca6a43c6b2419a627b8dc
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 6977815409b4c3c02d74a7aee3fc29f01d632feb
|
|
@ -1 +1 @@
|
|||
Subproject commit 04bfb921c44fb0d2337df70f5660899bc8d2844f
|
||||
Subproject commit 8a7705be070190a88b9a9d3619fa2fb7eabc951e
|
|
@ -1,2 +0,0 @@
|
|||
flatpak remove lol.deadzone.discord-awesomeaudio
|
||||
rm ~/.local/share/applications/discord-awesomeaudio.desktop
|
Loading…
Reference in New Issue