#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(); cpuText->SetText("CPU: " + Utils::ToStringPrecision(usage * 100, "%0.1f") + "% " + Utils::ToStringPrecision(temp, "%0.1f") + "°C"); sensor.SetValue(usage); return TimerResult::Ok; } static Text* batteryText; static TimerResult UpdateBattery(Sensor& sensor) { double percentage = System::GetBatteryPercentage(); batteryText->SetText("Battery: " + Utils::ToStringPrecision(percentage * 100, "%0.1f") + "%"); 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; ramText->SetText("RAM: " + Utils::ToStringPrecision(used, "%0.2f") + "GiB/" + Utils::ToStringPrecision(info.totalGiB, "%0.2f") + "GiB"); 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(); gpuText->SetText("GPU: " + Utils::ToStringPrecision(info.utilisation, "%0.1f") + "% " + Utils::ToStringPrecision(info.coreTemp, "%0.1f") + "°C"); sensor.SetValue(info.utilisation / 100); return TimerResult::Ok; } static Text* vramText; static TimerResult UpdateVRAM(Sensor& sensor) { System::VRAMInfo info = System::GetVRAMInfo(); vramText->SetText("VRAM: " + Utils::ToStringPrecision(info.usedGiB, "%0.2f") + "GiB/" + Utils::ToStringPrecision(info.totalGiB, "%0.2f") + "GiB"); sensor.SetValue(info.usedGiB / info.totalGiB); return TimerResult::Ok; } #endif static Text* diskText; static TimerResult UpdateDisk(Sensor& sensor) { System::DiskInfo info = System::GetDiskInfo(); diskText->SetText("Disk: " + Utils::ToStringPrecision(info.usedGiB, "%0.2f") + "GiB/" + Utils::ToStringPrecision(info.totalGiB, "%0.2f") + "GiB"); 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; } void OnChangeVolumeSink(Slider&, double value) { System::SetVolumeSink(value); } void OnChangeVolumeSource(Slider&, double value) { System::SetVolumeSource(value); } Slider* audioSlider; Slider* micSlider; Text* audioIcon; Text* micIcon; TimerResult UpdateAudio(Widget&) { System::AudioInfo info = System::GetAudioInfo(); audioSlider->SetValue(info.sinkVolume); if (info.sinkMuted) { audioIcon->SetText("󰝟"); } else { audioIcon->SetText("󰕾"); } if (Config::Get().audioInput) { micSlider->SetValue(info.sourceVolume); 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"); 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::array 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 } inline void SetTransform(Widget& widget, const Transform& primary, const Transform& secondary = {}) { if (Config::Get().location == 'T' || Config::Get().location == 'B') { widget.SetHorizontalTransform(primary); widget.SetVerticalTransform(secondary); } else if (Config::Get().location == 'R' || Config::Get().location == 'L') { widget.SetVerticalTransform(primary); widget.SetHorizontalTransform(secondary); } } inline TransitionType GetTransitionType() { switch (Config::Get().location) { case 'T': case 'B': return TransitionType::SlideLeft; case 'L': case 'R': return TransitionType::SlideUp; default: LOG("Invalid location char \"" << Config::Get().location << "\"!") return TransitionType::SlideLeft; } } inline Orientation GetOrientation() { switch (Config::Get().location) { case 'T': case 'B': return Orientation::Horizontal; case 'L': case 'R': return Orientation::Vertical; default: LOG("Invalid location char \"" << Config::Get().location << "\"!") return Orientation::Horizontal; } } inline double GetAngle() { if (Config::Get().location == 'T' || Config::Get().location == 'B') { return 0; } else if (Config::Get().location == 'L') { return 270; // 90 is buggy (Clipped text) } else if (Config::Get().location == 'R') { return 270; } LOG("Invalid location char \"" << Config::Get().location << "\"!"); return 0; } 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}); SetTransform(*box, {-1, true, Alignment::Right}); box->SetOrientation(GetOrientation()); { auto revealer = Widget::Create(); revealer->SetTransition({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(GetAngle()); SetTransform(*text, {-1, true, Alignment::Fill, 0, 6}); textPtr = text.get(); revealer->AddChild(std::move(text)); } 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); SetTransform(*sensor, {24, true, Alignment::Fill}); box->AddChild(std::move(revealer)); 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 widgetAudioSlider = [](Widget& parent, AudioType type) { auto slider = Widget::Create(); slider->SetOrientation(GetOrientation()); 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 = [&widgetAudioSlider](Widget& parent, AudioType type) { auto box = Widget::Create(); box->SetSpacing({8, false}); SetTransform(*box, {-1, true, Alignment::Right}); box->SetOrientation(GetOrientation()); { auto icon = Widget::Create(); icon->SetAngle(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("󰕾 "); 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({GetTransitionType(), 500}); // Add event to eventbox for the revealer to open eventBox.SetHoverFn( [slideRevealer = revealer.get()](EventBox&, bool hovered) { slideRevealer->SetRevealed(hovered); }); { widgetAudioSlider(*revealer, type); } box->AddChild(std::move(revealer)); } else { // Straight forward widgetAudioSlider(*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(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(GetOrientation()); { auto devText = Widget::Create(); devText->SetAngle(GetAngle()); DynCtx::btDevText = devText.get(); devText->SetClass("bt-num"); auto iconText = Widget::Create