From 98218dfde37ce595401a0c1446d9e40418cc15d6 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Tue, 12 Mar 2024 04:15:21 -0400
Subject: initial stages stuff
---
src/windows/voicewindow.cpp | 16 ++++++++++++++++
src/windows/voicewindow.hpp | 2 ++
2 files changed, 18 insertions(+)
(limited to 'src/windows')
diff --git a/src/windows/voicewindow.cpp b/src/windows/voicewindow.cpp
index 18f4a41..9033857 100644
--- a/src/windows/voicewindow.cpp
+++ b/src/windows/voicewindow.cpp
@@ -248,12 +248,26 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
combos_combos->pack_start(m_playback_combo);
combos_combos->pack_start(m_capture_combo);
+ discord.signal_stage_instance_create().connect([this](const StageInstance &instance) {
+ m_TMP_stagelabel.show();
+ m_TMP_stagelabel.set_markup("" + instance.Topic + "");
+ });
+
+ discord.signal_stage_instance_update().connect([this](const StageInstance &instance) {
+ m_TMP_stagelabel.set_markup("" + instance.Topic + "");
+ });
+
+ discord.signal_stage_instance_delete().connect([this](const StageInstance &instance) {
+ m_TMP_stagelabel.hide();
+ });
+
m_scroll.add(m_user_list);
m_controls.add(m_mute);
m_controls.add(m_deafen);
m_controls.add(m_noise_suppression);
m_controls.add(m_mix_mono);
m_main.pack_start(m_menu_bar, false, true);
+ m_main.pack_start(m_TMP_stagelabel, false, true);
m_main.pack_start(m_controls, false, true);
m_main.pack_start(m_vad_value, false, true);
m_main.pack_start(*Gtk::make_managed("Input Settings"), false, true);
@@ -263,6 +277,8 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
add(m_main);
show_all_children();
+ m_TMP_stagelabel.hide();
+
Glib::signal_timeout().connect(sigc::mem_fun(*this, &VoiceWindow::UpdateVoiceMeters), 40);
}
diff --git a/src/windows/voicewindow.hpp b/src/windows/voicewindow.hpp
index 018934b..6d46084 100644
--- a/src/windows/voicewindow.hpp
+++ b/src/windows/voicewindow.hpp
@@ -69,6 +69,8 @@ private:
Gtk::Menu m_menu_view_sub;
Gtk::MenuItem m_menu_view_settings;
+ Gtk::Label m_TMP_stagelabel;
+
public:
using type_signal_mute = sigc::signal;
using type_signal_deafen = sigc::signal;
--
cgit v1.2.3
From 092ff4289299fc0253bd72b5e62795f1a1ae589d Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Tue, 12 Mar 2024 21:31:28 -0400
Subject: split out and move some files
---
src/abaddon.cpp | 2 +-
src/windows/voice/voicewindow.cpp | 312 ++++++++++++++++++++
src/windows/voice/voicewindow.hpp | 92 ++++++
src/windows/voice/voicewindowuserlistentry.cpp | 58 ++++
src/windows/voice/voicewindowuserlistentry.hpp | 38 +++
src/windows/voicewindow.cpp | 384 -------------------------
src/windows/voicewindow.hpp | 91 ------
7 files changed, 501 insertions(+), 476 deletions(-)
create mode 100644 src/windows/voice/voicewindow.cpp
create mode 100644 src/windows/voice/voicewindow.hpp
create mode 100644 src/windows/voice/voicewindowuserlistentry.cpp
create mode 100644 src/windows/voice/voicewindowuserlistentry.hpp
delete mode 100644 src/windows/voicewindow.cpp
delete mode 100644 src/windows/voicewindow.hpp
(limited to 'src/windows')
diff --git a/src/abaddon.cpp b/src/abaddon.cpp
index 045b8a7..486d526 100644
--- a/src/abaddon.cpp
+++ b/src/abaddon.cpp
@@ -19,7 +19,7 @@
#include "windows/profilewindow.hpp"
#include "windows/pinnedwindow.hpp"
#include "windows/threadswindow.hpp"
-#include "windows/voicewindow.hpp"
+#include "windows/voice/voicewindow.hpp"
#include "startup.hpp"
#include "notifications/notifications.hpp"
#include "remoteauth/remoteauthdialog.hpp"
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
new file mode 100644
index 0000000..5e050ce
--- /dev/null
+++ b/src/windows/voice/voicewindow.cpp
@@ -0,0 +1,312 @@
+#ifdef WITH_VOICE
+
+// clang-format off
+
+#include "voicewindow.hpp"
+
+#include "abaddon.hpp"
+#include "audio/manager.hpp"
+#include "components/lazyimage.hpp"
+#include "voicewindowuserlistentry.hpp"
+#include "windows/voicesettingswindow.hpp"
+
+// clang-format on
+
+VoiceWindow::VoiceWindow(Snowflake channel_id)
+ : m_main(Gtk::ORIENTATION_VERTICAL)
+ , m_controls(Gtk::ORIENTATION_HORIZONTAL)
+ , m_mute("Mute")
+ , m_deafen("Deafen")
+ , m_noise_suppression("Suppress Noise")
+ , m_mix_mono("Mix Mono")
+ , m_channel_id(channel_id)
+ , m_menu_view("View")
+ , m_menu_view_settings("More _Settings", true) {
+ get_style_context()->add_class("app-window");
+
+ set_default_size(300, 300);
+
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ auto &audio = Abaddon::Get().GetAudio();
+
+ SetUsers(discord.GetUsersInVoiceChannel(m_channel_id));
+
+ discord.signal_voice_user_disconnect().connect(sigc::mem_fun(*this, &VoiceWindow::OnUserDisconnect));
+ discord.signal_voice_user_connect().connect(sigc::mem_fun(*this, &VoiceWindow::OnUserConnect));
+
+ if (const auto self_state = discord.GetVoiceState(discord.GetUserData().ID); self_state.has_value()) {
+ m_mute.set_active((self_state->second & VoiceStateFlags::SelfMute) == VoiceStateFlags::SelfMute);
+ m_deafen.set_active((self_state->second & VoiceStateFlags::SelfDeaf) == VoiceStateFlags::SelfDeaf);
+ }
+
+ m_mute.signal_toggled().connect(sigc::mem_fun(*this, &VoiceWindow::OnMuteChanged));
+ m_deafen.signal_toggled().connect(sigc::mem_fun(*this, &VoiceWindow::OnDeafenChanged));
+
+ m_scroll.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
+ m_scroll.set_hexpand(true);
+ m_scroll.set_vexpand(true);
+
+ m_vad_value.SetShowTick(true);
+
+ m_vad_param.set_range(0.0, 100.0);
+ m_vad_param.set_value_pos(Gtk::POS_LEFT);
+ m_vad_param.signal_value_changed().connect([this]() {
+ auto &audio = Abaddon::Get().GetAudio();
+ const double val = m_vad_param.get_value() * 0.01;
+ switch (audio.GetVADMethod()) {
+ case AudioManager::VADMethod::Gate:
+ audio.SetCaptureGate(val);
+ m_vad_value.SetTick(val);
+ break;
+#ifdef WITH_RNNOISE
+ case AudioManager::VADMethod::RNNoise:
+ audio.SetRNNProbThreshold(val);
+ m_vad_value.SetTick(val);
+ break;
+#endif
+ };
+ });
+ UpdateVADParamValue();
+
+ m_capture_gain.set_range(0.0, 200.0);
+ m_capture_gain.set_value_pos(Gtk::POS_LEFT);
+ m_capture_gain.set_value(audio.GetCaptureGain() * 100.0);
+ m_capture_gain.signal_value_changed().connect([this]() {
+ const double val = m_capture_gain.get_value() / 100.0;
+ Abaddon::Get().GetAudio().SetCaptureGain(val);
+ });
+
+ m_vad_combo.set_valign(Gtk::ALIGN_END);
+ m_vad_combo.set_hexpand(true);
+ m_vad_combo.set_halign(Gtk::ALIGN_FILL);
+ m_vad_combo.set_tooltip_text(
+ "Voice Activation Detection method\n"
+ "Gate - Simple volume threshold. Slider changes threshold\n"
+ "RNNoise - Heavier on CPU. Slider changes probability threshold");
+ m_vad_combo.append("gate", "Gate");
+#ifdef WITH_RNNOISE
+ m_vad_combo.append("rnnoise", "RNNoise");
+#endif
+ if (!m_vad_combo.set_active_id(Abaddon::Get().GetSettings().VAD)) {
+#ifdef WITH_RNNOISE
+ m_vad_combo.set_active_id("rnnoise");
+#else
+ m_vad_combo.set_active_id("gate");
+#endif
+ }
+ m_vad_combo.signal_changed().connect([this]() {
+ auto &audio = Abaddon::Get().GetAudio();
+ const auto id = m_vad_combo.get_active_id();
+
+ audio.SetVADMethod(id);
+ Abaddon::Get().GetSettings().VAD = id;
+ UpdateVADParamValue();
+ });
+
+ m_noise_suppression.set_active(audio.GetSuppressNoise());
+ m_noise_suppression.signal_toggled().connect([this]() {
+ Abaddon::Get().GetAudio().SetSuppressNoise(m_noise_suppression.get_active());
+ });
+
+ m_mix_mono.set_active(audio.GetMixMono());
+ m_mix_mono.signal_toggled().connect([this]() {
+ Abaddon::Get().GetAudio().SetMixMono(m_mix_mono.get_active());
+ });
+
+ auto *playback_renderer = Gtk::make_managed();
+ m_playback_combo.set_valign(Gtk::ALIGN_END);
+ m_playback_combo.set_hexpand(true);
+ m_playback_combo.set_halign(Gtk::ALIGN_FILL);
+ m_playback_combo.set_model(audio.GetDevices().GetPlaybackDeviceModel());
+ if (const auto iter = audio.GetDevices().GetActivePlaybackDevice()) {
+ m_playback_combo.set_active(iter);
+ }
+ m_playback_combo.pack_start(*playback_renderer);
+ m_playback_combo.add_attribute(*playback_renderer, "text", 0);
+ m_playback_combo.signal_changed().connect([this]() {
+ Abaddon::Get().GetAudio().SetPlaybackDevice(m_playback_combo.get_active());
+ });
+
+ auto *capture_renderer = Gtk::make_managed();
+ m_capture_combo.set_valign(Gtk::ALIGN_END);
+ m_capture_combo.set_hexpand(true);
+ m_capture_combo.set_halign(Gtk::ALIGN_FILL);
+ m_capture_combo.set_model(Abaddon::Get().GetAudio().GetDevices().GetCaptureDeviceModel());
+ if (const auto iter = Abaddon::Get().GetAudio().GetDevices().GetActiveCaptureDevice()) {
+ m_capture_combo.set_active(iter);
+ }
+ m_capture_combo.pack_start(*capture_renderer);
+ m_capture_combo.add_attribute(*capture_renderer, "text", 0);
+ m_capture_combo.signal_changed().connect([this]() {
+ Abaddon::Get().GetAudio().SetCaptureDevice(m_capture_combo.get_active());
+ });
+
+ m_menu_bar.append(m_menu_view);
+ m_menu_view.set_submenu(m_menu_view_sub);
+ m_menu_view_sub.append(m_menu_view_settings);
+ m_menu_view_settings.signal_activate().connect([this]() {
+ auto *window = new VoiceSettingsWindow;
+ const auto cb = [this](double gain) {
+ m_capture_gain.set_value(gain * 100.0);
+ Abaddon::Get().GetAudio().SetCaptureGain(gain);
+ };
+ window->signal_gain().connect(sigc::track_obj(cb, *this));
+ window->show();
+ });
+
+ auto *sliders_container = Gtk::make_managed();
+ auto *sliders_labels = Gtk::make_managed();
+ auto *sliders_sliders = Gtk::make_managed();
+ sliders_container->pack_start(*sliders_labels, false, true, 2);
+ sliders_container->pack_start(*sliders_sliders);
+ sliders_labels->pack_start(*Gtk::make_managed("Threshold", Gtk::ALIGN_END));
+ sliders_labels->pack_start(*Gtk::make_managed("Gain", Gtk::ALIGN_END));
+ sliders_sliders->pack_start(m_vad_param);
+ sliders_sliders->pack_start(m_capture_gain);
+
+ auto *combos_container = Gtk::make_managed();
+ auto *combos_labels = Gtk::make_managed();
+ auto *combos_combos = Gtk::make_managed();
+ combos_container->pack_start(*combos_labels, false, true, 6);
+ combos_container->pack_start(*combos_combos, Gtk::PACK_EXPAND_WIDGET, 6);
+ combos_labels->pack_start(*Gtk::make_managed("VAD Method", Gtk::ALIGN_END));
+ combos_labels->pack_start(*Gtk::make_managed("Output Device", Gtk::ALIGN_END));
+ combos_labels->pack_start(*Gtk::make_managed("Input Device", Gtk::ALIGN_END));
+ combos_combos->pack_start(m_vad_combo);
+ combos_combos->pack_start(m_playback_combo);
+ combos_combos->pack_start(m_capture_combo);
+
+ discord.signal_stage_instance_create().connect([this](const StageInstance &instance) {
+ m_TMP_stagelabel.show();
+ m_TMP_stagelabel.set_markup("" + instance.Topic + "");
+ });
+
+ discord.signal_stage_instance_update().connect([this](const StageInstance &instance) {
+ m_TMP_stagelabel.set_markup("" + instance.Topic + "");
+ });
+
+ discord.signal_stage_instance_delete().connect([this](const StageInstance &instance) {
+ m_TMP_stagelabel.hide();
+ });
+
+ m_scroll.add(m_user_list);
+ m_controls.add(m_mute);
+ m_controls.add(m_deafen);
+ m_controls.add(m_noise_suppression);
+ m_controls.add(m_mix_mono);
+ m_main.pack_start(m_menu_bar, false, true);
+ m_main.pack_start(m_TMP_stagelabel, false, true);
+ m_main.pack_start(m_controls, false, true);
+ m_main.pack_start(m_vad_value, false, true);
+ m_main.pack_start(*Gtk::make_managed("Input Settings"), false, true);
+ m_main.pack_start(*sliders_container, false, true);
+ m_main.pack_start(m_scroll);
+ m_main.pack_start(*combos_container, false, true, 2);
+ add(m_main);
+ show_all_children();
+
+ m_TMP_stagelabel.hide();
+
+ Glib::signal_timeout().connect(sigc::mem_fun(*this, &VoiceWindow::UpdateVoiceMeters), 40);
+}
+
+void VoiceWindow::SetUsers(const std::unordered_set &user_ids) {
+ const auto me = Abaddon::Get().GetDiscordClient().GetUserData().ID;
+ for (auto id : user_ids) {
+ if (id == me) continue;
+ m_user_list.add(*CreateRow(id));
+ }
+}
+
+Gtk::ListBoxRow *VoiceWindow::CreateRow(Snowflake id) {
+ auto *row = Gtk::make_managed(id);
+ m_rows[id] = row;
+ auto &vc = Abaddon::Get().GetDiscordClient().GetVoiceClient();
+ row->RestoreGain(vc.GetUserVolume(id));
+ row->signal_mute_cs().connect([this, id](bool is_muted) {
+ m_signal_mute_user_cs.emit(id, is_muted);
+ });
+ row->signal_volume().connect([this, id](double volume) {
+ m_signal_user_volume_changed.emit(id, volume);
+ });
+ row->show_all();
+ return row;
+}
+
+void VoiceWindow::OnMuteChanged() {
+ m_signal_mute.emit(m_mute.get_active());
+}
+
+void VoiceWindow::OnDeafenChanged() {
+ m_signal_deafen.emit(m_deafen.get_active());
+}
+
+bool VoiceWindow::UpdateVoiceMeters() {
+ auto &audio = Abaddon::Get().GetAudio();
+ switch (audio.GetVADMethod()) {
+ case AudioManager::VADMethod::Gate:
+ m_vad_value.SetVolume(audio.GetCaptureVolumeLevel());
+ break;
+#ifdef WITH_RNNOISE
+ case AudioManager::VADMethod::RNNoise:
+ m_vad_value.SetVolume(audio.GetCurrentVADProbability());
+ break;
+#endif
+ }
+
+ for (auto [id, row] : m_rows) {
+ const auto ssrc = Abaddon::Get().GetDiscordClient().GetSSRCOfUser(id);
+ if (ssrc.has_value()) {
+ row->SetVolumeMeter(audio.GetSSRCVolumeLevel(*ssrc));
+ }
+ }
+ return true;
+}
+
+void VoiceWindow::UpdateVADParamValue() {
+ auto &audio = Abaddon::Get().GetAudio();
+ switch (audio.GetVADMethod()) {
+ case AudioManager::VADMethod::Gate:
+ m_vad_param.set_value(audio.GetCaptureGate() * 100.0);
+ break;
+#ifdef WITH_RNNOISE
+ case AudioManager::VADMethod::RNNoise:
+ m_vad_param.set_value(audio.GetRNNProbThreshold() * 100.0);
+ break;
+#endif
+ }
+}
+
+void VoiceWindow::OnUserConnect(Snowflake user_id, Snowflake to_channel_id) {
+ if (m_channel_id == to_channel_id) {
+ if (auto it = m_rows.find(user_id); it == m_rows.end()) {
+ m_user_list.add(*CreateRow(user_id));
+ }
+ }
+}
+
+void VoiceWindow::OnUserDisconnect(Snowflake user_id, Snowflake from_channel_id) {
+ if (m_channel_id == from_channel_id) {
+ if (auto it = m_rows.find(user_id); it != m_rows.end()) {
+ delete it->second;
+ m_rows.erase(it);
+ }
+ }
+}
+
+VoiceWindow::type_signal_mute VoiceWindow::signal_mute() {
+ return m_signal_mute;
+}
+
+VoiceWindow::type_signal_deafen VoiceWindow::signal_deafen() {
+ return m_signal_deafen;
+}
+
+VoiceWindow::type_signal_mute_user_cs VoiceWindow::signal_mute_user_cs() {
+ return m_signal_mute_user_cs;
+}
+
+VoiceWindow::type_signal_user_volume_changed VoiceWindow::signal_user_volume_changed() {
+ return m_signal_user_volume_changed;
+}
+#endif
diff --git a/src/windows/voice/voicewindow.hpp b/src/windows/voice/voicewindow.hpp
new file mode 100644
index 0000000..202a0ac
--- /dev/null
+++ b/src/windows/voice/voicewindow.hpp
@@ -0,0 +1,92 @@
+#pragma once
+#ifdef WITH_VOICE
+// clang-format off
+
+#include "components/volumemeter.hpp"
+#include "discord/snowflake.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+// clang-format on
+
+class VoiceWindowUserListEntry;
+
+class VoiceWindow : public Gtk::Window {
+public:
+ VoiceWindow(Snowflake channel_id);
+
+private:
+ void SetUsers(const std::unordered_set &user_ids);
+
+ Gtk::ListBoxRow *CreateRow(Snowflake id);
+
+ void OnUserConnect(Snowflake user_id, Snowflake to_channel_id);
+ void OnUserDisconnect(Snowflake user_id, Snowflake from_channel_id);
+
+ void OnMuteChanged();
+ void OnDeafenChanged();
+
+ bool UpdateVoiceMeters();
+
+ void UpdateVADParamValue();
+
+ Gtk::Box m_main;
+ Gtk::Box m_controls;
+
+ Gtk::CheckButton m_mute;
+ Gtk::CheckButton m_deafen;
+
+ Gtk::ScrolledWindow m_scroll;
+ Gtk::ListBox m_user_list;
+
+ // Shows volume for gate VAD method
+ // Shows probability for RNNoise VAD method
+ VolumeMeter m_vad_value;
+ // Volume threshold for gate VAD method
+ // VAD probability threshold for RNNoise VAD method
+ Gtk::Scale m_vad_param;
+ Gtk::Scale m_capture_gain;
+
+ Gtk::CheckButton m_noise_suppression;
+ Gtk::CheckButton m_mix_mono;
+
+ Gtk::ComboBoxText m_vad_combo;
+ Gtk::ComboBox m_playback_combo;
+ Gtk::ComboBox m_capture_combo;
+
+ Snowflake m_channel_id;
+
+ std::unordered_map m_rows;
+
+ Gtk::MenuBar m_menu_bar;
+ Gtk::MenuItem m_menu_view;
+ Gtk::Menu m_menu_view_sub;
+ Gtk::MenuItem m_menu_view_settings;
+
+ Gtk::Label m_TMP_stagelabel;
+
+public:
+ using type_signal_mute = sigc::signal;
+ using type_signal_deafen = sigc::signal;
+ using type_signal_mute_user_cs = sigc::signal;
+ using type_signal_user_volume_changed = sigc::signal;
+
+ type_signal_mute signal_mute();
+ type_signal_deafen signal_deafen();
+ type_signal_mute_user_cs signal_mute_user_cs();
+ type_signal_user_volume_changed signal_user_volume_changed();
+
+private:
+ type_signal_mute m_signal_mute;
+ type_signal_deafen m_signal_deafen;
+ type_signal_mute_user_cs m_signal_mute_user_cs;
+ type_signal_user_volume_changed m_signal_user_volume_changed;
+};
+#endif
diff --git a/src/windows/voice/voicewindowuserlistentry.cpp b/src/windows/voice/voicewindowuserlistentry.cpp
new file mode 100644
index 0000000..97a3031
--- /dev/null
+++ b/src/windows/voice/voicewindowuserlistentry.cpp
@@ -0,0 +1,58 @@
+#include "voicewindowuserlistentry.hpp"
+
+#include "abaddon.hpp"
+
+VoiceWindowUserListEntry::VoiceWindowUserListEntry(Snowflake id)
+ : m_main(Gtk::ORIENTATION_VERTICAL)
+ , m_horz(Gtk::ORIENTATION_HORIZONTAL)
+ , m_avatar(32, 32)
+ , m_mute("Mute") {
+ m_name.set_halign(Gtk::ALIGN_START);
+ m_name.set_hexpand(true);
+ m_mute.set_halign(Gtk::ALIGN_END);
+
+ m_volume.set_range(0.0, 200.0);
+ m_volume.set_value_pos(Gtk::POS_LEFT);
+ m_volume.set_value(100.0);
+ m_volume.signal_value_changed().connect([this]() {
+ m_signal_volume.emit(m_volume.get_value() * 0.01);
+ });
+
+ m_horz.add(m_avatar);
+ m_horz.add(m_name);
+ m_horz.add(m_mute);
+ m_main.add(m_horz);
+ m_main.add(m_volume);
+ m_main.add(m_meter);
+ add(m_main);
+ show_all_children();
+
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ const auto user = discord.GetUser(id);
+ if (user.has_value()) {
+ m_name.set_text(user->GetUsername());
+ m_avatar.SetURL(user->GetAvatarURL("png", "32"));
+ } else {
+ m_name.set_text("Unknown user");
+ }
+
+ m_mute.signal_toggled().connect([this]() {
+ m_signal_mute_cs.emit(m_mute.get_active());
+ });
+}
+
+void VoiceWindowUserListEntry::SetVolumeMeter(double frac) {
+ m_meter.SetVolume(frac);
+}
+
+void VoiceWindowUserListEntry::RestoreGain(double frac) {
+ m_volume.set_value(frac * 100.0);
+}
+
+VoiceWindowUserListEntry::type_signal_mute_cs VoiceWindowUserListEntry::signal_mute_cs() {
+ return m_signal_mute_cs;
+}
+
+VoiceWindowUserListEntry::type_signal_volume VoiceWindowUserListEntry::signal_volume() {
+ return m_signal_volume;
+}
diff --git a/src/windows/voice/voicewindowuserlistentry.hpp b/src/windows/voice/voicewindowuserlistentry.hpp
new file mode 100644
index 0000000..4e8c028
--- /dev/null
+++ b/src/windows/voice/voicewindowuserlistentry.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "components/lazyimage.hpp"
+#include "components/volumemeter.hpp"
+#include "discord/snowflake.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+class VoiceWindowUserListEntry : public Gtk::ListBoxRow {
+public:
+ VoiceWindowUserListEntry(Snowflake id);
+
+ void SetVolumeMeter(double frac);
+ void RestoreGain(double frac);
+
+private:
+ Gtk::Box m_main;
+ Gtk::Box m_horz;
+ LazyImage m_avatar;
+ Gtk::Label m_name;
+ Gtk::CheckButton m_mute;
+ Gtk::Scale m_volume;
+ VolumeMeter m_meter;
+
+public:
+ using type_signal_mute_cs = sigc::signal;
+ using type_signal_volume = sigc::signal;
+ type_signal_mute_cs signal_mute_cs();
+ type_signal_volume signal_volume();
+
+private:
+ type_signal_mute_cs m_signal_mute_cs;
+ type_signal_volume m_signal_volume;
+};
diff --git a/src/windows/voicewindow.cpp b/src/windows/voicewindow.cpp
deleted file mode 100644
index 9033857..0000000
--- a/src/windows/voicewindow.cpp
+++ /dev/null
@@ -1,384 +0,0 @@
-#ifdef WITH_VOICE
-
-// clang-format off
-
-#include "abaddon.hpp"
-#include "audio/manager.hpp"
-#include "components/lazyimage.hpp"
-#include "voicesettingswindow.hpp"
-#include "voicewindow.hpp"
-
-// clang-format on
-
-class VoiceWindowUserListEntry : public Gtk::ListBoxRow {
-public:
- VoiceWindowUserListEntry(Snowflake id)
- : m_main(Gtk::ORIENTATION_VERTICAL)
- , m_horz(Gtk::ORIENTATION_HORIZONTAL)
- , m_avatar(32, 32)
- , m_mute("Mute") {
- m_name.set_halign(Gtk::ALIGN_START);
- m_name.set_hexpand(true);
- m_mute.set_halign(Gtk::ALIGN_END);
-
- m_volume.set_range(0.0, 200.0);
- m_volume.set_value_pos(Gtk::POS_LEFT);
- m_volume.set_value(100.0);
- m_volume.signal_value_changed().connect([this]() {
- m_signal_volume.emit(m_volume.get_value() * 0.01);
- });
-
- m_horz.add(m_avatar);
- m_horz.add(m_name);
- m_horz.add(m_mute);
- m_main.add(m_horz);
- m_main.add(m_volume);
- m_main.add(m_meter);
- add(m_main);
- show_all_children();
-
- auto &discord = Abaddon::Get().GetDiscordClient();
- const auto user = discord.GetUser(id);
- if (user.has_value()) {
- m_name.set_text(user->GetUsername());
- m_avatar.SetURL(user->GetAvatarURL("png", "32"));
- } else {
- m_name.set_text("Unknown user");
- }
-
- m_mute.signal_toggled().connect([this]() {
- m_signal_mute_cs.emit(m_mute.get_active());
- });
- }
-
- void SetVolumeMeter(double frac) {
- m_meter.SetVolume(frac);
- }
-
- void RestoreGain(double frac) {
- m_volume.set_value(frac * 100.0);
- }
-
-private:
- Gtk::Box m_main;
- Gtk::Box m_horz;
- LazyImage m_avatar;
- Gtk::Label m_name;
- Gtk::CheckButton m_mute;
- Gtk::Scale m_volume;
- VolumeMeter m_meter;
-
-public:
- using type_signal_mute_cs = sigc::signal;
- using type_signal_volume = sigc::signal;
- type_signal_mute_cs signal_mute_cs() {
- return m_signal_mute_cs;
- }
-
- type_signal_volume signal_volume() {
- return m_signal_volume;
- }
-
-private:
- type_signal_mute_cs m_signal_mute_cs;
- type_signal_volume m_signal_volume;
-};
-
-VoiceWindow::VoiceWindow(Snowflake channel_id)
- : m_main(Gtk::ORIENTATION_VERTICAL)
- , m_controls(Gtk::ORIENTATION_HORIZONTAL)
- , m_mute("Mute")
- , m_deafen("Deafen")
- , m_noise_suppression("Suppress Noise")
- , m_mix_mono("Mix Mono")
- , m_channel_id(channel_id)
- , m_menu_view("View")
- , m_menu_view_settings("More _Settings", true) {
- get_style_context()->add_class("app-window");
-
- set_default_size(300, 300);
-
- auto &discord = Abaddon::Get().GetDiscordClient();
- auto &audio = Abaddon::Get().GetAudio();
-
- SetUsers(discord.GetUsersInVoiceChannel(m_channel_id));
-
- discord.signal_voice_user_disconnect().connect(sigc::mem_fun(*this, &VoiceWindow::OnUserDisconnect));
- discord.signal_voice_user_connect().connect(sigc::mem_fun(*this, &VoiceWindow::OnUserConnect));
-
- if (const auto self_state = discord.GetVoiceState(discord.GetUserData().ID); self_state.has_value()) {
- m_mute.set_active((self_state->second & VoiceStateFlags::SelfMute) == VoiceStateFlags::SelfMute);
- m_deafen.set_active((self_state->second & VoiceStateFlags::SelfDeaf) == VoiceStateFlags::SelfDeaf);
- }
-
- m_mute.signal_toggled().connect(sigc::mem_fun(*this, &VoiceWindow::OnMuteChanged));
- m_deafen.signal_toggled().connect(sigc::mem_fun(*this, &VoiceWindow::OnDeafenChanged));
-
- m_scroll.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
- m_scroll.set_hexpand(true);
- m_scroll.set_vexpand(true);
-
- m_vad_value.SetShowTick(true);
-
- m_vad_param.set_range(0.0, 100.0);
- m_vad_param.set_value_pos(Gtk::POS_LEFT);
- m_vad_param.signal_value_changed().connect([this]() {
- auto &audio = Abaddon::Get().GetAudio();
- const double val = m_vad_param.get_value() * 0.01;
- switch (audio.GetVADMethod()) {
- case AudioManager::VADMethod::Gate:
- audio.SetCaptureGate(val);
- m_vad_value.SetTick(val);
- break;
-#ifdef WITH_RNNOISE
- case AudioManager::VADMethod::RNNoise:
- audio.SetRNNProbThreshold(val);
- m_vad_value.SetTick(val);
- break;
-#endif
- };
- });
- UpdateVADParamValue();
-
- m_capture_gain.set_range(0.0, 200.0);
- m_capture_gain.set_value_pos(Gtk::POS_LEFT);
- m_capture_gain.set_value(audio.GetCaptureGain() * 100.0);
- m_capture_gain.signal_value_changed().connect([this]() {
- const double val = m_capture_gain.get_value() / 100.0;
- Abaddon::Get().GetAudio().SetCaptureGain(val);
- });
-
- m_vad_combo.set_valign(Gtk::ALIGN_END);
- m_vad_combo.set_hexpand(true);
- m_vad_combo.set_halign(Gtk::ALIGN_FILL);
- m_vad_combo.set_tooltip_text(
- "Voice Activation Detection method\n"
- "Gate - Simple volume threshold. Slider changes threshold\n"
- "RNNoise - Heavier on CPU. Slider changes probability threshold");
- m_vad_combo.append("gate", "Gate");
-#ifdef WITH_RNNOISE
- m_vad_combo.append("rnnoise", "RNNoise");
-#endif
- if (!m_vad_combo.set_active_id(Abaddon::Get().GetSettings().VAD)) {
-#ifdef WITH_RNNOISE
- m_vad_combo.set_active_id("rnnoise");
-#else
- m_vad_combo.set_active_id("gate");
-#endif
- }
- m_vad_combo.signal_changed().connect([this]() {
- auto &audio = Abaddon::Get().GetAudio();
- const auto id = m_vad_combo.get_active_id();
-
- audio.SetVADMethod(id);
- Abaddon::Get().GetSettings().VAD = id;
- UpdateVADParamValue();
- });
-
- m_noise_suppression.set_active(audio.GetSuppressNoise());
- m_noise_suppression.signal_toggled().connect([this]() {
- Abaddon::Get().GetAudio().SetSuppressNoise(m_noise_suppression.get_active());
- });
-
- m_mix_mono.set_active(audio.GetMixMono());
- m_mix_mono.signal_toggled().connect([this]() {
- Abaddon::Get().GetAudio().SetMixMono(m_mix_mono.get_active());
- });
-
- auto *playback_renderer = Gtk::make_managed();
- m_playback_combo.set_valign(Gtk::ALIGN_END);
- m_playback_combo.set_hexpand(true);
- m_playback_combo.set_halign(Gtk::ALIGN_FILL);
- m_playback_combo.set_model(audio.GetDevices().GetPlaybackDeviceModel());
- if (const auto iter = audio.GetDevices().GetActivePlaybackDevice()) {
- m_playback_combo.set_active(iter);
- }
- m_playback_combo.pack_start(*playback_renderer);
- m_playback_combo.add_attribute(*playback_renderer, "text", 0);
- m_playback_combo.signal_changed().connect([this]() {
- Abaddon::Get().GetAudio().SetPlaybackDevice(m_playback_combo.get_active());
- });
-
- auto *capture_renderer = Gtk::make_managed();
- m_capture_combo.set_valign(Gtk::ALIGN_END);
- m_capture_combo.set_hexpand(true);
- m_capture_combo.set_halign(Gtk::ALIGN_FILL);
- m_capture_combo.set_model(Abaddon::Get().GetAudio().GetDevices().GetCaptureDeviceModel());
- if (const auto iter = Abaddon::Get().GetAudio().GetDevices().GetActiveCaptureDevice()) {
- m_capture_combo.set_active(iter);
- }
- m_capture_combo.pack_start(*capture_renderer);
- m_capture_combo.add_attribute(*capture_renderer, "text", 0);
- m_capture_combo.signal_changed().connect([this]() {
- Abaddon::Get().GetAudio().SetCaptureDevice(m_capture_combo.get_active());
- });
-
- m_menu_bar.append(m_menu_view);
- m_menu_view.set_submenu(m_menu_view_sub);
- m_menu_view_sub.append(m_menu_view_settings);
- m_menu_view_settings.signal_activate().connect([this]() {
- auto *window = new VoiceSettingsWindow;
- const auto cb = [this](double gain) {
- m_capture_gain.set_value(gain * 100.0);
- Abaddon::Get().GetAudio().SetCaptureGain(gain);
- };
- window->signal_gain().connect(sigc::track_obj(cb, *this));
- window->show();
- });
-
- auto *sliders_container = Gtk::make_managed();
- auto *sliders_labels = Gtk::make_managed();
- auto *sliders_sliders = Gtk::make_managed();
- sliders_container->pack_start(*sliders_labels, false, true, 2);
- sliders_container->pack_start(*sliders_sliders);
- sliders_labels->pack_start(*Gtk::make_managed("Threshold", Gtk::ALIGN_END));
- sliders_labels->pack_start(*Gtk::make_managed("Gain", Gtk::ALIGN_END));
- sliders_sliders->pack_start(m_vad_param);
- sliders_sliders->pack_start(m_capture_gain);
-
- auto *combos_container = Gtk::make_managed();
- auto *combos_labels = Gtk::make_managed();
- auto *combos_combos = Gtk::make_managed();
- combos_container->pack_start(*combos_labels, false, true, 6);
- combos_container->pack_start(*combos_combos, Gtk::PACK_EXPAND_WIDGET, 6);
- combos_labels->pack_start(*Gtk::make_managed("VAD Method", Gtk::ALIGN_END));
- combos_labels->pack_start(*Gtk::make_managed("Output Device", Gtk::ALIGN_END));
- combos_labels->pack_start(*Gtk::make_managed("Input Device", Gtk::ALIGN_END));
- combos_combos->pack_start(m_vad_combo);
- combos_combos->pack_start(m_playback_combo);
- combos_combos->pack_start(m_capture_combo);
-
- discord.signal_stage_instance_create().connect([this](const StageInstance &instance) {
- m_TMP_stagelabel.show();
- m_TMP_stagelabel.set_markup("" + instance.Topic + "");
- });
-
- discord.signal_stage_instance_update().connect([this](const StageInstance &instance) {
- m_TMP_stagelabel.set_markup("" + instance.Topic + "");
- });
-
- discord.signal_stage_instance_delete().connect([this](const StageInstance &instance) {
- m_TMP_stagelabel.hide();
- });
-
- m_scroll.add(m_user_list);
- m_controls.add(m_mute);
- m_controls.add(m_deafen);
- m_controls.add(m_noise_suppression);
- m_controls.add(m_mix_mono);
- m_main.pack_start(m_menu_bar, false, true);
- m_main.pack_start(m_TMP_stagelabel, false, true);
- m_main.pack_start(m_controls, false, true);
- m_main.pack_start(m_vad_value, false, true);
- m_main.pack_start(*Gtk::make_managed("Input Settings"), false, true);
- m_main.pack_start(*sliders_container, false, true);
- m_main.pack_start(m_scroll);
- m_main.pack_start(*combos_container, false, true, 2);
- add(m_main);
- show_all_children();
-
- m_TMP_stagelabel.hide();
-
- Glib::signal_timeout().connect(sigc::mem_fun(*this, &VoiceWindow::UpdateVoiceMeters), 40);
-}
-
-void VoiceWindow::SetUsers(const std::unordered_set &user_ids) {
- const auto me = Abaddon::Get().GetDiscordClient().GetUserData().ID;
- for (auto id : user_ids) {
- if (id == me) continue;
- m_user_list.add(*CreateRow(id));
- }
-}
-
-Gtk::ListBoxRow *VoiceWindow::CreateRow(Snowflake id) {
- auto *row = Gtk::make_managed(id);
- m_rows[id] = row;
- auto &vc = Abaddon::Get().GetDiscordClient().GetVoiceClient();
- row->RestoreGain(vc.GetUserVolume(id));
- row->signal_mute_cs().connect([this, id](bool is_muted) {
- m_signal_mute_user_cs.emit(id, is_muted);
- });
- row->signal_volume().connect([this, id](double volume) {
- m_signal_user_volume_changed.emit(id, volume);
- });
- row->show_all();
- return row;
-}
-
-void VoiceWindow::OnMuteChanged() {
- m_signal_mute.emit(m_mute.get_active());
-}
-
-void VoiceWindow::OnDeafenChanged() {
- m_signal_deafen.emit(m_deafen.get_active());
-}
-
-bool VoiceWindow::UpdateVoiceMeters() {
- auto &audio = Abaddon::Get().GetAudio();
- switch (audio.GetVADMethod()) {
- case AudioManager::VADMethod::Gate:
- m_vad_value.SetVolume(audio.GetCaptureVolumeLevel());
- break;
-#ifdef WITH_RNNOISE
- case AudioManager::VADMethod::RNNoise:
- m_vad_value.SetVolume(audio.GetCurrentVADProbability());
- break;
-#endif
- }
-
- for (auto [id, row] : m_rows) {
- const auto ssrc = Abaddon::Get().GetDiscordClient().GetSSRCOfUser(id);
- if (ssrc.has_value()) {
- row->SetVolumeMeter(audio.GetSSRCVolumeLevel(*ssrc));
- }
- }
- return true;
-}
-
-void VoiceWindow::UpdateVADParamValue() {
- auto &audio = Abaddon::Get().GetAudio();
- switch (audio.GetVADMethod()) {
- case AudioManager::VADMethod::Gate:
- m_vad_param.set_value(audio.GetCaptureGate() * 100.0);
- break;
-#ifdef WITH_RNNOISE
- case AudioManager::VADMethod::RNNoise:
- m_vad_param.set_value(audio.GetRNNProbThreshold() * 100.0);
- break;
-#endif
- }
-}
-
-void VoiceWindow::OnUserConnect(Snowflake user_id, Snowflake to_channel_id) {
- if (m_channel_id == to_channel_id) {
- if (auto it = m_rows.find(user_id); it == m_rows.end()) {
- m_user_list.add(*CreateRow(user_id));
- }
- }
-}
-
-void VoiceWindow::OnUserDisconnect(Snowflake user_id, Snowflake from_channel_id) {
- if (m_channel_id == from_channel_id) {
- if (auto it = m_rows.find(user_id); it != m_rows.end()) {
- delete it->second;
- m_rows.erase(it);
- }
- }
-}
-
-VoiceWindow::type_signal_mute VoiceWindow::signal_mute() {
- return m_signal_mute;
-}
-
-VoiceWindow::type_signal_deafen VoiceWindow::signal_deafen() {
- return m_signal_deafen;
-}
-
-VoiceWindow::type_signal_mute_user_cs VoiceWindow::signal_mute_user_cs() {
- return m_signal_mute_user_cs;
-}
-
-VoiceWindow::type_signal_user_volume_changed VoiceWindow::signal_user_volume_changed() {
- return m_signal_user_volume_changed;
-}
-#endif
diff --git a/src/windows/voicewindow.hpp b/src/windows/voicewindow.hpp
deleted file mode 100644
index 6d46084..0000000
--- a/src/windows/voicewindow.hpp
+++ /dev/null
@@ -1,91 +0,0 @@
-#pragma once
-#ifdef WITH_VOICE
-// clang-format off
-
-#include "components/volumemeter.hpp"
-#include "discord/snowflake.hpp"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-// clang-format on
-
-class VoiceWindowUserListEntry;
-class VoiceWindow : public Gtk::Window {
-public:
- VoiceWindow(Snowflake channel_id);
-
-private:
- void SetUsers(const std::unordered_set &user_ids);
-
- Gtk::ListBoxRow *CreateRow(Snowflake id);
-
- void OnUserConnect(Snowflake user_id, Snowflake to_channel_id);
- void OnUserDisconnect(Snowflake user_id, Snowflake from_channel_id);
-
- void OnMuteChanged();
- void OnDeafenChanged();
-
- bool UpdateVoiceMeters();
-
- void UpdateVADParamValue();
-
- Gtk::Box m_main;
- Gtk::Box m_controls;
-
- Gtk::CheckButton m_mute;
- Gtk::CheckButton m_deafen;
-
- Gtk::ScrolledWindow m_scroll;
- Gtk::ListBox m_user_list;
-
- // Shows volume for gate VAD method
- // Shows probability for RNNoise VAD method
- VolumeMeter m_vad_value;
- // Volume threshold for gate VAD method
- // VAD probability threshold for RNNoise VAD method
- Gtk::Scale m_vad_param;
- Gtk::Scale m_capture_gain;
-
- Gtk::CheckButton m_noise_suppression;
- Gtk::CheckButton m_mix_mono;
-
- Gtk::ComboBoxText m_vad_combo;
- Gtk::ComboBox m_playback_combo;
- Gtk::ComboBox m_capture_combo;
-
- Snowflake m_channel_id;
-
- std::unordered_map m_rows;
-
- Gtk::MenuBar m_menu_bar;
- Gtk::MenuItem m_menu_view;
- Gtk::Menu m_menu_view_sub;
- Gtk::MenuItem m_menu_view_settings;
-
- Gtk::Label m_TMP_stagelabel;
-
-public:
- using type_signal_mute = sigc::signal;
- using type_signal_deafen = sigc::signal;
- using type_signal_mute_user_cs = sigc::signal;
- using type_signal_user_volume_changed = sigc::signal;
-
- type_signal_mute signal_mute();
- type_signal_deafen signal_deafen();
- type_signal_mute_user_cs signal_mute_user_cs();
- type_signal_user_volume_changed signal_user_volume_changed();
-
-private:
- type_signal_mute m_signal_mute;
- type_signal_deafen m_signal_deafen;
- type_signal_mute_user_cs m_signal_mute_user_cs;
- type_signal_user_volume_changed m_signal_user_volume_changed;
-};
-#endif
--
cgit v1.2.3
From 019ae0428bc3ad878dffa6218eb03b96457d7dcb Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Thu, 14 Mar 2024 01:06:39 -0400
Subject: fix crash in temp stuff
---
src/windows/voice/voicewindow.cpp | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
(limited to 'src/windows')
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
index 5e050ce..05b2ade 100644
--- a/src/windows/voice/voicewindow.cpp
+++ b/src/windows/voice/voicewindow.cpp
@@ -176,18 +176,21 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
combos_combos->pack_start(m_playback_combo);
combos_combos->pack_start(m_capture_combo);
- discord.signal_stage_instance_create().connect([this](const StageInstance &instance) {
+ discord.signal_stage_instance_create().connect(sigc::track_obj([this](const StageInstance &instance) {
m_TMP_stagelabel.show();
m_TMP_stagelabel.set_markup("" + instance.Topic + "");
- });
+ },
+ *this));
- discord.signal_stage_instance_update().connect([this](const StageInstance &instance) {
+ discord.signal_stage_instance_update().connect(sigc::track_obj([this](const StageInstance &instance) {
m_TMP_stagelabel.set_markup("" + instance.Topic + "");
- });
+ },
+ *this));
- discord.signal_stage_instance_delete().connect([this](const StageInstance &instance) {
+ discord.signal_stage_instance_delete().connect(sigc::track_obj([this](const StageInstance &instance) {
m_TMP_stagelabel.hide();
- });
+ },
+ *this));
m_scroll.add(m_user_list);
m_controls.add(m_mute);
--
cgit v1.2.3
From 533157ece22c020dbf09d991b23d1cf4e5281e7a Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Thu, 14 Mar 2024 01:08:13 -0400
Subject: preliminary speaker checks
---
.../channellist/cellrendererchannels.hpp | 2 +-
src/components/channellist/channellisttree.cpp | 2 +-
src/discord/discord.cpp | 9 +++++--
src/discord/discord.hpp | 7 +++---
src/discord/objects.cpp | 1 +
src/discord/objects.hpp | 1 +
src/discord/voicestate.cpp | 5 ++++
src/discord/voicestate.hpp | 29 ++++++++++++++++++++++
src/discord/voicestateflags.hpp | 19 --------------
src/misc/bitwise.hpp | 7 ++++++
src/windows/voice/voicewindow.cpp | 18 ++++++++++----
src/windows/voice/voicewindow.hpp | 1 +
12 files changed, 70 insertions(+), 31 deletions(-)
create mode 100644 src/discord/voicestate.cpp
create mode 100644 src/discord/voicestate.hpp
delete mode 100644 src/discord/voicestateflags.hpp
(limited to 'src/windows')
diff --git a/src/components/channellist/cellrendererchannels.hpp b/src/components/channellist/cellrendererchannels.hpp
index a1c020b..813a996 100644
--- a/src/components/channellist/cellrendererchannels.hpp
+++ b/src/components/channellist/cellrendererchannels.hpp
@@ -6,7 +6,7 @@
#include
#include
#include "discord/snowflake.hpp"
-#include "discord/voicestateflags.hpp"
+#include "discord/voicestate.hpp"
#include "misc/bitwise.hpp"
enum class RenderType : uint8_t {
diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp
index 8b313a3..9233941 100644
--- a/src/components/channellist/channellisttree.cpp
+++ b/src/components/channellist/channellisttree.cpp
@@ -1061,7 +1061,7 @@ Gtk::TreeModel::iterator ChannelListTree::CreateVoiceParticipantRow(const UserDa
const auto voice_state = Abaddon::Get().GetDiscordClient().GetVoiceState(user.ID);
if (voice_state.has_value()) {
- row[m_columns.m_voice_flags] = voice_state->second;
+ row[m_columns.m_voice_flags] = voice_state->second.Flags;
}
auto &img = Abaddon::Get().GetImageManager();
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp
index ab0374d..d997bc2 100644
--- a/src/discord/discord.cpp
+++ b/src/discord/discord.cpp
@@ -1290,13 +1290,18 @@ std::optional DiscordClient::GetSSRCOfUser(Snowflake id) const {
return m_voice.GetSSRCOfUser(id);
}
-std::optional> DiscordClient::GetVoiceState(Snowflake user_id) const {
+std::optional> DiscordClient::GetVoiceState(Snowflake user_id) const {
if (const auto it = m_voice_states.find(user_id); it != m_voice_states.end()) {
return it->second;
}
return std::nullopt;
}
+bool DiscordClient::IsUserSpeaker(Snowflake user_id) const {
+ const auto state = GetVoiceState(user_id);
+ return state.has_value() && state->second.IsSpeaker();
+}
+
DiscordVoiceClient &DiscordClient::GetVoiceClient() {
return m_voice;
}
@@ -2962,7 +2967,7 @@ void DiscordClient::SetVoiceState(Snowflake user_id, const VoiceState &state) {
if (state.IsSelfVideo) flags |= VoiceStateFlags::SelfVideo;
if (state.IsSuppressed) flags |= VoiceStateFlags::Suppressed;
- m_voice_states[user_id] = std::make_pair(*state.ChannelID, flags);
+ m_voice_states[user_id] = std::make_pair(*state.ChannelID, PackedVoiceState { flags, state.RequestToSpeakTimestamp });
m_voice_state_channel_users[*state.ChannelID].insert(user_id);
m_signal_voice_state_set.emit(user_id, *state.ChannelID, flags);
diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp
index a74b5f6..155079f 100644
--- a/src/discord/discord.hpp
+++ b/src/discord/discord.hpp
@@ -5,7 +5,7 @@
#include "objects.hpp"
#include "store.hpp"
#include "voiceclient.hpp"
-#include "voicestateflags.hpp"
+#include "voicestate.hpp"
#include "websocket.hpp"
#include
#include
@@ -202,7 +202,8 @@ public:
[[nodiscard]] Snowflake GetVoiceChannelID() const noexcept;
[[nodiscard]] std::unordered_set GetUsersInVoiceChannel(Snowflake channel_id);
[[nodiscard]] std::optional GetSSRCOfUser(Snowflake id) const;
- [[nodiscard]] std::optional> GetVoiceState(Snowflake user_id) const;
+ [[nodiscard]] std::optional> GetVoiceState(Snowflake user_id) const;
+ [[nodiscard]] bool IsUserSpeaker(Snowflake user_id) const;
DiscordVoiceClient &GetVoiceClient();
@@ -380,7 +381,7 @@ private:
Snowflake m_voice_channel_id;
// todo sql i guess
- std::unordered_map> m_voice_states;
+ std::unordered_map> m_voice_states;
std::unordered_map> m_voice_state_channel_users;
void SendVoiceStateUpdate();
diff --git a/src/discord/objects.cpp b/src/discord/objects.cpp
index 804f10d..1c5dd39 100644
--- a/src/discord/objects.cpp
+++ b/src/discord/objects.cpp
@@ -714,4 +714,5 @@ void from_json(const nlohmann::json &j, VoiceState &m) {
JS_D("user_id", m.UserID);
JS_ON("member", m.Member);
JS_D("session_id", m.SessionID);
+ JS_ON("request_to_speak_timestamp", m.RequestToSpeakTimestamp);
}
diff --git a/src/discord/objects.hpp b/src/discord/objects.hpp
index 67474a3..e026311 100644
--- a/src/discord/objects.hpp
+++ b/src/discord/objects.hpp
@@ -921,6 +921,7 @@ struct VoiceState {
std::string SessionID;
bool IsSuppressed;
Snowflake UserID;
+ std::optional RequestToSpeakTimestamp;
friend void from_json(const nlohmann::json &j, VoiceState &m);
};
diff --git a/src/discord/voicestate.cpp b/src/discord/voicestate.cpp
new file mode 100644
index 0000000..05c050d
--- /dev/null
+++ b/src/discord/voicestate.cpp
@@ -0,0 +1,5 @@
+#include "voicestate.hpp"
+
+bool PackedVoiceState::IsSpeaker() const noexcept {
+ return ((Flags & VoiceStateFlags::Suppressed) != VoiceStateFlags::Suppressed) && !RequestToSpeakTimestamp.has_value();
+}
diff --git a/src/discord/voicestate.hpp b/src/discord/voicestate.hpp
new file mode 100644
index 0000000..cc75b0c
--- /dev/null
+++ b/src/discord/voicestate.hpp
@@ -0,0 +1,29 @@
+#pragma once
+#include
+#include
+#include
+#include "misc/bitwise.hpp"
+
+// this is packed into a enum cuz it makes implementing tree models easier
+enum class VoiceStateFlags : uint8_t {
+ Clear = 0,
+ Deaf = 1 << 0,
+ Mute = 1 << 1,
+ SelfDeaf = 1 << 2,
+ SelfMute = 1 << 3,
+ SelfStream = 1 << 4,
+ SelfVideo = 1 << 5,
+ Suppressed = 1 << 6,
+};
+
+struct PackedVoiceState {
+ VoiceStateFlags Flags;
+ std::optional RequestToSpeakTimestamp;
+
+ [[nodiscard]] bool IsSpeaker() const noexcept;
+};
+
+template<>
+struct Bitwise {
+ static const bool enable = true;
+};
diff --git a/src/discord/voicestateflags.hpp b/src/discord/voicestateflags.hpp
deleted file mode 100644
index e725d9a..0000000
--- a/src/discord/voicestateflags.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-#include
-#include "misc/bitwise.hpp"
-
-enum class VoiceStateFlags : uint8_t {
- Clear = 0,
- Deaf = 1 << 0,
- Mute = 1 << 1,
- SelfDeaf = 1 << 2,
- SelfMute = 1 << 3,
- SelfStream = 1 << 4,
- SelfVideo = 1 << 5,
- Suppressed = 1 << 6,
-};
-
-template<>
-struct Bitwise {
- static const bool enable = true;
-};
diff --git a/src/misc/bitwise.hpp b/src/misc/bitwise.hpp
index ecce333..4d4cf8f 100644
--- a/src/misc/bitwise.hpp
+++ b/src/misc/bitwise.hpp
@@ -1,6 +1,13 @@
#pragma once
#include
+namespace util {
+template
+bool FlagSet(T flags, T value) {
+ return (flags & value) == value;
+}
+} // namespace util
+
template
struct Bitwise {
static const bool enable = false;
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
index 05b2ade..1ada8ee 100644
--- a/src/windows/voice/voicewindow.cpp
+++ b/src/windows/voice/voicewindow.cpp
@@ -29,14 +29,17 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
auto &discord = Abaddon::Get().GetDiscordClient();
auto &audio = Abaddon::Get().GetAudio();
+ const auto channel = discord.GetChannel(m_channel_id);
+ m_is_stage = channel.has_value() && channel->Type == ChannelType::GUILD_STAGE_VOICE;
+
SetUsers(discord.GetUsersInVoiceChannel(m_channel_id));
discord.signal_voice_user_disconnect().connect(sigc::mem_fun(*this, &VoiceWindow::OnUserDisconnect));
discord.signal_voice_user_connect().connect(sigc::mem_fun(*this, &VoiceWindow::OnUserConnect));
if (const auto self_state = discord.GetVoiceState(discord.GetUserData().ID); self_state.has_value()) {
- m_mute.set_active((self_state->second & VoiceStateFlags::SelfMute) == VoiceStateFlags::SelfMute);
- m_deafen.set_active((self_state->second & VoiceStateFlags::SelfDeaf) == VoiceStateFlags::SelfDeaf);
+ m_mute.set_active(util::FlagSet(self_state->second.Flags, VoiceStateFlags::SelfMute));
+ m_deafen.set_active(util::FlagSet(self_state->second.Flags, VoiceStateFlags::SelfDeaf));
}
m_mute.signal_toggled().connect(sigc::mem_fun(*this, &VoiceWindow::OnMuteChanged));
@@ -214,10 +217,13 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
}
void VoiceWindow::SetUsers(const std::unordered_set &user_ids) {
- const auto me = Abaddon::Get().GetDiscordClient().GetUserData().ID;
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ const auto me = discord.GetUserData().ID;
for (auto id : user_ids) {
if (id == me) continue;
- m_user_list.add(*CreateRow(id));
+ if (discord.IsUserSpeaker(id)) {
+ m_user_list.add(*CreateRow(id));
+ }
}
}
@@ -283,7 +289,9 @@ void VoiceWindow::UpdateVADParamValue() {
void VoiceWindow::OnUserConnect(Snowflake user_id, Snowflake to_channel_id) {
if (m_channel_id == to_channel_id) {
if (auto it = m_rows.find(user_id); it == m_rows.end()) {
- m_user_list.add(*CreateRow(user_id));
+ if (Abaddon::Get().GetDiscordClient().IsUserSpeaker(user_id)) {
+ m_user_list.add(*CreateRow(user_id));
+ }
}
}
}
diff --git a/src/windows/voice/voicewindow.hpp b/src/windows/voice/voicewindow.hpp
index 202a0ac..5d73e97 100644
--- a/src/windows/voice/voicewindow.hpp
+++ b/src/windows/voice/voicewindow.hpp
@@ -62,6 +62,7 @@ private:
Gtk::ComboBox m_capture_combo;
Snowflake m_channel_id;
+ bool m_is_stage;
std::unordered_map m_rows;
--
cgit v1.2.3
From eca1a9f0e07b9c942502e3b4ed797d61f23ccc15 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Thu, 14 Mar 2024 01:38:36 -0400
Subject: track stage speakers only
---
src/discord/discord.cpp | 15 ++++++++++++---
src/discord/discord.hpp | 3 +++
src/windows/voice/voicewindow.cpp | 15 +++++++++++++++
src/windows/voice/voicewindow.hpp | 1 +
4 files changed, 31 insertions(+), 3 deletions(-)
(limited to 'src/windows')
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp
index d997bc2..40af498 100644
--- a/src/discord/discord.cpp
+++ b/src/discord/discord.cpp
@@ -2426,9 +2426,14 @@ void DiscordClient::CheckVoiceState(const VoiceState &data) {
if (data.ChannelID.has_value()) {
const auto old_state = GetVoiceState(data.UserID);
SetVoiceState(data.UserID, data);
- if (old_state.has_value() && old_state->first != *data.ChannelID) {
- m_signal_voice_user_disconnect.emit(data.UserID, old_state->first);
- m_signal_voice_user_connect.emit(data.UserID, *data.ChannelID);
+ const auto new_state = GetVoiceState(data.UserID);
+ if (old_state.has_value()) {
+ if (old_state->first != *data.ChannelID) {
+ m_signal_voice_user_disconnect.emit(data.UserID, old_state->first);
+ m_signal_voice_user_connect.emit(data.UserID, *data.ChannelID);
+ } else if (old_state->second.IsSpeaker() != new_state.value().second.IsSpeaker()) {
+ m_signal_voice_speaker_state_changed.emit(*data.ChannelID, data.UserID, new_state->second.IsSpeaker());
+ }
} else if (!old_state.has_value()) {
m_signal_voice_user_connect.emit(data.UserID, *data.ChannelID);
}
@@ -3308,4 +3313,8 @@ DiscordClient::type_signal_voice_channel_changed DiscordClient::signal_voice_cha
DiscordClient::type_signal_voice_state_set DiscordClient::signal_voice_state_set() {
return m_signal_voice_state_set;
}
+
+DiscordClient::type_signal_voice_speaker_state_changed DiscordClient::signal_voice_speaker_state_changed() {
+ return m_signal_voice_speaker_state_changed;
+}
#endif
diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp
index 155079f..ed7245e 100644
--- a/src/discord/discord.hpp
+++ b/src/discord/discord.hpp
@@ -480,6 +480,7 @@ public:
using type_signal_voice_client_state_update = sigc::signal;
using type_signal_voice_channel_changed = sigc::signal;
using type_signal_voice_state_set = sigc::signal;
+ using type_signal_voice_speaker_state_changed = sigc::signal;
#endif
type_signal_gateway_ready signal_gateway_ready();
@@ -551,6 +552,7 @@ public:
type_signal_voice_client_state_update signal_voice_client_state_update();
type_signal_voice_channel_changed signal_voice_channel_changed();
type_signal_voice_state_set signal_voice_state_set();
+ type_signal_voice_speaker_state_changed signal_voice_speaker_state_changed();
#endif
protected:
@@ -623,5 +625,6 @@ protected:
type_signal_voice_client_state_update m_signal_voice_client_state_update;
type_signal_voice_channel_changed m_signal_voice_channel_changed;
type_signal_voice_state_set m_signal_voice_state_set;
+ type_signal_voice_speaker_state_changed m_signal_voice_speaker_state_changed;
#endif
};
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
index 1ada8ee..079d0f7 100644
--- a/src/windows/voice/voicewindow.cpp
+++ b/src/windows/voice/voicewindow.cpp
@@ -36,6 +36,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
discord.signal_voice_user_disconnect().connect(sigc::mem_fun(*this, &VoiceWindow::OnUserDisconnect));
discord.signal_voice_user_connect().connect(sigc::mem_fun(*this, &VoiceWindow::OnUserConnect));
+ discord.signal_voice_speaker_state_changed().connect(sigc::mem_fun(*this, &VoiceWindow::OnSpeakerStateChanged));
if (const auto self_state = discord.GetVoiceState(discord.GetUserData().ID); self_state.has_value()) {
m_mute.set_active(util::FlagSet(self_state->second.Flags, VoiceStateFlags::SelfMute));
@@ -305,6 +306,20 @@ void VoiceWindow::OnUserDisconnect(Snowflake user_id, Snowflake from_channel_id)
}
}
+void VoiceWindow::OnSpeakerStateChanged(Snowflake channel_id, Snowflake user_id, bool is_speaker) {
+ if (m_channel_id != channel_id) return;
+ if (is_speaker) {
+ if (auto it = m_rows.find(user_id); it == m_rows.end()) {
+ m_user_list.add(*CreateRow(user_id));
+ }
+ } else {
+ if (auto it = m_rows.find(user_id); it != m_rows.end()) {
+ delete it->second;
+ m_rows.erase(it);
+ }
+ }
+}
+
VoiceWindow::type_signal_mute VoiceWindow::signal_mute() {
return m_signal_mute;
}
diff --git a/src/windows/voice/voicewindow.hpp b/src/windows/voice/voicewindow.hpp
index 5d73e97..fea998c 100644
--- a/src/windows/voice/voicewindow.hpp
+++ b/src/windows/voice/voicewindow.hpp
@@ -29,6 +29,7 @@ private:
void OnUserConnect(Snowflake user_id, Snowflake to_channel_id);
void OnUserDisconnect(Snowflake user_id, Snowflake from_channel_id);
+ void OnSpeakerStateChanged(Snowflake channel_id, Snowflake user_id, bool is_speaker);
void OnMuteChanged();
void OnDeafenChanged();
--
cgit v1.2.3
From 1367e162c0581bdea7ec1d2129b18e70d7d43d2b Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Tue, 19 Mar 2024 21:59:50 -0400
Subject: separate audience section in voice
---
src/windows/voice/voicewindow.cpp | 61 ++++++++++++++--------
src/windows/voice/voicewindow.hpp | 15 ++++--
src/windows/voice/voicewindowaudiencelistentry.cpp | 22 ++++++++
src/windows/voice/voicewindowaudiencelistentry.hpp | 18 +++++++
src/windows/voice/voicewindowspeakerlistentry.cpp | 58 ++++++++++++++++++++
src/windows/voice/voicewindowspeakerlistentry.hpp | 38 ++++++++++++++
src/windows/voice/voicewindowuserlistentry.cpp | 58 --------------------
src/windows/voice/voicewindowuserlistentry.hpp | 38 --------------
8 files changed, 185 insertions(+), 123 deletions(-)
create mode 100644 src/windows/voice/voicewindowaudiencelistentry.cpp
create mode 100644 src/windows/voice/voicewindowaudiencelistentry.hpp
create mode 100644 src/windows/voice/voicewindowspeakerlistentry.cpp
create mode 100644 src/windows/voice/voicewindowspeakerlistentry.hpp
delete mode 100644 src/windows/voice/voicewindowuserlistentry.cpp
delete mode 100644 src/windows/voice/voicewindowuserlistentry.hpp
(limited to 'src/windows')
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
index 079d0f7..5e91aad 100644
--- a/src/windows/voice/voicewindow.cpp
+++ b/src/windows/voice/voicewindow.cpp
@@ -7,7 +7,8 @@
#include "abaddon.hpp"
#include "audio/manager.hpp"
#include "components/lazyimage.hpp"
-#include "voicewindowuserlistentry.hpp"
+#include "voicewindowaudiencelistentry.hpp"
+#include "voicewindowspeakerlistentry.hpp"
#include "windows/voicesettingswindow.hpp"
// clang-format on
@@ -196,7 +197,13 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
},
*this));
- m_scroll.add(m_user_list);
+ m_TMP_speakers_label.set_markup("Speakers");
+ m_listing.pack_start(m_TMP_speakers_label, false, true);
+ m_listing.pack_start(m_speakers_list, false, true);
+ m_TMP_audience_label.set_markup("Audience");
+ m_listing.pack_start(m_TMP_audience_label, false, true);
+ m_listing.pack_start(m_audience_list, false, true);
+ m_scroll.add(m_listing);
m_controls.add(m_mute);
m_controls.add(m_deafen);
m_controls.add(m_noise_suppression);
@@ -221,15 +228,16 @@ void VoiceWindow::SetUsers(const std::unordered_set &user_ids) {
auto &discord = Abaddon::Get().GetDiscordClient();
const auto me = discord.GetUserData().ID;
for (auto id : user_ids) {
- if (id == me) continue;
if (discord.IsUserSpeaker(id)) {
- m_user_list.add(*CreateRow(id));
+ if (id != me) m_speakers_list.add(*CreateSpeakerRow(id));
+ } else {
+ m_audience_list.add(*CreateAudienceRow(id));
}
}
}
-Gtk::ListBoxRow *VoiceWindow::CreateRow(Snowflake id) {
- auto *row = Gtk::make_managed(id);
+Gtk::ListBoxRow *VoiceWindow::CreateSpeakerRow(Snowflake id) {
+ auto *row = Gtk::make_managed(id);
m_rows[id] = row;
auto &vc = Abaddon::Get().GetDiscordClient().GetVoiceClient();
row->RestoreGain(vc.GetUserVolume(id));
@@ -239,7 +247,14 @@ Gtk::ListBoxRow *VoiceWindow::CreateRow(Snowflake id) {
row->signal_volume().connect([this, id](double volume) {
m_signal_user_volume_changed.emit(id, volume);
});
- row->show_all();
+ row->show();
+ return row;
+}
+
+Gtk::ListBoxRow *VoiceWindow::CreateAudienceRow(Snowflake id) {
+ auto *row = Gtk::make_managed(id);
+ m_rows[id] = row;
+ row->show();
return row;
}
@@ -251,6 +266,13 @@ void VoiceWindow::OnDeafenChanged() {
m_signal_deafen.emit(m_deafen.get_active());
}
+void VoiceWindow::TryDeleteRow(Snowflake id) {
+ if (auto it = m_rows.find(id); it != m_rows.end()) {
+ delete it->second;
+ m_rows.erase(it);
+ }
+}
+
bool VoiceWindow::UpdateVoiceMeters() {
auto &audio = Abaddon::Get().GetAudio();
switch (audio.GetVADMethod()) {
@@ -267,7 +289,9 @@ bool VoiceWindow::UpdateVoiceMeters() {
for (auto [id, row] : m_rows) {
const auto ssrc = Abaddon::Get().GetDiscordClient().GetSSRCOfUser(id);
if (ssrc.has_value()) {
- row->SetVolumeMeter(audio.GetSSRCVolumeLevel(*ssrc));
+ if (auto *speaker_row = dynamic_cast(row)) {
+ speaker_row->SetVolumeMeter(audio.GetSSRCVolumeLevel(*ssrc));
+ }
}
}
return true;
@@ -291,32 +315,25 @@ void VoiceWindow::OnUserConnect(Snowflake user_id, Snowflake to_channel_id) {
if (m_channel_id == to_channel_id) {
if (auto it = m_rows.find(user_id); it == m_rows.end()) {
if (Abaddon::Get().GetDiscordClient().IsUserSpeaker(user_id)) {
- m_user_list.add(*CreateRow(user_id));
+ m_speakers_list.add(*CreateSpeakerRow(user_id));
+ } else {
+ m_audience_list.add(*CreateAudienceRow(user_id));
}
}
}
}
void VoiceWindow::OnUserDisconnect(Snowflake user_id, Snowflake from_channel_id) {
- if (m_channel_id == from_channel_id) {
- if (auto it = m_rows.find(user_id); it != m_rows.end()) {
- delete it->second;
- m_rows.erase(it);
- }
- }
+ if (m_channel_id == from_channel_id) TryDeleteRow(user_id);
}
void VoiceWindow::OnSpeakerStateChanged(Snowflake channel_id, Snowflake user_id, bool is_speaker) {
if (m_channel_id != channel_id) return;
+ TryDeleteRow(user_id);
if (is_speaker) {
- if (auto it = m_rows.find(user_id); it == m_rows.end()) {
- m_user_list.add(*CreateRow(user_id));
- }
+ m_speakers_list.add(*CreateSpeakerRow(user_id));
} else {
- if (auto it = m_rows.find(user_id); it != m_rows.end()) {
- delete it->second;
- m_rows.erase(it);
- }
+ m_audience_list.add(*CreateAudienceRow(user_id));
}
}
diff --git a/src/windows/voice/voicewindow.hpp b/src/windows/voice/voicewindow.hpp
index fea998c..7008f9a 100644
--- a/src/windows/voice/voicewindow.hpp
+++ b/src/windows/voice/voicewindow.hpp
@@ -16,8 +16,6 @@
#include
// clang-format on
-class VoiceWindowUserListEntry;
-
class VoiceWindow : public Gtk::Window {
public:
VoiceWindow(Snowflake channel_id);
@@ -25,7 +23,8 @@ public:
private:
void SetUsers(const std::unordered_set &user_ids);
- Gtk::ListBoxRow *CreateRow(Snowflake id);
+ Gtk::ListBoxRow *CreateSpeakerRow(Snowflake id);
+ Gtk::ListBoxRow *CreateAudienceRow(Snowflake id);
void OnUserConnect(Snowflake user_id, Snowflake to_channel_id);
void OnUserDisconnect(Snowflake user_id, Snowflake from_channel_id);
@@ -34,6 +33,8 @@ private:
void OnMuteChanged();
void OnDeafenChanged();
+ void TryDeleteRow(Snowflake id);
+
bool UpdateVoiceMeters();
void UpdateVADParamValue();
@@ -45,7 +46,9 @@ private:
Gtk::CheckButton m_deafen;
Gtk::ScrolledWindow m_scroll;
- Gtk::ListBox m_user_list;
+ Gtk::VBox m_listing;
+ Gtk::ListBox m_speakers_list;
+ Gtk::ListBox m_audience_list;
// Shows volume for gate VAD method
// Shows probability for RNNoise VAD method
@@ -65,7 +68,7 @@ private:
Snowflake m_channel_id;
bool m_is_stage;
- std::unordered_map m_rows;
+ std::unordered_map m_rows;
Gtk::MenuBar m_menu_bar;
Gtk::MenuItem m_menu_view;
@@ -73,6 +76,8 @@ private:
Gtk::MenuItem m_menu_view_settings;
Gtk::Label m_TMP_stagelabel;
+ Gtk::Label m_TMP_speakers_label;
+ Gtk::Label m_TMP_audience_label;
public:
using type_signal_mute = sigc::signal;
diff --git a/src/windows/voice/voicewindowaudiencelistentry.cpp b/src/windows/voice/voicewindowaudiencelistentry.cpp
new file mode 100644
index 0000000..aa7dad6
--- /dev/null
+++ b/src/windows/voice/voicewindowaudiencelistentry.cpp
@@ -0,0 +1,22 @@
+#include "voicewindowaudiencelistentry.hpp"
+
+VoiceWindowAudienceListEntry::VoiceWindowAudienceListEntry(Snowflake id)
+ : m_main(Gtk::ORIENTATION_HORIZONTAL)
+ , m_avatar(32, 32) {
+ m_name.set_halign(Gtk::ALIGN_START);
+ m_name.set_hexpand(true);
+
+ m_main.add(m_avatar);
+ m_main.add(m_name);
+ add(m_main);
+ show_all_children();
+
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ const auto user = discord.GetUser(id);
+ if (user.has_value()) {
+ m_name.set_text(user->GetUsername());
+ m_avatar.SetURL(user->GetAvatarURL("png", "32"));
+ } else {
+ m_name.set_text("Unknown user");
+ }
+}
diff --git a/src/windows/voice/voicewindowaudiencelistentry.hpp b/src/windows/voice/voicewindowaudiencelistentry.hpp
new file mode 100644
index 0000000..e7bdbb1
--- /dev/null
+++ b/src/windows/voice/voicewindowaudiencelistentry.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "components/lazyimage.hpp"
+#include "discord/snowflake.hpp"
+
+#include
+#include
+#include
+
+class VoiceWindowAudienceListEntry : public Gtk::ListBoxRow {
+public:
+ VoiceWindowAudienceListEntry(Snowflake id);
+
+private:
+ Gtk::Box m_main;
+ LazyImage m_avatar;
+ Gtk::Label m_name;
+};
diff --git a/src/windows/voice/voicewindowspeakerlistentry.cpp b/src/windows/voice/voicewindowspeakerlistentry.cpp
new file mode 100644
index 0000000..a7bf2b8
--- /dev/null
+++ b/src/windows/voice/voicewindowspeakerlistentry.cpp
@@ -0,0 +1,58 @@
+#include "voicewindowspeakerlistentry.hpp"
+
+#include "abaddon.hpp"
+
+VoiceWindowSpeakerListEntry::VoiceWindowSpeakerListEntry(Snowflake id)
+ : m_main(Gtk::ORIENTATION_VERTICAL)
+ , m_horz(Gtk::ORIENTATION_HORIZONTAL)
+ , m_avatar(32, 32)
+ , m_mute("Mute") {
+ m_name.set_halign(Gtk::ALIGN_START);
+ m_name.set_hexpand(true);
+ m_mute.set_halign(Gtk::ALIGN_END);
+
+ m_volume.set_range(0.0, 200.0);
+ m_volume.set_value_pos(Gtk::POS_LEFT);
+ m_volume.set_value(100.0);
+ m_volume.signal_value_changed().connect([this]() {
+ m_signal_volume.emit(m_volume.get_value() * 0.01);
+ });
+
+ m_horz.add(m_avatar);
+ m_horz.add(m_name);
+ m_horz.add(m_mute);
+ m_main.add(m_horz);
+ m_main.add(m_volume);
+ m_main.add(m_meter);
+ add(m_main);
+ show_all_children();
+
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ const auto user = discord.GetUser(id);
+ if (user.has_value()) {
+ m_name.set_text(user->GetUsername());
+ m_avatar.SetURL(user->GetAvatarURL("png", "32"));
+ } else {
+ m_name.set_text("Unknown user");
+ }
+
+ m_mute.signal_toggled().connect([this]() {
+ m_signal_mute_cs.emit(m_mute.get_active());
+ });
+}
+
+void VoiceWindowSpeakerListEntry::SetVolumeMeter(double frac) {
+ m_meter.SetVolume(frac);
+}
+
+void VoiceWindowSpeakerListEntry::RestoreGain(double frac) {
+ m_volume.set_value(frac * 100.0);
+}
+
+VoiceWindowSpeakerListEntry::type_signal_mute_cs VoiceWindowSpeakerListEntry::signal_mute_cs() {
+ return m_signal_mute_cs;
+}
+
+VoiceWindowSpeakerListEntry::type_signal_volume VoiceWindowSpeakerListEntry::signal_volume() {
+ return m_signal_volume;
+}
diff --git a/src/windows/voice/voicewindowspeakerlistentry.hpp b/src/windows/voice/voicewindowspeakerlistentry.hpp
new file mode 100644
index 0000000..a3b6429
--- /dev/null
+++ b/src/windows/voice/voicewindowspeakerlistentry.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "components/lazyimage.hpp"
+#include "components/volumemeter.hpp"
+#include "discord/snowflake.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+class VoiceWindowSpeakerListEntry : public Gtk::ListBoxRow {
+public:
+ VoiceWindowSpeakerListEntry(Snowflake id);
+
+ void SetVolumeMeter(double frac);
+ void RestoreGain(double frac);
+
+private:
+ Gtk::Box m_main;
+ Gtk::Box m_horz;
+ LazyImage m_avatar;
+ Gtk::Label m_name;
+ Gtk::CheckButton m_mute;
+ Gtk::Scale m_volume;
+ VolumeMeter m_meter;
+
+public:
+ using type_signal_mute_cs = sigc::signal;
+ using type_signal_volume = sigc::signal;
+ type_signal_mute_cs signal_mute_cs();
+ type_signal_volume signal_volume();
+
+private:
+ type_signal_mute_cs m_signal_mute_cs;
+ type_signal_volume m_signal_volume;
+};
diff --git a/src/windows/voice/voicewindowuserlistentry.cpp b/src/windows/voice/voicewindowuserlistentry.cpp
deleted file mode 100644
index 97a3031..0000000
--- a/src/windows/voice/voicewindowuserlistentry.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include "voicewindowuserlistentry.hpp"
-
-#include "abaddon.hpp"
-
-VoiceWindowUserListEntry::VoiceWindowUserListEntry(Snowflake id)
- : m_main(Gtk::ORIENTATION_VERTICAL)
- , m_horz(Gtk::ORIENTATION_HORIZONTAL)
- , m_avatar(32, 32)
- , m_mute("Mute") {
- m_name.set_halign(Gtk::ALIGN_START);
- m_name.set_hexpand(true);
- m_mute.set_halign(Gtk::ALIGN_END);
-
- m_volume.set_range(0.0, 200.0);
- m_volume.set_value_pos(Gtk::POS_LEFT);
- m_volume.set_value(100.0);
- m_volume.signal_value_changed().connect([this]() {
- m_signal_volume.emit(m_volume.get_value() * 0.01);
- });
-
- m_horz.add(m_avatar);
- m_horz.add(m_name);
- m_horz.add(m_mute);
- m_main.add(m_horz);
- m_main.add(m_volume);
- m_main.add(m_meter);
- add(m_main);
- show_all_children();
-
- auto &discord = Abaddon::Get().GetDiscordClient();
- const auto user = discord.GetUser(id);
- if (user.has_value()) {
- m_name.set_text(user->GetUsername());
- m_avatar.SetURL(user->GetAvatarURL("png", "32"));
- } else {
- m_name.set_text("Unknown user");
- }
-
- m_mute.signal_toggled().connect([this]() {
- m_signal_mute_cs.emit(m_mute.get_active());
- });
-}
-
-void VoiceWindowUserListEntry::SetVolumeMeter(double frac) {
- m_meter.SetVolume(frac);
-}
-
-void VoiceWindowUserListEntry::RestoreGain(double frac) {
- m_volume.set_value(frac * 100.0);
-}
-
-VoiceWindowUserListEntry::type_signal_mute_cs VoiceWindowUserListEntry::signal_mute_cs() {
- return m_signal_mute_cs;
-}
-
-VoiceWindowUserListEntry::type_signal_volume VoiceWindowUserListEntry::signal_volume() {
- return m_signal_volume;
-}
diff --git a/src/windows/voice/voicewindowuserlistentry.hpp b/src/windows/voice/voicewindowuserlistentry.hpp
deleted file mode 100644
index 4e8c028..0000000
--- a/src/windows/voice/voicewindowuserlistentry.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include "components/lazyimage.hpp"
-#include "components/volumemeter.hpp"
-#include "discord/snowflake.hpp"
-
-#include
-#include
-#include
-#include
-#include
-
-class VoiceWindowUserListEntry : public Gtk::ListBoxRow {
-public:
- VoiceWindowUserListEntry(Snowflake id);
-
- void SetVolumeMeter(double frac);
- void RestoreGain(double frac);
-
-private:
- Gtk::Box m_main;
- Gtk::Box m_horz;
- LazyImage m_avatar;
- Gtk::Label m_name;
- Gtk::CheckButton m_mute;
- Gtk::Scale m_volume;
- VolumeMeter m_meter;
-
-public:
- using type_signal_mute_cs = sigc::signal;
- using type_signal_volume = sigc::signal;
- type_signal_mute_cs signal_mute_cs();
- type_signal_volume signal_volume();
-
-private:
- type_signal_mute_cs m_signal_mute_cs;
- type_signal_volume m_signal_volume;
-};
--
cgit v1.2.3
From 565213450a891d728b3b46102e3c485cdfa86844 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sun, 31 Mar 2024 22:03:25 -0400
Subject: include
---
src/windows/voice/voicewindowaudiencelistentry.cpp | 1 +
1 file changed, 1 insertion(+)
(limited to 'src/windows')
diff --git a/src/windows/voice/voicewindowaudiencelistentry.cpp b/src/windows/voice/voicewindowaudiencelistentry.cpp
index aa7dad6..cf93343 100644
--- a/src/windows/voice/voicewindowaudiencelistentry.cpp
+++ b/src/windows/voice/voicewindowaudiencelistentry.cpp
@@ -1,4 +1,5 @@
#include "voicewindowaudiencelistentry.hpp"
+#include "abaddon.hpp"
VoiceWindowAudienceListEntry::VoiceWindowAudienceListEntry(Snowflake id)
: m_main(Gtk::ORIENTATION_HORIZONTAL)
--
cgit v1.2.3
From 3dc8fa8e65bdd1bd45dfa0171a06dfbb448baf0d Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Tue, 23 Apr 2024 22:14:14 -0400
Subject: show thingy topic right away
---
src/discord/discord.cpp | 9 +++++++++
src/discord/discord.hpp | 1 +
src/windows/voice/voicewindow.cpp | 12 +++++++++---
3 files changed, 19 insertions(+), 3 deletions(-)
(limited to 'src/windows')
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp
index b3f8ca6..c54583a 100644
--- a/src/discord/discord.cpp
+++ b/src/discord/discord.cpp
@@ -358,6 +358,14 @@ std::optional DiscordClient::GetWebhookMessageData(Snowflake
return m_store.GetWebhookMessage(message_id);
}
+std::optional DiscordClient::GetStageInstanceFromChannel(Snowflake channel_id) const {
+ const auto iter1 = m_channel_to_stage_instance.find(channel_id);
+ if (iter1 == m_channel_to_stage_instance.end()) return {};
+ const auto iter2 = m_stage_instances.find(iter1->second);
+ if (iter2 == m_stage_instances.end()) return {};
+ return iter2->second;
+}
+
bool DiscordClient::IsThreadJoined(Snowflake thread_id) const {
return std::find(m_joined_threads.begin(), m_joined_threads.end(), thread_id) != m_joined_threads.end();
}
@@ -1722,6 +1730,7 @@ void DiscordClient::ProcessNewGuild(GuildData &guild) {
if (guild.StageInstances.has_value()) {
for (const auto &stage : *guild.StageInstances) {
+ spdlog::get("discord")->debug("storing stage {} in channel {}", stage.ID, stage.ChannelID);
m_stage_instances[stage.ID] = stage;
m_channel_to_stage_instance[stage.ChannelID] = stage.ID;
}
diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp
index 44a7328..eca25a6 100644
--- a/src/discord/discord.hpp
+++ b/src/discord/discord.hpp
@@ -65,6 +65,7 @@ public:
void GetArchivedPrivateThreads(Snowflake channel_id, const sigc::slot &callback);
std::vector GetChildChannelIDs(Snowflake parent_id) const;
std::optional GetWebhookMessageData(Snowflake message_id) const;
+ std::optional GetStageInstanceFromChannel(Snowflake channel_id) const;
// get ids of given list of members for who we do not have the member data
template
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
index 5e91aad..8c4eb2c 100644
--- a/src/windows/voice/voicewindow.cpp
+++ b/src/windows/voice/voicewindow.cpp
@@ -181,6 +181,14 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
combos_combos->pack_start(m_playback_combo);
combos_combos->pack_start(m_capture_combo);
+ if (const auto instance = discord.GetStageInstanceFromChannel(channel_id); instance.has_value()) {
+ printf("%s\n", instance->Topic.c_str());
+ m_TMP_stagelabel.show();
+ m_TMP_stagelabel.set_markup("" + instance->Topic + "");
+ } else {
+ m_TMP_stagelabel.hide();
+ }
+
discord.signal_stage_instance_create().connect(sigc::track_obj([this](const StageInstance &instance) {
m_TMP_stagelabel.show();
m_TMP_stagelabel.set_markup("" + instance.Topic + "");
@@ -218,9 +226,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
m_main.pack_start(*combos_container, false, true, 2);
add(m_main);
show_all_children();
-
- m_TMP_stagelabel.hide();
-
+
Glib::signal_timeout().connect(sigc::mem_fun(*this, &VoiceWindow::UpdateVoiceMeters), 40);
}
--
cgit v1.2.3
From af9f9ad803fab8e293e7d783f6e84fae6c5f21cb Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Wed, 26 Jun 2024 04:26:32 -0400
Subject: request to speak button
---
src/discord/discord.cpp | 26 ++++++++++++++++++++++++++
src/discord/discord.hpp | 3 +++
src/discord/objects.cpp | 12 ++++++++++++
src/discord/objects.hpp | 8 ++++++++
src/windows/voice/voicewindow.cpp | 22 ++++++++++++++++++++--
src/windows/voice/voicewindow.hpp | 6 ++++++
6 files changed, 75 insertions(+), 2 deletions(-)
(limited to 'src/windows')
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp
index 8f88a91..2ee8493 100644
--- a/src/discord/discord.cpp
+++ b/src/discord/discord.cpp
@@ -1301,6 +1301,32 @@ bool DiscordClient::IsUserSpeaker(Snowflake user_id) const {
return state.has_value() && state->second.IsSpeaker();
}
+bool DiscordClient::HasUserRequestedToSpeak(Snowflake user_id) const {
+ const auto state = GetVoiceState(user_id);
+ return state.has_value() && state->second.RequestToSpeakTimestamp.has_value() && util::FlagSet(state->second.Flags, VoiceStateFlags::Suppressed);
+}
+
+void DiscordClient::RequestToSpeak(Snowflake channel_id, bool want, const sigc::slot &callback) {
+ if (want && !HasSelfChannelPermission(channel_id, Permission::REQUEST_TO_SPEAK)) return;
+ const auto channel = GetChannel(channel_id);
+ if (!channel.has_value() || !channel->GuildID.has_value()) return;
+
+ ModifyCurrentUserVoiceStateObject d;
+ d.ChannelID = channel_id;
+ if (want) {
+ d.RequestToSpeakTimestamp = Glib::DateTime::create_now_utc().format_iso8601();
+ } else {
+ d.RequestToSpeakTimestamp = "";
+ }
+ m_http.MakePATCH("/guilds/" + std::to_string(*channel->GuildID) + "/voice-states/@me", nlohmann::json(d).dump(), [callback](const http::response_type &response) {
+ if (CheckCode(response, 204)) {
+ callback(DiscordError::NONE);
+ } else {
+ callback(GetCodeFromResponse(response));
+ }
+ });
+}
+
DiscordVoiceClient &DiscordClient::GetVoiceClient() {
return m_voice;
}
diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp
index ab051aa..55bd308 100644
--- a/src/discord/discord.hpp
+++ b/src/discord/discord.hpp
@@ -203,6 +203,9 @@ public:
[[nodiscard]] Snowflake GetVoiceChannelID() const noexcept;
[[nodiscard]] std::optional GetSSRCOfUser(Snowflake id) const;
[[nodiscard]] bool IsUserSpeaker(Snowflake user_id) const;
+ [[nodiscard]] bool HasUserRequestedToSpeak(Snowflake user_id) const;
+
+ void RequestToSpeak(Snowflake channel_id, bool want, const sigc::slot &callback);
DiscordVoiceClient &GetVoiceClient();
diff --git a/src/discord/objects.cpp b/src/discord/objects.cpp
index 1c5dd39..e6b7675 100644
--- a/src/discord/objects.cpp
+++ b/src/discord/objects.cpp
@@ -699,6 +699,18 @@ void from_json(const nlohmann::json &j, CallCreateData &m) {
JS_D("channel_id", m.ChannelID);
JS_ON("voice_states", m.VoiceStates);
}
+
+void to_json(nlohmann::json &j, const ModifyCurrentUserVoiceStateObject &m) {
+ JS_IF("channel_id", m.ChannelID);
+ JS_IF("suppress", m.Suppress);
+ if (m.RequestToSpeakTimestamp.has_value()) {
+ if (m.RequestToSpeakTimestamp->empty()) {
+ j["request_to_speak_timestamp"] = nullptr;
+ } else {
+ j["request_to_speak_timestamp"] = *m.RequestToSpeakTimestamp;
+ }
+ }
+}
#endif
void from_json(const nlohmann::json &j, VoiceState &m) {
diff --git a/src/discord/objects.hpp b/src/discord/objects.hpp
index e026311..44afe8d 100644
--- a/src/discord/objects.hpp
+++ b/src/discord/objects.hpp
@@ -957,4 +957,12 @@ struct CallCreateData {
friend void from_json(const nlohmann::json &j, CallCreateData &m);
};
+
+struct ModifyCurrentUserVoiceStateObject {
+ std::optional ChannelID;
+ std::optional Suppress;
+ std::optional RequestToSpeakTimestamp;
+
+ friend void to_json(nlohmann::json &j, const ModifyCurrentUserVoiceStateObject &m);
+};
#endif
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
index 7607a0f..bfea175 100644
--- a/src/windows/voice/voicewindow.cpp
+++ b/src/windows/voice/voicewindow.cpp
@@ -1,3 +1,4 @@
+#include "util.hpp"
#ifdef WITH_VOICE
// clang-format off
@@ -20,6 +21,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
, m_deafen("Deafen")
, m_noise_suppression("Suppress Noise")
, m_mix_mono("Mix Mono")
+ , m_request_to_speak("Request to Speak")
, m_disconnect("Disconnect")
, m_channel_id(channel_id)
, m_menu_view("View")
@@ -39,6 +41,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
discord.signal_voice_user_disconnect().connect(sigc::mem_fun(*this, &VoiceWindow::OnUserDisconnect));
discord.signal_voice_user_connect().connect(sigc::mem_fun(*this, &VoiceWindow::OnUserConnect));
discord.signal_voice_speaker_state_changed().connect(sigc::mem_fun(*this, &VoiceWindow::OnSpeakerStateChanged));
+ discord.signal_voice_state_set().connect(sigc::mem_fun(*this, &VoiceWindow::OnVoiceStateUpdate));
if (const auto self_state = discord.GetVoiceState(discord.GetUserData().ID); self_state.has_value()) {
m_mute.set_active(util::FlagSet(self_state->second.Flags, VoiceStateFlags::SelfMute));
@@ -210,6 +213,12 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
},
*this));
+ m_request_to_speak.signal_clicked().connect([this]() {
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ const bool requested = discord.HasUserRequestedToSpeak(discord.GetUserData().ID);
+ Abaddon::Get().GetDiscordClient().RequestToSpeak(m_channel_id, !requested, NOOP_CALLBACK);
+ });
+
m_TMP_speakers_label.set_markup("Speakers");
m_listing.pack_start(m_TMP_speakers_label, false, true);
m_listing.pack_start(m_speakers_list, false, true);
@@ -221,10 +230,13 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
m_controls.add(m_deafen);
m_controls.add(m_noise_suppression);
m_controls.add(m_mix_mono);
- m_controls.pack_end(m_disconnect, false, true);
+ m_buttons.set_halign(Gtk::ALIGN_CENTER);
+ m_buttons.pack_start(m_request_to_speak, false, true);
+ m_buttons.pack_start(m_disconnect, false, true);
m_main.pack_start(m_menu_bar, false, true);
m_main.pack_start(m_TMP_stagelabel, false, true);
m_main.pack_start(m_controls, false, true);
+ m_main.pack_start(m_buttons, false, true);
m_main.pack_start(m_vad_value, false, true);
m_main.pack_start(*Gtk::make_managed("Input Settings"), false, true);
m_main.pack_start(*sliders_container, false, true);
@@ -232,7 +244,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
m_main.pack_start(*combos_container, false, true, 2);
add(m_main);
show_all_children();
-
+
Glib::signal_timeout().connect(sigc::mem_fun(*this, &VoiceWindow::UpdateVoiceMeters), 40);
}
@@ -349,6 +361,12 @@ void VoiceWindow::OnSpeakerStateChanged(Snowflake channel_id, Snowflake user_id,
}
}
+void VoiceWindow::OnVoiceStateUpdate(Snowflake user_id, Snowflake channel_id, VoiceStateFlags flags) {
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ m_has_requested_to_speak = discord.HasUserRequestedToSpeak(discord.GetUserData().ID);
+ m_request_to_speak.set_label(m_has_requested_to_speak ? "Cancel Request" : "Request to Speak");
+}
+
VoiceWindow::type_signal_mute VoiceWindow::signal_mute() {
return m_signal_mute;
}
diff --git a/src/windows/voice/voicewindow.hpp b/src/windows/voice/voicewindow.hpp
index 0df9fa8..7803f85 100644
--- a/src/windows/voice/voicewindow.hpp
+++ b/src/windows/voice/voicewindow.hpp
@@ -1,4 +1,5 @@
#pragma once
+#include "discord/voicestate.hpp"
#ifdef WITH_VOICE
// clang-format off
@@ -29,6 +30,7 @@ private:
void OnUserConnect(Snowflake user_id, Snowflake to_channel_id);
void OnUserDisconnect(Snowflake user_id, Snowflake from_channel_id);
void OnSpeakerStateChanged(Snowflake channel_id, Snowflake user_id, bool is_speaker);
+ void OnVoiceStateUpdate(Snowflake user_id, Snowflake channel_id, VoiceStateFlags flags);
void OnMuteChanged();
void OnDeafenChanged();
@@ -61,7 +63,11 @@ private:
Gtk::CheckButton m_noise_suppression;
Gtk::CheckButton m_mix_mono;
+ Gtk::HBox m_buttons;
Gtk::Button m_disconnect;
+ Gtk::Button m_request_to_speak;
+
+ bool m_has_requested_to_speak = false;
Gtk::ComboBoxText m_vad_combo;
Gtk::ComboBox m_playback_combo;
--
cgit v1.2.3
From 7f709ce89cea9540b623b97df0a50a7cefc1f823 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sun, 30 Jun 2024 19:53:15 -0400
Subject: request/speak/leave stage and stuff
---
src/discord/discord.cpp | 28 +++++++++++++++++++++++
src/discord/discord.hpp | 3 +++
src/windows/voice/voicewindow.cpp | 48 +++++++++++++++++++++++++++++++++------
src/windows/voice/voicewindow.hpp | 5 ++--
4 files changed, 74 insertions(+), 10 deletions(-)
(limited to 'src/windows')
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp
index 2ee8493..d99e57a 100644
--- a/src/discord/discord.cpp
+++ b/src/discord/discord.cpp
@@ -470,6 +470,10 @@ bool DiscordClient::CanManageMember(Snowflake guild_id, Snowflake actor, Snowfla
return actor_highest->Position > target_highest->Position;
}
+bool DiscordClient::IsStageModerator(Snowflake user_id, Snowflake channel_id) const {
+ return HasChannelPermission(user_id, channel_id, Permission::MANAGE_CHANNELS | Permission::MOVE_MEMBERS | Permission::MUTE_MEMBERS);
+}
+
void DiscordClient::ChatMessageCallback(const std::string &nonce, const http::response_type &response, const sigc::slot &callback) {
if (!CheckCode(response)) {
if (response.status_code == http::TooManyRequests) {
@@ -1306,6 +1310,11 @@ bool DiscordClient::HasUserRequestedToSpeak(Snowflake user_id) const {
return state.has_value() && state->second.RequestToSpeakTimestamp.has_value() && util::FlagSet(state->second.Flags, VoiceStateFlags::Suppressed);
}
+bool DiscordClient::IsUserInvitedToSpeak(Snowflake user_id) const {
+ const auto state = GetVoiceState(user_id);
+ return state.has_value() && state->second.RequestToSpeakTimestamp.has_value() && !util::FlagSet(state->second.Flags, VoiceStateFlags::Suppressed);
+}
+
void DiscordClient::RequestToSpeak(Snowflake channel_id, bool want, const sigc::slot &callback) {
if (want && !HasSelfChannelPermission(channel_id, Permission::REQUEST_TO_SPEAK)) return;
const auto channel = GetChannel(channel_id);
@@ -1327,6 +1336,25 @@ void DiscordClient::RequestToSpeak(Snowflake channel_id, bool want, const sigc::
});
}
+void DiscordClient::SetStageSpeaking(Snowflake channel_id, bool want, const sigc::slot &callback) {
+ const auto channel = GetChannel(channel_id);
+ if (!channel.has_value() || !channel->GuildID.has_value()) return;
+
+ ModifyCurrentUserVoiceStateObject d;
+ d.ChannelID = channel_id;
+ d.Suppress = !want;
+ if (want) {
+ d.RequestToSpeakTimestamp = "";
+ }
+ m_http.MakePATCH("/guilds/" + std::to_string(*channel->GuildID) + "/voice-states/@me", nlohmann::json(d).dump(), [callback](const http::response_type &response) {
+ if (CheckCode(response, 204)) {
+ callback(DiscordError::NONE);
+ } else {
+ callback(GetCodeFromResponse(response));
+ }
+ });
+}
+
DiscordVoiceClient &DiscordClient::GetVoiceClient() {
return m_voice;
}
diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp
index 55bd308..42fa1cb 100644
--- a/src/discord/discord.hpp
+++ b/src/discord/discord.hpp
@@ -87,6 +87,7 @@ public:
Permission ComputePermissions(Snowflake member_id, Snowflake guild_id) const;
Permission ComputeOverwrites(Permission base, Snowflake member_id, Snowflake channel_id) const;
bool CanManageMember(Snowflake guild_id, Snowflake actor, Snowflake target) const; // kick, ban, edit nickname (cant think of a better name)
+ bool IsStageModerator(Snowflake user_id, Snowflake channel_id) const;
void ChatMessageCallback(const std::string &nonce, const http::response_type &response, const sigc::slot &callback);
void SendChatMessageNoAttachments(const ChatSubmitParams ¶ms, const sigc::slot &callback);
@@ -204,8 +205,10 @@ public:
[[nodiscard]] std::optional GetSSRCOfUser(Snowflake id) const;
[[nodiscard]] bool IsUserSpeaker(Snowflake user_id) const;
[[nodiscard]] bool HasUserRequestedToSpeak(Snowflake user_id) const;
+ [[nodiscard]] bool IsUserInvitedToSpeak(Snowflake user_id) const;
void RequestToSpeak(Snowflake channel_id, bool want, const sigc::slot &callback);
+ void SetStageSpeaking(Snowflake channel_id, bool want, const sigc::slot &callback);
DiscordVoiceClient &GetVoiceClient();
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
index bfea175..27dd734 100644
--- a/src/windows/voice/voicewindow.cpp
+++ b/src/windows/voice/voicewindow.cpp
@@ -21,7 +21,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
, m_deafen("Deafen")
, m_noise_suppression("Suppress Noise")
, m_mix_mono("Mix Mono")
- , m_request_to_speak("Request to Speak")
+ , m_stage_command("Request to Speak")
, m_disconnect("Disconnect")
, m_channel_id(channel_id)
, m_menu_view("View")
@@ -213,10 +213,21 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
},
*this));
- m_request_to_speak.signal_clicked().connect([this]() {
+ m_stage_command.signal_clicked().connect([this]() {
auto &discord = Abaddon::Get().GetDiscordClient();
- const bool requested = discord.HasUserRequestedToSpeak(discord.GetUserData().ID);
- Abaddon::Get().GetDiscordClient().RequestToSpeak(m_channel_id, !requested, NOOP_CALLBACK);
+ const auto user_id = discord.GetUserData().ID;
+ const bool is_moderator = discord.IsStageModerator(user_id, m_channel_id);
+ const bool is_speaker = discord.IsUserSpeaker(user_id);
+ const bool is_invited_to_speak = discord.IsUserInvitedToSpeak(user_id);
+
+ if (is_speaker) {
+ discord.SetStageSpeaking(m_channel_id, false, NOOP_CALLBACK);
+ } else if (is_moderator || is_invited_to_speak) {
+ discord.SetStageSpeaking(m_channel_id, true, NOOP_CALLBACK);
+ } else {
+ const bool requested = discord.HasUserRequestedToSpeak(user_id);
+ discord.RequestToSpeak(m_channel_id, !requested, NOOP_CALLBACK);
+ }
});
m_TMP_speakers_label.set_markup("Speakers");
@@ -231,7 +242,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
m_controls.add(m_noise_suppression);
m_controls.add(m_mix_mono);
m_buttons.set_halign(Gtk::ALIGN_CENTER);
- m_buttons.pack_start(m_request_to_speak, false, true);
+ m_buttons.pack_start(m_stage_command, false, true);
m_buttons.pack_start(m_disconnect, false, true);
m_main.pack_start(m_menu_bar, false, true);
m_main.pack_start(m_TMP_stagelabel, false, true);
@@ -246,6 +257,8 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
show_all_children();
Glib::signal_timeout().connect(sigc::mem_fun(*this, &VoiceWindow::UpdateVoiceMeters), 40);
+
+ UpdateStageCommand();
}
void VoiceWindow::SetUsers(const std::unordered_set &user_ids) {
@@ -335,6 +348,26 @@ void VoiceWindow::UpdateVADParamValue() {
}
}
+void VoiceWindow::UpdateStageCommand() {
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ const auto user_id = discord.GetUserData().ID;
+
+ m_has_requested_to_speak = discord.HasUserRequestedToSpeak(user_id);
+ const bool is_moderator = discord.IsStageModerator(user_id, m_channel_id);
+ const bool is_speaker = discord.IsUserSpeaker(user_id);
+ const bool is_invited_to_speak = discord.IsUserInvitedToSpeak(user_id);
+
+ if (is_speaker) {
+ m_stage_command.set_label("Leave the Stage");
+ } else if (is_moderator || is_invited_to_speak) {
+ m_stage_command.set_label("Speak on Stage");
+ } else if (m_has_requested_to_speak) {
+ m_stage_command.set_label("Cancel Request");
+ } else {
+ m_stage_command.set_label("Request to Speak");
+ }
+}
+
void VoiceWindow::OnUserConnect(Snowflake user_id, Snowflake to_channel_id) {
if (m_channel_id == to_channel_id) {
if (auto it = m_rows.find(user_id); it == m_rows.end()) {
@@ -363,8 +396,9 @@ void VoiceWindow::OnSpeakerStateChanged(Snowflake channel_id, Snowflake user_id,
void VoiceWindow::OnVoiceStateUpdate(Snowflake user_id, Snowflake channel_id, VoiceStateFlags flags) {
auto &discord = Abaddon::Get().GetDiscordClient();
- m_has_requested_to_speak = discord.HasUserRequestedToSpeak(discord.GetUserData().ID);
- m_request_to_speak.set_label(m_has_requested_to_speak ? "Cancel Request" : "Request to Speak");
+ if (user_id != discord.GetUserData().ID) return;
+
+ UpdateStageCommand();
}
VoiceWindow::type_signal_mute VoiceWindow::signal_mute() {
diff --git a/src/windows/voice/voicewindow.hpp b/src/windows/voice/voicewindow.hpp
index 7803f85..5005b90 100644
--- a/src/windows/voice/voicewindow.hpp
+++ b/src/windows/voice/voicewindow.hpp
@@ -36,10 +36,9 @@ private:
void OnDeafenChanged();
void TryDeleteRow(Snowflake id);
-
bool UpdateVoiceMeters();
-
void UpdateVADParamValue();
+ void UpdateStageCommand();
Gtk::Box m_main;
Gtk::Box m_controls;
@@ -65,7 +64,7 @@ private:
Gtk::HBox m_buttons;
Gtk::Button m_disconnect;
- Gtk::Button m_request_to_speak;
+ Gtk::Button m_stage_command;
bool m_has_requested_to_speak = false;
--
cgit v1.2.3
From 3109089e8c557c1385d3e6a34d9a097f905cec00 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sun, 30 Jun 2024 20:39:11 -0400
Subject: accept/decline stage invite
---
src/discord/discord.cpp | 17 +++++++++++++++++
src/discord/discord.hpp | 1 +
src/windows/voice/voicewindow.cpp | 27 +++++++++++++++++++++++++--
src/windows/voice/voicewindow.hpp | 6 ++++++
4 files changed, 49 insertions(+), 2 deletions(-)
(limited to 'src/windows')
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp
index d99e57a..a7712d6 100644
--- a/src/discord/discord.cpp
+++ b/src/discord/discord.cpp
@@ -1355,6 +1355,23 @@ void DiscordClient::SetStageSpeaking(Snowflake channel_id, bool want, const sigc
});
}
+void DiscordClient::DeclineInviteToSpeak(Snowflake channel_id, const sigc::slot &callback) {
+ const auto channel = GetChannel(channel_id);
+ if (!channel.has_value() || !channel->GuildID.has_value()) return;
+
+ ModifyCurrentUserVoiceStateObject d;
+ d.ChannelID = channel_id;
+ d.Suppress = true;
+ d.RequestToSpeakTimestamp = "";
+ m_http.MakePATCH("/guilds/" + std::to_string(*channel->GuildID) + "/voice-states/@me", nlohmann::json(d).dump(), [callback](const http::response_type &response) {
+ if (CheckCode(response, 204)) {
+ callback(DiscordError::NONE);
+ } else {
+ callback(GetCodeFromResponse(response));
+ }
+ });
+}
+
DiscordVoiceClient &DiscordClient::GetVoiceClient() {
return m_voice;
}
diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp
index 42fa1cb..483abf0 100644
--- a/src/discord/discord.hpp
+++ b/src/discord/discord.hpp
@@ -209,6 +209,7 @@ public:
void RequestToSpeak(Snowflake channel_id, bool want, const sigc::slot &callback);
void SetStageSpeaking(Snowflake channel_id, bool want, const sigc::slot &callback);
+ void DeclineInviteToSpeak(Snowflake channel_id, const sigc::slot &callback);
DiscordVoiceClient &GetVoiceClient();
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
index 27dd734..5930156 100644
--- a/src/windows/voice/voicewindow.cpp
+++ b/src/windows/voice/voicewindow.cpp
@@ -23,6 +23,9 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
, m_mix_mono("Mix Mono")
, m_stage_command("Request to Speak")
, m_disconnect("Disconnect")
+ , m_stage_invite_lbl("You've been invited to speak")
+ , m_stage_accept("Accept")
+ , m_stage_decline("Decline")
, m_channel_id(channel_id)
, m_menu_view("View")
, m_menu_view_settings("More _Settings", true) {
@@ -222,14 +225,24 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
if (is_speaker) {
discord.SetStageSpeaking(m_channel_id, false, NOOP_CALLBACK);
- } else if (is_moderator || is_invited_to_speak) {
+ } else if (is_moderator) {
discord.SetStageSpeaking(m_channel_id, true, NOOP_CALLBACK);
+ } else if (is_invited_to_speak) {
+ discord.DeclineInviteToSpeak(m_channel_id, NOOP_CALLBACK);
} else {
const bool requested = discord.HasUserRequestedToSpeak(user_id);
discord.RequestToSpeak(m_channel_id, !requested, NOOP_CALLBACK);
}
});
+ m_stage_accept.signal_clicked().connect([this]() {
+ Abaddon::Get().GetDiscordClient().SetStageSpeaking(m_channel_id, true, NOOP_CALLBACK);
+ });
+
+ m_stage_decline.signal_clicked().connect([this]() {
+ Abaddon::Get().GetDiscordClient().DeclineInviteToSpeak(m_channel_id, NOOP_CALLBACK);
+ });
+
m_TMP_speakers_label.set_markup("Speakers");
m_listing.pack_start(m_TMP_speakers_label, false, true);
m_listing.pack_start(m_speakers_list, false, true);
@@ -244,10 +257,16 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
m_buttons.set_halign(Gtk::ALIGN_CENTER);
m_buttons.pack_start(m_stage_command, false, true);
m_buttons.pack_start(m_disconnect, false, true);
+ m_stage_invite_box.pack_start(m_stage_invite_lbl, false, true);
+ m_stage_invite_box.pack_start(m_stage_invite_btns);
+ m_stage_invite_btns.set_halign(Gtk::ALIGN_CENTER);
+ m_stage_invite_btns.pack_start(m_stage_accept, false, true);
+ m_stage_invite_btns.pack_start(m_stage_decline, false, true);
m_main.pack_start(m_menu_bar, false, true);
m_main.pack_start(m_TMP_stagelabel, false, true);
m_main.pack_start(m_controls, false, true);
m_main.pack_start(m_buttons, false, true);
+ m_main.pack_start(m_stage_invite_box, false, true);
m_main.pack_start(m_vad_value, false, true);
m_main.pack_start(*Gtk::make_managed("Input Settings"), false, true);
m_main.pack_start(*sliders_container, false, true);
@@ -357,12 +376,16 @@ void VoiceWindow::UpdateStageCommand() {
const bool is_speaker = discord.IsUserSpeaker(user_id);
const bool is_invited_to_speak = discord.IsUserInvitedToSpeak(user_id);
+ m_stage_invite_box.set_visible(is_invited_to_speak);
+
if (is_speaker) {
m_stage_command.set_label("Leave the Stage");
- } else if (is_moderator || is_invited_to_speak) {
+ } else if (is_moderator) {
m_stage_command.set_label("Speak on Stage");
} else if (m_has_requested_to_speak) {
m_stage_command.set_label("Cancel Request");
+ } else if (is_invited_to_speak) {
+ m_stage_command.set_label("Decline Invite");
} else {
m_stage_command.set_label("Request to Speak");
}
diff --git a/src/windows/voice/voicewindow.hpp b/src/windows/voice/voicewindow.hpp
index 5005b90..7c5a137 100644
--- a/src/windows/voice/voicewindow.hpp
+++ b/src/windows/voice/voicewindow.hpp
@@ -66,6 +66,12 @@ private:
Gtk::Button m_disconnect;
Gtk::Button m_stage_command;
+ Gtk::VBox m_stage_invite_box;
+ Gtk::Label m_stage_invite_lbl;
+ Gtk::HBox m_stage_invite_btns;
+ Gtk::Button m_stage_accept;
+ Gtk::Button m_stage_decline;
+
bool m_has_requested_to_speak = false;
Gtk::ComboBoxText m_vad_combo;
--
cgit v1.2.3
From d685fdc48822c2604aebc34903ce348591cdddfb Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Thu, 4 Jul 2024 00:48:46 -0400
Subject: make non-stage voice ui normal again
---
src/windows/voice/voicewindow.cpp | 14 +++++++-------
src/windows/voice/voicewindow.hpp | 4 ++--
2 files changed, 9 insertions(+), 9 deletions(-)
(limited to 'src/windows')
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
index 5930156..944d848 100644
--- a/src/windows/voice/voicewindow.cpp
+++ b/src/windows/voice/voicewindow.cpp
@@ -243,19 +243,19 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
Abaddon::Get().GetDiscordClient().DeclineInviteToSpeak(m_channel_id, NOOP_CALLBACK);
});
- m_TMP_speakers_label.set_markup("Speakers");
- m_listing.pack_start(m_TMP_speakers_label, false, true);
+ m_speakers_label.set_markup("Speakers");
+ if (m_is_stage) m_listing.pack_start(m_speakers_label, false, true);
m_listing.pack_start(m_speakers_list, false, true);
- m_TMP_audience_label.set_markup("Audience");
- m_listing.pack_start(m_TMP_audience_label, false, true);
- m_listing.pack_start(m_audience_list, false, true);
+ m_audience_label.set_markup("Audience");
+ if (m_is_stage) m_listing.pack_start(m_audience_label, false, true);
+ if (m_is_stage) m_listing.pack_start(m_audience_list, false, true);
m_scroll.add(m_listing);
m_controls.add(m_mute);
m_controls.add(m_deafen);
m_controls.add(m_noise_suppression);
m_controls.add(m_mix_mono);
m_buttons.set_halign(Gtk::ALIGN_CENTER);
- m_buttons.pack_start(m_stage_command, false, true);
+ if (m_is_stage) m_buttons.pack_start(m_stage_command, false, true);
m_buttons.pack_start(m_disconnect, false, true);
m_stage_invite_box.pack_start(m_stage_invite_lbl, false, true);
m_stage_invite_box.pack_start(m_stage_invite_btns);
@@ -284,7 +284,7 @@ void VoiceWindow::SetUsers(const std::unordered_set &user_ids) {
auto &discord = Abaddon::Get().GetDiscordClient();
const auto me = discord.GetUserData().ID;
for (auto id : user_ids) {
- if (discord.IsUserSpeaker(id)) {
+ if (!m_is_stage || discord.IsUserSpeaker(id)) {
if (id != me) m_speakers_list.add(*CreateSpeakerRow(id));
} else {
m_audience_list.add(*CreateAudienceRow(id));
diff --git a/src/windows/voice/voicewindow.hpp b/src/windows/voice/voicewindow.hpp
index 7c5a137..64b14a9 100644
--- a/src/windows/voice/voicewindow.hpp
+++ b/src/windows/voice/voicewindow.hpp
@@ -89,8 +89,8 @@ private:
Gtk::MenuItem m_menu_view_settings;
Gtk::Label m_TMP_stagelabel;
- Gtk::Label m_TMP_speakers_label;
- Gtk::Label m_TMP_audience_label;
+ Gtk::Label m_speakers_label;
+ Gtk::Label m_audience_label;
public:
using type_signal_mute = sigc::signal;
--
cgit v1.2.3
From b19782c16dffd38ac5651641131a48f8ff961b32 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Thu, 4 Jul 2024 01:11:55 -0400
Subject: update how stage topic is shown
---
src/windows/voice/voicewindow.cpp | 46 ++++++++++++++++++++++-----------------
src/windows/voice/voicewindow.hpp | 7 +++++-
2 files changed, 32 insertions(+), 21 deletions(-)
(limited to 'src/windows')
diff --git a/src/windows/voice/voicewindow.cpp b/src/windows/voice/voicewindow.cpp
index 944d848..e59705a 100644
--- a/src/windows/voice/voicewindow.cpp
+++ b/src/windows/voice/voicewindow.cpp
@@ -193,28 +193,15 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
combos_combos->pack_start(m_capture_combo);
if (const auto instance = discord.GetStageInstanceFromChannel(channel_id); instance.has_value()) {
- printf("%s\n", instance->Topic.c_str());
- m_TMP_stagelabel.show();
- m_TMP_stagelabel.set_markup("" + instance->Topic + "");
+ m_stage_topic_label.show();
+ UpdateStageTopicLabel(instance->Topic);
} else {
- m_TMP_stagelabel.hide();
+ m_stage_topic_label.hide();
}
- discord.signal_stage_instance_create().connect(sigc::track_obj([this](const StageInstance &instance) {
- m_TMP_stagelabel.show();
- m_TMP_stagelabel.set_markup("" + instance.Topic + "");
- },
- *this));
-
- discord.signal_stage_instance_update().connect(sigc::track_obj([this](const StageInstance &instance) {
- m_TMP_stagelabel.set_markup("" + instance.Topic + "");
- },
- *this));
-
- discord.signal_stage_instance_delete().connect(sigc::track_obj([this](const StageInstance &instance) {
- m_TMP_stagelabel.hide();
- },
- *this));
+ discord.signal_stage_instance_create().connect(sigc::mem_fun(*this, &VoiceWindow::OnStageInstanceCreate));
+ discord.signal_stage_instance_update().connect(sigc::mem_fun(*this, &VoiceWindow::OnStageInstanceUpdate));
+ discord.signal_stage_instance_delete().connect(sigc::mem_fun(*this, &VoiceWindow::OnStageInstanceDelete));
m_stage_command.signal_clicked().connect([this]() {
auto &discord = Abaddon::Get().GetDiscordClient();
@@ -263,7 +250,6 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
m_stage_invite_btns.pack_start(m_stage_accept, false, true);
m_stage_invite_btns.pack_start(m_stage_decline, false, true);
m_main.pack_start(m_menu_bar, false, true);
- m_main.pack_start(m_TMP_stagelabel, false, true);
m_main.pack_start(m_controls, false, true);
m_main.pack_start(m_buttons, false, true);
m_main.pack_start(m_stage_invite_box, false, true);
@@ -271,6 +257,9 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
m_main.pack_start(*Gtk::make_managed("Input Settings"), false, true);
m_main.pack_start(*sliders_container, false, true);
m_main.pack_start(m_scroll);
+ m_stage_topic_label.set_ellipsize(Pango::ELLIPSIZE_END);
+ m_stage_topic_label.set_halign(Gtk::ALIGN_CENTER);
+ m_main.pack_start(m_stage_topic_label, false, true);
m_main.pack_start(*combos_container, false, true, 2);
add(m_main);
show_all_children();
@@ -391,6 +380,10 @@ void VoiceWindow::UpdateStageCommand() {
}
}
+void VoiceWindow::UpdateStageTopicLabel(const std::string &topic) {
+ m_stage_topic_label.set_markup("Topic: " + topic);
+}
+
void VoiceWindow::OnUserConnect(Snowflake user_id, Snowflake to_channel_id) {
if (m_channel_id == to_channel_id) {
if (auto it = m_rows.find(user_id); it == m_rows.end()) {
@@ -424,6 +417,19 @@ void VoiceWindow::OnVoiceStateUpdate(Snowflake user_id, Snowflake channel_id, Vo
UpdateStageCommand();
}
+void VoiceWindow::OnStageInstanceCreate(const StageInstance &instance) {
+ m_stage_topic_label.show();
+ UpdateStageTopicLabel(instance.Topic);
+}
+
+void VoiceWindow::OnStageInstanceUpdate(const StageInstance &instance) {
+ UpdateStageTopicLabel(instance.Topic);
+}
+
+void VoiceWindow::OnStageInstanceDelete(const StageInstance &instance) {
+ m_stage_topic_label.hide();
+}
+
VoiceWindow::type_signal_mute VoiceWindow::signal_mute() {
return m_signal_mute;
}
diff --git a/src/windows/voice/voicewindow.hpp b/src/windows/voice/voicewindow.hpp
index 64b14a9..05033d9 100644
--- a/src/windows/voice/voicewindow.hpp
+++ b/src/windows/voice/voicewindow.hpp
@@ -1,4 +1,5 @@
#pragma once
+#include "discord/stage.hpp"
#include "discord/voicestate.hpp"
#ifdef WITH_VOICE
// clang-format off
@@ -31,6 +32,9 @@ private:
void OnUserDisconnect(Snowflake user_id, Snowflake from_channel_id);
void OnSpeakerStateChanged(Snowflake channel_id, Snowflake user_id, bool is_speaker);
void OnVoiceStateUpdate(Snowflake user_id, Snowflake channel_id, VoiceStateFlags flags);
+ void OnStageInstanceCreate(const StageInstance &instance);
+ void OnStageInstanceUpdate(const StageInstance &instance);
+ void OnStageInstanceDelete(const StageInstance &instance);
void OnMuteChanged();
void OnDeafenChanged();
@@ -39,6 +43,7 @@ private:
bool UpdateVoiceMeters();
void UpdateVADParamValue();
void UpdateStageCommand();
+ void UpdateStageTopicLabel(const std::string &topic);
Gtk::Box m_main;
Gtk::Box m_controls;
@@ -88,7 +93,7 @@ private:
Gtk::Menu m_menu_view_sub;
Gtk::MenuItem m_menu_view_settings;
- Gtk::Label m_TMP_stagelabel;
+ Gtk::Label m_stage_topic_label;
Gtk::Label m_speakers_label;
Gtk::Label m_audience_label;
--
cgit v1.2.3