Add package widget

Polling is done via user script and a pipe. Since it is only run every
5 minutes by default, this should be fine

Partially implements https://github.com/scorpion-26/gBar/issues/14
This commit is contained in:
scorpion-26 2023-05-03 18:37:36 +02:00
parent 3517fa5de8
commit 299b497748
10 changed files with 128 additions and 1 deletions

View file

@ -87,6 +87,7 @@ Bar:
- GPU stats (Nvidia/AMD only): Utilisation, temperature, VRAM
- Disk: Free/Total
- Network: Current upload and download speed
- Update checking (Non-Arch systems need to be configured manually)
Bluetooth:
- Scanning of nearby bluetooth devices

View file

@ -101,6 +101,12 @@ highlight {
background-color: #bd93f9;
}
.package-outofdate {
font-size: 24px;
margin-right: -5px;
color: #ff5555;
}
.bt-num {
font-size: 16px;
color: #1793D1;

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;;;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"}
{"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;EACA;EACA,OA3IE;;;AA8IN;EACI,WAzIO;EA0IP,OA5IK;EA6IL;;;AAEJ;EACI;EACA,OAjJK;EAkJL;;;AAEJ;EACI;EACA,OAtJK;EAuJL;;;AAEJ;EACI;EACA,OA3JK;EA4JL;;;AAGJ;EACI,OArKK;EAsKL,kBA5KO;EA6KP,WAhKO;;;AAkKX;EACI,OA1KK;EA2KL;EACA,WArKO;;;AAwKX;EACI,OAlLK;EAmLL,kBAvLO;;;AAyLX;EACI,OAtLK;EAuLL;EACA,WA/KO;;;AAkLX;EACI,OAxLK;EAyLL,kBAjMO;;;AAmMX;EACI,OA5LK;EA6LL;EACA,WAzLO;;;AA4LX;EACI,OAxMG;EAyMH,kBA3MO;;;AA6MX;EACI,OA5MG;EA6MH;EACA,WAnMO;;;AAsMX;EACI,OAjNI;EAkNJ,kBArNO;EAsNP,WAzMO;;;AA2MX;EACI,OAtNI;EAuNJ;EACA,WA9MO;;;AAiNX;EACI,OA1NG;EA2NH,kBAhOO;EAiOP,WApNO;;;AAsNX;EACI,OA/NG;EAgOH;EACA,WAzNO;;;AA4NX;EACI,OAvOI;EAwOJ;EACA,WA/NO;;;AAmOX;EACI,OAjPO;;;AAoPX;EACI,OAlPI;;;AAqPR;EACI,OAjPK;;;AAoPT;EACI,OAzPK;;;AA4PT;EACI,OA3PK;;;AA8PT;EACI,OA9PE;;;AAkQN;EACI,OA1QO;;;AA6QX;EACI,OA3QI;;;AA8QR;EACI,OA1QK;;;AA6QT;EACI,OAlRK;;;AAqRT;EACI,OApRK;;;AAuRT;EACI,OAvRE;;;AA0RN;EACI,OAlSO;EAmSP,WAtRO;;;AAwRX;EACI,OArSO;EAsSP,WA1RO;;;AA4RX;EACI,OAxSG;EAySH,WA9RO;;;AAgSX;EACI,OAtSK;EAuSL,WAlSO;;;AAoSX;EACI,OA/SI;EAgTJ,WAtSO;;;AA0SX;EACI;IACI;;EAEJ;IACI;;;AAGR;EACI;IACI;;EAEJ;IACI;;;AAIR;EACI;IACI,OAlUC;;EAoUL;IACI,OA1UA;;;AA6UR;EACI;IACI,OA/UA;;EAiVJ;IACI,OA7UC;;;AAiVT;EACI,kBA5VC;EA6VD;;;AAEJ;EACI;EACA;EACA;EACA;EACA,OAvVK;;;AAyVT;EACI;EACA;;;AAEJ;EAgBI;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;AAxBA;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI,OA/WF;;;AA6XN;EACI,OA9XE;EA+XF,kBAtYO;EAuYP;EACH;EACA;;;AAED;EAaI,OAhZK;EAiZL,kBAzZO;EA0ZP;EACA;EACH;EACG;;AAjBA;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA","file":"style.css"}

View file

@ -145,6 +145,12 @@ highlight {
}
}
.package-outofdate {
font-size: 24px;
margin-right: -5px;
color: $red;
}
.bt-num {
font-size: $textsize;
color: $btblue;

View file

@ -52,6 +52,14 @@ AudioRevealer: false
# Sets the rate of change of the slider on each scroll. In Percent
AudioScrollSpeed: 5
# Command that is run to check if there are out-of-date packages.
# The script should return *ONLY* a number. If it doesn't output a number, updates are no longer checked.
# Default value is applicable for Arch Linux. (See src/Config.h for explanation on the default command)
CheckPackagesCommand: pac="$(checkupdates)"; if [ $? -eq 127 ] ; then exit 127; fi; echo $pac | wc -l
# How often to check for updates. In seconds
CheckUpdateInterval: 300
# Limits the range of the audio slider. Only works for audio output.
# Slider "empty" is AudioMinVolume, Slider "full" is AudioMaxVolume
# AudioMinVolume: 30 # Audio can't get below 30%

View file

@ -3,6 +3,7 @@
#include "System.h"
#include "Common.h"
#include "Config.h"
#include <mutex>
namespace Bar
{
@ -134,6 +135,30 @@ namespace Bar
}
#endif
static std::mutex packageTextLock;
static TimerResult UpdatePackages(Text& text)
{
System::GetOutdatedPackagesAsync(
[&](uint32_t numOutdatedPackages)
{
packageTextLock.lock();
if (numOutdatedPackages)
{
text.SetText("󰏔 ");
text.SetClass("package-outofdate");
text.SetTooltip("Updates available! (" + std::to_string(numOutdatedPackages) + " packages)");
}
else
{
text.SetText("");
text.SetClass("package-empty");
text.SetTooltip("");
}
packageTextLock.unlock();
});
return TimerResult::Ok;
}
void OnChangeVolumeSink(Slider&, double value)
{
System::SetVolumeSink(value);
@ -385,6 +410,16 @@ namespace Bar
parent.AddTimer<Widget>(DynCtx::UpdateAudio, DynCtx::updateTimeFast);
}
void WidgetPackages(Widget& parent)
{
auto text = Widget::Create<Text>();
text->SetText("");
text->SetClass("package-empty");
text->AddTimer<Text>(DynCtx::UpdatePackages, 1000 * Config::Get().checkUpdateInterval,
TimerDispatchBehaviour::ImmediateDispatch);
parent.AddChild(std::move(text));
}
#ifdef WITH_BLUEZ
void WidgetBluetooth(Widget& parent)
{
@ -609,6 +644,9 @@ namespace Bar
right->SetSpacing({8, false});
right->SetHorizontalTransform({-1, true, Alignment::Right});
{
// Needs to be rightmost, since an empty package string introduces additional padding
WidgetPackages(*right);
WidgetAudio(*right);
#ifdef WITH_BLUEZ

View file

@ -133,6 +133,7 @@ void Config::Load()
AddConfigVar("ExitCommand", config.exitCommand, lineView, foundProperty);
AddConfigVar("BatteryFolder", config.batteryFolder, lineView, foundProperty);
AddConfigVar("DefaultWorkspaceSymbol", config.defaultWorkspaceSymbol, lineView, foundProperty);
AddConfigVar("CheckPackagesCommand", config.checkPackagesCommand, lineView, foundProperty);
for (int i = 1; i < 10; i++)
{
// Subtract 1 to index from 1 to 9 rather than 0 to 8
@ -152,6 +153,8 @@ void Config::Load()
AddConfigVar("MinDownloadBytes", config.minDownloadBytes, lineView, foundProperty);
AddConfigVar("MaxDownloadBytes", config.maxDownloadBytes, lineView, foundProperty);
AddConfigVar("CheckUpdateInterval", config.checkUpdateInterval, lineView, foundProperty);
AddConfigVar("AudioScrollSpeed", config.audioScrollSpeed, lineView, foundProperty);
AddConfigVar("AudioMinVolume", config.audioMinVolume, lineView, foundProperty);

View file

@ -14,6 +14,11 @@ public:
std::vector<std::string> workspaceSymbols = std::vector<std::string>(9, "");
std::string defaultWorkspaceSymbol = "";
// Script that returns how many packages are out-of-date. The script should only print a number!
// The default script runs checkupdates, and forcefully exits when checkupdates is not found, so gBar can disable the package widget.
// "checkupdates | wc -l" would always return 0 on stdout, which gBar accepts
std::string checkPackagesCommand = "pac=\"$(checkupdates)\"; if [ $? -eq 127 ] ; then exit 127; fi; echo $pac | wc -l";
bool centerTime = true;
bool audioRevealer = false;
bool audioInput = false;
@ -30,6 +35,8 @@ public:
uint32_t audioScrollSpeed = 5; // 5% each scroll
uint32_t checkUpdateInterval = 5 * 60; // Interval to run the "checkPackagesCommand". In seconds
// Only affects outputs (i.e.: speakers, not microphones). This remaps the range of the volume; In percent
double audioMinVolume = 0.f; // Map the minimum volume to this value
double audioMaxVolume = 100.f; // Map the maximum volume to this value
@ -67,5 +74,7 @@ public:
bool hasNet = true;
bool hasPackagesScript = true;
static RuntimeConfig& Get();
};

View file

@ -8,6 +8,7 @@
#include <cstdlib>
#include <fstream>
#include <mutex>
#include <string>
#include <sstream>
#include <iomanip>
@ -534,6 +535,59 @@ namespace System
return GetNetworkBpsCommon(dt, prevDownloadBytes, "/sys/class/net/" + Config::Get().networkAdapter + "/statistics/rx_bytes");
}
void GetOutdatedPackagesAsync(std::function<void(uint32_t)>&& returnVal)
{
static std::mutex configMutex;
configMutex.lock();
if (!RuntimeConfig::Get().hasPackagesScript)
{
configMutex.unlock();
return; // Don't bother
}
configMutex.unlock();
std::thread(
[](std::function<void(uint32_t)> returnVal)
{
// We need a pipe, since there is no "libpacman". This should only be called every so often anyways
std::string number;
char buf[2056];
FILE* pipe = popen(Config::Get().checkPackagesCommand.c_str(), "r"); // Redirect stderr
ASSERT(pipe, "GetOutdatedPackages: Couldn't open pipe");
while (fgets(buf, sizeof(buf), pipe) != 0)
{
number.append(buf);
}
ASSERT(feof(pipe), "GetOutdatedPackages: Cannot read to eof!");
int exitCode = pclose(pipe) / 256;
if (exitCode == 127)
{
configMutex.lock();
// Invalid script
LOG("GetOutdatedPackages: Invalid command. Disabling package widget!");
RuntimeConfig::Get().hasPackagesScript = false;
configMutex.unlock();
return;
}
try
{
returnVal(std::stoul(buf));
}
catch (std::invalid_argument)
{
configMutex.lock();
LOG("GetOutdatedPackages: Invalid output of the package script. Disabling package widget!");
RuntimeConfig::Get().hasPackagesScript = false;
configMutex.unlock();
return;
}
},
std::move(returnVal))
.detach();
}
std::string GetTime()
{
time_t stdTime = time(NULL);

View file

@ -105,6 +105,8 @@ namespace System
// Bytes per second download. dx is time since last call. Will always return 0 on first run
double GetNetworkBpsDownload(double dt);
void GetOutdatedPackagesAsync(std::function<void(uint32_t)>&& returnVal);
std::string GetTime();
void Shutdown();