Merge branch 'header_fmt' of cgzones/htop

This commit is contained in:
Daniel Lange 2021-08-23 14:56:05 +02:00
commit ec2307688e
14 changed files with 458 additions and 194 deletions

View File

@ -84,12 +84,8 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
static void Action_runSetup(State* st) { static void Action_runSetup(State* st) {
ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true); ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true);
CategoriesPanel* panelCategories = CategoriesPanel_new(scr, st->settings, st->header, st->pl); CategoriesPanel_new(scr, st->settings, st->header, st->pl);
ScreenManager_add(scr, (Panel*) panelCategories, 16); ScreenManager_run(scr, NULL, NULL);
CategoriesPanel_makeMetersPage(panelCategories);
Panel* panelFocus;
int ch;
ScreenManager_run(scr, &panelFocus, &ch);
ScreenManager_delete(scr); ScreenManager_delete(scr);
if (st->settings->changed) { if (st->settings->changed) {
Header_writeBackToSettings(st->header); Header_writeBackToSettings(st->header);

View File

@ -30,14 +30,15 @@ static void AvailableMetersPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
AvailableMetersPanel* this = (AvailableMetersPanel*) object; AvailableMetersPanel* this = (AvailableMetersPanel*) object;
Panel_done(super); Panel_done(super);
free(this->meterPanels);
free(this); free(this);
} }
static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, const MeterClass* type, unsigned int param, int column) { static inline void AvailableMetersPanel_addMeter(Header* header, MetersPanel* panel, const MeterClass* type, unsigned int param, size_t column) {
const Meter* meter = Header_addMeterByClass(header, type, param, column); const Meter* meter = Header_addMeterByClass(header, type, param, column);
Panel_add(panel, (Object*) Meter_toListItem(meter, false)); Panel_add((Panel*)panel, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(panel, Panel_size(panel) - 1); Panel_setSelected((Panel*)panel, Panel_size((Panel*)panel) - 1);
MetersPanel_setMoving((MetersPanel*)panel, true); MetersPanel_setMoving(panel, true);
} }
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
@ -58,7 +59,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
case 'l': case 'l':
case 'L': case 'L':
{ {
AvailableMetersPanel_addMeter(header, this->leftPanel, Platform_meterTypes[type], param, 0); AvailableMetersPanel_addMeter(header, this->meterPanels[0], Platform_meterTypes[type], param, 0);
result = HANDLED; result = HANDLED;
update = true; update = true;
break; break;
@ -70,7 +71,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
case 'r': case 'r':
case 'R': case 'R':
{ {
AvailableMetersPanel_addMeter(header, this->rightPanel, Platform_meterTypes[type], param, 1); AvailableMetersPanel_addMeter(header, this->meterPanels[this->columns - 1], Platform_meterTypes[type], param, this->columns - 1);
result = (KEY_LEFT << 16) | SYNTH_KEY; result = (KEY_LEFT << 16) | SYNTH_KEY;
update = true; update = true;
break; break;
@ -138,7 +139,7 @@ static void AvailableMetersPanel_addPlatformMeter(Panel* super, const MeterClass
Panel_add(super, (Object*) ListItem_new(label, offset << 16)); Panel_add(super, (Object*) ListItem_new(label, offset << 16));
} }
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, const ProcessList* pl) { AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr, const ProcessList* pl) {
AvailableMetersPanel* this = AllocThis(AvailableMetersPanel); AvailableMetersPanel* this = AllocThis(AvailableMetersPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done "); FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done ");
@ -146,8 +147,8 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
this->settings = settings; this->settings = settings;
this->header = header; this->header = header;
this->leftPanel = leftMeters; this->columns = columns;
this->rightPanel = rightMeters; this->meterPanels = meterPanels;
this->scr = scr; this->scr = scr;
Panel_setHeader(super, "Available meters"); Panel_setHeader(super, "Available meters");

View File

@ -8,6 +8,7 @@ in the source distribution for its full text.
*/ */
#include "Header.h" #include "Header.h"
#include "MetersPanel.h"
#include "Panel.h" #include "Panel.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "ScreenManager.h" #include "ScreenManager.h"
@ -20,12 +21,12 @@ typedef struct AvailableMetersPanel_ {
Settings* settings; Settings* settings;
Header* header; Header* header;
Panel* leftPanel; size_t columns;
Panel* rightPanel; MetersPanel** meterPanels;
} AvailableMetersPanel; } AvailableMetersPanel;
extern const PanelClass AvailableMetersPanel_class; extern const PanelClass AvailableMetersPanel_class;
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, const ProcessList* pl); AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel **meterPanels, ScreenManager* scr, const ProcessList* pl);
#endif #endif

View File

@ -17,6 +17,8 @@ in the source distribution for its full text.
#include "ColumnsPanel.h" #include "ColumnsPanel.h"
#include "DisplayOptionsPanel.h" #include "DisplayOptionsPanel.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Header.h"
#include "HeaderOptionsPanel.h"
#include "ListItem.h" #include "ListItem.h"
#include "MetersPanel.h" #include "MetersPanel.h"
#include "Object.h" #include "Object.h"
@ -33,14 +35,24 @@ static void CategoriesPanel_delete(Object* object) {
free(this); free(this);
} }
void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
MetersPanel* leftMeters = MetersPanel_new(this->settings, "Left column", this->header->columns[0], this->scr); size_t columns = HeaderLayout_getColumns(this->scr->header->headerLayout);
MetersPanel* rightMeters = MetersPanel_new(this->settings, "Right column", this->header->columns[1], this->scr); MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel));
leftMeters->rightNeighbor = rightMeters;
rightMeters->leftNeighbor = leftMeters; for (size_t i = 0; i < columns; i++) {
Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, (Panel*) leftMeters, (Panel*) rightMeters, this->scr, this->pl); char titleBuffer[32];
ScreenManager_add(this->scr, (Panel*) leftMeters, 20); xSnprintf(titleBuffer, sizeof(titleBuffer), "Column %zu", i + 1);
ScreenManager_add(this->scr, (Panel*) rightMeters, 20); meterPanels[i] = MetersPanel_new(this->settings, titleBuffer, this->header->columns[i], this->scr);
if (i != 0) {
meterPanels[i]->leftNeighbor = meterPanels[i - 1];
meterPanels[i - 1]->rightNeighbor = meterPanels[i];
}
ScreenManager_add(this->scr, (Panel*) meterPanels[i], 20);
}
Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, columns, meterPanels, this->scr, this->pl);
ScreenManager_add(this->scr, availableMeters, -1); ScreenManager_add(this->scr, availableMeters, -1);
} }
@ -61,6 +73,25 @@ static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
ScreenManager_add(this->scr, availableColumns, -1); ScreenManager_add(this->scr, availableColumns, -1);
} }
static void CategoriesPanel_makeHeaderOptionsPage(CategoriesPanel* this) {
Panel* colors = (Panel*) HeaderOptionsPanel_new(this->settings, this->scr);
ScreenManager_add(this->scr, colors, -1);
}
typedef void (* CategoriesPanel_makePageFunc)(CategoriesPanel* ref);
typedef struct CategoriesPanelPage_ {
const char* name;
CategoriesPanel_makePageFunc ctor;
} CategoriesPanelPage;
static const CategoriesPanelPage categoriesPanelPages[] = {
{ .name = "Display options", .ctor = CategoriesPanel_makeDisplayOptionsPage },
{ .name = "Header layout", .ctor = CategoriesPanel_makeHeaderOptionsPage },
{ .name = "Meters", .ctor = CategoriesPanel_makeMetersPage },
{ .name = "Columns", .ctor = CategoriesPanel_makeColumnsPage },
{ .name = "Colors", .ctor = CategoriesPanel_makeColorsPage },
};
static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
CategoriesPanel* this = (CategoriesPanel*) super; CategoriesPanel* this = (CategoriesPanel*) super;
@ -98,19 +129,8 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
for (int i = 1; i < size; i++) for (int i = 1; i < size; i++)
ScreenManager_remove(this->scr, 1); ScreenManager_remove(this->scr, 1);
switch (selected) { if (selected >= 0 && (size_t)selected < ARRAYSIZE(categoriesPanelPages)) {
case 0: categoriesPanelPages[selected].ctor(this);
CategoriesPanel_makeMetersPage(this);
break;
case 1:
CategoriesPanel_makeDisplayOptionsPage(this);
break;
case 2:
CategoriesPanel_makeColorsPage(this);
break;
case 3:
CategoriesPanel_makeColumnsPage(this);
break;
} }
} }
return result; return result;
@ -135,9 +155,10 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
this->header = header; this->header = header;
this->pl = pl; this->pl = pl;
Panel_setHeader(super, "Setup"); Panel_setHeader(super, "Setup");
Panel_add(super, (Object*) ListItem_new("Meters", 0)); for (size_t i = 0; i < ARRAYSIZE(categoriesPanelPages); i++)
Panel_add(super, (Object*) ListItem_new("Display options", 0)); Panel_add(super, (Object*) ListItem_new(categoriesPanelPages[i].name, 0));
Panel_add(super, (Object*) ListItem_new("Colors", 0));
Panel_add(super, (Object*) ListItem_new("Columns", 0)); ScreenManager_add(scr, super, 16);
categoriesPanelPages[0].ctor(this);
return this; return this;
} }

View File

@ -23,8 +23,6 @@ typedef struct CategoriesPanel_ {
ProcessList* pl; ProcessList* pl;
} CategoriesPanel; } CategoriesPanel;
void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
extern const PanelClass CategoriesPanel_class; extern const PanelClass CategoriesPanel_class;
CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl); CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl);

197
Header.c
View File

@ -7,6 +7,7 @@ in the source distribution for its full text.
#include "Header.h" #include "Header.h"
#include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -22,15 +23,17 @@ in the source distribution for its full text.
#include "XUtils.h" #include "XUtils.h"
Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns) { Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout) {
Header* this = xCalloc(1, sizeof(Header)); Header* this = xCalloc(1, sizeof(Header));
this->columns = xCalloc(nrColumns, sizeof(Vector*)); this->columns = xMallocArray(HeaderLayout_getColumns(hLayout), sizeof(Vector*));
this->settings = settings; this->settings = settings;
this->pl = pl; this->pl = pl;
this->nrColumns = nrColumns; this->headerLayout = hLayout;
Header_forEachColumn(this, i) { Header_forEachColumn(this, i) {
this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE); this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE);
} }
return this; return this;
} }
@ -38,37 +41,109 @@ void Header_delete(Header* this) {
Header_forEachColumn(this, i) { Header_forEachColumn(this, i) {
Vector_delete(this->columns[i]); Vector_delete(this->columns[i]);
} }
free(this->columns); free(this->columns);
free(this); free(this);
} }
void Header_setLayout(Header* this, HeaderLayout hLayout) {
size_t oldColumns = HeaderLayout_getColumns(this->headerLayout);
size_t newColumns = HeaderLayout_getColumns(hLayout);
this->headerLayout = hLayout;
if (newColumns == oldColumns)
return;
if (newColumns > oldColumns) {
this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*));
for (size_t i = oldColumns; i < newColumns; i++)
this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE);
} else {
// move meters from to-be-deleted columns into last one
for (size_t i = newColumns; i < oldColumns; i++) {
for (int j = this->columns[i]->items - 1; j >= 0; j--) {
Vector_add(this->columns[newColumns - 1], Vector_take(this->columns[i], j));
}
Vector_delete(this->columns[i]);
}
this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*));
}
Header_calculateHeight(this);
}
static void Header_addMeterByName(Header* this, const char* name, MeterModeId mode, unsigned int column) {
assert(column < HeaderLayout_getColumns(this->headerLayout));
Vector* meters = this->columns[column];
const char* paren = strchr(name, '(');
unsigned int param = 0;
size_t nameLen;
if (paren) {
int ok = sscanf(paren, "(%10u)", &param); // CPUMeter
if (!ok) {
char* end, dynamic[32] = {0};
if (sscanf(paren, "(%30s)", dynamic)) { // DynamicMeter
if ((end = strrchr(dynamic, ')')) == NULL)
return; // htoprc parse failure
*end = '\0';
if (!DynamicMeter_search(this->pl->dynamicMeters, dynamic, &param))
return; // name lookup failure
} else {
param = 0;
}
}
nameLen = paren - name;
} else {
nameLen = strlen(name);
}
for (const MeterClass* const* type = Platform_meterTypes; *type; type++) {
if (0 == strncmp(name, (*type)->name, nameLen) && (*type)->name[nameLen] == '\0') {
Meter* meter = Meter_new(this->pl, param, *type);
if (mode != 0) {
Meter_setMode(meter, mode);
}
Vector_add(meters, meter);
break;
}
}
}
void Header_populateFromSettings(Header* this) { void Header_populateFromSettings(Header* this) {
Header_setLayout(this, this->settings->hLayout);
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
const MeterColumnSettings* colSettings = &this->settings->columns[col]; const MeterColumnSetting* colSettings = &this->settings->hColumns[col];
for (int i = 0; i < colSettings->len; i++) { Vector_prune(this->columns[col]);
if (!Header_addMeterByName(this, colSettings->names[i], col)) { for (size_t i = 0; i < colSettings->len; i++) {
continue; Header_addMeterByName(this, colSettings->names[i], colSettings->modes[i], col);
}
if (colSettings->modes[i] != 0) {
Header_setMode(this, i, colSettings->modes[i], col);
}
} }
} }
Header_calculateHeight(this); Header_calculateHeight(this);
} }
void Header_writeBackToSettings(const Header* this) { void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) { Settings_setHeaderLayout(this->settings, this->headerLayout);
MeterColumnSettings* colSettings = &this->settings->columns[col];
String_freeArray(colSettings->names); Header_forEachColumn(this, col) {
MeterColumnSetting* colSettings = &this->settings->hColumns[col];
if (colSettings->names) {
for (size_t j = 0; j < colSettings->len; j++)
free(colSettings->names[j]);
free(colSettings->names);
}
free(colSettings->modes); free(colSettings->modes);
const Vector* vec = this->columns[col]; const Vector* vec = this->columns[col];
int len = Vector_size(vec); int len = Vector_size(vec);
colSettings->names = xCalloc(len + 1, sizeof(char*)); colSettings->names = len ? xCalloc(len, sizeof(char*)) : NULL;
colSettings->modes = xCalloc(len, sizeof(int)); colSettings->modes = len ? xCalloc(len, sizeof(int)) : NULL;
colSettings->len = len; colSettings->len = len;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
@ -88,50 +163,9 @@ void Header_writeBackToSettings(const Header* this) {
} }
} }
bool Header_addMeterByName(Header* this, const char* name, int column) { Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column) {
Vector* meters = this->columns[column]; assert(column < HeaderLayout_getColumns(this->headerLayout));
char* paren = strchr(name, '(');
unsigned int param = 0;
if (paren) {
char* end, dynamic[32] = {0};
int ok = sscanf(paren, "(%10u)", &param); // CPUMeter
if (!ok) {
if (sscanf(paren, "(%30s)", dynamic)) { // DynamicMeter
if ((end = strrchr(dynamic, ')')) == NULL)
return false; // indicate htoprc parse failure
*end = '\0';
if (!DynamicMeter_search(this->pl->dynamicMeters, dynamic, &param))
return false; // indicates name lookup failure
}
}
*paren = '\0';
}
for (const MeterClass* const* type = Platform_meterTypes; *type; type++) {
if (String_eq(name, (*type)->name)) {
Meter* meter = Meter_new(this->pl, param, *type);
Vector_add(meters, meter);
break;
}
}
if (paren)
*paren = '(';
return true;
}
void Header_setMode(Header* this, int i, MeterModeId mode, int column) {
Vector* meters = this->columns[column];
if (i >= Vector_size(meters))
return;
Meter* meter = (Meter*) Vector_get(meters, i);
Meter_setMode(meter, mode);
}
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, int column) {
Vector* meters = this->columns[column]; Vector* meters = this->columns[column];
Meter* meter = Meter_new(this->pl, param, type); Meter* meter = Meter_new(this->pl, param, type);
@ -139,18 +173,6 @@ Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int
return meter; return meter;
} }
int Header_size(const Header* this, int column) {
const Vector* meters = this->columns[column];
return Vector_size(meters);
}
MeterModeId Header_readMeterMode(const Header* this, int i, int column) {
const Vector* meters = this->columns[column];
const Meter* meter = (const Meter*) Vector_get(meters, i);
return meter->mode;
}
void Header_reinit(Header* this) { void Header_reinit(Header* this) {
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
for (int i = 0; i < Vector_size(this->columns[col]); i++) { for (int i = 0; i < Vector_size(this->columns[col]); i++) {
@ -169,25 +191,36 @@ void Header_draw(const Header* this) {
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
mvhline(y, 0, ' ', COLS); mvhline(y, 0, ' ', COLS);
} }
const int width = COLS / this->nrColumns - pad; const int width = COLS - pad;
int x = pad; int x = pad;
float roundingLoss = 0.0f;
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
Vector* meters = this->columns[col]; Vector* meters = this->columns[col];
float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0f;
roundingLoss += colWidth - floorf(colWidth);
if (roundingLoss >= 1.0f) {
colWidth += 1.0f;
roundingLoss -= 1.0f;
}
for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) { for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
int actualWidth; float actualWidth = colWidth;
if (meter->mode == TEXT_METERMODE) if (meter->mode == TEXT_METERMODE) {
actualWidth = meter->columnWidthCount * width + (meter->columnWidthCount - 1) * (2 * pad + 1); for (int j = 1; j < meter->columnWidthCount; j++) {
else actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0f;
actualWidth = width; }
}
assert(meter->draw); assert(meter->draw);
meter->draw(meter, x, y, actualWidth); meter->draw(meter, x, y, floorf(actualWidth));
y += meter->h; y += meter->h;
} }
x += width + pad;
x += floorf(colWidth);
} }
} }
@ -207,8 +240,8 @@ void Header_updateData(Header* this) {
* by counting how many columns to the right are empty or contain a BlankMeter. * by counting how many columns to the right are empty or contain a BlankMeter.
* Returns the number of columns to span, i.e. if the direct neighbor is occupied 1. * Returns the number of columns to span, i.e. if the direct neighbor is occupied 1.
*/ */
static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const int pad, const int curColumn, const int curHeight) { static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const int pad, const unsigned int curColumn, const int curHeight) {
for (int i = curColumn + 1; i < this->nrColumns; i++) { for (size_t i = curColumn + 1; i < HeaderLayout_getColumns(this->headerLayout); i++) {
const Vector* meters = this->columns[i]; const Vector* meters = this->columns[i];
int height = pad; int height = pad;
@ -227,7 +260,7 @@ static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const
} }
} }
return this->nrColumns - curColumn; return HeaderLayout_getColumns(this->headerLayout) - curColumn;
} }
int Header_calculateHeight(Header* this) { int Header_calculateHeight(Header* this) {

View File

@ -17,30 +17,24 @@ typedef struct Header_ {
Vector** columns; Vector** columns;
Settings* settings; Settings* settings;
ProcessList* pl; ProcessList* pl;
int nrColumns; HeaderLayout headerLayout;
int pad; int pad;
int height; int height;
} Header; } Header;
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_)) #define Header_forEachColumn(this_, i_) for (size_t (i_)=0; (i_) < HeaderLayout_getColumns((this_)->headerLayout); ++(i_))
Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns); Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout);
void Header_delete(Header* this); void Header_delete(Header* this);
void Header_setLayout(Header* this, HeaderLayout hLayout);
void Header_populateFromSettings(Header* this); void Header_populateFromSettings(Header* this);
void Header_writeBackToSettings(const Header* this); void Header_writeBackToSettings(const Header* this);
bool Header_addMeterByName(Header* this, const char* name, int column); Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column);
void Header_setMode(Header* this, int i, MeterModeId mode, int column);
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, int column);
int Header_size(const Header* this, int column);
MeterModeId Header_readMeterMode(const Header* this, int i, int column);
void Header_reinit(Header* this); void Header_reinit(Header* this);

54
HeaderLayout.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef HEADER_HeaderLayout
#define HEADER_HeaderLayout
/*
htop - HeaderLayout.h
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "Macros.h"
typedef enum HeaderLayout_ {
HF_TWO_50_50,
HF_TWO_33_67,
HF_TWO_67_33,
HF_THREE_33_34_33,
HF_THREE_25_25_50,
HF_THREE_25_50_25,
HF_THREE_50_25_25,
HF_THREE_40_20_40,
HF_FOUR_25_25_25_25,
LAST_HEADER_LAYOUT
} HeaderLayout;
static const struct {
uint8_t columns;
const uint8_t widths[4];
const char* description;
} HeaderLayout_layouts[LAST_HEADER_LAYOUT] = {
[HF_TWO_50_50] = { 2, { 50, 50, 0, 0 }, "2 columns - 50/50 (default)", },
[HF_TWO_33_67] = { 2, { 33, 67, 0, 0 }, "2 columns - 33/67", },
[HF_TWO_67_33] = { 2, { 67, 33, 0, 0 }, "2 columns - 67/33", },
[HF_THREE_33_34_33] = { 3, { 33, 34, 33, 0 }, "3 columns - 33/34/33", },
[HF_THREE_25_25_50] = { 3, { 25, 25, 50, 0 }, "3 columns - 25/25/50", },
[HF_THREE_25_50_25] = { 3, { 25, 50, 25, 0 }, "3 columns - 25/50/25", },
[HF_THREE_50_25_25] = { 3, { 50, 25, 25, 0 }, "3 columns - 50/25/25", },
[HF_THREE_40_20_40] = { 3, { 40, 20, 40, 0 }, "3 columns - 40/20/40", },
[HF_FOUR_25_25_25_25] = { 4, { 25, 25, 25, 25 }, "4 columns - 25/25/25/25", },
};
static inline size_t HeaderLayout_getColumns(HeaderLayout hLayout) {
/* assert the layout is initialized */
assert(0 <= hLayout);
assert(hLayout < LAST_HEADER_LAYOUT);
assert(HeaderLayout_layouts[hLayout].description[0]);
return HeaderLayout_layouts[hLayout].columns;
}
#endif /* HEADER_HeaderLayout */

87
HeaderOptionsPanel.c Normal file
View File

@ -0,0 +1,87 @@
/*
htop - HeaderOptionsPanel.c
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "HeaderOptionsPanel.h"
#include <stdbool.h>
#include <stdlib.h>
#include "CRT.h"
#include "FunctionBar.h"
#include "Header.h"
#include "Object.h"
#include "OptionItem.h"
#include "ProvideCurses.h"
#include "RichString.h"
#include "Vector.h"
static const char* const HeaderOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static void HeaderOptionsPanel_delete(Object* object) {
Panel* super = (Panel*) object;
HeaderOptionsPanel* this = (HeaderOptionsPanel*) object;
Panel_done(super);
free(this);
}
static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
HeaderOptionsPanel* this = (HeaderOptionsPanel*) super;
HandlerResult result = IGNORED;
int mark;
switch(ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
case ' ':
mark = Panel_getSelectedIndex(super);
assert(mark >= 0);
assert(mark < LAST_HEADER_LAYOUT);
for (int i = 0; i < LAST_HEADER_LAYOUT; i++)
CheckItem_set((CheckItem*)Panel_get(super, i), false);
CheckItem_set((CheckItem*)Panel_get(super, mark), true);
Header_setLayout(this->scr->header, mark);
this->settings->changed = true;
ScreenManager_resize(this->scr);
result = HANDLED;
}
return result;
}
const PanelClass HeaderOptionsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = HeaderOptionsPanel_delete
},
.eventHandler = HeaderOptionsPanel_eventHandler
};
HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr) {
HeaderOptionsPanel* this = AllocThis(HeaderOptionsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(HeaderOptionsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
this->scr = scr;
this->settings = settings;
Panel_setHeader(super, "Header Layout");
for (int i = 0; i < LAST_HEADER_LAYOUT; i++) {
Panel_add(super, (Object*) CheckItem_newByVal(HeaderLayout_layouts[i].description, false));
}
CheckItem_set((CheckItem*)Panel_get(super, settings->hLayout), true);
return this;
}

26
HeaderOptionsPanel.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef HEADER_HeaderOptionsPanel
#define HEADER_HeaderOptionsPanel
/*
htop - ColorsPanel.h
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "ScreenManager.h"
#include "Settings.h"
typedef struct HeaderOptionsPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
} HeaderOptionsPanel;
extern const PanelClass HeaderOptionsPanel_class;
HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr);
#endif /* HEADER_HeaderOptionsPanel */

View File

@ -45,6 +45,7 @@ myhtopsources = \
FunctionBar.c \ FunctionBar.c \
Hashtable.c \ Hashtable.c \
Header.c \ Header.c \
HeaderOptionsPanel.c \
HostnameMeter.c \ HostnameMeter.c \
IncSet.c \ IncSet.c \
InfoScreen.c \ InfoScreen.c \
@ -102,6 +103,8 @@ myhtopheaders = \
FunctionBar.h \ FunctionBar.h \
Hashtable.h \ Hashtable.h \
Header.h \ Header.h \
HeaderLayout.h \
HeaderOptionsPanel.h \
HostnameMeter.h \ HostnameMeter.h \
IncSet.h \ IncSet.h \
InfoScreen.h \ InfoScreen.h \

View File

@ -185,7 +185,6 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
Header* header = this->scr->header; Header* header = this->scr->header;
this->settings->changed = true; this->settings->changed = true;
Header_calculateHeight(header); Header_calculateHeight(header);
Header_draw(header);
ScreenManager_resize(this->scr); ScreenManager_resize(this->scr);
} }
return result; return result;

View File

@ -25,21 +25,27 @@ in the source distribution for its full text.
void Settings_delete(Settings* this) { void Settings_delete(Settings* this) {
free(this->filename); free(this->filename);
free(this->fields); free(this->fields);
for (unsigned int i = 0; i < ARRAYSIZE(this->columns); i++) { for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
String_freeArray(this->columns[i].names); if (this->hColumns[i].names) {
free(this->columns[i].modes); for (uint8_t j = 0; j < this->hColumns[i].len; j++)
free(this->hColumns[i].names[j]);
free(this->hColumns[i].names);
} }
free(this->hColumns[i].modes);
}
free(this->hColumns);
free(this); free(this);
} }
static void Settings_readMeters(Settings* this, const char* line, int column) { static void Settings_readMeters(Settings* this, const char* line, unsigned int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
char** ids = String_split(trim, ' ', NULL); char** ids = String_split(trim, ' ', NULL);
free(trim); free(trim);
this->columns[column].names = ids; column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1);
this->hColumns[column].names = ids;
} }
static void Settings_readMeterModes(Settings* this, const char* line, int column) { static void Settings_readMeterModes(Settings* this, const char* line, unsigned int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
char** ids = String_split(trim, ' ', NULL); char** ids = String_split(trim, ' ', NULL);
free(trim); free(trim);
@ -47,13 +53,14 @@ static void Settings_readMeterModes(Settings* this, const char* line, int column
for (int i = 0; ids[i]; i++) { for (int i = 0; ids[i]; i++) {
len++; len++;
} }
this->columns[column].len = len; column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1);
this->hColumns[column].len = len;
int* modes = len ? xCalloc(len, sizeof(int)) : NULL; int* modes = len ? xCalloc(len, sizeof(int)) : NULL;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]); modes[i] = atoi(ids[i]);
} }
String_freeArray(ids); String_freeArray(ids);
this->columns[column].modes = modes; this->hColumns[column].modes = modes;
} }
static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount) { static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount) {
@ -62,50 +69,50 @@ static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount)
sizes[1]++; sizes[1]++;
} }
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*)); this->hColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->columns[i].modes = xCalloc(sizes[i], sizeof(int)); this->hColumns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i]; this->hColumns[i].len = sizes[i];
} }
int r = 0; int r = 0;
if (initialCpuCount > 128) { if (initialCpuCount > 128) {
// Just show the average, ricers need to config for impressive screenshots // Just show the average, ricers need to config for impressive screenshots
this->columns[0].names[0] = xStrdup("CPU"); this->hColumns[0].names[0] = xStrdup("CPU");
this->columns[0].modes[0] = BAR_METERMODE; this->hColumns[0].modes[0] = BAR_METERMODE;
} else if (initialCpuCount > 32) { } else if (initialCpuCount > 32) {
this->columns[0].names[0] = xStrdup("LeftCPUs8"); this->hColumns[0].names[0] = xStrdup("LeftCPUs8");
this->columns[0].modes[0] = BAR_METERMODE; this->hColumns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs8"); this->hColumns[1].names[r] = xStrdup("RightCPUs8");
this->columns[1].modes[r++] = BAR_METERMODE; this->hColumns[1].modes[r++] = BAR_METERMODE;
} else if (initialCpuCount > 16) { } else if (initialCpuCount > 16) {
this->columns[0].names[0] = xStrdup("LeftCPUs4"); this->hColumns[0].names[0] = xStrdup("LeftCPUs4");
this->columns[0].modes[0] = BAR_METERMODE; this->hColumns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs4"); this->hColumns[1].names[r] = xStrdup("RightCPUs4");
this->columns[1].modes[r++] = BAR_METERMODE; this->hColumns[1].modes[r++] = BAR_METERMODE;
} else if (initialCpuCount > 8) { } else if (initialCpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2"); this->hColumns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE; this->hColumns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2"); this->hColumns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE; this->hColumns[1].modes[r++] = BAR_METERMODE;
} else if (initialCpuCount > 4) { } else if (initialCpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs"); this->hColumns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE; this->hColumns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs"); this->hColumns[1].names[r] = xStrdup("RightCPUs");
this->columns[1].modes[r++] = BAR_METERMODE; this->hColumns[1].modes[r++] = BAR_METERMODE;
} else { } else {
this->columns[0].names[0] = xStrdup("AllCPUs"); this->hColumns[0].names[0] = xStrdup("AllCPUs");
this->columns[0].modes[0] = BAR_METERMODE; this->hColumns[0].modes[0] = BAR_METERMODE;
} }
this->columns[0].names[1] = xStrdup("Memory"); this->hColumns[0].names[1] = xStrdup("Memory");
this->columns[0].modes[1] = BAR_METERMODE; this->hColumns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap"); this->hColumns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE; this->hColumns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks"); this->hColumns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE; this->hColumns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage"); this->hColumns[1].names[r] = xStrdup("LoadAverage");
this->columns[1].modes[r++] = TEXT_METERMODE; this->hColumns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("Uptime"); this->hColumns[1].names[r] = xStrdup("Uptime");
this->columns[1].modes[r++] = TEXT_METERMODE; this->hColumns[1].modes[r++] = TEXT_METERMODE;
} }
static void Settings_readFields(Settings* settings, const char* line) { static void Settings_readFields(Settings* settings, const char* line) {
@ -258,6 +265,12 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini
} else if (String_eq(option[0], "enable_mouse")) { } else if (String_eq(option[0], "enable_mouse")) {
this->enableMouse = atoi(option[1]); this->enableMouse = atoi(option[1]);
#endif #endif
} else if (String_eq(option[0], "header_layout")) {
this->hLayout = atoi(option[1]);
if (this->hLayout < 0 || this->hLayout >= LAST_HEADER_LAYOUT)
this->hLayout = HF_TWO_50_50;
free(this->hColumns);
this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting));
} else if (String_eq(option[0], "left_meters")) { } else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], 0); Settings_readMeters(this, option[1], 0);
didReadMeters = true; didReadMeters = true;
@ -270,6 +283,12 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini
} else if (String_eq(option[0], "right_meter_modes")) { } else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1); Settings_readMeterModes(this, option[1], 1);
didReadMeters = true; didReadMeters = true;
} else if (String_startsWith(option[0], "column_meters_")) {
Settings_readMeters(this, option[1], atoi(option[0] + strlen("column_meters_")));
didReadMeters = true;
} else if (String_startsWith(option[0], "column_meter_modes_")) {
Settings_readMeterModes(this, option[1], atoi(option[0] + strlen("column_meter_modes_")));
didReadMeters = true;
} else if (String_eq(option[0], "hide_function_bar")) { } else if (String_eq(option[0], "hide_function_bar")) {
this->hideFunctionBar = atoi(option[1]); this->hideFunctionBar = atoi(option[1]);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
@ -302,19 +321,19 @@ static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns
fprintf(fd, "\n"); fprintf(fd, "\n");
} }
static void writeMeters(const Settings* this, FILE* fd, int column) { static void writeMeters(const Settings* this, FILE* fd, unsigned int column) {
const char* sep = ""; const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) { for (uint8_t i = 0; i < this->hColumns[column].len; i++) {
fprintf(fd, "%s%s", sep, this->columns[column].names[i]); fprintf(fd, "%s%s", sep, this->hColumns[column].names[i]);
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fprintf(fd, "\n");
} }
static void writeMeterModes(const Settings* this, FILE* fd, int column) { static void writeMeterModes(const Settings* this, FILE* fd, unsigned int column) {
const char* sep = ""; const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) { for (uint8_t i = 0; i < this->hColumns[column].len; i++) {
fprintf(fd, "%s%d", sep, this->columns[column].modes[i]); fprintf(fd, "%s%d", sep, this->hColumns[column].modes[i]);
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fprintf(fd, "\n");
@ -375,10 +394,13 @@ int Settings_write(const Settings* this, bool onCrash) {
fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse); fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse);
#endif #endif
fprintf(fd, "delay=%d\n", (int) this->delay); fprintf(fd, "delay=%d\n", (int) this->delay);
fprintf(fd, "left_meters="); writeMeters(this, fd, 0); fprintf(fd, "header_layout=%d\n", (int) this->hLayout);
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
fprintf(fd, "right_meters="); writeMeters(this, fd, 1); fprintf(fd, "column_meters_%u=", i);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1); writeMeters(this, fd, i);
fprintf(fd, "column_meter_modes_%u=", i);
writeMeterModes(this, fd, i);
}
fprintf(fd, "hide_function_bar=%d\n", (int) this->hideFunctionBar); fprintf(fd, "hide_function_bar=%d\n", (int) this->hideFunctionBar);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity); fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity);
@ -402,6 +424,8 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
Settings* this = xCalloc(1, sizeof(Settings)); Settings* this = xCalloc(1, sizeof(Settings));
this->dynamicColumns = dynamicColumns; this->dynamicColumns = dynamicColumns;
this->hLayout = HF_TWO_50_50;
this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting));
this->sortKey = PERCENT_CPU; this->sortKey = PERCENT_CPU;
this->treeSortKey = PID; this->treeSortKey = PID;
this->direction = -1; this->direction = -1;
@ -531,3 +555,26 @@ void Settings_enableReadonly(void) {
bool Settings_isReadonly(void) { bool Settings_isReadonly(void) {
return readonly; return readonly;
} }
void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout) {
unsigned int oldColumns = HeaderLayout_getColumns(this->hLayout);
unsigned int newColumns = HeaderLayout_getColumns(hLayout);
if (newColumns > oldColumns) {
this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting));
memset(this->hColumns + oldColumns, 0, (newColumns - oldColumns) * sizeof(MeterColumnSetting));
} else if (newColumns < oldColumns) {
for (unsigned int i = newColumns; i < oldColumns; i++) {
if (this->hColumns[i].names) {
for (uint8_t j = 0; j < this->hColumns[i].len; j++)
free(this->hColumns[i].names[j]);
free(this->hColumns[i].names);
}
free(this->hColumns[i].modes);
}
this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting));
}
this->hLayout = hLayout;
this->changed = true;
}

View File

@ -13,6 +13,7 @@ in the source distribution for its full text.
#include <stdint.h> #include <stdint.h>
#include "Hashtable.h" #include "Hashtable.h"
#include "HeaderLayout.h"
#include "Process.h" #include "Process.h"
@ -21,15 +22,16 @@ in the source distribution for its full text.
#define CONFIG_READER_MIN_VERSION 2 #define CONFIG_READER_MIN_VERSION 2
typedef struct { typedef struct {
int len; uint8_t len;
char** names; char** names;
int* modes; int* modes;
} MeterColumnSettings; } MeterColumnSetting;
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* filename;
int config_version; int config_version;
MeterColumnSettings columns[2]; HeaderLayout hLayout;
MeterColumnSetting* hColumns;
Hashtable* dynamicColumns; Hashtable* dynamicColumns;
ProcessField* fields; ProcessField* fields;
@ -107,4 +109,6 @@ void Settings_enableReadonly(void);
bool Settings_isReadonly(void); bool Settings_isReadonly(void);
void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout);
#endif #endif