Skip to content

Commit

Permalink
Support dynamic screens with 'top-most' entities beyond processes
Browse files Browse the repository at this point in the history
This implements our concept of 'dynamic screens' in htop, with a
first use-case of pcp-htop displaying things like top-filesystem
and top-cgroups under new screen tabs.  However the idea is more
general than use in pcp-htop and we've paved the way here for us
to collectively build mroe general tabular screens in core htop,
as well.

From the pcp-htop side of things, dynamic screens are configured
using text-based configuration files that define the mapping for
PCP metrics to columns (and metric instances to rows).  Metrics
are defined either directly (via metric names) or indirectly via
PCP derived metric specifications.  Value scaling and the units
displayed is automatic based on PCP metric units and data types.

This commit represents a collaborative effort of several months,
primarily between myself, Nathan and BenBE.

Signed-off-by: Sohaib Mohamed <[email protected]>
Signed-off-by: Nathan Scott <[email protected]>
  • Loading branch information
smalinux authored and natoscott committed Jun 19, 2023
1 parent 278f6ae commit 0f75e00
Show file tree
Hide file tree
Showing 55 changed files with 2,054 additions and 197 deletions.
58 changes: 45 additions & 13 deletions Action.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,16 @@ Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {

// ----------------------------------------

static bool Action_writeableProcess(State* st) {
const Settings* settings = st->host->settings;
return !(Settings_isReadonly() || settings->ss->dynamic);
}

static bool Action_readableProcess(State* st) {
const Settings* settings = st->host->settings;
return !(settings->ss->dynamic);
}

static Htop_Reaction actionSetSortColumn(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel "));
Expand Down Expand Up @@ -303,15 +313,15 @@ static Htop_Reaction actionIncSearch(State* st) {
}

static Htop_Reaction actionHigherPriority(State* st) {
if (Settings_isReadonly())
if (!Action_writeableProcess(st))
return HTOP_OK;

bool changed = changePriority(st->mainPanel, -1);
return changed ? HTOP_REFRESH : HTOP_OK;
}

static Htop_Reaction actionLowerPriority(State* st) {
if (Settings_isReadonly())
if (!Action_writeableProcess(st))
return HTOP_OK;

bool changed = changePriority(st->mainPanel, 1);
Expand Down Expand Up @@ -345,13 +355,25 @@ static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->host->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}

static inline void setActiveScreen(Settings* settings, State* st, unsigned int ssIdx) {
assert(settings->ssIndex == ssIdx);
Machine* host = st->host;

settings->ss = settings->screens[ssIdx];
host->activeTable = settings->ss->table;

// set correct functionBar - readonly if requested, and/or with non-process screens
bool readonly = Settings_isReadonly() || (host->activeTable != host->processTable);
MainPanel_setFunctionBar(st->mainPanel, readonly);
}

static Htop_Reaction actionNextScreen(State* st) {
Settings* settings = st->host->settings;
settings->ssIndex++;
if (settings->ssIndex == settings->nScreens) {
settings->ssIndex = 0;
}
settings->ss = settings->screens[settings->ssIndex];
setActiveScreen(settings, st, settings->ssIndex);
return HTOP_UPDATE_PANELHDR | HTOP_REFRESH | HTOP_REDRAW_BAR;
}

Expand All @@ -362,21 +384,22 @@ static Htop_Reaction actionPrevScreen(State* st) {
} else {
settings->ssIndex--;
}
settings->ss = settings->screens[settings->ssIndex];
setActiveScreen(settings, st, settings->ssIndex);
return HTOP_UPDATE_PANELHDR | HTOP_REFRESH | HTOP_REDRAW_BAR;
}

Htop_Reaction Action_setScreenTab(Settings* settings, int x) {
Htop_Reaction Action_setScreenTab(State* st, int x) {
Settings* settings = st->host->settings;
int s = 2;
for (unsigned int i = 0; i < settings->nScreens; i++) {
if (x < s) {
return 0;
}
const char* name = settings->screens[i]->name;
int len = strlen(name);
const char* tab = settings->screens[i]->heading;
int len = strlen(tab);
if (x <= s + len + 1) {
settings->ssIndex = i;
settings->ss = settings->screens[i];
setActiveScreen(settings, st, i);
return HTOP_UPDATE_PANELHDR | HTOP_REFRESH | HTOP_REDRAW_BAR;
}
s += len + 3;
Expand All @@ -389,7 +412,7 @@ static Htop_Reaction actionQuit(ATTR_UNUSED State* st) {
}

static Htop_Reaction actionSetAffinity(State* st) {
if (Settings_isReadonly())
if (!Action_writeableProcess(st))
return HTOP_OK;

Machine* host = st->host;
Expand Down Expand Up @@ -427,7 +450,7 @@ static Htop_Reaction actionSetAffinity(State* st) {

#ifdef SCHEDULER_SUPPORT
static Htop_Reaction actionSetSchedPolicy(State* st) {
if (Settings_isReadonly())
if (!Action_writeableProcess(st))
return HTOP_KEEP_FOLLOWING;

static int preSelectedPolicy = SCHEDULINGPANEL_INITSELECTEDPOLICY;
Expand Down Expand Up @@ -471,7 +494,7 @@ static Htop_Reaction actionSetSchedPolicy(State* st) {
#endif /* SCHEDULER_SUPPORT */

static Htop_Reaction actionKill(State* st) {
if (Settings_isReadonly())
if (!Action_writeableProcess(st))
return HTOP_OK;

static int preSelectedSignal = SIGNALSPANEL_INITSELECTEDSIGNAL;
Expand Down Expand Up @@ -522,7 +545,7 @@ static Htop_Reaction actionSetup(State* st) {
}

static Htop_Reaction actionLsof(State* st) {
if (Settings_isReadonly())
if (!Action_writeableProcess(st))
return HTOP_OK;

const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
Expand All @@ -540,6 +563,9 @@ static Htop_Reaction actionLsof(State* st) {
}

static Htop_Reaction actionShowLocks(State* st) {
if (!Action_readableProcess(st))
return HTOP_OK;

const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p)
return HTOP_OK;
Expand All @@ -555,7 +581,7 @@ static Htop_Reaction actionShowLocks(State* st) {
}

static Htop_Reaction actionStrace(State* st) {
if (Settings_isReadonly())
if (!Action_writeableProcess(st))
return HTOP_OK;

const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
Expand Down Expand Up @@ -805,6 +831,9 @@ static Htop_Reaction actionTagAllChildren(State* st) {
}

static Htop_Reaction actionShowEnvScreen(State* st) {
if (!Action_readableProcess(st))
return HTOP_OK;

Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p)
return HTOP_OK;
Expand All @@ -820,6 +849,9 @@ static Htop_Reaction actionShowEnvScreen(State* st) {
}

static Htop_Reaction actionShowCommandScreen(State* st) {
if (!Action_readableProcess(st))
return HTOP_OK;

Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!p)
return HTOP_OK;
Expand Down
2 changes: 1 addition & 1 deletion Action.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ bool Action_setUserOnly(const char* userName, uid_t* userId);

Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);

Htop_Reaction Action_setScreenTab(Settings* settings, int x);
Htop_Reaction Action_setScreenTab(State* st, int x);

Htop_Reaction Action_follow(State* st);

Expand Down
38 changes: 28 additions & 10 deletions AvailableColumnsPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ in the source distribution for its full text.
#include "Hashtable.h"
#include "ListItem.h"
#include "Object.h"
#include "Platform.h"
#include "Process.h"
#include "ProvideCurses.h"
#include "XUtils.h"
Expand All @@ -35,7 +36,7 @@ static void AvailableColumnsPanel_delete(Object* object) {
static void AvailableColumnsPanel_insert(AvailableColumnsPanel* this, int at, int key) {
const char* name;
if (key >= ROW_DYNAMIC_FIELDS)
name = DynamicColumn_init(key);
name = DynamicColumn_name(key);
else
name = Process_fields[key].name;
Panel_insert(this->columns, at, (Object*) ListItem_new(name, key));
Expand Down Expand Up @@ -81,42 +82,59 @@ const PanelClass AvailableColumnsPanel_class = {

static void AvailableColumnsPanel_addDynamicColumn(ht_key_t key, void* value, void* data) {
const DynamicColumn* column = (const DynamicColumn*) value;
Panel* super = (Panel*) data;
if (column->table) /* DynamicScreen, handled differently */
return;
AvailableColumnsPanel* this = (AvailableColumnsPanel*) data;
const char* title = column->caption ? column->caption : column->heading;
if (!title)
title = column->name; // fallback to the only mandatory field
char description[256];
xSnprintf(description, sizeof(description), "%s - %s", title, column->description);
Panel_add(super, (Object*) ListItem_new(description, key));
Panel_add(&this->super, (Object*) ListItem_new(description, key));
}

// Handle DynamicColumns entries in the AvailableColumnsPanel
static void AvailableColumnsPanel_addDynamicColumns(Panel* super, Hashtable* dynamicColumns) {
static void AvailableColumnsPanel_addDynamicColumns(AvailableColumnsPanel* this, Hashtable* dynamicColumns) {
assert(dynamicColumns);
Hashtable_foreach(dynamicColumns, AvailableColumnsPanel_addDynamicColumn, super);
Hashtable_foreach(dynamicColumns, AvailableColumnsPanel_addDynamicColumn, this);
}

// Handle remaining Platform Meter entries in the AvailableColumnsPanel
static void AvailableColumnsPanel_addPlatformColumn(Panel* super) {
static void AvailableColumnsPanel_addPlatformColumns(AvailableColumnsPanel* this) {
for (int i = 1; i < LAST_PROCESSFIELD; i++) {
if (i != COMM && Process_fields[i].description) {
char description[256];
xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);
Panel_add(super, (Object*) ListItem_new(description, i));
Panel_add(&this->super, (Object*) ListItem_new(description, i));
}
}
}

// Handle DynamicColumns entries associated with DynamicScreens
static void AvailableColumnsPanel_addDynamicScreens(AvailableColumnsPanel* this, const char* screen) {
Platform_addDynamicScreenAvailableColumns(&this->super, screen);
}

void AvailableColumnsPanel_fill(AvailableColumnsPanel* this, const char* dynamicScreen, Hashtable* dynamicColumns) {
Panel* super = (Panel*) this;
Panel_prune(super);
if (dynamicScreen) {
AvailableColumnsPanel_addDynamicScreens(this, dynamicScreen);
} else {
AvailableColumnsPanel_addPlatformColumns(this);
AvailableColumnsPanel_addDynamicColumns(this, dynamicColumns);
}
}

AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns, Hashtable* dynamicColumns) {
AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(AvailableColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);

Panel_setHeader(super, "Available Columns");
AvailableColumnsPanel_addPlatformColumn(super);
AvailableColumnsPanel_addDynamicColumns(super, dynamicColumns);

this->columns = columns;
AvailableColumnsPanel_fill(this, NULL, dynamicColumns);

return this;
}
3 changes: 3 additions & 0 deletions AvailableColumnsPanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ in the source distribution for its full text.

#include "Hashtable.h"
#include "Panel.h"
#include "Settings.h"


typedef struct AvailableColumnsPanel_ {
Expand All @@ -20,4 +21,6 @@ extern const PanelClass AvailableColumnsPanel_class;

AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns, Hashtable* dynamicColumns);

void AvailableColumnsPanel_fill(AvailableColumnsPanel* this, const char* dynamicScreen, Hashtable* dynamicColumns);

#endif
10 changes: 6 additions & 4 deletions CategoriesPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) {
Settings* settings = this->host->settings;
Panel* screens = (Panel*) ScreensPanel_new(settings);
Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns, settings->dynamicColumns);
ScreenManager_add(this->scr, screens, 20);
ScreenManager_add(this->scr, columns, 20);
Panel* activeScreens = (Panel*) ((ScreensPanel*)screens)->activeScreens;
Panel* activeColumns = (Panel*) ((ActiveScreensPanel*)activeScreens)->activeColumns;
Panel* availableColumns = (Panel*) ((ActiveScreensPanel*)activeScreens)->availableColumns;
ScreenManager_add(this->scr, screens, 14);
ScreenManager_add(this->scr, activeScreens, 16);
ScreenManager_add(this->scr, activeColumns, 20);
ScreenManager_add(this->scr, availableColumns, -1);
}

Expand Down
13 changes: 6 additions & 7 deletions CommandLine.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ in the source distribution for its full text.
#include "CRT.h"
#include "DynamicColumn.h"
#include "DynamicMeter.h"
#include "DynamicScreen.h"
#include "Hashtable.h"
#include "Header.h"
#include "IncSet.h"
Expand Down Expand Up @@ -337,15 +338,12 @@ int CommandLine_run(int argc, char** argv) {
UsersTable* ut = UsersTable_new();
Hashtable* dm = DynamicMeters_new();
Hashtable* dc = DynamicColumns_new();
if (!dc)
dc = Hashtable_new(0, true);
Hashtable* ds = DynamicScreens_new();

Machine* host = Machine_new(ut, flags.userId);
ProcessList* pl = ProcessList_new(host, flags.pidMatchList);
Settings* settings = Settings_new(host->activeCPUs, dm, dc);

host->settings = settings;
Machine_addTable(host, &pl->super, true);
Settings* settings = Settings_new(host->activeCPUs, dm, dc, ds);
Machine_populateTablesFromSettings(host, settings, &pl->super);

Header* header = Header_new(host, 2);
Header_populateFromSettings(header);
Expand Down Expand Up @@ -377,7 +375,7 @@ int CommandLine_run(int argc, char** argv) {
CRT_init(settings, flags.allowUnicode, flags.iterationsRemaining != -1);

MainPanel* panel = MainPanel_new();
Table_setPanel(&pl->super, (Panel*) panel);
Machine_setTablesPanel(host, (Panel*) panel);

MainPanel_updateLabels(panel, settings->ss->treeView, flags.commFilter);

Expand Down Expand Up @@ -435,6 +433,7 @@ int CommandLine_run(int argc, char** argv) {
Settings_delete(settings);
DynamicColumns_delete(dc);
DynamicMeters_delete(dm);
DynamicScreens_delete(ds);

return 0;
}
2 changes: 1 addition & 1 deletion DisplayOptionsPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*

#define TABMSG "For current screen tab: \0"
char tabheader[sizeof(TABMSG) + SCREEN_NAME_LEN + 1] = TABMSG;
strncat(tabheader, settings->ss->name, SCREEN_NAME_LEN);
strncat(tabheader, settings->ss->heading, SCREEN_NAME_LEN);
Panel_add(super, (Object*) TextItem_new(tabheader));
#undef TABMSG

Expand Down
15 changes: 12 additions & 3 deletions DynamicColumn.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ in the source distribution for its full text.


Hashtable* DynamicColumns_new(void) {
return Platform_dynamicColumns();
Hashtable* dynamics = Platform_dynamicColumns();
if (!dynamics)
dynamics = Hashtable_new(0, true);
return dynamics;
}

void DynamicColumns_delete(Hashtable* dynamics) {
Expand All @@ -29,8 +32,14 @@ void DynamicColumns_delete(Hashtable* dynamics) {
}
}

const char* DynamicColumn_init(unsigned int key) {
return Platform_dynamicColumnInit(key);
const char* DynamicColumn_name(unsigned int key) {
return Platform_dynamicColumnName(key);
}

void DynamicColumn_done(DynamicColumn* this) {
free(this->heading);
free(this->caption);
free(this->description);
}

typedef struct {
Expand Down
Loading

0 comments on commit 0f75e00

Please sign in to comment.