diff options
Diffstat (limited to 'src/windows')
-rw-r--r-- | src/windows/voice/voicewindow.cpp (renamed from src/windows/voicewindow.cpp) | 197 | ||||
-rw-r--r-- | src/windows/voice/voicewindow.hpp (renamed from src/windows/voicewindow.hpp) | 24 | ||||
-rw-r--r-- | src/windows/voice/voicewindowaudiencelistentry.cpp | 23 | ||||
-rw-r--r-- | src/windows/voice/voicewindowaudiencelistentry.hpp | 18 | ||||
-rw-r--r-- | src/windows/voice/voicewindowspeakerlistentry.cpp | 58 | ||||
-rw-r--r-- | src/windows/voice/voicewindowspeakerlistentry.hpp | 38 |
6 files changed, 261 insertions, 97 deletions
diff --git a/src/windows/voicewindow.cpp b/src/windows/voice/voicewindow.cpp index a005e79..bfea175 100644 --- a/src/windows/voicewindow.cpp +++ b/src/windows/voice/voicewindow.cpp @@ -1,89 +1,19 @@ +#include "util.hpp" #ifdef WITH_VOICE // clang-format off +#include "voicewindow.hpp" + #include "abaddon.hpp" #include "audio/manager.hpp" #include "components/lazyimage.hpp" -#include "voicesettingswindow.hpp" -#include "voicewindow.hpp" +#include "voicewindowaudiencelistentry.hpp" +#include "voicewindowspeakerlistentry.hpp" +#include "windows/voicesettingswindow.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<void(bool)>; - using type_signal_volume = sigc::signal<void(double)>; - 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) @@ -91,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") @@ -102,14 +33,19 @@ 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)); + 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((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)); @@ -253,14 +189,54 @@ VoiceWindow::VoiceWindow(Snowflake channel_id) combos_combos->pack_start(m_playback_combo); combos_combos->pack_start(m_capture_combo); - m_scroll.add(m_user_list); + 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("<span foreground='green'>" + instance->Topic + "</span>"); + } 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("<span foreground='green'>" + instance.Topic + "</span>"); + }, + *this)); + + discord.signal_stage_instance_update().connect(sigc::track_obj([this](const StageInstance &instance) { + m_TMP_stagelabel.set_markup("<span foreground='green'>" + instance.Topic + "</span>"); + }, + *this)); + + discord.signal_stage_instance_delete().connect(sigc::track_obj([this](const StageInstance &instance) { + m_TMP_stagelabel.hide(); + }, + *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("<b>Speakers</b>"); + 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("<b>Audience</b>"); + 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); 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<Gtk::Label>("Input Settings"), false, true); m_main.pack_start(*sliders_container, false, true); @@ -273,15 +249,19 @@ VoiceWindow::VoiceWindow(Snowflake channel_id) } void VoiceWindow::SetUsers(const std::unordered_set<Snowflake> &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)) { + 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<VoiceWindowUserListEntry>(id); +Gtk::ListBoxRow *VoiceWindow::CreateSpeakerRow(Snowflake id) { + auto *row = Gtk::make_managed<VoiceWindowSpeakerListEntry>(id); m_rows[id] = row; auto &vc = Abaddon::Get().GetDiscordClient().GetVoiceClient(); row->RestoreGain(vc.GetUserVolume(id)); @@ -291,7 +271,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<VoiceWindowAudienceListEntry>(id); + m_rows[id] = row; + row->show(); return row; } @@ -303,6 +290,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()) { @@ -319,7 +313,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<VoiceWindowSpeakerListEntry *>(row)) { + speaker_row->SetVolumeMeter(audio.GetSSRCVolumeLevel(*ssrc)); + } } } return true; @@ -342,20 +338,35 @@ 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_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) { + m_speakers_list.add(*CreateSpeakerRow(user_id)); + } else { + m_audience_list.add(*CreateAudienceRow(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/voicewindow.hpp b/src/windows/voice/voicewindow.hpp index fb64010..7803f85 100644 --- a/src/windows/voicewindow.hpp +++ b/src/windows/voice/voicewindow.hpp @@ -1,4 +1,5 @@ #pragma once +#include "discord/voicestate.hpp" #ifdef WITH_VOICE // clang-format off @@ -16,7 +17,6 @@ #include <unordered_set> // clang-format on -class VoiceWindowUserListEntry; class VoiceWindow : public Gtk::Window { public: VoiceWindow(Snowflake channel_id); @@ -24,14 +24,19 @@ public: private: void SetUsers(const std::unordered_set<Snowflake> &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); + 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(); + void TryDeleteRow(Snowflake id); + bool UpdateVoiceMeters(); void UpdateVADParamValue(); @@ -43,7 +48,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 @@ -56,21 +63,30 @@ 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; Gtk::ComboBox m_capture_combo; Snowflake m_channel_id; + bool m_is_stage; - std::unordered_map<Snowflake, VoiceWindowUserListEntry *> m_rows; + std::unordered_map<Snowflake, Gtk::ListBoxRow *> 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; + Gtk::Label m_TMP_speakers_label; + Gtk::Label m_TMP_audience_label; + public: using type_signal_mute = sigc::signal<void(bool)>; using type_signal_deafen = sigc::signal<void(bool)>; diff --git a/src/windows/voice/voicewindowaudiencelistentry.cpp b/src/windows/voice/voicewindowaudiencelistentry.cpp new file mode 100644 index 0000000..cf93343 --- /dev/null +++ b/src/windows/voice/voicewindowaudiencelistentry.cpp @@ -0,0 +1,23 @@ +#include "voicewindowaudiencelistentry.hpp" +#include "abaddon.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 <gtkmm/box.h> +#include <gtkmm/label.h> +#include <gtkmm/listboxrow.h> + +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 <gtkmm/box.h> +#include <gtkmm/checkbutton.h> +#include <gtkmm/label.h> +#include <gtkmm/listboxrow.h> +#include <gtkmm/scale.h> + +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<void(bool)>; + using type_signal_volume = sigc::signal<void(double)>; + 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; +}; |