Compare commits
52 Commits
v1.0.0-rc.
...
testing_ha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdabf68c9b | ||
|
|
7c8f72b8d8 | ||
|
|
27cdd9f9a5 | ||
|
|
d184823ee9 | ||
|
|
dd2beed4eb | ||
|
|
3740553aba | ||
|
|
555f5c62f5 | ||
|
|
af3c62c263 | ||
|
|
c4f6c33f9d | ||
|
|
758ffa8cb5 | ||
|
|
753da8b1d3 | ||
|
|
5f37ab314f | ||
|
|
c7bff3f428 | ||
|
|
34e23f7187 | ||
|
|
c325dd6798 | ||
|
|
b42fde85d1 | ||
|
|
cc379ada85 | ||
|
|
fdb6145bbd | ||
|
|
8bd4d1896a | ||
|
|
1a2d09fe3d | ||
|
|
f01c941c90 | ||
|
|
95d391e869 | ||
|
|
af107168db | ||
|
|
e7e6c5d82b | ||
|
|
ee0c66762d | ||
|
|
c3483a42db | ||
|
|
d68797cebb | ||
|
|
ef221d4574 | ||
|
|
3fa2e3fa3c | ||
|
|
bda1bc0df6 | ||
|
|
cb0f084d42 | ||
|
|
674ac146f2 | ||
|
|
1ab00ee774 | ||
|
|
38bcc30ed4 | ||
|
|
204439af94 | ||
|
|
8fc529102c | ||
|
|
a7a8346b63 | ||
|
|
88b129f863 | ||
|
|
f4fc2e1f19 | ||
|
|
e1b35e3ca8 | ||
|
|
a773795e38 | ||
|
|
c8695ca5ff | ||
|
|
bc23d3423a | ||
|
|
f5217dfdfa | ||
|
|
6ef16b9625 | ||
|
|
b62f1bab1f | ||
|
|
5f9211f98f | ||
|
|
cfbb4152c3 | ||
|
|
fa83e2d9a8 | ||
|
|
58ecbbc6f4 | ||
|
|
77300ed178 | ||
|
|
99df18ef89 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/build
|
/build
|
||||||
|
.vscode
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(discord-screenaudio)
|
project(discord-screenaudio)
|
||||||
|
|
||||||
|
include(version.cmake)
|
||||||
|
determine_version("${CMAKE_CURRENT_SOURCE_DIR}" DISCORD_SCEENAUDIO)
|
||||||
|
add_definitions( -DDISCORD_SCEENAUDIO_VERSION_FULL="${DISCORD_SCEENAUDIO_VERSION_FULL}" )
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||||
|
string(TIMESTAMP TIMESTAMP %s)
|
||||||
# set(CMAKE_AUTOUIC ON)
|
# set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
find_package(Qt5 CONFIG REQUIRED COMPONENTS
|
find_package(Qt5 CONFIG REQUIRED COMPONENTS
|
||||||
@@ -13,12 +18,28 @@ find_package(Qt5 CONFIG REQUIRED COMPONENTS
|
|||||||
WebEngineWidgets
|
WebEngineWidgets
|
||||||
)
|
)
|
||||||
|
|
||||||
|
find_package(KF5Notifications)
|
||||||
|
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
|
set(discord-screenaudio_SRC
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/mainwindow.cpp
|
src/mainwindow.cpp
|
||||||
src/virtmic.cpp
|
src/virtmic.cpp
|
||||||
src/discordpage.cpp
|
src/discordpage.cpp
|
||||||
src/streamdialog.cpp
|
src/streamdialog.cpp
|
||||||
|
src/log.cpp
|
||||||
resources.qrc
|
resources.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,5 +68,19 @@ add_executable(discord-screenaudio ${discord-screenaudio_SRC})
|
|||||||
|
|
||||||
target_link_libraries(discord-screenaudio Qt5::Widgets Qt5::WebEngineWidgets rohrkabel)
|
target_link_libraries(discord-screenaudio Qt5::Widgets Qt5::WebEngineWidgets rohrkabel)
|
||||||
|
|
||||||
|
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(TARGETS discord-screenaudio DESTINATION bin)
|
||||||
install(PROGRAMS assets/discord-screenaudio.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
|
install(FILES assets/de.shorsh.discord-screenaudio.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps)
|
||||||
|
install(PROGRAMS assets/de.shorsh.discord-screenaudio.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
|
||||||
|
configure_file(assets/de.shorsh.discord-screenaudio.metainfo.xml.in de.shorsh.discord-screenaudio.metainfo.xml)
|
||||||
|
install(FILES ${CMAKE_BINARY_DIR}/de.shorsh.discord-screenaudio.metainfo.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo)
|
||||||
|
|||||||
53
README.md
53
README.md
@@ -1,25 +1,49 @@
|
|||||||
# discord-screenaudio
|
# discord-screenaudio
|
||||||
|
|
||||||
A very WIP custom discord client that supports streaming with audio on Linux,
|
A custom discord client that supports streaming with audio on Linux, made
|
||||||
made possible by the
|
possible by the
|
||||||
[great work](https://github.com/edisionnano/Screenshare-with-audio-on-Discord-with-Linux)
|
[great work](https://github.com/edisionnano/Screenshare-with-audio-on-Discord-with-Linux)
|
||||||
of [@edisionnano](https://github.com/edisionnano) and the
|
of [@edisionnano](https://github.com/edisionnano) and the
|
||||||
[Rohrkabel library](https://github.com/Soundux/rohrkabel) by
|
[Rohrkabel library](https://github.com/Soundux/rohrkabel) by
|
||||||
[@Curve](https://github.com/Curve).
|
[@Curve](https://github.com/Curve).
|
||||||
|
|
||||||

|
Unlike a lot of other solutions, the audio here is directly fed into the
|
||||||
|
screenshare and not passed to the user microphone
|
||||||
|
([see explanation](#how-it-works)).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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
|
||||||
|
case you want to stream something, maybe used with a second account. For
|
||||||
|
anything else, this client has way too many things that work less well than in
|
||||||
|
the original client.
|
||||||
|
|
||||||
|
Technically this could be against
|
||||||
|
[Discord's TOS](https://discord.com/terms#software-in-discord%E2%80%99s-services),
|
||||||
|
so be warned. Discord probably won't ban you for using this, but if they do I
|
||||||
|
told you and it's not my fault.
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
- Only works with **PipeWire**
|
- Only works with **PipeWire**
|
||||||
- Only works on **X11**
|
- Can only share primary screen on X11 (no other screens or specific
|
||||||
- Can only share primary screen (no other screens or specific applications) (see [#1](https://github.com/maltejur/discord-screenaudio/issues/1))
|
applications) (see
|
||||||
|
[#1](https://github.com/maltejur/discord-screenaudio/issues/1))
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
If you are using Arch, you can build and install
|
You have multiple options:
|
||||||
[from the AUR](https://aur.archlinux.org/packages/discord-screenaudio). If you
|
|
||||||
aren't, you will have to [build it yourself](#building-from-source).
|
- Install the Flatpak (recommended):
|
||||||
|
`flatpak install de.shorsh.discord-screenaudio`
|
||||||
|
- If you are on Arch, you can build and install
|
||||||
|
[`discord-screenaudio`](https://aur.archlinux.org/packages/discord-screenaudio)
|
||||||
|
from the AUR
|
||||||
|
- If you are on openSUSE, you can use the
|
||||||
|
[Open Build Service package](https://software.opensuse.org//download.html?project=games%3Atools&package=discord-screenaudio)
|
||||||
|
by [@VortexAcherontic](https://github.com/VortexAcherontic)
|
||||||
|
- You can [build it yourself](#building-from-source)
|
||||||
|
|
||||||
## Building from Source
|
## Building from Source
|
||||||
|
|
||||||
@@ -30,13 +54,22 @@ aren't, you will have to [build it yourself](#building-from-source).
|
|||||||
- Qt5 and QtWebEngine
|
- Qt5 and QtWebEngine
|
||||||
- **PipeWire** (it currently doesn't work with PulseAudio)
|
- **PipeWire** (it currently doesn't work with PulseAudio)
|
||||||
- Git
|
- Git
|
||||||
|
- _Kf5Notifications (optional, for better notifications)_
|
||||||
|
- _KXMLGui and KGlobalAccel (optional, for keybinds)_
|
||||||
|
|
||||||
On Debian:
|
On Debian:
|
||||||
`apt install -y build-essential cmake qtbase5-dev qtwebengine5-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
|
||||||
|
|
||||||
To build the program, run this in the source directory:
|
First, clone the repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/maltejur/discord-screenaudio.git
|
||||||
|
cd discord-screenaudio
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, to build the program, run this in the source directory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cmake -B build
|
cmake -B build
|
||||||
|
|||||||
8
assets/de.shorsh.discord-screenaudio.desktop
Normal file
8
assets/de.shorsh.discord-screenaudio.desktop
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=discord-screenaudio
|
||||||
|
Exec=discord-screenaudio
|
||||||
|
Comment=A custom discord client that supports streaming with audio on Linux.
|
||||||
|
Icon=de.shorsh.discord-screenaudio
|
||||||
|
Categories=Network;InstantMessaging;
|
||||||
|
Terminal=false
|
||||||
50
assets/de.shorsh.discord-screenaudio.metainfo.xml.in
Normal file
50
assets/de.shorsh.discord-screenaudio.metainfo.xml.in
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component type="desktop-application">
|
||||||
|
<id>de.shorsh.discord-screenaudio</id>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>GPL-3.0+</project_license>
|
||||||
|
<name>discord-screenaudio</name>
|
||||||
|
<releases>
|
||||||
|
<release version="${DISCORD_SCEENAUDIO_VERSION_FULL}" timestamp="${TIMESTAMP}" />
|
||||||
|
</releases>
|
||||||
|
|
||||||
|
<summary>A custom discord client that supports streaming with audio on Linux</summary>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
<p>A custom discord client that supports streaming with audio on Linux, made possible by the great work of @edisionnano and the Rohrkabel library by @Curve.</p>
|
||||||
|
<p>Unlike a lot of other solutions, the audio here is directly fed into the screenshare and not passed to the user microphone.</p>
|
||||||
|
<p>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 case you want to stream something, maybe used with a second account. For anything else, this client has way too many things that work less well than in the original client.</p>
|
||||||
|
<p>Technically this could be against Discord's TOS, so be warned. Discord probably won't ban you for using this, but if they do I told you and it's not my fault.</p>
|
||||||
|
<p>Known issues:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Only works with PipeWire</li>
|
||||||
|
<li>Can only share primary screen on X11</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<launchable type="desktop-id">
|
||||||
|
de.shorsh.discord-screenaudio.desktop
|
||||||
|
</launchable>
|
||||||
|
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<image>
|
||||||
|
https://user-images.githubusercontent.com/48161361/192137080-33466cf7-8c56-4373-90c6-01ea74b6fb83.png
|
||||||
|
</image>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
|
||||||
|
<url type="homepage">https://github.com/maltejur/discord-screenaudio</url>
|
||||||
|
|
||||||
|
<provides>
|
||||||
|
<binary>discord-screenaudio</binary>
|
||||||
|
</provides>
|
||||||
|
|
||||||
|
<content_rating type="oars-1.0">
|
||||||
|
<content_attribute id="social-chat">intense</content_attribute>
|
||||||
|
<content_attribute id="social-info">intense</content_attribute>
|
||||||
|
<content_attribute id="social-audio">intense</content_attribute>
|
||||||
|
<content_attribute id="social-contacts">intense</content_attribute>
|
||||||
|
<content_attribute id="money-purchasing">mild</content_attribute>
|
||||||
|
</content_rating>
|
||||||
|
</component>
|
||||||
BIN
assets/de.shorsh.discord-screenaudio.png
Normal file
BIN
assets/de.shorsh.discord-screenaudio.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -1,6 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Name=discord-screenaudio
|
|
||||||
Exec=discord-screenaudio
|
|
||||||
Icon=discord
|
|
||||||
Terminal=false
|
|
||||||
8
assets/discord-screenaudio.notifyrc
Normal file
8
assets/discord-screenaudio.notifyrc
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Global]
|
||||||
|
IconName=de.shorsh.discord-screenaudio
|
||||||
|
Name=discord-screenaudio
|
||||||
|
DesktopEntry=de.shorsh.discord-screenaudio.desktop
|
||||||
|
|
||||||
|
[Event/discordNotification]
|
||||||
|
Name=Discord Notification
|
||||||
|
Action=Popup
|
||||||
@@ -3,12 +3,25 @@
|
|||||||
navigator.mediaDevices.chromiumGetDisplayMedia =
|
navigator.mediaDevices.chromiumGetDisplayMedia =
|
||||||
navigator.mediaDevices.getDisplayMedia;
|
navigator.mediaDevices.getDisplayMedia;
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
const getAudioDevice = async (nameOfAudioDevice) => {
|
const getAudioDevice = async (nameOfAudioDevice) => {
|
||||||
await navigator.mediaDevices.getUserMedia({
|
await navigator.mediaDevices.getUserMedia({
|
||||||
audio: true,
|
audio: true,
|
||||||
});
|
});
|
||||||
let devices = await navigator.mediaDevices.enumerateDevices();
|
let audioDevice;
|
||||||
let audioDevice = devices.find(({ label }) => label === nameOfAudioDevice);
|
while (audioDevice === undefined) {
|
||||||
|
let devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
audioDevice = devices.find(({ label }) => label === nameOfAudioDevice);
|
||||||
|
if (!audioDevice)
|
||||||
|
console.log(
|
||||||
|
`dsa: Did not find '${nameOfAudioDevice}', trying again in 100ms`
|
||||||
|
);
|
||||||
|
await sleep(100);
|
||||||
|
}
|
||||||
|
console.log(`dsa: Found '${nameOfAudioDevice}'`);
|
||||||
return audioDevice;
|
return audioDevice;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,11 +94,16 @@ setInterval(() => {
|
|||||||
});
|
});
|
||||||
hiddenElements.length = 0;
|
hiddenElements.length = 0;
|
||||||
} else {
|
} else {
|
||||||
for (const el of document.querySelectorAll(
|
for (const el of [
|
||||||
'[aria-label="Share Your Screen"]'
|
document.getElementsByClassName("actionButtons-2vEOUh")?.[0]?.children[1],
|
||||||
)) {
|
document.querySelector(
|
||||||
|
".wrapper-3t3Yqv > div > div > div > div > .controlButton-2PMNom"
|
||||||
|
),
|
||||||
|
]) {
|
||||||
|
if (!el) continue;
|
||||||
|
if (el.classList.contains("discord-screenaudio-cloned")) continue;
|
||||||
|
el.classList.add("discord-screenaudio-cloned");
|
||||||
elClone = el.cloneNode(true);
|
elClone = el.cloneNode(true);
|
||||||
elClone.ariaLabel = "Share Your Screen with Audio";
|
|
||||||
elClone.title = "Share Your Screen with Audio";
|
elClone.title = "Share Your Screen with Audio";
|
||||||
elClone.addEventListener("click", () => {
|
elClone.addEventListener("click", () => {
|
||||||
console.log("!discord-screenaudio-start-stream");
|
console.log("!discord-screenaudio-start-stream");
|
||||||
@@ -94,6 +112,7 @@ setInterval(() => {
|
|||||||
const initialDisplay = el.style.display;
|
const initialDisplay = el.style.display;
|
||||||
|
|
||||||
window.discordScreenaudioStartStream = (width, height, frameRate) => {
|
window.discordScreenaudioStartStream = (width, height, frameRate) => {
|
||||||
|
window.discordScreenaudioResolutionString = `${height}p ${frameRate}FPS`;
|
||||||
setGetDisplayMedia({
|
setGetDisplayMedia({
|
||||||
audio: true,
|
audio: true,
|
||||||
video: { width, height, frameRate },
|
video: { width, height, frameRate },
|
||||||
@@ -116,11 +135,21 @@ setInterval(() => {
|
|||||||
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")) {
|
||||||
const aboutEl = document.createElement("div");
|
let aboutEl;
|
||||||
aboutEl.innerText = "discord-screenaudio v1.0.0-rc.6";
|
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.fontSize = "12px";
|
||||||
aboutEl.style.color = "var(--text-muted)";
|
aboutEl.style.color = "var(--text-muted)";
|
||||||
|
aboutEl.style.textTransform = "none";
|
||||||
aboutEl.classList.add("dirscordScreenaudioAboutText");
|
aboutEl.classList.add("dirscordScreenaudioAboutText");
|
||||||
|
aboutEl.style.cursor = "pointer";
|
||||||
el.appendChild(aboutEl);
|
el.appendChild(aboutEl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,4 +157,50 @@ setInterval(() => {
|
|||||||
// Remove stream settings if stream is active
|
// Remove stream settings if stream is active
|
||||||
document.getElementById("manage-streams-change-windows")?.remove();
|
document.getElementById("manage-streams-change-windows")?.remove();
|
||||||
document.querySelector(`[aria-label="Stream Settings"]`)?.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"
|
||||||
|
)) {
|
||||||
|
el.innerHTML = window.discordScreenaudioResolutionString;
|
||||||
|
}
|
||||||
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
|
// Fix for broken discord notifications after restart
|
||||||
|
// (https://github.com/maltejur/discord-screenaudio/issues/17)
|
||||||
|
Notification.requestPermission();
|
||||||
|
|||||||
@@ -2,5 +2,6 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource>
|
<qresource>
|
||||||
<file>assets/userscript.js</file>
|
<file>assets/userscript.js</file>
|
||||||
|
<file>assets/de.shorsh.discord-screenaudio.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
@@ -1,9 +1,26 @@
|
|||||||
#include "discordpage.h"
|
#include "discordpage.h"
|
||||||
|
#include "log.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>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QNetworkReply>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWebChannel>
|
#include <QWebChannel>
|
||||||
#include <QWebEngineScript>
|
#include <QWebEngineScript>
|
||||||
@@ -17,6 +34,11 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
|
|||||||
connect(this, &QWebEnginePage::featurePermissionRequested, this,
|
connect(this, &QWebEnginePage::featurePermissionRequested, this,
|
||||||
&DiscordPage::featurePermissionRequested);
|
&DiscordPage::featurePermissionRequested);
|
||||||
|
|
||||||
|
connect(this, &QWebEnginePage::loadStarted, [=]() {
|
||||||
|
runJavaScript(QString("window.discordScreenaudioVersion = '%1';")
|
||||||
|
.arg(QApplication::applicationVersion()));
|
||||||
|
});
|
||||||
|
|
||||||
settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
|
settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
|
||||||
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
|
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
|
||||||
settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent,
|
settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent,
|
||||||
@@ -26,35 +48,94 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
|
|||||||
settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
|
settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
|
||||||
settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
|
settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
|
||||||
false);
|
false);
|
||||||
|
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
|
||||||
|
settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true);
|
||||||
|
|
||||||
setUrl(QUrl("https://discord.com/app"));
|
setUrl(QUrl("https://discord.com/app"));
|
||||||
|
|
||||||
injectScript(":/assets/userscript.js");
|
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,
|
connect(&m_streamDialog, &StreamDialog::requestedStreamStart, this,
|
||||||
&DiscordPage::startStream);
|
&DiscordPage::startStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscordPage::injectScript(QString source) {
|
void DiscordPage::injectScriptText(QString name, QString content) {
|
||||||
qDebug() << "[main ] Injecting " << source;
|
qDebug(mainLog) << "Injecting " << name;
|
||||||
|
|
||||||
QFile userscript(source);
|
QWebEngineScript script;
|
||||||
|
|
||||||
if (!userscript.open(QIODevice::ReadOnly)) {
|
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 file(source);
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
qFatal("Failed to load %s with error: %s", source.toLatin1().constData(),
|
qFatal("Failed to load %s with error: %s", source.toLatin1().constData(),
|
||||||
userscript.errorString().toLatin1().constData());
|
file.errorString().toLatin1().constData());
|
||||||
} else {
|
} else {
|
||||||
QByteArray userscriptJs = userscript.readAll();
|
injectScriptText(name, file.readAll());
|
||||||
|
|
||||||
QWebEngineScript script;
|
|
||||||
|
|
||||||
script.setSourceCode(userscriptJs);
|
|
||||||
script.setName("userscript.js");
|
|
||||||
script.setWorldId(QWebEngineScript::MainWorld);
|
|
||||||
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
|
|
||||||
script.setRunsOnSubFrames(false);
|
|
||||||
|
|
||||||
scripts().insert(script);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,8 +147,8 @@ void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
|
|||||||
|
|
||||||
if (feature == QWebEnginePage::Feature::MediaAudioCapture) {
|
if (feature == QWebEnginePage::Feature::MediaAudioCapture) {
|
||||||
if (m_virtmicProcess.state() == QProcess::NotRunning) {
|
if (m_virtmicProcess.state() == QProcess::NotRunning) {
|
||||||
qDebug() << "[virtmic] 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_virtmicProcess.start(QApplication::arguments()[0],
|
||||||
{"--virtmic", "None"});
|
{"--virtmic", "None"});
|
||||||
}
|
}
|
||||||
@@ -77,7 +158,6 @@ void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
|
|||||||
bool DiscordPage::acceptNavigationRequest(const QUrl &url,
|
bool DiscordPage::acceptNavigationRequest(const QUrl &url,
|
||||||
QWebEnginePage::NavigationType type,
|
QWebEnginePage::NavigationType type,
|
||||||
bool isMainFrame) {
|
bool isMainFrame) {
|
||||||
qDebug() << url;
|
|
||||||
if (type == QWebEnginePage::NavigationTypeLinkClicked) {
|
if (type == QWebEnginePage::NavigationTypeLinkClicked) {
|
||||||
QDesktopServices::openUrl(url);
|
QDesktopServices::openUrl(url);
|
||||||
return false;
|
return false;
|
||||||
@@ -85,9 +165,21 @@ bool DiscordPage::acceptNavigationRequest(const QUrl &url,
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool ExternalPage::acceptNavigationRequest(const QUrl &url,
|
||||||
|
QWebEnginePage::NavigationType type,
|
||||||
|
bool isMainFrame) {
|
||||||
|
QDesktopServices::openUrl(url);
|
||||||
|
deleteLater();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWebEnginePage *DiscordPage::createWindow(QWebEnginePage::WebWindowType type) {
|
||||||
|
return new ExternalPage;
|
||||||
|
}
|
||||||
|
|
||||||
void DiscordPage::stopVirtmic() {
|
void DiscordPage::stopVirtmic() {
|
||||||
if (m_virtmicProcess.state() == QProcess::Running) {
|
if (m_virtmicProcess.state() == QProcess::Running) {
|
||||||
qDebug() << "[virtmic] Stopping Virtmic";
|
qDebug(virtmicLog) << "Stopping Virtmic";
|
||||||
m_virtmicProcess.kill();
|
m_virtmicProcess.kill();
|
||||||
m_virtmicProcess.waitForFinished();
|
m_virtmicProcess.waitForFinished();
|
||||||
}
|
}
|
||||||
@@ -95,7 +187,7 @@ void DiscordPage::stopVirtmic() {
|
|||||||
|
|
||||||
void DiscordPage::startVirtmic(QString target) {
|
void DiscordPage::startVirtmic(QString target) {
|
||||||
if (target != "None") {
|
if (target != "None") {
|
||||||
qDebug() << "[virtmic] Starting Virtmic with target" << target;
|
qDebug(virtmicLog) << "Starting Virtmic with target" << target;
|
||||||
m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target});
|
m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,8 +203,30 @@ void DiscordPage::javaScriptConsoleMessage(
|
|||||||
m_streamDialog.updateTargets();
|
m_streamDialog.updateTargets();
|
||||||
} else if (message == "!discord-screenaudio-stream-stopped") {
|
} else if (message == "!discord-screenaudio-stream-stopped") {
|
||||||
stopVirtmic();
|
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 {
|
} else {
|
||||||
qDebug() << "[discord]" << message;
|
qDebug(discordLog) << message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,3 +242,13 @@ void DiscordPage::startStream(QString target, uint width, uint height,
|
|||||||
.arg(frameRate));
|
.arg(frameRate));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DiscordPage::toggleMute() {
|
||||||
|
qDebug(shortcutLog) << "Toggling mute";
|
||||||
|
runJavaScript("window.discordScreenaudioToggleMute();");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiscordPage::toggleDeafen() {
|
||||||
|
qDebug(shortcutLog) << "Toggling deafen";
|
||||||
|
runJavaScript("window.discordScreenaudioToggleDeafen();");
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,14 @@
|
|||||||
#include "streamdialog.h"
|
#include "streamdialog.h"
|
||||||
#include "virtmic.h"
|
#include "virtmic.h"
|
||||||
|
|
||||||
|
#ifdef KXMLGUI
|
||||||
|
#include <KActionCollection>
|
||||||
|
#include <KHelpMenu>
|
||||||
|
#include <KShortcutsDialog>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QWebEngineFullScreenRequest>
|
||||||
#include <QWebEnginePage>
|
#include <QWebEnginePage>
|
||||||
|
|
||||||
class DiscordPage : public QWebEnginePage {
|
class DiscordPage : public QWebEnginePage {
|
||||||
@@ -15,19 +22,42 @@ public:
|
|||||||
private:
|
private:
|
||||||
StreamDialog m_streamDialog;
|
StreamDialog m_streamDialog;
|
||||||
QProcess m_virtmicProcess;
|
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;
|
||||||
|
QWebEnginePage *createWindow(QWebEnginePage::WebWindowType type) override;
|
||||||
void
|
void
|
||||||
javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level,
|
javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level,
|
||||||
const QString &message, int lineNumber,
|
const QString &message, int lineNumber,
|
||||||
const QString &sourceID) override;
|
const QString &sourceID) override;
|
||||||
void injectScript(QString source);
|
void injectScriptText(QString name, QString content);
|
||||||
|
void injectScriptFile(QString name, QString source);
|
||||||
void stopVirtmic();
|
void stopVirtmic();
|
||||||
void startVirtmic(QString target);
|
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(QString target, uint width, uint height, uint frameRate);
|
void startStream(QString target, uint width, uint height, uint frameRate);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Will immediately get destroyed again but is needed for navigation to
|
||||||
|
// target="_blank" links, since QWebEnginePage::newWindowRequested is
|
||||||
|
// only available sinec Qt 6.3.
|
||||||
|
class ExternalPage : public QWebEnginePage {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool acceptNavigationRequest(const QUrl &url,
|
||||||
|
QWebEnginePage::NavigationType type,
|
||||||
|
bool isMainFrame) override;
|
||||||
|
};
|
||||||
|
|||||||
7
src/log.cpp
Normal file
7
src/log.cpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
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");
|
||||||
9
src/log.h
Normal file
9
src/log.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
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);
|
||||||
31
src/main.cpp
31
src/main.cpp
@@ -1,13 +1,23 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "virtmic.h"
|
#include "virtmic.h"
|
||||||
|
|
||||||
|
#ifdef KXMLGUI
|
||||||
|
#include <KAboutData>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
QApplication::setApplicationName("discord-screenaudio");
|
QApplication::setApplicationName("discord-screenaudio");
|
||||||
QApplication::setApplicationVersion("1.0.0-rc.6");
|
QApplication::setWindowIcon(
|
||||||
|
QIcon(":assets/de.shorsh.discord-screenaudio.png"));
|
||||||
|
QApplication::setApplicationVersion(DISCORD_SCEENAUDIO_VERSION_FULL);
|
||||||
|
QApplication::setDesktopFileName("de.shorsh.discord-screenaudio");
|
||||||
|
|
||||||
|
qSetMessagePattern("[%{category}] %{message}");
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(
|
parser.setApplicationDescription(
|
||||||
@@ -17,16 +27,27 @@ int main(int argc, char *argv[]) {
|
|||||||
QCommandLineOption virtmicOption("virtmic", "Start the Virtual Microphone",
|
QCommandLineOption virtmicOption("virtmic", "Start the Virtual Microphone",
|
||||||
"target");
|
"target");
|
||||||
parser.addOption(virtmicOption);
|
parser.addOption(virtmicOption);
|
||||||
#ifdef DEBUG
|
QCommandLineOption degubOption("remote-debugging",
|
||||||
parser.addOption(QCommandLineOption(
|
"Open Chromium Remote Debugging on port 9222");
|
||||||
"remote-debugging-port", "Chromium Remote Debugging Port", "port"));
|
parser.addOption(degubOption);
|
||||||
#endif
|
|
||||||
parser.process(app);
|
parser.process(app);
|
||||||
|
|
||||||
if (parser.isSet(virtmicOption)) {
|
if (parser.isSet(virtmicOption)) {
|
||||||
Virtmic::start(parser.value(virtmicOption));
|
Virtmic::start(parser.value(virtmicOption));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qputenv("QTWEBENGINE_CHROMIUM_FLAGS",
|
||||||
|
"--ignore-gpu-blacklist --enable-gpu-rasterization "
|
||||||
|
"--enable-native-gpu-memory-buffers --num-raster-threads=4 "
|
||||||
|
"--enable-features=WebRTCPipeWireCapturer " +
|
||||||
|
qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"));
|
||||||
|
|
||||||
|
if (parser.isSet(degubOption))
|
||||||
|
qputenv("QTWEBENGINE_CHROMIUM_FLAGS",
|
||||||
|
"--remote-debugging-port=9222 " +
|
||||||
|
qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"));
|
||||||
|
|
||||||
MainWindow w;
|
MainWindow w;
|
||||||
w.show();
|
w.show();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "virtmic.h"
|
#include "virtmic.h"
|
||||||
|
|
||||||
|
#ifdef KNOTIFICATIONS
|
||||||
|
#include <KNotification>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
@@ -11,20 +15,62 @@
|
|||||||
#include <QSpacerItem>
|
#include <QSpacerItem>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QWebEngineNotification>
|
||||||
|
#include <QWebEngineProfile>
|
||||||
#include <QWebEngineScript>
|
#include <QWebEngineScript>
|
||||||
#include <QWebEngineScriptCollection>
|
#include <QWebEngineScriptCollection>
|
||||||
#include <QWebEngineSettings>
|
#include <QWebEngineSettings>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
MainWindow *MainWindow::m_instance = nullptr;
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
||||||
|
assert(MainWindow::m_instance == nullptr);
|
||||||
|
MainWindow::m_instance = this;
|
||||||
setupWebView();
|
setupWebView();
|
||||||
resize(1000, 700);
|
resize(1000, 700);
|
||||||
showMaximized();
|
showMaximized();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setupWebView() {
|
void MainWindow::setupWebView() {
|
||||||
m_webView = new QWebEngineView(this);
|
|
||||||
auto page = new DiscordPage(this);
|
auto page = new DiscordPage(this);
|
||||||
|
connect(page, &QWebEnginePage::fullScreenRequested, this,
|
||||||
|
&MainWindow::fullScreenRequested);
|
||||||
|
|
||||||
|
m_webView = new QWebEngineView(this);
|
||||||
m_webView->setPage(page);
|
m_webView->setPage(page);
|
||||||
|
|
||||||
|
#ifdef KNOTIFICATIONS
|
||||||
|
QWebEngineProfile::defaultProfile()->setNotificationPresenter(
|
||||||
|
[&](std::unique_ptr<QWebEngineNotification> notificationInfo) {
|
||||||
|
KNotification *notification = new KNotification("discordNotification");
|
||||||
|
notification->setTitle(notificationInfo->title());
|
||||||
|
notification->setText(notificationInfo->message());
|
||||||
|
notification->setPixmap(QPixmap::fromImage(notificationInfo->icon()));
|
||||||
|
notification->setDefaultAction("View");
|
||||||
|
connect(notification, &KNotification::defaultActivated,
|
||||||
|
[&, notificationInfo = std::move(notificationInfo)]() {
|
||||||
|
notificationInfo->click();
|
||||||
|
activateWindow();
|
||||||
|
});
|
||||||
|
notification->sendEvent();
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
setCentralWidget(m_webView);
|
setCentralWidget(m_webView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::fullScreenRequested(
|
||||||
|
QWebEngineFullScreenRequest fullScreenRequest) {
|
||||||
|
fullScreenRequest.accept();
|
||||||
|
if (fullScreenRequest.toggleOn()) {
|
||||||
|
m_wasMaximized = isMaximized();
|
||||||
|
showFullScreen();
|
||||||
|
} else {
|
||||||
|
m_wasMaximized ? showMaximized() : showNormal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); }
|
||||||
|
|
||||||
|
MainWindow *MainWindow::instance() { return m_instance; }
|
||||||
|
|||||||
@@ -15,10 +15,17 @@ class MainWindow : public QMainWindow {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = nullptr);
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
|
static MainWindow *instance();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupWebView();
|
void setupWebView();
|
||||||
QWebEngineView *m_webView;
|
QWebEngineView *m_webView;
|
||||||
QWebEngineProfile *prepareProfile();
|
QWebEngineProfile *prepareProfile();
|
||||||
DiscordPage *m_discordPage;
|
DiscordPage *m_discordPage;
|
||||||
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
bool m_wasMaximized;
|
||||||
|
static MainWindow *m_instance;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,14 +13,25 @@ StreamDialog::StreamDialog() : QWidget() {
|
|||||||
setAttribute(Qt::WA_QuitOnClose, false);
|
setAttribute(Qt::WA_QuitOnClose, false);
|
||||||
|
|
||||||
auto layout = new QVBoxLayout(this);
|
auto layout = new QVBoxLayout(this);
|
||||||
|
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||||
|
|
||||||
auto targetLabel = new QLabel(this);
|
auto targetLabel = new QLabel(this);
|
||||||
targetLabel->setText("Which app do you want to stream sound from?");
|
targetLabel->setText("Which app do you want to stream sound from?");
|
||||||
layout->addWidget(targetLabel);
|
layout->addWidget(targetLabel);
|
||||||
|
|
||||||
|
auto targetHBox = new QHBoxLayout(this);
|
||||||
|
layout->addLayout(targetHBox);
|
||||||
|
|
||||||
m_targetComboBox = new QComboBox(this);
|
m_targetComboBox = new QComboBox(this);
|
||||||
updateTargets();
|
updateTargets();
|
||||||
layout->addWidget(m_targetComboBox);
|
targetHBox->addWidget(m_targetComboBox);
|
||||||
|
|
||||||
|
auto refreshTargetsButton = new QPushButton(this);
|
||||||
|
refreshTargetsButton->setFixedSize(30, 30);
|
||||||
|
refreshTargetsButton->setIcon(QIcon::fromTheme("view-refresh"));
|
||||||
|
connect(refreshTargetsButton, &QPushButton::clicked, this,
|
||||||
|
&StreamDialog::updateTargets);
|
||||||
|
targetHBox->addWidget(refreshTargetsButton);
|
||||||
|
|
||||||
auto qualityLabel = new QLabel(this);
|
auto qualityLabel = new QLabel(this);
|
||||||
qualityLabel->setText("Stream Quality");
|
qualityLabel->setText("Stream Quality");
|
||||||
@@ -57,7 +68,6 @@ StreamDialog::StreamDialog() : QWidget() {
|
|||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
setWindowTitle("discord-screenaudio Stream Dialog");
|
setWindowTitle("discord-screenaudio Stream Dialog");
|
||||||
setFixedSize(0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamDialog::startStream() {
|
void StreamDialog::startStream() {
|
||||||
@@ -70,9 +80,17 @@ void StreamDialog::startStream() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StreamDialog::updateTargets() {
|
void StreamDialog::updateTargets() {
|
||||||
|
auto lastTarget = m_targetComboBox->currentText();
|
||||||
|
|
||||||
m_targetComboBox->clear();
|
m_targetComboBox->clear();
|
||||||
m_targetComboBox->addItem("None");
|
m_targetComboBox->addItem("[None]");
|
||||||
|
m_targetComboBox->addItem("[All Desktop Audio]");
|
||||||
for (auto target : Virtmic::getTargets()) {
|
for (auto target : Virtmic::getTargets()) {
|
||||||
m_targetComboBox->addItem(target);
|
m_targetComboBox->addItem(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_targetComboBox->findText(lastTarget) != -1)
|
||||||
|
m_targetComboBox->setCurrentText(lastTarget);
|
||||||
|
else
|
||||||
|
m_targetComboBox->setCurrentText("[All Desktop Audio]");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
#include "virtmic.h"
|
#include "virtmic.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
#include <rohrkabel/loop/main.hpp>
|
#include <rohrkabel/loop/main.hpp>
|
||||||
#include <rohrkabel/registry/registry.hpp>
|
#include <rohrkabel/registry/registry.hpp>
|
||||||
|
|
||||||
namespace Virtmic {
|
namespace Virtmic {
|
||||||
|
|
||||||
|
const QStringList EXCLUDE_TARGETS{"Chromium input", "discord-screenaudio"};
|
||||||
|
|
||||||
QVector<QString> getTargets() {
|
QVector<QString> getTargets() {
|
||||||
auto main_loop = pipewire::main_loop();
|
auto main_loop = pipewire::main_loop();
|
||||||
auto context = pipewire::context(main_loop);
|
auto context = pipewire::context(main_loop);
|
||||||
@@ -19,11 +22,11 @@ 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"]);
|
||||||
|
|
||||||
if (info.props.count("node.name")) {
|
if (name != "" && !EXCLUDE_TARGETS.contains(name) &&
|
||||||
auto name = QString::fromStdString(info.props["node.name"]);
|
!targets.contains(name)) {
|
||||||
if (!targets.contains(name))
|
targets.append(name);
|
||||||
targets.append(name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -64,20 +67,24 @@ void start(QString _target) {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto &parent = nodes.at(parent_id);
|
auto &parent = nodes.at(parent_id);
|
||||||
|
auto name = parent.props["application.name"];
|
||||||
|
|
||||||
if (parent.props["node.name"].find(target) != std::string::npos) {
|
if (name == target ||
|
||||||
std::cout << "[virtmic] "
|
(target == "[All Desktop Audio]" &&
|
||||||
<< "Link : " << target << ":" << port_id << " -> ";
|
!EXCLUDE_TARGETS.contains(QString::fromStdString(name)))) {
|
||||||
|
auto fl = port.info().props["audio.channel"] == "FL";
|
||||||
if (port.info().props["audio.channel"] == "FL") {
|
links.emplace(
|
||||||
links.emplace(port_id, core.create<pipewire::link_factory>(
|
port_id,
|
||||||
{virt_fl->info().id, port_id}));
|
core.create<pipewire::link_factory>(
|
||||||
std::cout << "[virtmic] " << virt_fl->info().id << std::endl;
|
{fl ? virt_fl->info().id : virt_fr->info().id, port_id}));
|
||||||
} else {
|
qDebug(virtmicLog) << QString("Link: %1:%2 -> %3")
|
||||||
links.emplace(port_id, core.create<pipewire::link_factory>(
|
.arg(QString::fromStdString(
|
||||||
{virt_fr->info().id, port_id}));
|
parent.props["application.name"]))
|
||||||
std::cout << "[virtmic] " << virt_fr->info().id << std::endl;
|
.arg(port_id)
|
||||||
}
|
.arg(fl ? virt_fl->info().id
|
||||||
|
: virt_fr->info().id)
|
||||||
|
.toUtf8()
|
||||||
|
.data();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -93,7 +100,7 @@ void start(QString _target) {
|
|||||||
pipewire::node::type, pipewire::node::version,
|
pipewire::node::type, pipewire::node::version,
|
||||||
pipewire::update_strategy::none);
|
pipewire::update_strategy::none);
|
||||||
|
|
||||||
if (target == "None") {
|
if (target == "[None]") {
|
||||||
while (true) {
|
while (true) {
|
||||||
main_loop.run();
|
main_loop.run();
|
||||||
}
|
}
|
||||||
@@ -105,9 +112,13 @@ 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);
|
||||||
std::cout << "[virtmic] "
|
if (!node.info().props.count("application.name"))
|
||||||
<< "Added : " << node.info().props["node.name"]
|
return;
|
||||||
<< std::endl;
|
qDebug(virtmicLog) << QString("Added: %1")
|
||||||
|
.arg(QString::fromStdString(
|
||||||
|
node.info().props["application.name"]))
|
||||||
|
.toUtf8()
|
||||||
|
.data();
|
||||||
|
|
||||||
if (!nodes.count(global.id)) {
|
if (!nodes.count(global.id)) {
|
||||||
nodes.emplace(global.id, node.info());
|
nodes.emplace(global.id, node.info());
|
||||||
@@ -141,8 +152,11 @@ 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::cout << "[virtmic] "
|
qDebug(virtmicLog) << QString("Removed: %1")
|
||||||
<< "Removed: " << info.props["node.name"] << std::endl;
|
.arg(QString::fromStdString(
|
||||||
|
info.props["application.name"].data()))
|
||||||
|
.toUtf8()
|
||||||
|
.data();
|
||||||
nodes.erase(id);
|
nodes.erase(id);
|
||||||
}
|
}
|
||||||
if (ports.count(id)) {
|
if (ports.count(id)) {
|
||||||
|
|||||||
1
submodules/Vencord
Submodule
1
submodules/Vencord
Submodule
Submodule submodules/Vencord added at 0d996633f2
164
version.cmake
Normal file
164
version.cmake
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
# Copyright (c) 2018-2021 Ribose Inc.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
# 1. Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
|
||||||
|
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
# desired length of commit hash
|
||||||
|
set(GIT_REV_LEN 7)
|
||||||
|
|
||||||
|
# call git, store output in var (can fail)
|
||||||
|
macro(_git var)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" ${ARGN}
|
||||||
|
WORKING_DIRECTORY "${source_dir}"
|
||||||
|
RESULT_VARIABLE _git_ec
|
||||||
|
OUTPUT_VARIABLE ${var}
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
ERROR_QUIET
|
||||||
|
)
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
function(extract_version_info version var_prefix)
|
||||||
|
# extract the main components
|
||||||
|
# v1.9.0-3-g5b92266+1546836556
|
||||||
|
# v1.9.0-3-g5b92266-dirty+1546836556
|
||||||
|
string(REGEX MATCH "^v?([0-9]+\\.[0-9]+\\.[0-9]+)(-([0-9]+)-g([0-9a-f]+)(-dirty)?)?(\\+([0-9]+))?$" matches "${version}")
|
||||||
|
if (NOT matches)
|
||||||
|
message(FATAL_ERROR "Failed to extract version components from ${version}.")
|
||||||
|
endif()
|
||||||
|
set(${var_prefix}_VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) # 1.9.0
|
||||||
|
if (NOT CMAKE_MATCH_3)
|
||||||
|
set(CMAKE_MATCH_3 "0")
|
||||||
|
endif()
|
||||||
|
set(${var_prefix}_VERSION_NCOMMITS "${CMAKE_MATCH_3}" PARENT_SCOPE) # 3
|
||||||
|
if (NOT CMAKE_MATCH_4)
|
||||||
|
set(CMAKE_MATCH_4 "0")
|
||||||
|
endif()
|
||||||
|
set(${var_prefix}_VERSION_GIT_REV "${CMAKE_MATCH_4}" PARENT_SCOPE) # 5b92266
|
||||||
|
if (CMAKE_MATCH_5 STREQUAL "-dirty")
|
||||||
|
set(${var_prefix}_VERSION_IS_DIRTY TRUE PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
set(${var_prefix}_VERSION_IS_DIRTY FALSE PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
# timestamp is optional, default to 0
|
||||||
|
if (NOT CMAKE_MATCH_7)
|
||||||
|
set(CMAKE_MATCH_7 "0")
|
||||||
|
endif()
|
||||||
|
set(${var_prefix}_VERSION_COMMIT_TIMESTAMP "${CMAKE_MATCH_7}" PARENT_SCOPE) # 1546836556
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(determine_version source_dir var_prefix)
|
||||||
|
set(has_release_tag NO)
|
||||||
|
set(has_version_txt NO)
|
||||||
|
set(local_prefix "_determine_ver")
|
||||||
|
# find out base version via version.txt
|
||||||
|
set(base_version "0.0.0")
|
||||||
|
if (EXISTS "${source_dir}/version.txt")
|
||||||
|
set(has_version_txt YES)
|
||||||
|
file(STRINGS "${source_dir}/version.txt" version_file)
|
||||||
|
extract_version_info("${version_file}" "${local_prefix}")
|
||||||
|
set(base_version "${${local_prefix}_VERSION}")
|
||||||
|
message(STATUS "Found version.txt with ${version_file}")
|
||||||
|
else()
|
||||||
|
message(STATUS "Found no version.txt.")
|
||||||
|
endif()
|
||||||
|
# for GIT_EXECUTABLE
|
||||||
|
find_package(Git)
|
||||||
|
# get a description of the version, something like:
|
||||||
|
# v1.9.1-0-g38ffe82 (a tagged release)
|
||||||
|
# v1.9.1-0-g38ffe82-dirty (a tagged release with local modifications)
|
||||||
|
# v1.9.0-3-g5b92266 (post-release snapshot)
|
||||||
|
# v1.9.0-3-g5b92266-dirty (post-release snapshot with local modifications)
|
||||||
|
_git(version describe --abbrev=${GIT_REV_LEN} --match "v[0-9]*" --long --dirty)
|
||||||
|
if (NOT _git_ec EQUAL 0)
|
||||||
|
# no annotated tags, fake one
|
||||||
|
message(STATUS "Found no annotated tags.")
|
||||||
|
_git(revision rev-parse --short=${GIT_REV_LEN} --verify HEAD)
|
||||||
|
if (_git_ec EQUAL 0)
|
||||||
|
set(version "v${base_version}-0-g${revision}")
|
||||||
|
# check if dirty (this won't detect untracked files, but should be ok)
|
||||||
|
_git(changes diff-index --quiet HEAD --)
|
||||||
|
if (NOT _git_ec EQUAL 0)
|
||||||
|
string(APPEND version "-dirty")
|
||||||
|
endif()
|
||||||
|
# append the commit timestamp of the most recent commit (only
|
||||||
|
# in non-release branches -- typically master)
|
||||||
|
_git(commit_timestamp show -s --format=%ct)
|
||||||
|
if (_git_ec EQUAL 0)
|
||||||
|
string(APPEND version "+${commit_timestamp}")
|
||||||
|
endif()
|
||||||
|
elseif(has_version_txt)
|
||||||
|
# Nothing to get from git - so use version.txt completely
|
||||||
|
set(version "${version_file}")
|
||||||
|
else()
|
||||||
|
# Sad case - no git, no version.txt
|
||||||
|
set(version "v${base_version}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(has_release_tag YES)
|
||||||
|
message(STATUS "Found annotated tag ${version}")
|
||||||
|
endif()
|
||||||
|
extract_version_info("${version}" "${local_prefix}")
|
||||||
|
if ("${has_version_txt}" AND NOT ${base_version} STREQUAL ${local_prefix}_VERSION)
|
||||||
|
message(WARNING "Tagged version ${${local_prefix}_VERSION} doesn't match one from the version.txt: ${base_version}")
|
||||||
|
if (${base_version} VERSION_GREATER ${local_prefix}_VERSION)
|
||||||
|
set(${local_prefix}_VERSION ${base_version})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
foreach(suffix VERSION VERSION_NCOMMITS VERSION_GIT_REV VERSION_IS_DIRTY VERSION_COMMIT_TIMESTAMP)
|
||||||
|
if (NOT DEFINED ${local_prefix}_${suffix})
|
||||||
|
message(FATAL_ERROR "Unable to determine version.")
|
||||||
|
endif()
|
||||||
|
set(${var_prefix}_${suffix} "${${local_prefix}_${suffix}}" PARENT_SCOPE)
|
||||||
|
message(STATUS "${var_prefix}_${suffix}: ${${local_prefix}_${suffix}}")
|
||||||
|
endforeach()
|
||||||
|
# Set VERSION_SUFFIX and VERSION_FULL. When making changes, be aware that
|
||||||
|
# this is used in packaging as well and will affect ordering.
|
||||||
|
# | state | version_full |
|
||||||
|
# |-----------------------------------------------------|
|
||||||
|
# | exact tag | 0.9.0 |
|
||||||
|
# | exact tag, dirty | 0.9.0+git20180604 |
|
||||||
|
# | after tag | 0.9.0+git20180604.1.085039f |
|
||||||
|
# | no tag, version.txt | 0.9.0 |
|
||||||
|
# | no tag, no version.txt| 0.0.0+git20180604.2ee02af |
|
||||||
|
string(TIMESTAMP date "%Y%m%d" UTC)
|
||||||
|
set(version_suffix "")
|
||||||
|
if (has_version_txt)
|
||||||
|
elseif (NOT ${local_prefix}_VERSION_NCOMMITS EQUAL 0)
|
||||||
|
# 0.9.0+git20150604.4.289818b
|
||||||
|
string(APPEND version_suffix "+git${date}.${${local_prefix}_VERSION_NCOMMITS}.${${local_prefix}_VERSION_GIT_REV}")
|
||||||
|
elseif ((NOT has_release_tag) AND ((NOT has_version_txt) OR ("${base_version}" STREQUAL "0.0.0") OR (NOT "${revision}" STREQUAL "")))
|
||||||
|
# 0.9.0+git20150604.289818b
|
||||||
|
string(APPEND version_suffix "+git${date}.${${local_prefix}_VERSION_GIT_REV}")
|
||||||
|
elseif(${local_prefix}_VERSION_IS_DIRTY)
|
||||||
|
# 0.9.0+git20150604
|
||||||
|
string(APPEND version_suffix "+git${date}")
|
||||||
|
endif()
|
||||||
|
set(version_full "${${local_prefix}_VERSION}${version_suffix}")
|
||||||
|
# set the results
|
||||||
|
set(${var_prefix}_VERSION_SUFFIX "${version_suffix}" PARENT_SCOPE)
|
||||||
|
set(${var_prefix}_VERSION_FULL "${version_full}" PARENT_SCOPE)
|
||||||
|
# for informational purposes
|
||||||
|
message(STATUS "${var_prefix}_VERSION_SUFFIX: ${version_suffix}")
|
||||||
|
message(STATUS "${var_prefix}_VERSION_FULL: ${version_full}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
Reference in New Issue
Block a user