Add Microphone support

This commit adds a new widget for the bar: A Microphone widget. It is
disabled by default and can can
be enabled with AudioInput: true.
Implements a flyin as well.

https://github.com/scorpion-26/gBar/issues/5 for the ping
This commit is contained in:
scorpion-26 2023-02-22 16:45:29 +01:00
parent f5adfebfe9
commit 3c8c93d774
14 changed files with 376 additions and 129 deletions

View file

@ -44,6 +44,10 @@ gBar bar 0
``` ```
gBar audio [monitor] gBar audio [monitor]
``` ```
*Open microphone flyin, this is equivalent to the audio flyin*
```
gBar mic [monitor]
```
*Open bluetooth widget* *Open bluetooth widget*
``` ```
gBar bluetooth [monitor] gBar bluetooth [monitor]
@ -68,6 +72,7 @@ Bar:
- Time - Time
- Bluetooth (BlueZ only) - Bluetooth (BlueZ only)
- Audio control - Audio control
- Microphone control
- Power control - Power control
- Shutdown - Shutdown
- Restart - Restart
@ -87,6 +92,7 @@ Bluetooth:
Audio Flyin: Audio Flyin:
- Audio control - Audio control
- Microphone control
## Configuration for your system ## Configuration for your system
Copy the example config (found under data/config) into ~/.config/gBar/config and modify it to your needs. Copy the example config (found under data/config) into ~/.config/gBar/config and modify it to your needs.

View file

@ -42,12 +42,65 @@
margin-right: 6px; margin-right: 6px;
} }
trough {
border-radius: 3px;
border-width: 1px;
border-style: none;
background-color: #44475a;
margin-top: 2px;
min-width: 4px;
min-height: 4px;
}
slider {
border-radius: 0%;
border-width: 1px;
border-style: none;
margin: -9px -9px -9px -9px;
min-width: 16px;
min-height: 16px;
background-color: transparent;
}
highlight {
border-radius: 3px;
border-width: 1px;
border-style: none;
min-height: 6px;
}
.audio-icon { .audio-icon {
font-size: 24px; font-size: 24px;
color: #ffb86c; color: #ffb86c;
margin-right: 12px; margin-right: 12px;
} }
.audio-volume trough {
background-color: #44475a;
}
.audio-volume slider {
background-color: transparent;
}
.audio-volume highlight {
background-color: #ffb86c;
}
.mic-icon {
font-size: 24px;
color: #bd93f9;
margin-right: 0px;
}
.mic-volume trough {
background-color: #44475a;
}
.mic-volume slider {
background-color: transparent;
}
.mic-volume highlight {
background-color: #bd93f9;
}
.bt-num { .bt-num {
font-size: 16px; font-size: 16px;
color: #1793D1; color: #1793D1;
@ -325,32 +378,4 @@
animation-fill-mode: forwards; animation-fill-mode: forwards;
} }
trough {
border-radius: 3px;
border-width: 1px;
border-style: none;
background-color: #44475a;
margin-top: 2px;
min-width: 4px;
min-height: 4px;
}
slider {
border-radius: 0%;
border-width: 1px;
border-style: none;
margin: -9px -9px -9px -9px;
min-width: 16px;
min-height: 16px;
background-color: transparent;
}
highlight {
border-radius: 3px;
border-width: 1px;
border-style: none;
min-height: 6px;
background-color: #ffb86c;
}
/*# sourceMappingURL=style.css.map */ /*# sourceMappingURL=style.css.map */

View file

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["style.scss"],"names":[],"mappings":"AAmBA;EACI;EACA;;;AASJ;EACI,kBA7BC;EA8BD;;;AAGJ;EACI;EACA;;;AAGJ;EACI,WAxBO;;;AA2BX;EACI;EAEA;EAEA,OA5CO;;;AA8CX;EACI;EAGA,OAlDO;;;AAoDX;EACI;EAGA,OAxDO;;;AA2DX;EACI;EAGA,OAzDE;;;AA+DN;EACI;;;AAGJ;EACI;EACA,OAxEK;EAyEL;;;AAGJ;EACI,WApEO;EAqEP,OAvEK;EAwEL;;;AAEJ;EACI;EACA,OA5EK;EA6EL;;;AAEJ;EACI;EACA,OAjFK;EAkFL;;;AAEJ;EACI;EACA,OAtFK;EAuFL;;;AAGJ;EACI,OAhGK;EAiGL,kBAvGO;EAwGP,WA3FO;;;AA6FX;EACI,OArGK;EAsGL;EACA,WAhGO;;;AAmGX;EACI,OA7GK;EA8GL,kBAlHO;;;AAoHX;EACI,OAjHK;EAkHL;EACA,WA1GO;;;AA6GX;EACI,OAnHK;EAoHL,kBA5HO;;;AA8HX;EACI,OAvHK;EAwHL;EACA,WApHO;;;AAuHX;EACI,OAnIG;EAoIH,kBAtIO;;;AAwIX;EACI,OAvIG;EAwIH;EACA,WA9HO;;;AAiIX;EACI,OA5II;EA6IJ,kBAhJO;EAiJP,WApIO;;;AAsIX;EACI,OAjJI;EAkJJ;EACA,WAzIO;;;AA4IX;EACI,OArJG;EAsJH,kBA3JO;EA4JP,WA/IO;;;AAiJX;EACI,OA1JG;EA2JH;EACA,WApJO;;;AAuJX;EACI,OAlKI;EAmKJ;EACA,WA1JO;;;AA8JX;EACI,OA5KO;;;AA+KX;EACI,OA7KI;;;AAgLR;EACI,OA5KK;;;AA+KT;EACI,OApLK;;;AAuLT;EACI,OAtLK;;;AAyLT;EACI,OAzLE;;;AA6LN;EACI,OArMO;;;AAwMX;EACI,OAtMI;;;AAyMR;EACI,OArMK;;;AAwMT;EACI,OA7MK;;;AAgNT;EACI,OA/MK;;;AAkNT;EACI,OAlNE;;;AAqNN;EACI,OA7NO;EA8NP,WAjNO;;;AAmNX;EACI,OAhOO;EAiOP,WArNO;;;AAuNX;EACI,OAnOG;EAoOH,WAzNO;;;AA2NX;EACI,OAjOK;EAkOL,WA7NO;;;AA+NX;EACI,OA1OI;EA2OJ,WAjOO;;;AAqOX;EACI;IACI;;EAEJ;IACI;;;AAGR;EACI;IACI;;EAEJ;IACI;;;AAIR;EACI;IACI,OA7PC;;EA+PL;IACI,OArQA;;;AAwQR;EACI;IACI,OA1QA;;EA4QJ;IACI,OAxQC;;;AA4QT;EACI,kBAvRC;EAwRD;;;AAEJ;EACI;EACA;EACA;EACA;EACA,OAlRK;;;AAoRT;EACI;EACA;;;AAEJ;EAgBI;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;AAxBA;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI,OA1SF;;;AAwTN;EACI,OAzTE;EA0TF,kBAjUO;EAkUP;EACH;EACA;;;AAED;EAaI,OA3UK;EA4UL,kBApVO;EAqVP;EACA;EACH;EACG;;AAjBA;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;;AAWR;EACI;EACA;EACH;EACG,kBAhWO;EAiWP;EACH;EACA;;;AAGD;EAEC;EACG;EACH;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACG;EACH;EACG;EACH,kBAlXQ","file":"style.css"} {"version":3,"sourceRoot":"","sources":["style.scss"],"names":[],"mappings":"AAmBA;EACI;EACA;;;AASJ;EACI,kBA7BC;EA8BD;;;AAGJ;EACI;EACA;;;AAGJ;EACI,WAxBO;;;AA2BX;EACI;EAEA;EAEA,OA5CO;;;AA8CX;EACI;EAGA,OAlDO;;;AAoDX;EACI;EAGA,OAxDO;;;AA2DX;EACI;EAGA,OAzDE;;;AA+DN;EACI;;;AAIJ;EACI;EACA;EACA;EACA,kBA/EO;EAgFP;EACA;EACA;;;AAGJ;EAEI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA,OArGK;EAsGL;;;AAIA;EACI,kBA/GG;;AAkHP;EACI;;AAGJ;EACI,kBAnHC;;;AAuHT;EACI;EACA,OAvHK;EAwHL;;;AAIA;EACI,kBAnIG;;AAsIP;EACI;;AAGJ;EACI,kBArIC;;;AAyIT;EACI,WAnIO;EAoIP,OAtIK;EAuIL;;;AAEJ;EACI;EACA,OA3IK;EA4IL;;;AAEJ;EACI;EACA,OAhJK;EAiJL;;;AAEJ;EACI;EACA,OArJK;EAsJL;;;AAGJ;EACI,OA/JK;EAgKL,kBAtKO;EAuKP,WA1JO;;;AA4JX;EACI,OApKK;EAqKL;EACA,WA/JO;;;AAkKX;EACI,OA5KK;EA6KL,kBAjLO;;;AAmLX;EACI,OAhLK;EAiLL;EACA,WAzKO;;;AA4KX;EACI,OAlLK;EAmLL,kBA3LO;;;AA6LX;EACI,OAtLK;EAuLL;EACA,WAnLO;;;AAsLX;EACI,OAlMG;EAmMH,kBArMO;;;AAuMX;EACI,OAtMG;EAuMH;EACA,WA7LO;;;AAgMX;EACI,OA3MI;EA4MJ,kBA/MO;EAgNP,WAnMO;;;AAqMX;EACI,OAhNI;EAiNJ;EACA,WAxMO;;;AA2MX;EACI,OApNG;EAqNH,kBA1NO;EA2NP,WA9MO;;;AAgNX;EACI,OAzNG;EA0NH;EACA,WAnNO;;;AAsNX;EACI,OAjOI;EAkOJ;EACA,WAzNO;;;AA6NX;EACI,OA3OO;;;AA8OX;EACI,OA5OI;;;AA+OR;EACI,OA3OK;;;AA8OT;EACI,OAnPK;;;AAsPT;EACI,OArPK;;;AAwPT;EACI,OAxPE;;;AA4PN;EACI,OApQO;;;AAuQX;EACI,OArQI;;;AAwQR;EACI,OApQK;;;AAuQT;EACI,OA5QK;;;AA+QT;EACI,OA9QK;;;AAiRT;EACI,OAjRE;;;AAoRN;EACI,OA5RO;EA6RP,WAhRO;;;AAkRX;EACI,OA/RO;EAgSP,WApRO;;;AAsRX;EACI,OAlSG;EAmSH,WAxRO;;;AA0RX;EACI,OAhSK;EAiSL,WA5RO;;;AA8RX;EACI,OAzSI;EA0SJ,WAhSO;;;AAoSX;EACI;IACI;;EAEJ;IACI;;;AAGR;EACI;IACI;;EAEJ;IACI;;;AAIR;EACI;IACI,OA5TC;;EA8TL;IACI,OApUA;;;AAuUR;EACI;IACI,OAzUA;;EA2UJ;IACI,OAvUC;;;AA2UT;EACI,kBAtVC;EAuVD;;;AAEJ;EACI;EACA;EACA;EACA;EACA,OAjVK;;;AAmVT;EACI;EACA;;;AAEJ;EAgBI;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;AAxBA;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI,OAzWF;;;AAuXN;EACI,OAxXE;EAyXF,kBAhYO;EAiYP;EACH;EACA;;;AAED;EAaI,OA1YK;EA2YL,kBAnZO;EAoZP;EACA;EACH;EACG;;AAjBA;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA","file":"style.css"}

View file

@ -76,12 +76,75 @@ $textsize: 16px;
margin-right: 6px; margin-right: 6px;
} }
// Common slider settings
trough {
border-radius: 3px;
border-width: 1px;
border-style: none;
background-color: $inactive;
margin-top: 2px;
min-width: 4px;
min-height: 4px;
}
slider {
// Controls the size of the control area (set border-style to solid to see)
border-radius: 0%;
border-width: 1px;
border-style: none;
margin: -9px -9px -9px -9px;
min-width: 16px;
min-height: 16px;
background-color: transparent;
}
highlight {
border-radius: 3px;
border-width: 1px;
border-style: none;
min-height: 6px;
}
.audio-icon { .audio-icon {
font-size: 24px; font-size: 24px;
color: $orange; color: $orange;
margin-right: 12px; margin-right: 12px;
} }
.audio-volume {
trough {
background-color: $inactive;
}
slider {
background-color: transparent;
}
highlight {
background-color: $orange;
}
}
.mic-icon {
font-size: 24px;
color: $purple;
margin-right: 0px;
}
.mic-volume {
trough {
background-color: $inactive;
}
slider {
background-color: transparent;
}
highlight {
background-color: $purple;
}
}
.bt-num { .bt-num {
font-size: $textsize; font-size: $textsize;
color: $btblue; color: $btblue;
@ -348,34 +411,3 @@ $textsize: 16px;
margin: 0px 0px 0px 10px; margin: 0px 0px 0px 10px;
font-size: 18px; font-size: 18px;
} }
// Sliders
trough {
border-radius: 3px;
border-width: 1px;
border-style: none;
background-color: $inactive;
margin-top: 2px;
min-width: 4px;
min-height: 4px;
}
slider {
// Controls the size of the control area (set border-style to solid to see)
border-radius: 0%;
border-width: 1px;
border-style: none;
margin: -9px -9px -9px -9px;
min-width: 16px;
min-height: 16px;
background-color: transparent;
}
highlight {
border-radius: 3px;
border-width: 1px;
border-style: none;
min-height: 6px;
background-color: $orange;
}

View file

@ -38,6 +38,9 @@ WorkspaceScrollInvert: false
# This can cause issues, if there is not enough space on screen (e.g. when opening the text) # This can cause issues, if there is not enough space on screen (e.g. when opening the text)
CenterTime: true CenterTime: true
# Adds a audio input(aka. microphone) widget
AudioInput: false
# Sets the audio slider to be on reveal (Just like the sensors) when true. Only affects the bar. # Sets the audio slider to be on reveal (Just like the sensors) when true. Only affects the bar.
AudioRevealer: false AudioRevealer: false

View file

@ -5,6 +5,8 @@ namespace AudioFlyin
{ {
namespace DynCtx namespace DynCtx
{ {
Type type;
Window* win; Window* win;
Slider* slider; Slider* slider;
Text* icon; Text* icon;
@ -19,22 +21,31 @@ namespace AudioFlyin
void OnChangeVolume(Slider&, double value) void OnChangeVolume(Slider&, double value)
{ {
System::SetVolume(value); if (type == Type::Speaker)
{
System::SetVolumeSink(value);
}
else if (type == Type::Microphone)
{
System::SetVolumeSource(value);
}
} }
TimerResult Main(Box&) TimerResult Main(Box&)
{ {
System::AudioInfo info = System::GetAudioInfo(); System::AudioInfo info = System::GetAudioInfo();
if (sliderVal != info.volume || muted != info.muted) if (type == Type::Speaker)
{
if (sliderVal != info.sinkVolume || muted != info.sinkMuted)
{ {
// Extend timer // Extend timer
curCloseTime = msOpen + closeTime; curCloseTime = msOpen + closeTime;
sliderVal = info.volume; sliderVal = info.sinkVolume;
slider->SetValue(info.volume); slider->SetValue(info.sinkVolume);
muted = info.muted; muted = info.sinkMuted;
if (info.muted) if (info.sinkMuted)
{ {
icon->SetText(""); icon->SetText("");
} }
@ -43,6 +54,28 @@ namespace AudioFlyin
icon->SetText(""); icon->SetText("");
} }
} }
}
else if (type == Type::Microphone)
{
if (sliderVal != info.sourceVolume || muted != info.sourceMuted)
{
// Extend timer
curCloseTime = msOpen + closeTime;
sliderVal = info.sourceVolume;
slider->SetValue(info.sourceVolume);
muted = info.sourceMuted;
if (info.sourceMuted)
{
icon->SetText("󰍭");
}
else
{
icon->SetText("󰍬");
}
}
}
msOpen++; msOpen++;
auto marginFunction = [](int32_t x) -> int32_t auto marginFunction = [](int32_t x) -> int32_t
@ -66,23 +99,40 @@ namespace AudioFlyin
slider->SetOrientation(Orientation::Horizontal); slider->SetOrientation(Orientation::Horizontal);
slider->SetHorizontalTransform({100, true, Alignment::Fill}); slider->SetHorizontalTransform({100, true, Alignment::Fill});
slider->SetInverted(true); slider->SetInverted(true);
if (DynCtx::type == Type::Speaker)
{
slider->SetClass("audio-volume"); slider->SetClass("audio-volume");
}
else if (DynCtx::type == Type::Microphone)
{
slider->SetClass("mic-volume");
}
slider->OnValueChange(DynCtx::OnChangeVolume); slider->OnValueChange(DynCtx::OnChangeVolume);
slider->SetRange({0, 1, 0.01}); slider->SetRange({0, 1, 0.01});
DynCtx::slider = slider.get(); DynCtx::slider = slider.get();
auto icon = Widget::Create<Text>(); auto icon = Widget::Create<Text>();
if (DynCtx::type == Type::Speaker)
{
icon->SetClass("audio-icon"); icon->SetClass("audio-icon");
icon->SetText(""); icon->SetText("󰕾 ");
}
else if (DynCtx::type == Type::Microphone)
{
icon->SetClass("mic-icon");
icon->SetText("󰍬");
}
DynCtx::icon = icon.get(); DynCtx::icon = icon.get();
parent.AddChild(std::move(slider)); parent.AddChild(std::move(slider));
parent.AddChild(std::move(icon)); parent.AddChild(std::move(icon));
} }
void Create(Window& window, int32_t monitor) void Create(Window& window, int32_t monitor, Type type)
{ {
DynCtx::win = &window; DynCtx::win = &window;
DynCtx::type = type;
auto mainWidget = Widget::Create<Box>(); auto mainWidget = Widget::Create<Box>();
mainWidget->SetSpacing({8, false}); mainWidget->SetSpacing({8, false});
mainWidget->SetVerticalTransform({16, true, Alignment::Fill}); mainWidget->SetVerticalTransform({16, true, Alignment::Fill});

View file

@ -4,5 +4,10 @@
namespace AudioFlyin namespace AudioFlyin
{ {
void Create(Window& window, int32_t monitor); enum class Type
{
Speaker,
Microphone
};
void Create(Window& window, int32_t monitor, Type type);
} }

View file

@ -134,23 +134,43 @@ namespace Bar
} }
#endif #endif
void OnChangeVolume(Slider&, double value) void OnChangeVolumeSink(Slider&, double value)
{ {
System::SetVolume(value); System::SetVolumeSink(value);
} }
void OnChangeVolumeSource(Slider&, double value)
{
System::SetVolumeSource(value);
}
Slider* audioSlider;
Slider* micSlider;
Text* audioIcon; Text* audioIcon;
TimerResult UpdateAudio(Slider& slider) Text* micIcon;
TimerResult UpdateAudio(Widget&)
{ {
System::AudioInfo info = System::GetAudioInfo(); System::AudioInfo info = System::GetAudioInfo();
slider.SetValue(info.volume); audioSlider->SetValue(info.sinkVolume);
if (info.muted) if (info.sinkMuted)
{ {
audioIcon->SetText(""); audioIcon->SetText("󰝟");
} }
else else
{ {
audioIcon->SetText(""); audioIcon->SetText("󰕾");
}
if (Config::Get().audioInput)
{
micSlider->SetValue(info.sourceVolume);
if (info.sourceMuted)
{
micIcon->SetText("󰍭");
}
else
{
micIcon->SetText("󰍬");
}
} }
return TimerResult::Ok; return TimerResult::Ok;
} }
@ -256,33 +276,59 @@ namespace Bar
parent.AddChild(std::move(eventBox)); parent.AddChild(std::move(eventBox));
} }
// Handles in and out
void WidgetAudio(Widget& parent) void WidgetAudio(Widget& parent)
{ {
auto widgetAudioSlider = [](Widget& parent) enum class AudioType
{
Input,
Output
};
auto widgetAudioSlider = [](Widget& parent, AudioType type)
{ {
auto slider = Widget::Create<Slider>(); auto slider = Widget::Create<Slider>();
slider->SetOrientation(Orientation::Horizontal); slider->SetOrientation(Orientation::Horizontal);
slider->SetHorizontalTransform({100, true, Alignment::Fill}); slider->SetHorizontalTransform({100, true, Alignment::Fill});
slider->SetInverted(true); slider->SetInverted(true);
switch (type)
{
case AudioType::Input:
slider->SetClass("mic-volume");
slider->OnValueChange(DynCtx::OnChangeVolumeSource);
DynCtx::micSlider = slider.get();
break;
case AudioType::Output:
slider->SetClass("audio-volume"); slider->SetClass("audio-volume");
slider->AddTimer<Slider>(DynCtx::UpdateAudio, DynCtx::updateTimeFast); slider->OnValueChange(DynCtx::OnChangeVolumeSink);
slider->OnValueChange(DynCtx::OnChangeVolume); DynCtx::audioSlider = slider.get();
break;
}
slider->SetRange({0, 1, 0.01}); slider->SetRange({0, 1, 0.01});
slider->SetScrollSpeed((double)Config::Get().audioScrollSpeed / 100.); slider->SetScrollSpeed((double)Config::Get().audioScrollSpeed / 100.);
parent.AddChild(std::move(slider)); parent.AddChild(std::move(slider));
}; };
auto widgetAudioBody = [&widgetAudioSlider](Widget& parent) auto widgetAudioBody = [&widgetAudioSlider](Widget& parent, AudioType type)
{ {
auto box = Widget::Create<Box>(); auto box = Widget::Create<Box>();
box->SetSpacing({8, false}); box->SetSpacing({8, false});
box->SetHorizontalTransform({-1, true, Alignment::Right}); box->SetHorizontalTransform({-1, true, Alignment::Right});
{ {
auto icon = Widget::Create<Text>(); auto icon = Widget::Create<Text>();
switch (type)
{
case AudioType::Input:
icon->SetClass("mic-icon");
icon->SetText("󰍬");
DynCtx::micIcon = icon.get();
break;
case AudioType::Output:
icon->SetClass("audio-icon"); icon->SetClass("audio-icon");
icon->SetText(""); icon->SetText("󰕾 ");
DynCtx::audioIcon = icon.get(); DynCtx::audioIcon = icon.get();
break;
}
if (Config::Get().audioRevealer) if (Config::Get().audioRevealer)
{ {
@ -296,7 +342,7 @@ namespace Bar
slideRevealer->SetRevealed(hovered); slideRevealer->SetRevealed(hovered);
}); });
{ {
widgetAudioSlider(*revealer); widgetAudioSlider(*revealer, type);
} }
box->AddChild(std::move(revealer)); box->AddChild(std::move(revealer));
@ -304,7 +350,7 @@ namespace Bar
else else
{ {
// Straight forward // Straight forward
widgetAudioSlider(*box); widgetAudioSlider(*box, type);
} }
box->AddChild(std::move(icon)); box->AddChild(std::move(icon));
@ -315,15 +361,27 @@ namespace Bar
if (Config::Get().audioRevealer) if (Config::Get().audioRevealer)
{ {
// Need an EventBox // Need an EventBox
if (Config::Get().audioInput)
{
auto eventBox = Widget::Create<EventBox>(); auto eventBox = Widget::Create<EventBox>();
widgetAudioBody(*eventBox); widgetAudioBody(*eventBox, AudioType::Input);
parent.AddChild(std::move(eventBox));
}
// Need an EventBox
auto eventBox = Widget::Create<EventBox>();
widgetAudioBody(*eventBox, AudioType::Output);
parent.AddChild(std::move(eventBox)); parent.AddChild(std::move(eventBox));
} }
else else
{ {
// Just invoke it. // Just invoke it.
widgetAudioBody(parent); if (Config::Get().audioInput)
{
widgetAudioBody(parent, AudioType::Input);
} }
widgetAudioBody(parent, AudioType::Output);
}
parent.AddTimer<Widget>(DynCtx::UpdateAudio, DynCtx::updateTimeFast);
} }
#ifdef WITH_BLUEZ #ifdef WITH_BLUEZ

View file

@ -132,6 +132,7 @@ void Config::Load()
} }
AddConfigVar("CenterTime", config.centerTime, lineView, foundProperty); AddConfigVar("CenterTime", config.centerTime, lineView, foundProperty);
AddConfigVar("AudioInput", config.audioInput, lineView, foundProperty);
AddConfigVar("AudioRevealer", config.audioRevealer, lineView, foundProperty); AddConfigVar("AudioRevealer", config.audioRevealer, lineView, foundProperty);
AddConfigVar("NetworkWidget", config.networkWidget, lineView, foundProperty); AddConfigVar("NetworkWidget", config.networkWidget, lineView, foundProperty);
AddConfigVar("WorkspaceScrollOnMonitor", config.workspaceScrollOnMonitor, lineView, foundProperty); AddConfigVar("WorkspaceScrollOnMonitor", config.workspaceScrollOnMonitor, lineView, foundProperty);

View file

@ -16,6 +16,7 @@ public:
bool centerTime = true; bool centerTime = true;
bool audioRevealer = false; bool audioRevealer = false;
bool audioInput = false;
bool networkWidget = true; bool networkWidget = true;
bool workspaceScrollOnMonitor = true; // Scroll through workspaces on monitor instead of all bool workspaceScrollOnMonitor = true; // Scroll through workspaces on monitor instead of all
bool workspaceScrollInvert = false; // Up = +1, instead of Up = -1 bool workspaceScrollInvert = false; // Up = +1, instead of Up = -1

View file

@ -2,6 +2,7 @@
#include "System.h" #include "System.h"
#include "Common.h" #include "Common.h"
#include <cmath>
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include <stdlib.h> #include <stdlib.h>
#include <algorithm> #include <algorithm>
@ -77,20 +78,36 @@ namespace PulseAudio
ASSERT(res >= 0, "pa_context_connect failed!"); ASSERT(res >= 0, "pa_context_connect failed!");
} }
inline double PAVolumeToDouble(const pa_cvolume* volume)
{
double vol = (double)pa_cvolume_avg(volume) / (double)PA_VOLUME_NORM;
// Just round to 1% precision, should be enough
constexpr double precision = 0.01;
double volRounded = std::round(vol * 1 / precision) * precision;
return volRounded;
}
inline System::AudioInfo GetInfo() inline System::AudioInfo GetInfo()
{
struct ServerInfo
{ {
const char* defaultSink = nullptr; const char* defaultSink = nullptr;
const char* defaultSource = nullptr;
} serverInfo;
System::AudioInfo info{}; System::AudioInfo info{};
// 1. Get default sink // 1. Get default sink
auto serverInfo = [](pa_context*, const pa_server_info* info, void* sink) auto getServerInfo = [](pa_context*, const pa_server_info* info, void* out)
{ {
if (!info) if (!info)
return; return;
*(const char**)sink = info->default_sink_name; ServerInfo* serverInfo = (ServerInfo*)out;
serverInfo->defaultSink = info->default_sink_name;
serverInfo->defaultSource = info->default_source_name;
}; };
pa_operation* op = pa_context_get_server_info(context, +serverInfo, &defaultSink); pa_operation* op = pa_context_get_server_info(context, +getServerInfo, &serverInfo);
pa_operation_ref(op); pa_operation_ref(op);
pendingOperations.push_back(op); pendingOperations.push_back(op);
@ -103,22 +120,43 @@ namespace PulseAudio
System::AudioInfo* out = (System::AudioInfo*)audioInfo; System::AudioInfo* out = (System::AudioInfo*)audioInfo;
double vol = (double)pa_cvolume_avg(&info->volume) / (double)PA_VOLUME_NORM; double vol = PAVolumeToDouble(&info->volume);
out->volume = vol; out->sinkVolume = vol;
out->muted = info->mute; out->sinkMuted = info->mute;
}; };
op = pa_context_get_sink_info_by_name(context, defaultSink, +sinkInfo, &info); if (serverInfo.defaultSink)
{
op = pa_context_get_sink_info_by_name(context, serverInfo.defaultSink, +sinkInfo, &info);
pa_operation_ref(op); pa_operation_ref(op);
pendingOperations.push_back(op); pendingOperations.push_back(op);
FlushLoop(); FlushLoop();
}
auto sourceInfo = [](pa_context*, const pa_source_info* info, int, void* audioInfo)
{
if (!info)
return;
System::AudioInfo* out = (System::AudioInfo*)audioInfo;
double vol = PAVolumeToDouble(&info->volume);
out->sourceVolume = vol;
out->sourceMuted = info->mute;
};
if (serverInfo.defaultSource)
{
op = pa_context_get_source_info_by_name(context, serverInfo.defaultSource, +sourceInfo, &info);
pa_operation_ref(op);
pendingOperations.push_back(op);
FlushLoop();
}
return info; return info;
} }
inline void SetVolume(double value) inline void SetVolumeSink(double value)
{ {
double valClamped = std::clamp(value, 0., 1.); double valClamped = std::clamp(value, 0., 1.);
// I'm too lazy to implement the c api for this. Since it will only be called when needed and doesn't pipe, it shouldn't be a problem to // I'm too lazy to implement the c api for this. Since it will only be called when needed and doesn't pipe, it shouldn't be a problem to
@ -127,6 +165,15 @@ namespace PulseAudio
system(cmd.c_str()); system(cmd.c_str());
} }
inline void SetVolumeSource(double value)
{
double valClamped = std::clamp(value, 0., 1.);
// I'm too lazy to implement the c api for this. Since it will only be called when needed and doesn't pipe, it shouldn't be a problem to
// fallback for a command
std::string cmd = "pamixer --default-source --set-volume " + std::to_string((uint32_t)(valClamped * 100));
system(cmd.c_str());
}
inline void Shutdown() inline void Shutdown()
{ {
pa_mainloop_free(mainLoop); pa_mainloop_free(mainLoop);

View file

@ -437,9 +437,13 @@ namespace System
{ {
return PulseAudio::GetInfo(); return PulseAudio::GetInfo();
} }
void SetVolume(double volume) void SetVolumeSink(double volume)
{ {
PulseAudio::SetVolume(volume); PulseAudio::SetVolumeSink(volume);
}
void SetVolumeSource(double volume)
{
PulseAudio::SetVolumeSource(volume);
} }
#ifdef WITH_HYPRLAND #ifdef WITH_HYPRLAND

View file

@ -73,11 +73,15 @@ namespace System
struct AudioInfo struct AudioInfo
{ {
double volume; double sinkVolume;
bool muted; bool sinkMuted;
double sourceVolume;
bool sourceMuted;
}; };
AudioInfo GetAudioInfo(); AudioInfo GetAudioInfo();
void SetVolume(double volume); void SetVolumeSink(double volume);
void SetVolumeSource(double volume);
#ifdef WITH_HYPRLAND #ifdef WITH_HYPRLAND
enum class WorkspaceStatus enum class WorkspaceStatus

View file

@ -13,6 +13,24 @@
const char* audioTmpFileOpen = "/tmp/gBar__audio"; const char* audioTmpFileOpen = "/tmp/gBar__audio";
static bool flyin = false;
void OpenAudioFlyin(Window& window, int32_t monitor, AudioFlyin::Type type)
{
flyin = true;
if (access(audioTmpFileOpen, F_OK) != 0)
{
FILE* audioTempFile = fopen(audioTmpFileOpen, "w");
AudioFlyin::Create(window, monitor, type);
fclose(audioTempFile);
}
else
{
// Already open, close
LOG("Audio flyin already open");
exit(0);
}
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
System::Init(); System::Init();
@ -31,18 +49,11 @@ int main(int argc, char** argv)
} }
else if (strcmp(argv[1], "audio") == 0) else if (strcmp(argv[1], "audio") == 0)
{ {
if (access(audioTmpFileOpen, F_OK) != 0) OpenAudioFlyin(window, monitor, AudioFlyin::Type::Speaker);
{
FILE* audioTempFile = fopen(audioTmpFileOpen, "w");
AudioFlyin::Create(window, monitor);
fclose(audioTempFile);
} }
else else if (strcmp(argv[1], "mic") == 0)
{ {
// Already open, close OpenAudioFlyin(window, monitor, AudioFlyin::Type::Microphone);
LOG("Audio already open");
exit(0);
}
} }
#ifdef WITH_BLUEZ #ifdef WITH_BLUEZ
else if (strcmp(argv[1], "bluetooth") == 0) else if (strcmp(argv[1], "bluetooth") == 0)
@ -66,7 +77,7 @@ int main(int argc, char** argv)
window.Run(argc, argv); window.Run(argc, argv);
System::FreeResources(); System::FreeResources();
if (strcmp(argv[1], "audio") == 0) if (flyin)
{ {
remove(audioTmpFileOpen); remove(audioTmpFileOpen);
} }