Skip to content

Commit 2a39429

Browse files
committed
FlexMeter: Add FlexMeter functionality
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]>
1 parent 4b3dfa2 commit 2a39429

28 files changed

+291
-30
lines changed

CRT.c

+6
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
129129
[FAILED_READ] = A_BOLD | ColorPair(Red, Black),
130130
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
131131
[UPTIME] = A_BOLD | ColorPair(Cyan, Black),
132+
[FLEX] = A_BOLD | ColorPair(Cyan, Black),
132133
[BATTERY] = A_BOLD | ColorPair(Cyan, Black),
133134
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
134135
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
@@ -247,6 +248,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
247248
[FAILED_READ] = A_BOLD,
248249
[PAUSED] = A_BOLD | A_REVERSE,
249250
[UPTIME] = A_BOLD,
251+
[FLEX] = A_BOLD,
250252
[BATTERY] = A_BOLD,
251253
[LARGE_NUMBER] = A_BOLD,
252254
[METER_SHADOW] = A_DIM,
@@ -365,6 +367,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
365367
[FAILED_READ] = ColorPair(Red, White),
366368
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
367369
[UPTIME] = ColorPair(Yellow, White),
370+
[FLEX] = ColorPair(Yellow, White),
368371
[BATTERY] = ColorPair(Yellow, White),
369372
[LARGE_NUMBER] = ColorPair(Red, White),
370373
[METER_SHADOW] = ColorPair(Blue, White),
@@ -483,6 +486,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
483486
[FAILED_READ] = ColorPair(Red, Black),
484487
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
485488
[UPTIME] = ColorPair(Yellow, Black),
489+
[FLEX] = ColorPair(Yellow, Black),
486490
[BATTERY] = ColorPair(Yellow, Black),
487491
[LARGE_NUMBER] = ColorPair(Red, Black),
488492
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
@@ -601,6 +605,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
601605
[FAILED_READ] = A_BOLD | ColorPair(Red, Blue),
602606
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
603607
[UPTIME] = A_BOLD | ColorPair(Yellow, Blue),
608+
[FLEX] = A_BOLD | ColorPair(Yellow, Blue),
604609
[BATTERY] = A_BOLD | ColorPair(Yellow, Blue),
605610
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue),
606611
[METER_SHADOW] = ColorPair(Cyan, Blue),
@@ -719,6 +724,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
719724
[FAILED_READ] = A_BOLD | ColorPair(Red, Black),
720725
[PAUSED] = A_BOLD | ColorPair(Yellow, Green),
721726
[UPTIME] = ColorPair(Green, Black),
727+
[FLEX] = ColorPair(Green, Black),
722728
[BATTERY] = ColorPair(Green, Black),
723729
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
724730
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,

CRT.h

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ typedef enum ColorElements_ {
154154
DYNAMIC_MAGENTA,
155155
DYNAMIC_YELLOW,
156156
DYNAMIC_WHITE,
157+
FLEX,
157158
LAST_COLORELEMENT
158159
} ColorElements;
159160

FlexMeter.c

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
htop - FlexMeter.c
3+
(C) 2024 Stoyan Bogdanov
4+
Released under the GNU GPLv2+, see the COPYING file
5+
in the source distribution for its full text.
6+
*/
7+
8+
#include <sys/time.h>
9+
#include <sys/types.h>
10+
#include <unistd.h>
11+
#include <dirent.h>
12+
#include <time.h>
13+
#include <pwd.h>
14+
#include "FlexMeter.h"
15+
#include "Object.h"
16+
#include "config.h"
17+
#include "CRT.h"
18+
19+
#define FLEX_CFG_FOLDER ".config/htop/FlexMeter"
20+
21+
typedef struct {
22+
char* name;
23+
char* command;
24+
char* type;
25+
char* caption;
26+
char* uiName;
27+
}_flex_meter;
28+
29+
_flex_meter meter_list[30];
30+
31+
int meter_list_idx=0;
32+
static int meters_count=0;
33+
34+
static const int DateMeter_attributes[] = {
35+
FLEX
36+
};
37+
38+
MeterClass * FlexMeter_class = NULL;
39+
40+
int check_for_meters(void);
41+
42+
static int parse_input(char * line)
43+
{
44+
if (!strncmp(line,"name=",5))
45+
{
46+
meter_list[meters_count].uiName = xStrdup(line+5);
47+
}
48+
else if (!strncmp(line,"command=",7))
49+
{
50+
meter_list[meters_count].command = xStrdup(line+8);
51+
}
52+
else if (!strncmp(line,"caption=",7))
53+
{
54+
meter_list[meters_count].caption = xStrdup(line+8);
55+
}
56+
else if (!strncmp(line,"type=",5))
57+
{
58+
meter_list[meters_count].type = xStrdup(line+6);
59+
}
60+
else
61+
{
62+
return -1;
63+
}
64+
65+
return 0;
66+
}
67+
68+
static int load_config(char * file)
69+
{
70+
int ret = -1;
71+
char *buff;
72+
FILE *fp = fopen( file , "r");
73+
74+
if (fp != NULL) {
75+
while(1)
76+
{
77+
buff = String_readLine(fp);
78+
if (buff!=NULL)
79+
parse_input(buff);
80+
else
81+
break;
82+
}
83+
84+
fclose(fp);
85+
ret = 0;
86+
}
87+
88+
return ret;
89+
}
90+
91+
int check_for_meters(void)
92+
{
93+
char path[400]; // full path
94+
DIR *d;
95+
struct dirent *dir;
96+
struct passwd *pw = getpwuid(getuid());
97+
const char *homedir = pw->pw_dir;
98+
99+
// path to home folder 1 for zero 1 for slash
100+
char * home = (char * ) xCalloc(1,(strlen(homedir) + strlen(FLEX_CFG_FOLDER) + 2));
101+
102+
xSnprintf(home,(strlen(homedir) + strlen(FLEX_CFG_FOLDER) + 2),"%s/%s",homedir,FLEX_CFG_FOLDER);
103+
104+
d = opendir(home);
105+
if (d) {
106+
while ((dir = readdir(d)) != NULL) {
107+
if ( dir->d_name[0]!='.')
108+
{
109+
// We are ignoring all files starting with . like ".Template" and "." ".." directories
110+
meter_list[meters_count].name = xStrdup(dir->d_name);
111+
memset(path,0,80);
112+
xSnprintf(path,400,"%s/%s",home,dir->d_name);
113+
if( 0 != load_config(path) )
114+
{
115+
break;
116+
}
117+
118+
if ( meters_count < MAX_METERS_COUNT )
119+
{
120+
meters_count++;
121+
}
122+
else
123+
{
124+
break; // go out we reach the limit
125+
}
126+
}
127+
}
128+
closedir(d);
129+
}
130+
else
131+
{
132+
mkdir(home,0700);
133+
}
134+
135+
free(home);
136+
return meters_count;
137+
}
138+
139+
static void FlexMeter_updateValues(Meter* this)
140+
{
141+
for (int i =0 ; i < meters_count; i++)
142+
{
143+
if (this->m_ptr == &FlexMeter_class[i] )
144+
{
145+
char buff[256]={0};
146+
int ret = -1;
147+
memset(buff,0,256);
148+
if ( meter_list[i].command[0] != 0 )
149+
{
150+
FILE* fd = popen(meter_list[i].command, "r");
151+
if (fd)
152+
{
153+
if (fgets(buff, 256, fd) == NULL)
154+
ret = pclose(fd);
155+
else
156+
ret = pclose(fd);
157+
}
158+
159+
if( (buff[0] != 0) && (ret == 0) )
160+
{
161+
int l = strlen(buff);
162+
buff[l-1] = '\0';
163+
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s",buff);
164+
}
165+
else
166+
{
167+
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", "Read CMD ERR");
168+
}
169+
}
170+
}
171+
}
172+
}
173+
174+
const MeterClass FlexMeter_class_template = {
175+
.super = {
176+
.extends = Class(Meter),
177+
.delete = Meter_delete
178+
},
179+
.updateValues = FlexMeter_updateValues,
180+
.defaultMode = TEXT_METERMODE,
181+
.maxItems = 1,
182+
.total = 100,
183+
.attributes = DateMeter_attributes,
184+
.name = NULL,
185+
.uiName = NULL,
186+
.caption = NULL,
187+
};
188+
189+
int load_flex_modules(void)
190+
{
191+
uint8_t meters_num = check_for_meters();
192+
if (FlexMeter_class == NULL && meters_num!=0)
193+
{
194+
FlexMeter_class = (MeterClass*) xCalloc(meters_num,sizeof(MeterClass));
195+
for (uint8_t i=0 ; i<meters_num;i++)
196+
{
197+
memcpy(&FlexMeter_class[i],&FlexMeter_class_template,sizeof(MeterClass));
198+
199+
FlexMeter_class[i].name = (const char *) xStrdup(meter_list[i].name);
200+
FlexMeter_class[i].uiName = (const char *) xStrdup(meter_list[i].uiName);
201+
FlexMeter_class[i].caption = (const char *) xStrdup(meter_list[i].caption);
202+
}
203+
}
204+
return (int)meters_num;
205+
}

FlexMeter.h

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#ifndef HEADER_FlexMeter
2+
#define HEADER_FlexMeter
3+
/*
4+
htop - FlexMeter.c
5+
(C) 2021 Stoyan Bogdanov
6+
(C) 2021 htop dev team
7+
Released under the GNU GPLv2+, see the COPYING file
8+
in the source distribution for its full text.
9+
*/
10+
11+
#include "Meter.h"
12+
13+
#define METERS_LIST_SIZE 30
14+
#define MAX_METERS_COUNT METERS_LIST_SIZE-1
15+
16+
extern MeterClass *FlexMeter_class ;
17+
int load_flex_modules(void);
18+
19+
#endif

Header.c

+9-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ in the source distribution for its full text.
2626
#include "ProvideCurses.h"
2727
#include "Settings.h"
2828
#include "XUtils.h"
29-
29+
#include "FlexMeter.h"
3030

3131
Header* Header_new(Machine* host, HeaderLayout hLayout) {
3232
Header* this = xCalloc(1, sizeof(Header));
@@ -121,6 +121,14 @@ void Header_populateFromSettings(Header* this) {
121121
const Settings* settings = this->host->settings;
122122
Header_setLayout(this, settings->hLayout);
123123

124+
int num = load_flex_modules();
125+
int platform_size = 0;
126+
127+
for (platform_size = 0; Platform_meterTypes[platform_size] != NULL; platform_size++);
128+
for (int i = 0; i < num; i++) Platform_meterTypes[platform_size+i]=FlexMeter_class+i;
129+
130+
Platform_meterTypes[platform_size+num]=NULL;
131+
124132
Header_forEachColumn(this, col) {
125133
const MeterColumnSetting* colSettings = &settings->hColumns[col];
126134
Vector_prune(this->columns[col]);

Makefile.am

+4-2
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ myhtopsources = \
9191
UptimeMeter.c \
9292
UsersTable.c \
9393
Vector.c \
94-
XUtils.c
94+
XUtils.c \
95+
FlexMeter.c
9596

9697
myhtopheaders = \
9798
Action.h \
@@ -163,7 +164,8 @@ myhtopheaders = \
163164
UptimeMeter.h \
164165
UsersTable.h \
165166
Vector.h \
166-
XUtils.h
167+
XUtils.h \
168+
FlexMeter.h
167169

168170
# Linux
169171
# -----

Meter.c

+1
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type
377377
this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
378378
this->total = type->total;
379379
this->caption = xStrdup(type->caption);
380+
this->m_ptr = type;
380381
if (Meter_initFn(this)) {
381382
Meter_init(this);
382383
}

Meter.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ typedef struct MeterClass_ {
6767
const MeterModeId defaultMode;
6868
const double total;
6969
const int* const attributes;
70-
const char* const name; /* internal name of the meter, must not contain any space */
71-
const char* const uiName; /* display name in header setup menu */
72-
const char* const caption; /* prefix in the actual header */
70+
const char* name; /* internal name of the meter, must not contain any space */
71+
const char* uiName; /* display name in header setup menu */
72+
const char* caption; /* prefix in the actual header */
7373
const char* const description; /* optional meter description in header setup menu */
7474
const uint8_t maxItems;
7575
const bool isMultiColumn; /* whether the meter draws multiple sub-columns (defaults to false) */
@@ -102,8 +102,9 @@ typedef struct GraphData_ {
102102
struct Meter_ {
103103
Object super;
104104
Meter_Draw draw;
105-
const Machine* host;
106105

106+
const Machine* host;
107+
const MeterClass * m_ptr;
107108
char* caption;
108109
MeterModeId mode;
109110
unsigned int param;

configure.ac

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ AM_INIT_AUTOMAKE([-Wall std-options subdir-objects])
2525

2626
# ----------------------------------------------------------------------
2727

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

2930
# ----------------------------------------------------------------------
3031
# Checks for platform.

darwin/Platform.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ in the source distribution for its full text.
5353
#include "generic/fdstat_sysctl.h"
5454
#include "zfs/ZfsArcMeter.h"
5555
#include "zfs/ZfsCompressedArcMeter.h"
56+
#include "FlexMeter.h"
5657

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

113114
const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
114115

115-
const MeterClass* const Platform_meterTypes[] = {
116+
const MeterClass* Platform_meterTypes[MAX_PLATFORM_METERS] = {
116117
&CPUMeter_class,
117118
&ClockMeter_class,
118119
&DateMeter_class,

darwin/Platform.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ extern const SignalItem Platform_signals[];
3434

3535
extern const unsigned int Platform_numberOfSignals;
3636

37-
extern const MeterClass* const Platform_meterTypes[];
37+
extern const MeterClass* Platform_meterTypes[MAX_PLATFORM_METERS];
3838

3939
bool Platform_init(void);
4040

docs/FlexMeter/Template

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
name=template
2+
command=echo "`uptime`"
3+
type=TEXT_METERMODE
4+
caption="UPTIME"

0 commit comments

Comments
 (0)