Skip to content

Instantly share code, notes, and snippets.

@bathtime
Last active April 2, 2018 16:36
Show Gist options
  • Save bathtime/0ea6a0f69703b6d888aa02093dabd476 to your computer and use it in GitHub Desktop.
Save bathtime/0ea6a0f69703b6d888aa02093dabd476 to your computer and use it in GitHub Desktop.
Bar
// Compile with:
//
// g++ -std=c++11 -O2 -Wall bar.cpp -o bar -lasound -lpthread
//
#include<alsa/asoundlib.h> // install libasound2-dev
#include<fstream>
#include<iostream>
#include<string>
#include<chrono>
#include<thread>
#include"sys/sysinfo.h"
#include<future>
#include<sstream>
using namespace std;
static unsigned int mSec = 250;
std::string cpuPercent(){
static unsigned long long lastTotalUser, lastTotalUserLow, lastTotalSys, lastTotalIdle;
double percent;
long int intPercent;
std::string s;
FILE* file;
unsigned long long totalUser, totalUserLow, totalSys, totalIdle, total;
file = fopen("/proc/stat", "r");
fscanf(file, "cpu %llu %llu %llu %llu", &totalUser, &totalUserLow, &totalSys, &totalIdle);
fclose(file);
if ( totalUser < lastTotalUser || totalUserLow < lastTotalUserLow ||
totalSys < lastTotalSys || totalIdle < lastTotalIdle){
//Overflow detection. Just skip this value.
percent = -1.0;
}else{
total = (totalUser - lastTotalUser) + (totalUserLow - lastTotalUserLow) + (totalSys - lastTotalSys);
percent = total * 100;
total += (totalIdle - lastTotalIdle);
percent /= total;
}
lastTotalUser = totalUser;
lastTotalUserLow = totalUserLow;
lastTotalSys = totalSys;
lastTotalIdle = totalIdle;
intPercent = percent;
s = to_string(intPercent);
// Stop overflow by making all numbers '100' if above 2 digits
return (s.length() > 2 ? "100" : s);
}
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
if (!pipe) throw std::runtime_error("popen() failed!");
while (!feof(pipe.get()))
if (fgets(buffer.data(), 128, pipe.get()) != nullptr){
result += buffer.data();
}
return result.substr(0, result.length() - 1);
}
std::string procs(void){
struct sysinfo s;
sysinfo(&s);
return to_string(s.procs);
}
std::string date_and_time(void){
char date[48];
time_t now = time(0);
strftime(date, 48, "%a %b %d, %H:%M:%S", localtime(&now));
std::string charToStr (date);
return charToStr;
}
std::string get_vol(void)
{
int vol;
std::string volume;
snd_hctl_t *hctl;
snd_ctl_elem_id_t *id;
snd_ctl_elem_value_t *control;
// To find card and subdevice: /proc/asound/, aplay -L, amixer controls
snd_hctl_open(&hctl, "hw:0", 0);
snd_hctl_load(hctl);
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
// amixer controls
snd_ctl_elem_id_set_name(id, "Master Playback Volume");
snd_hctl_elem_t *elem = snd_hctl_find_elem(hctl, id);
snd_ctl_elem_value_alloca(&control);
snd_ctl_elem_value_set_id(control, id);
snd_hctl_elem_read(elem, control);
snd_hctl_close(hctl);
vol = (int)snd_ctl_elem_value_get_integer(control,0);
volume = to_string(vol*100/127);
return volume;
}
std::string get_mem_total() {
const std::string info[] = {"MemTotal:", "MemFree:", "Buffers:", "Cached:"};
int intInfo[4];
std::string token;
std::ifstream file("/proc/meminfo");
while(file >> token)
{
for(int i = 0; i < 4; i++)
if(token == info[i]){
file >> intInfo[i];
if(i == 3)
return std::to_string((intInfo[0] - intInfo[1] - (intInfo[2] + intInfo[3])) / 1024);
}
}
return "Information unavailable.";
}
std::string get_net() {
std::string netInd;
char txChar[16]; // Just grab the first 16 bytes
char rxChar[16];
long int trInt = 0;
long int rxInt = 0;
/* The machine seems to update these files only about every 1 second,
so if this program checks within this time frame, there would be no
difference in net activity, where perhaps there is net activity.
The result is a blinking net indicator even when net activity is
constant. To avoid this, a delay is used, which will allow the
indicator to remain on until the file updates. */
const unsigned int delay = (1100 / mSec); // delay for 1 cycle per 1100ms (auto-adjust to rest interval)
static unsigned int TXdelay = 0;
static unsigned int RXdelay = 0;
std::ifstream fileTX("/sys/class/net/eth0/statistics/tx_bytes");
fileTX.getline(txChar, 16);
fileTX.close();
std::ifstream fileRX("/sys/class/net/eth0/statistics/rx_bytes");
fileRX.getline(rxChar, 16);
fileRX.close();
// Calculate current reading by comparing past to present usage
trInt = std::stoi(txChar);
rxInt = std::stoi(txChar);
// Avoid net indicators flashing at program startup
static long int lastNetTX = trInt;
static long int lastNetRX = rxInt;
/* Net activity is in bytes, so '1000 > 0' is 1kb of net usage.
Although the might be temptation to set this lower, keep in
mind that the networking system sends out data every now
then. Too low a setting will mean that indicators keep flashing
regardless. */
if ((trInt - lastNetTX) / 1000 > 0) TXdelay = delay;
if ((rxInt - lastNetRX) / 1000 > 0) RXdelay = delay;
if (TXdelay--)
netInd += ".";
else{
netInd += " ";
TXdelay = 0; }
if (RXdelay--)
netInd += ".";
else{
netInd += " ";
RXdelay = 0; }
lastNetTX = trInt;
lastNetRX = rxInt;
return netInd;
}
void execFunc(const char * e, std::string &s, bool &done)
{
s = exec(e);
done = true;
}
int main(int argc, char* argv[])
{
if (argc > 1) mSec = atoi(argv[1]); // Check if delay was entered as parameter
unsigned int threadTicksGoal = (1000 / mSec) * 300; // Seconds until threads update
unsigned int refreshTicksGoal = (1000 / mSec) / 2; // 1x per 1000ms (adjusted to mSec cycles)
unsigned int threadTicks = 0;
unsigned int refreshTicks = 0;
// Variables to draw from until update
std::string cpu;
std::string vol;
std::string datetime;
std::string txrx;
// Setup commands which you would like to run as threads
const char* const command[] = { "Bennetto", "mailnum", "Canada.sh" };
size_t nThreads = (sizeof command / sizeof command[0]);
// Get thread stuff ready
std::thread myThreads[nThreads];
std::string funcStr[nThreads]; // The original string from the thread
std::string readyStr[nThreads]; // The string which will take a copy of funcStr which it completes
bool tReady[nThreads]; // A means of indicating the thread has ran and is OK to copy strings
// Timer function to sleep between checks
std::chrono::milliseconds timespan(mSec);
// const char * sysCharCmd;
while(1)
{
if (!threadTicks--) // Ticktimer reached 0, so refresh threads
{
for (unsigned int i = 0; i < nThreads; i++)
{
myThreads[i] = std::thread(&execFunc, ref(command[i]), std::ref(funcStr[i]), ref(tReady[i]));
myThreads[i].detach();
}
threadTicks = threadTicksGoal;
}
// Update every one second; cpu cannot be updated sooner; volume need not be
if (!refreshTicks--)
{
cpu = cpuPercent();
vol = get_vol();
datetime = date_and_time();
refreshTicks = refreshTicksGoal;
// Keep checking to see if the threads are finished and update strings immediately
for (unsigned int i = 0; i < nThreads; i++)
if (tReady[i])
{ // Is the thread is done?
tReady[i] = 0; // Reset for now
readyStr[i] = funcStr[i]; // Finally, the string is copied
}
}
std::string sysStrCmd =
"C:" +
cpu + "% " +
get_mem_total() + "m " +
"P:" + procs() + " " +
"V:" + vol + "% " +
readyStr[0] + (readyStr[0] == "" ? "": " ") +
readyStr[1] + (readyStr[1] == "" ? "": " ") +
readyStr[2] + (readyStr[2] == "" ? "": " ") +
datetime +
get_net();
// sysCharCmd = sysStrCmd.c_str();
// system(sysCharCmd);
std::cout << "\r" << sysStrCmd << std::flush;
std::this_thread::sleep_for(timespan);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment