#include "Bar.h" #include "System.h" #include "Common.h" #include "Config.h" #include "SNI.h" #include #include namespace Bar { static int32_t monitorID; namespace DynCtx { constexpr uint32_t updateTime = 1000; constexpr uint32_t updateTimeFast = 100; static Revealer* powerBoxRevealer; static void PowerBoxEvent(EventBox&, bool hovered) { powerBoxRevealer->SetRevealed(hovered); } static Text* cpuText; static TimerResult UpdateCPU(Sensor& sensor) { double usage = System::GetCPUUsage(); double temp = System::GetCPUTemp(); std::string text = "CPU: " + Utils::ToStringPrecision(usage * 100, "%0.1f") + "% " + Utils::ToStringPrecision(temp, "%0.1f") + "°C"; if (Config::Get().sensorTooltips) { sensor.SetTooltip(text); } else { cpuText->SetText(text); } sensor.SetValue(usage); return TimerResult::Ok; } static Text* batteryText; static TimerResult UpdateBattery(Sensor& sensor) { double percentage = System::GetBatteryPercentage(); std::string text = "Battery: " + Utils::ToStringPrecision(percentage * 100, "%0.1f") + "%"; if (Config::Get().sensorTooltips) { sensor.SetTooltip(text); } else { batteryText->SetText(text); } sensor.SetValue(percentage); return TimerResult::Ok; } static Text* ramText; static TimerResult UpdateRAM(Sensor& sensor) { System::RAMInfo info = System::GetRAMInfo(); double used = info.totalGiB - info.freeGiB; double usedPercent = used / info.totalGiB; std::string text = "RAM: " + Utils::ToStringPrecision(used, "%0.2f") + "GiB/" + Utils::ToStringPrecision(info.totalGiB, "%0.2f") + "GiB"; if (Config::Get().sensorTooltips) { sensor.SetTooltip(text); } else { ramText->SetText(text); } sensor.SetValue(usedPercent); return TimerResult::Ok; } #if defined WITH_NVIDIA || defined WITH_AMD static Text* gpuText; static TimerResult UpdateGPU(Sensor& sensor) { System::GPUInfo info = System::GetGPUInfo(); std::string text = "GPU: " + Utils::ToStringPrecision(info.utilisation, "%0.1f") + "% " + Utils::ToStringPrecision(info.coreTemp, "%0.1f") + "°C"; if (Config::Get().sensorTooltips) { sensor.SetTooltip(text); } else { gpuText->SetText(text); } sensor.SetValue(info.utilisation / 100); return TimerResult::Ok; } static Text* vramText; static TimerResult UpdateVRAM(Sensor& sensor) { System::VRAMInfo info = System::GetVRAMInfo(); std::string text = "VRAM: " + Utils::ToStringPrecision(info.usedGiB, "%0.2f") + "GiB/" + Utils::ToStringPrecision(info.totalGiB, "%0.2f") + "GiB"; if (Config::Get().sensorTooltips) { sensor.SetTooltip(text); } else { vramText->SetText(text); } sensor.SetValue(info.usedGiB / info.totalGiB); return TimerResult::Ok; } #endif static Text* diskText; static TimerResult UpdateDisk(Sensor& sensor) { System::DiskInfo info = System::GetDiskInfo(); std::string text = "Disk: " + Utils::ToStringPrecision(info.usedGiB, "%0.2f") + "GiB/" + Utils::ToStringPrecision(info.totalGiB, "%0.2f") + "GiB"; if (Config::Get().sensorTooltips) { sensor.SetTooltip(text); } else { diskText->SetText(text); } sensor.SetValue(info.usedGiB / info.totalGiB); return TimerResult::Ok; } #ifdef WITH_BLUEZ static Button* btIconText; static Text* btDevText; static TimerResult UpdateBluetooth(Box&) { System::BluetoothInfo info = System::GetBluetoothInfo(); if (info.defaultController.empty()) { btIconText->SetClass("bt-label-off"); btIconText->SetText("󰂲"); btDevText->SetText(""); } else if (info.devices.empty()) { btIconText->SetClass("bt-label-on"); btIconText->SetText("󰂯"); btDevText->SetText(""); } else { btIconText->SetClass("bt-label-connected"); btIconText->SetText("󰂱"); std::string btDev; std::string tooltip; for (auto& dev : info.devices) { if (!dev.connected) continue; std::string ico = System::BTTypeToIcon(dev); tooltip += dev.name + " & "; btDev += ico; } // Delete last delim if (tooltip.size()) tooltip.erase(tooltip.end() - 3, tooltip.end()); btDevText->SetTooltip(tooltip); btDevText->SetText(std::move(btDev)); } return TimerResult::Ok; } void OnBTClick(Button&) { System::OpenBTWidget(); } #endif static std::mutex packageTextLock; static TimerResult UpdatePackages(Text& text) { System::GetOutdatedPackagesAsync( [&](uint32_t numOutdatedPackages) { packageTextLock.lock(); if (numOutdatedPackages) { text.SetText("󰏔 "); text.SetVisible(true); text.SetClass("package-outofdate"); text.SetTooltip("Updates available! (" + std::to_string(numOutdatedPackages) + " packages)"); } else { text.SetText(""); text.SetVisible(false); text.SetClass("package-empty"); text.SetTooltip(""); } packageTextLock.unlock(); }); return TimerResult::Ok; } Widget* audioSlider; Widget* micSlider; Text* audioIcon; Text* micIcon; void OnChangeVolumeSink(Slider&, double value) { System::SetVolumeSink(value); } void OnChangeVolumeSource(Slider&, double value) { System::SetVolumeSource(value); } // For text double audioVolume = 0; void OnChangeVolumeSinkDelta(double delta) { audioVolume += delta; audioVolume = std::clamp(audioVolume, 0.0, 1.0); System::SetVolumeSink(audioVolume); } double micVolume = 0; void OnChangeVolumeSourceDelta(double delta) { micVolume += delta; micVolume = std::clamp(micVolume, 0.0, 1.0); System::SetVolumeSource(micVolume); } TimerResult UpdateAudio(Widget&) { System::AudioInfo info = System::GetAudioInfo(); if (Config::Get().audioNumbers) { audioVolume = info.sinkVolume; ((Text*)audioSlider)->SetText(Utils::ToStringPrecision(info.sinkVolume * 100, "%0.0f") + "%"); } else { ((Slider*)audioSlider)->SetValue(info.sinkVolume); } if (info.sinkMuted) { audioIcon->SetText("󰝟"); } else { audioIcon->SetText("󰕾"); } if (Config::Get().audioInput) { if (Config::Get().audioNumbers) { micVolume = info.sourceVolume; ((Text*)micSlider)->SetText(Utils::ToStringPrecision(info.sourceVolume * 100, "%0.0f") + "%"); } else { ((Slider*)micSlider)->SetValue(info.sinkVolume); } if (info.sourceMuted) { micIcon->SetText("󰍭"); } else { micIcon->SetText("󰍬"); } } return TimerResult::Ok; } Text* networkText; TimerResult UpdateNetwork(NetworkSensor& sensor) { double bpsUp = System::GetNetworkBpsUpload(updateTime / 1000.0); double bpsDown = System::GetNetworkBpsDownload(updateTime / 1000.0); std::string upload = Utils::StorageUnitDynamic(bpsUp, "%0.1f%s"); std::string download = Utils::StorageUnitDynamic(bpsDown, "%0.1f%s"); std::string text = Config::Get().networkAdapter + ": " + upload + " Up/" + download + " Down"; if (Config::Get().sensorTooltips) { sensor.SetTooltip(text); } else { networkText->SetText(Config::Get().networkAdapter + ": " + upload + " Up/" + download + " Down"); } sensor.SetUp(bpsUp); sensor.SetDown(bpsDown); return TimerResult::Ok; } TimerResult UpdateTime(Text& text) { text.SetText(System::GetTime()); return TimerResult::Ok; } #ifdef WITH_WORKSPACES static std::vector workspaces; TimerResult UpdateWorkspaces(Box&) { System::PollWorkspaces((uint32_t)monitorID, workspaces.size()); for (size_t i = 0; i < workspaces.size(); i++) { switch (System::GetWorkspaceStatus(i + 1)) { case System::WorkspaceStatus::Dead: workspaces[i]->SetClass("ws-dead"); break; case System::WorkspaceStatus::Inactive: workspaces[i]->SetClass("ws-inactive"); break; case System::WorkspaceStatus::Visible: workspaces[i]->SetClass("ws-visible"); break; case System::WorkspaceStatus::Current: workspaces[i]->SetClass("ws-current"); break; case System::WorkspaceStatus::Active: workspaces[i]->SetClass("ws-active"); break; } workspaces[i]->SetText(System::GetWorkspaceSymbol(i)); } return TimerResult::Ok; } void ScrollWorkspaces(EventBox&, ScrollDirection direction) { switch (direction) { case ScrollDirection::Up: if (Config::Get().workspaceScrollInvert) System::GotoNextWorkspace('+'); else System::GotoNextWorkspace('-'); break; case ScrollDirection::Down: if (Config::Get().workspaceScrollInvert) System::GotoNextWorkspace('-'); else System::GotoNextWorkspace('+'); break; default: break; } } #endif } void WidgetSensor(Widget& parent, TimerCallback&& callback, const std::string& sensorClass, const std::string& textClass, Text*& textPtr) { auto eventBox = Widget::Create(); { auto box = Widget::Create(); box->SetSpacing({0, false}); Utils::SetTransform(*box, {-1, true, Alignment::Right}); box->SetOrientation(Utils::GetOrientation()); { if (!Config::Get().sensorTooltips) { auto revealer = Widget::Create(); revealer->SetTransition({Utils::GetTransitionType(), 500}); // Add event to eventbox for the revealer to open eventBox->SetHoverFn( [textRevealer = revealer.get()](EventBox&, bool hovered) { textRevealer->SetRevealed(hovered); }); { auto text = Widget::Create(); text->SetClass(textClass); text->SetAngle(Utils::GetAngle()); Utils::SetTransform(*text, {-1, true, Alignment::Fill, 0, 6}); textPtr = text.get(); revealer->AddChild(std::move(text)); } box->AddChild(std::move(revealer)); } auto sensor = Widget::Create(); sensor->SetClass(sensorClass); double angle = -90; switch (Config::Get().location) { case 'T': case 'B': angle = -90; break; case 'L': case 'R': angle = 0; break; } sensor->SetStyle({angle}); sensor->AddTimer(std::move(callback), DynCtx::updateTime); Utils::SetTransform(*sensor, {24, true, Alignment::Fill}); box->AddChild(std::move(sensor)); } eventBox->AddChild(std::move(box)); } parent.AddChild(std::move(eventBox)); } // Handles in and out void WidgetAudio(Widget& parent) { enum class AudioType { Input, Output }; auto widgetAudioVolume = [](Widget& parent, AudioType type) { if (Config::Get().audioNumbers) { auto eventBox = Widget::Create(); auto text = Widget::Create(); text->SetAngle(Utils::GetAngle()); Utils::SetTransform(*text, {-1, true, Alignment::Fill}); switch (type) { case AudioType::Input: text->SetClass("mic-volume"); DynCtx::micSlider = text.get(); break; case AudioType::Output: text->SetClass("audio-volume"); DynCtx::audioSlider = text.get(); break; } eventBox->SetScrollFn( [type, text = text.get()](EventBox&, ScrollDirection direction) { double delta = (double)Config::Get().audioScrollSpeed / 100.; delta *= direction == ScrollDirection::Down ? -1 : 1; switch (type) { case AudioType::Input: DynCtx::OnChangeVolumeSourceDelta(delta); break; case AudioType::Output: DynCtx::OnChangeVolumeSinkDelta(delta); break; } }); eventBox->AddChild(std::move(text)); parent.AddChild(std::move(eventBox)); } else { auto slider = Widget::Create(); slider->SetOrientation(Utils::GetOrientation()); Utils::SetTransform(*slider, {100, true, Alignment::Fill}); 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->OnValueChange(DynCtx::OnChangeVolumeSink); DynCtx::audioSlider = slider.get(); break; } slider->SetRange({0, 1, 0.01}); slider->SetScrollSpeed((double)Config::Get().audioScrollSpeed / 100.); parent.AddChild(std::move(slider)); } }; auto widgetAudioBody = [&widgetAudioVolume](Widget& parent, AudioType type) { auto box = Widget::Create(); box->SetSpacing({8, false}); Utils::SetTransform(*box, {-1, true, Alignment::Right}); box->SetOrientation(Utils::GetOrientation()); { auto icon = Widget::Create(); icon->SetAngle(Utils::GetAngle()); switch (type) { case AudioType::Input: icon->SetClass("mic-icon"); icon->SetText("󰍬"); DynCtx::micIcon = icon.get(); break; case AudioType::Output: icon->SetClass("audio-icon"); icon->SetText("󰕾 "); Utils::SetTransform(*icon, {-1, true, Alignment::Fill, 0, 6}); DynCtx::audioIcon = icon.get(); break; } if (Config::Get().audioRevealer) { EventBox& eventBox = (EventBox&)parent; auto revealer = Widget::Create(); revealer->SetTransition({Utils::GetTransitionType(), 500}); // Add event to eventbox for the revealer to open eventBox.SetHoverFn( [slideRevealer = revealer.get()](EventBox&, bool hovered) { slideRevealer->SetRevealed(hovered); }); { widgetAudioVolume(*revealer, type); } box->AddChild(std::move(revealer)); } else { // Straight forward widgetAudioVolume(*box, type); } box->AddChild(std::move(icon)); } parent.AddChild(std::move(box)); }; if (Config::Get().audioRevealer) { // Need an EventBox if (Config::Get().audioInput) { auto eventBox = Widget::Create(); widgetAudioBody(*eventBox, AudioType::Input); parent.AddChild(std::move(eventBox)); } // Need an EventBox auto eventBox = Widget::Create(); widgetAudioBody(*eventBox, AudioType::Output); parent.AddChild(std::move(eventBox)); } else { // Just invoke it. if (Config::Get().audioInput) { widgetAudioBody(parent, AudioType::Input); } widgetAudioBody(parent, AudioType::Output); } parent.AddTimer(DynCtx::UpdateAudio, DynCtx::updateTimeFast); } void WidgetPackages(Widget& parent) { auto text = Widget::Create(); text->SetText(""); text->SetVisible(false); text->SetClass("package-empty"); text->SetAngle(Utils::GetAngle()); text->AddTimer(DynCtx::UpdatePackages, 1000 * Config::Get().checkUpdateInterval, TimerDispatchBehaviour::ImmediateDispatch); parent.AddChild(std::move(text)); } #ifdef WITH_BLUEZ void WidgetBluetooth(Widget& parent) { auto box = Widget::Create(); box->SetSpacing({0, false}); box->SetOrientation(Utils::GetOrientation()); { auto devText = Widget::Create(); devText->SetAngle(Utils::GetAngle()); DynCtx::btDevText = devText.get(); devText->SetClass("bt-num"); auto iconText = Widget::Create