From b0ed0d17e5fd64c903f6bad6c3a16f60f8fffb24 Mon Sep 17 00:00:00 2001 From: scorpion-26 <58082714+scorpion-26@users.noreply.github.com> Date: Fri, 13 Jan 2023 16:13:56 +0100 Subject: [PATCH] Initial commit! --- .clang-format | 36 ++++ LICENSE | 7 + README.md | 83 ++++++++ assets/audioflyin.png | Bin 0 -> 7515 bytes assets/bar.png | Bin 0 -> 26233 bytes css/dracula/LICENSE | 21 ++ css/style.css | 185 ++++++++++++++++++ css/style.css.map | 1 + css/style.scss | 207 ++++++++++++++++++++ meson.build | 31 +++ meson_options.txt | 6 + src/AudioFlyin.cpp | 105 ++++++++++ src/AudioFlyin.h | 8 + src/Bar.cpp | 444 ++++++++++++++++++++++++++++++++++++++++++ src/Bar.h | 8 + src/Common.h | 55 ++++++ src/Hyprland.h | 95 +++++++++ src/NvidiaGPU.h | 77 ++++++++ src/PulseAudio.h | 106 ++++++++++ src/System.cpp | 336 ++++++++++++++++++++++++++++++++ src/System.h | 89 +++++++++ src/Widget.cpp | 332 +++++++++++++++++++++++++++++++ src/Widget.h | 250 ++++++++++++++++++++++++ src/Window.cpp | 121 ++++++++++++ src/Window.h | 44 +++++ src/gBar.cpp | 55 ++++++ 26 files changed, 2702 insertions(+) create mode 100644 .clang-format create mode 100644 LICENSE create mode 100644 README.md create mode 100644 assets/audioflyin.png create mode 100644 assets/bar.png create mode 100644 css/dracula/LICENSE create mode 100644 css/style.css create mode 100644 css/style.css.map create mode 100644 css/style.scss create mode 100644 meson.build create mode 100644 meson_options.txt create mode 100644 src/AudioFlyin.cpp create mode 100644 src/AudioFlyin.h create mode 100644 src/Bar.cpp create mode 100644 src/Bar.h create mode 100644 src/Common.h create mode 100644 src/Hyprland.h create mode 100644 src/NvidiaGPU.h create mode 100644 src/PulseAudio.h create mode 100644 src/System.cpp create mode 100644 src/System.h create mode 100644 src/Widget.cpp create mode 100644 src/Widget.h create mode 100644 src/Window.cpp create mode 100644 src/Window.h create mode 100644 src/gBar.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..bae3e34 --- /dev/null +++ b/.clang-format @@ -0,0 +1,36 @@ +BasedOnStyle: Microsoft + +AlignAfterOpenBracket: Align +AlignArrayOfStructures: Left +AlignEscapedNewlines: Left +AlignOperands: Align +PenaltyBreakAssignment: 100 +PenaltyBreakBeforeFirstCallParameter: 20 +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Empty +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: Yes +SpaceAfterTemplateKeyword: false +Cpp11BracedListStyle: true +IncludeBlocks: Preserve +SortIncludes: false +IndentAccessModifiers: false +AccessModifierOffset: -4 # Stop indenting my access modifiers wierdly! I want them the same level as the class god damn! +NamespaceIndentation: All +FixNamespaceComments: false +PointerAlignment: Left +ColumnLimit: 150 +KeepEmptyLinesAtTheStartOfBlocks: false +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterCaseLabel: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + BeforeLambdaBody: true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..284f50d --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2023 scorpion_26 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..84457bd --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# gBar +My personal blazingly fast and efficient status bar + widgets, in case anyone finds a use for it. + +## Prerequisites +*If you don't have the optional dependencies, some features are not available and you must manually disable them.* +- wayland +- Hyprland(Optional -> For workspaces widget) +- nvidia-utils(Optional -> For GPU status) +- bluez(Optional -> For Bluetooth status) +- GTK 3.0 +- gtk-layer-shell +- PulseAudio server (PipeWire works too!) +- meson, gcc/clang, ninja + +## Building and installation +1. Configure with meson + *All optional dependencies enabled* + ``` + meson build --buildtype=release + ``` + *All optional dependencies are disabled* + ``` + meson build --buildtype=release -DHasHyprland=false -DHasNvidia=false -DHasBlueZ=false + ``` +4. Build and install + ``` + ninja -C build && sudo ninja -C build install + ``` +5. Copy css styling into your config directory($XDG_CONFIG_HOME). This will most likely be ~/.config + ``` + mkdir ~/.config/gBar && cp css/* ~/.config/gBar/ + ``` + +## Running gBar +*Open bar on monitor 0* +``` +gBar bar 0 +``` +*Open audio flyin (either on current monitor or on the specified monitor)* +``` +gBar audio [monitor] +``` + +## Gallery +![The bar with default css](/assets/bar.png) +*Bar with default css* +![The audio flyin with default css](/assets/audioflyin.png) +*Audio widget with default css* + +## FAQ +### There are already many GTK bars out there, why not use them? +- Waybar: +Great performance, though limited styling(Almost no dynamic sliders, revealers, ...) and (at least for me) buggy css. +- eww: +Really solid project with many great customization options. There is one problem though: Performance +Due to the way eww configuration is set up, for each dynamic variable (the number of them quickly grows) you need a shell command which opens a process. +This became quickly a bottleneck, where the bar took up 10% of the CPU-time due to the creation of many processes all the time (without even considering the workspace widget). +gBar implements all of the information gathering(CPU, RAM, GPU, Disk, ...) in native C++ code, which is WAY faster. In fact, gBar was meant to be a fast replacement/alternative for eww for me. + +And lastly: Implementing it myself is fun and a great excuse to learn something new! + +### What scheme are you using? +The colors are from the Dracula theme: https://draculatheme.com + +### I want to customize the colors +If you have SASS installed: Edit ~/.config/gBar/style.scss and regenerate style.css with it +Else: Edit ~/.config/gBar/style.css directly! + +### I want to modify the widgets behaviour/Add my own widgets +Unfortunately, you need to implement it yourself in C++. For inspiration look into src/Bar.cpp or src/AudioFlyin.cpp, or open an issue(Maybe I'll implement it for you). + +### The Audio widget doesn't open +Delete /tmp/gBar__audio. +This happens, when you kill the widget before it closes automatically after a few seconds. + +### CPU Temperature is wrong/Lock doesn't work +*This is caused by the way my system and/or Linux is setup.* +Temperature: Edit the variable ```tempFilePath``` in ```src/System.cpp``` to the correct thermal zone file and recompile. The one for my system is *very* likely wrong. +Lock: There is no generic way to lock a system. So please, implement it to suit your needs (Replace XXX by a shell command in ```src/System.cpp```) + +### The icons are not showing! +Please install a Nerd Font from https://www.nerdfonts.com (I use Caskaydia Cove NF), and change style.css/style.scss accordingly (Refer to 'I want to customize the colors' for that) + diff --git a/assets/audioflyin.png b/assets/audioflyin.png new file mode 100644 index 0000000000000000000000000000000000000000..f8efabaefcb44409830ccc9d4bb848e7b2e7d848 GIT binary patch literal 7515 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 HarS=zD$I9E literal 0 HcmV?d00001 diff --git a/assets/bar.png b/assets/bar.png new file mode 100644 index 0000000000000000000000000000000000000000..7dbaac77d1dd375a3229469a0e7d43f73c04c0c2 GIT binary patch literal 26233 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 e&NX5g$Jm?w&>z%{N}%xTpQ=0.45.1', + default_options: ['c_std=c++17', 'warning_level=3']) + +gtk = dependency('gtk+-3.0') +gtk_layer_shell = dependency('gtk-layer-shell-0') + +if get_option('HasHyprland') + add_global_arguments('-DHAS_HYPRLAND', language: 'cpp') +endif +if get_option('HasNvidia') + add_global_arguments('-DHAS_NVIDIA', language: 'cpp') +endif +if get_option('HasBlueZ') + add_global_arguments('-DHAS_BLUEZ', language: 'cpp') +endif +if get_option('HasSys') + add_global_arguments('-DHAS_SYS', language: 'cpp') +endif + +pulse = dependency('libpulse') + +executable( + 'gBar', + ['src/gBar.cpp', 'src/Window.cpp', 'src/Widget.cpp', 'src/System.cpp', 'src/Bar.cpp', 'src/AudioFlyin.cpp'], + dependencies: [gtk, gtk_layer_shell, pulse], + install: true +) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..5488172 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,6 @@ +option('HasHyprland', type: 'boolean', value : true) +option('HasNvidia', type: 'boolean', value : true) +option('HasBlueZ', type: 'boolean', value : true) + +# You shouldn't enable this, unless you know what you are doing! +option('HasSys', type: 'boolean', value : false) diff --git a/src/AudioFlyin.cpp b/src/AudioFlyin.cpp new file mode 100644 index 0000000..65758a8 --- /dev/null +++ b/src/AudioFlyin.cpp @@ -0,0 +1,105 @@ +#include "AudioFlyin.h" +#include "System.h" + +namespace AudioFlyin +{ + namespace DynCtx + { + Window* win; + Slider* slider; + Text* icon; + double sliderVal; + bool muted = false; + + int32_t msOpen = 0; + constexpr int32_t closeTime = 2000; + constexpr int32_t height = 50; + int32_t curCloseTime = closeTime; + int32_t transitionTime = 50; + + void OnChangeVolume(Slider&, double value) + { + System::SetVolume(value); + } + + TimerResult Main(Box&) + { + System::AudioInfo info = System::GetAudioInfo(); + if (sliderVal != info.volume || muted != info.muted) + { + // Extend timer + curCloseTime = msOpen + closeTime; + + sliderVal = info.volume; + slider->SetValue(info.volume); + + muted = info.muted; + if (info.muted) + { + icon->SetText("ﱝ"); + } + else + { + icon->SetText("墳"); + } + } + + msOpen++; + auto marginFunction = [](int32_t x) -> int32_t + { + // A inverted, cutoff 'V' shape + // Fly in -> hover -> fly out + double steepness = (double)height / (double)transitionTime; + return (int32_t)std::min(-std::abs((double)x - (double)curCloseTime / 2) * steepness + (double)curCloseTime / 2, (double)height); + }; + win->SetMargin(Anchor::Bottom, marginFunction(msOpen)); + if (msOpen >= curCloseTime) + { + win->Close(); + } + return TimerResult::Ok; + } + } + void WidgetAudio(Widget& parent) + { + auto slider = Widget::Create(); + slider->SetOrientation(Orientation::Horizontal); + slider->SetHorizontalTransform({100, true, Alignment::Fill}); + slider->SetInverted(true); + slider->SetClass("audio-volume"); + slider->OnValueChange(DynCtx::OnChangeVolume); + slider->SetRange({0, 1, 0.01}); + DynCtx::slider = slider.get(); + + auto icon = Widget::Create(); + icon->SetClass("audio-icon"); + icon->SetText("墳"); + DynCtx::icon = icon.get(); + + parent.AddChild(std::move(slider)); + parent.AddChild(std::move(icon)); + } + + void Create(Window& window, int32_t monitor) + { + DynCtx::win = &window; + auto mainWidget = Widget::Create(); + mainWidget->SetSpacing({8, false}); + mainWidget->SetVerticalTransform({16, true, Alignment::Fill}); + mainWidget->SetClass("bar"); + mainWidget->AddTimer(DynCtx::Main, 1); + + auto padding = Widget::Create(); + padding->SetHorizontalTransform({8, true, Alignment::Fill}); + mainWidget->AddChild(std::move(padding)); + + WidgetAudio(*mainWidget); + + padding = Widget::Create(); + mainWidget->AddChild(std::move(padding)); + + window = Window(std::move(mainWidget), monitor); + window.SetExclusive(false); + window.SetAnchor(Anchor::Bottom); + } +} diff --git a/src/AudioFlyin.h b/src/AudioFlyin.h new file mode 100644 index 0000000..9764105 --- /dev/null +++ b/src/AudioFlyin.h @@ -0,0 +1,8 @@ +#pragma once +#include "Widget.h" +#include "Window.h" + +namespace AudioFlyin +{ + void Create(Window& window, int32_t monitor); +} diff --git a/src/Bar.cpp b/src/Bar.cpp new file mode 100644 index 0000000..2ad0491 --- /dev/null +++ b/src/Bar.cpp @@ -0,0 +1,444 @@ +#include "Bar.h" + +#include "System.h" +#include "Common.h" + +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(CairoSensor& 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* ramText; + static TimerResult UpdateRAM(CairoSensor& 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; + } + +#ifdef HAS_NVIDIA + static Text* gpuText; + static TimerResult UpdateGPU(CairoSensor& 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(CairoSensor& 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(CairoSensor& 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 HAS_BLUEZ + static Text* 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; + + for (auto& dev : info.devices) + { + std::string ico = " "; + if (dev.type == "input-keyboard") + { + ico = " "; + } + else if (dev.type == "input-mouse") + { + ico = " "; + } + else if (dev.type == "audio-headset") + { + ico = " "; + } + btDev += ico; + } + + btDevText->SetText(std::move(btDev)); + } + return TimerResult::Ok; + } +#endif + + void OnChangeVolume(Slider&, double value) + { + System::SetVolume(value); + } + + Text* audioIcon; + TimerResult UpdateAudio(Slider& slider) + { + System::AudioInfo info = System::GetAudioInfo(); + slider.SetValue(info.volume); + if (info.muted) + { + audioIcon->SetText("ﱝ"); + } + else + { + audioIcon->SetText("墳"); + } + return TimerResult::Ok; + } + + TimerResult UpdateTime(Text& text) + { + text.SetText(System::GetTime()); + return TimerResult::Ok; + } + +#ifdef HAS_HYPRLAND + static std::array workspaces; + TimerResult UpdateWorkspaces(Box&) + { + for (size_t i = 0; i < workspaces.size(); i++) + { + switch (System::GetWorkspaceStatus((uint32_t)monitorID, i + 1)) + { + case System::WorkspaceStatus::Dead: + workspaces[i]->SetClass("ws-dead"); + workspaces[i]->SetText(" "); + break; + case System::WorkspaceStatus::Inactive: + workspaces[i]->SetClass("ws-inactive"); + workspaces[i]->SetText(" "); + break; + case System::WorkspaceStatus::Visible: + workspaces[i]->SetClass("ws-visible"); + workspaces[i]->SetText(" "); + break; + case System::WorkspaceStatus::Current: + workspaces[i]->SetClass("ws-current"); + workspaces[i]->SetText(" "); + break; + case System::WorkspaceStatus::Active: + workspaces[i]->SetClass("ws-active"); + workspaces[i]->SetText(" "); + break; + } + } + return TimerResult::Ok; + } +#endif + } + + void Sensor(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}); + box->SetHorizontalTransform({-1, true, Alignment::Right}); + { + auto revealer = Widget::Create(); + revealer->SetTransition({TransitionType::SlideLeft, 500}); + // Add event to eventbox for the revealer to open + eventBox->SetEventFn( + [textRevealer = revealer.get()](EventBox&, bool hovered) + { + textRevealer->SetRevealed(hovered); + }); + { + auto text = Widget::Create(); + text->SetClass(textClass); + textPtr = text.get(); + revealer->AddChild(std::move(text)); + } + + auto cairoSensor = Widget::Create(); + cairoSensor->SetClass(sensorClass); + cairoSensor->AddTimer(std::move(callback), DynCtx::updateTime); + cairoSensor->SetHorizontalTransform({24, true, Alignment::Fill}); + + box->AddChild(std::move(revealer)); + box->AddChild(std::move(cairoSensor)); + } + eventBox->AddChild(std::move(box)); + } + + parent.AddChild(std::move(eventBox)); + } + + void WidgetAudio(Widget& parent) + { + auto box = Widget::Create(); + box->SetSpacing({8, false}); + box->SetHorizontalTransform({-1, false, Alignment::Right}); + { + auto slider = Widget::Create(); + slider->SetOrientation(Orientation::Horizontal); + slider->SetHorizontalTransform({100, true, Alignment::Fill}); + slider->SetInverted(true); + slider->SetClass("audio-volume"); + slider->AddTimer(DynCtx::UpdateAudio, DynCtx::updateTimeFast); + slider->OnValueChange(DynCtx::OnChangeVolume); + slider->SetRange({0, 1, 0.01}); + + auto icon = Widget::Create(); + icon->SetClass("audio-icon"); + icon->SetText("墳"); + DynCtx::audioIcon = icon.get(); + + box->AddChild(std::move(slider)); + box->AddChild(std::move(icon)); + } + + parent.AddChild(std::move(box)); + } + +#ifdef HAS_BLUEZ + void WidgetBluetooth(Widget& parent) + { + auto box = Widget::Create(); + box->SetSpacing({0, false}); + box->AddTimer(DynCtx::UpdateBluetooth, DynCtx::updateTime); + { + auto devText = Widget::Create(); + DynCtx::btDevText = devText.get(); + devText->SetClass("bt-num"); + + auto iconText = Widget::Create(); + DynCtx::btIconText = iconText.get(); + + box->AddChild(std::move(devText)); + box->AddChild(std::move(iconText)); + } + + parent.AddChild(std::move(box)); + } +#endif + + void WidgetSensors(Widget& parent) + { + Sensor(parent, DynCtx::UpdateDisk, "disk-util-progress", "disk-data-text", DynCtx::diskText); +#ifdef HAS_NVIDIA + Sensor(parent, DynCtx::UpdateVRAM, "vram-util-progress", "vram-data-text", DynCtx::vramText); + Sensor(parent, DynCtx::UpdateGPU, "gpu-util-progress", "gpu-data-text", DynCtx::gpuText); +#endif + Sensor(parent, DynCtx::UpdateRAM, "ram-util-progress", "ram-data-text", DynCtx::ramText); + Sensor(parent, DynCtx::UpdateCPU, "cpu-util-progress", "cpu-data-text", DynCtx::cpuText); + } + + void WidgetPower(Widget& parent) + { + auto eventBox = Widget::Create(); + eventBox->SetEventFn(DynCtx::PowerBoxEvent); + { + auto powerBox = Widget::Create(); + powerBox->SetClass("power-box"); + powerBox->SetHorizontalTransform({-1, false, Alignment::Right}); + powerBox->SetSpacing({0, false}); + { + auto revealer = Widget::Create(); + DynCtx::powerBoxRevealer = revealer.get(); + revealer->SetTransition({TransitionType::SlideLeft, 500}); + { + auto powerBoxExpand = Widget::Create(); + powerBoxExpand->SetClass("power-box-expand"); + powerBoxExpand->SetSpacing({8, true}); + { + auto exitButton = Widget::Create