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 Aug 30, 2023
1 parent 0f751e9 commit 53bdcab
Show file tree
Hide file tree
Showing 60 changed files with 2,532 additions and 177 deletions.
61 changes: 48 additions & 13 deletions Action.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {

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

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

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 +314,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 +356,27 @@ 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];
if (!settings->ss->table)
settings->ss->table = host->processTable;
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 +387,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 +415,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 @@ -426,7 +452,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 @@ -470,7 +496,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 @@ -521,7 +547,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 @@ -539,6 +565,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 @@ -554,7 +583,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 +834,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 +852,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
48 changes: 34 additions & 14 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,61 @@ 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;
const char* title = column->caption ? column->caption : column->heading;
if (!title)
title = column->name; // fallback to the only mandatory field
if (column->table) /* DynamicScreen, handled differently */
return;
AvailableColumnsPanel* this = (AvailableColumnsPanel*) data;
const char* title = column->heading ? column->heading : column->name;
const char* text = column->description ? column->description : column->caption;
char description[256];
xSnprintf(description, sizeof(description), "%s - %s", title, column->description);
Panel_add(super, (Object*) ListItem_new(description, key));
if (text)
xSnprintf(description, sizeof(description), "%s - %s", title, text);
else
xSnprintf(description, sizeof(description), "%s", title);
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
18 changes: 16 additions & 2 deletions CategoriesPanel.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 "Object.h"
#include "ProvideCurses.h"
#include "ScreensPanel.h"
#include "ScreenTabsPanel.h"
#include "Vector.h"
#include "XUtils.h"

Expand Down Expand Up @@ -72,11 +73,21 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
ScreenManager_add(this->scr, colors, -1);
}

#if defined(HTOP_PCP) /* all platforms supporting dynamic screens */
static void CategoriesPanel_makeScreenTabsPage(CategoriesPanel* this) {
Settings* settings = this->host->settings;
Panel* screenTabs = (Panel*) ScreenTabsPanel_new(settings);
Panel* screenNames = (Panel*) ((ScreenTabsPanel*)screenTabs)->names;
ScreenManager_add(this->scr, screenTabs, 20);
ScreenManager_add(this->scr, screenNames, -1);
}
#endif

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);
Panel* availableColumns = (Panel*) ((ScreensPanel*)screens)->availableColumns;
ScreenManager_add(this->scr, screens, 20);
ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1);
Expand All @@ -94,10 +105,13 @@ typedef struct CategoriesPanelPage_ {
CategoriesPanel_makePageFunc ctor;
} CategoriesPanelPage;

static const CategoriesPanelPage categoriesPanelPages[] = {
static CategoriesPanelPage categoriesPanelPages[] = {
{ .name = "Display options", .ctor = CategoriesPanel_makeDisplayOptionsPage },
{ .name = "Header layout", .ctor = CategoriesPanel_makeHeaderOptionsPage },
{ .name = "Meters", .ctor = CategoriesPanel_makeMetersPage },
#if defined(HTOP_PCP) /* all platforms supporting dynamic screens */
{ .name = "Screen tabs", .ctor = CategoriesPanel_makeScreenTabsPage },
#endif
{ .name = "Screens", .ctor = CategoriesPanel_makeScreensPage },
{ .name = "Colors", .ctor = CategoriesPanel_makeColorsPage },
};
Expand Down
5 changes: 2 additions & 3 deletions ColumnsPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,8 @@ static void ColumnsPanel_add(Panel* super, unsigned int key, Hashtable* columns)
if (!column) {
name = NULL;
} else {
name = column->caption ? column->caption : column->heading;
if (!name)
name = column->name; /* name is a mandatory field */
/* heading preferred here but name is always available */
name = column->heading ? column->heading : column->name;
}
}
if (name == NULL)
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;
}
Loading

0 comments on commit 53bdcab

Please sign in to comment.