Skip to content

Commit

Permalink
FlexMeter: Add FlexMeter functionality
Browse files Browse the repository at this point in the history
FlexMeter provides functionality which will allow
users to make custom meters without need of rebuilding
every time htop binary and adding source to the project.
It can be used to print some device status, free disk space
CPU or other specific temeraturer, fan RPM and many more.
Everything that can be fetched from linux shell
with one line result can be printer. For fething information
can be used anything from shell, python, precompiled binary
or simply reading file located somewhere in file system.

New meter will appear uppon restart of htop in list with
available meters.

Configuration folder location where metes should be placed:
- /home/$USER/.config/htop/FlexMeter/
On start folder will be created if does not exist, together
with template file .Template in same folder.
Note: Files starting with '.' (.Template for examlpe) are
ignored

Meter Example:
File name : Template

name=<NAME SHOWN IN AvailableMeters>
command=<COMMAND WHICH WILL BE EXECUTED>
type=<METER TYPE FOR NO ONLY "TEXT_METER">
caption="CAPTION TEXT SHOWN IN THE BEGGINING OF THE METER"

According to this implementation 30 Flex meter can be added
Currently they have hardcoded limit of 30 meter in addition
to all that already exist.

Signed-off-by: Stoyan Bogdanov <[email protected]>
  • Loading branch information
bogdanovs committed Jan 2, 2025
1 parent 4b3dfa2 commit 2a39429
Show file tree
Hide file tree
Showing 28 changed files with 291 additions and 30 deletions.
6 changes: 6 additions & 0 deletions CRT.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = A_BOLD | ColorPair(Red, Black),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = A_BOLD | ColorPair(Cyan, Black),
[FLEX] = A_BOLD | ColorPair(Cyan, Black),
[BATTERY] = A_BOLD | ColorPair(Cyan, Black),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
Expand Down Expand Up @@ -247,6 +248,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = A_BOLD,
[PAUSED] = A_BOLD | A_REVERSE,
[UPTIME] = A_BOLD,
[FLEX] = A_BOLD,
[BATTERY] = A_BOLD,
[LARGE_NUMBER] = A_BOLD,
[METER_SHADOW] = A_DIM,
Expand Down Expand Up @@ -365,6 +367,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = ColorPair(Red, White),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = ColorPair(Yellow, White),
[FLEX] = ColorPair(Yellow, White),
[BATTERY] = ColorPair(Yellow, White),
[LARGE_NUMBER] = ColorPair(Red, White),
[METER_SHADOW] = ColorPair(Blue, White),
Expand Down Expand Up @@ -483,6 +486,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = ColorPair(Red, Black),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = ColorPair(Yellow, Black),
[FLEX] = ColorPair(Yellow, Black),
[BATTERY] = ColorPair(Yellow, Black),
[LARGE_NUMBER] = ColorPair(Red, Black),
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
Expand Down Expand Up @@ -601,6 +605,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = A_BOLD | ColorPair(Red, Blue),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = A_BOLD | ColorPair(Yellow, Blue),
[FLEX] = A_BOLD | ColorPair(Yellow, Blue),
[BATTERY] = A_BOLD | ColorPair(Yellow, Blue),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue),
[METER_SHADOW] = ColorPair(Cyan, Blue),
Expand Down Expand Up @@ -719,6 +724,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = A_BOLD | ColorPair(Red, Black),
[PAUSED] = A_BOLD | ColorPair(Yellow, Green),
[UPTIME] = ColorPair(Green, Black),
[FLEX] = ColorPair(Green, Black),
[BATTERY] = ColorPair(Green, Black),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
Expand Down
1 change: 1 addition & 0 deletions CRT.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ typedef enum ColorElements_ {
DYNAMIC_MAGENTA,
DYNAMIC_YELLOW,
DYNAMIC_WHITE,
FLEX,
LAST_COLORELEMENT
} ColorElements;

Expand Down
205 changes: 205 additions & 0 deletions FlexMeter.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
htop - FlexMeter.c
(C) 2024 Stoyan Bogdanov
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <pwd.h>
#include "FlexMeter.h"
#include "Object.h"
#include "config.h"
#include "CRT.h"

#define FLEX_CFG_FOLDER ".config/htop/FlexMeter"

typedef struct {
char* name;
char* command;
char* type;
char* caption;
char* uiName;
}_flex_meter;

_flex_meter meter_list[30];

int meter_list_idx=0;
static int meters_count=0;

static const int DateMeter_attributes[] = {
FLEX
};

MeterClass * FlexMeter_class = NULL;

int check_for_meters(void);

static int parse_input(char * line)
{
if (!strncmp(line,"name=",5))
{
meter_list[meters_count].uiName = xStrdup(line+5);
}
else if (!strncmp(line,"command=",7))
{
meter_list[meters_count].command = xStrdup(line+8);
}
else if (!strncmp(line,"caption=",7))
{
meter_list[meters_count].caption = xStrdup(line+8);
}
else if (!strncmp(line,"type=",5))
{
meter_list[meters_count].type = xStrdup(line+6);
}
else
{
return -1;
}

return 0;
}

static int load_config(char * file)
{
int ret = -1;
char *buff;
FILE *fp = fopen( file , "r");

if (fp != NULL) {
while(1)
{
buff = String_readLine(fp);
if (buff!=NULL)
parse_input(buff);
else
break;
}

fclose(fp);
ret = 0;
}

return ret;
}

int check_for_meters(void)
{
char path[400]; // full path
DIR *d;
struct dirent *dir;
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;

// path to home folder 1 for zero 1 for slash
char * home = (char * ) xCalloc(1,(strlen(homedir) + strlen(FLEX_CFG_FOLDER) + 2));

xSnprintf(home,(strlen(homedir) + strlen(FLEX_CFG_FOLDER) + 2),"%s/%s",homedir,FLEX_CFG_FOLDER);

d = opendir(home);
if (d) {
while ((dir = readdir(d)) != NULL) {
if ( dir->d_name[0]!='.')
{
// We are ignoring all files starting with . like ".Template" and "." ".." directories
meter_list[meters_count].name = xStrdup(dir->d_name);
memset(path,0,80);
xSnprintf(path,400,"%s/%s",home,dir->d_name);
if( 0 != load_config(path) )
{
break;
}

if ( meters_count < MAX_METERS_COUNT )
{
meters_count++;
}
else
{
break; // go out we reach the limit
}
}
}
closedir(d);
}
else
{
mkdir(home,0700);
}

free(home);
return meters_count;
}

static void FlexMeter_updateValues(Meter* this)
{
for (int i =0 ; i < meters_count; i++)
{
if (this->m_ptr == &FlexMeter_class[i] )
{
char buff[256]={0};
int ret = -1;
memset(buff,0,256);
if ( meter_list[i].command[0] != 0 )
{
FILE* fd = popen(meter_list[i].command, "r");
if (fd)
{
if (fgets(buff, 256, fd) == NULL)
ret = pclose(fd);
else
ret = pclose(fd);
}

if( (buff[0] != 0) && (ret == 0) )
{
int l = strlen(buff);
buff[l-1] = '\0';
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s",buff);
}
else
{
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", "Read CMD ERR");
}
}
}
}
}

const MeterClass FlexMeter_class_template = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete
},
.updateValues = FlexMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100,
.attributes = DateMeter_attributes,
.name = NULL,
.uiName = NULL,
.caption = NULL,
};

int load_flex_modules(void)
{
uint8_t meters_num = check_for_meters();
if (FlexMeter_class == NULL && meters_num!=0)
{
FlexMeter_class = (MeterClass*) xCalloc(meters_num,sizeof(MeterClass));
for (uint8_t i=0 ; i<meters_num;i++)
{
memcpy(&FlexMeter_class[i],&FlexMeter_class_template,sizeof(MeterClass));

FlexMeter_class[i].name = (const char *) xStrdup(meter_list[i].name);
FlexMeter_class[i].uiName = (const char *) xStrdup(meter_list[i].uiName);
FlexMeter_class[i].caption = (const char *) xStrdup(meter_list[i].caption);
}
}
return (int)meters_num;
}
19 changes: 19 additions & 0 deletions FlexMeter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef HEADER_FlexMeter
#define HEADER_FlexMeter
/*
htop - FlexMeter.c
(C) 2021 Stoyan Bogdanov
(C) 2021 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include "Meter.h"

#define METERS_LIST_SIZE 30
#define MAX_METERS_COUNT METERS_LIST_SIZE-1

extern MeterClass *FlexMeter_class ;
int load_flex_modules(void);

#endif
10 changes: 9 additions & 1 deletion Header.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ in the source distribution for its full text.
#include "ProvideCurses.h"
#include "Settings.h"
#include "XUtils.h"

#include "FlexMeter.h"

Header* Header_new(Machine* host, HeaderLayout hLayout) {
Header* this = xCalloc(1, sizeof(Header));
Expand Down Expand Up @@ -121,6 +121,14 @@ void Header_populateFromSettings(Header* this) {
const Settings* settings = this->host->settings;
Header_setLayout(this, settings->hLayout);

int num = load_flex_modules();
int platform_size = 0;

for (platform_size = 0; Platform_meterTypes[platform_size] != NULL; platform_size++);
for (int i = 0; i < num; i++) Platform_meterTypes[platform_size+i]=FlexMeter_class+i;

Platform_meterTypes[platform_size+num]=NULL;

Header_forEachColumn(this, col) {
const MeterColumnSetting* colSettings = &settings->hColumns[col];
Vector_prune(this->columns[col]);
Expand Down
6 changes: 4 additions & 2 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ myhtopsources = \
UptimeMeter.c \
UsersTable.c \
Vector.c \
XUtils.c
XUtils.c \
FlexMeter.c

myhtopheaders = \
Action.h \
Expand Down Expand Up @@ -163,7 +164,8 @@ myhtopheaders = \
UptimeMeter.h \
UsersTable.h \
Vector.h \
XUtils.h
XUtils.h \
FlexMeter.h

# Linux
# -----
Expand Down
1 change: 1 addition & 0 deletions Meter.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type
this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
this->total = type->total;
this->caption = xStrdup(type->caption);
this->m_ptr = type;
if (Meter_initFn(this)) {
Meter_init(this);
}
Expand Down
9 changes: 5 additions & 4 deletions Meter.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ typedef struct MeterClass_ {
const MeterModeId defaultMode;
const double total;
const int* const attributes;
const char* const name; /* internal name of the meter, must not contain any space */
const char* const uiName; /* display name in header setup menu */
const char* const caption; /* prefix in the actual header */
const char* name; /* internal name of the meter, must not contain any space */
const char* uiName; /* display name in header setup menu */
const char* caption; /* prefix in the actual header */
const char* const description; /* optional meter description in header setup menu */
const uint8_t maxItems;
const bool isMultiColumn; /* whether the meter draws multiple sub-columns (defaults to false) */
Expand Down Expand Up @@ -102,8 +102,9 @@ typedef struct GraphData_ {
struct Meter_ {
Object super;
Meter_Draw draw;
const Machine* host;

const Machine* host;
const MeterClass * m_ptr;
char* caption;
MeterModeId mode;
unsigned int param;
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ AM_INIT_AUTOMAKE([-Wall std-options subdir-objects])

# ----------------------------------------------------------------------

AC_DEFINE([MAX_PLATFORM_METERS], [100], [Set max meters for number])

# ----------------------------------------------------------------------
# Checks for platform.
Expand Down
3 changes: 2 additions & 1 deletion darwin/Platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ in the source distribution for its full text.
#include "generic/fdstat_sysctl.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "FlexMeter.h"

#ifdef HAVE_HOST_GET_CLOCK_SERVICE
#include <mach/clock.h>
Expand Down Expand Up @@ -112,7 +113,7 @@ const SignalItem Platform_signals[] = {

const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);

const MeterClass* const Platform_meterTypes[] = {
const MeterClass* Platform_meterTypes[MAX_PLATFORM_METERS] = {
&CPUMeter_class,
&ClockMeter_class,
&DateMeter_class,
Expand Down
2 changes: 1 addition & 1 deletion darwin/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ extern const SignalItem Platform_signals[];

extern const unsigned int Platform_numberOfSignals;

extern const MeterClass* const Platform_meterTypes[];
extern const MeterClass* Platform_meterTypes[MAX_PLATFORM_METERS];

bool Platform_init(void);

Expand Down
4 changes: 4 additions & 0 deletions docs/FlexMeter/Template
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name=template
command=echo "`uptime`"
type=TEXT_METERMODE
caption="UPTIME"
Loading

0 comments on commit 2a39429

Please sign in to comment.