From 72ba20fa5f077bab4286dd78f9944df15de3b3b4 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Tue, 31 Aug 2021 15:38:52 +1000 Subject: [PATCH] Introduce screen tabs This is a forward port (by nathans) of Hisham's original code. --- Action.c | 70 ++++-- Action.h | 2 + CRT.c | 36 ++- CRT.h | 6 + CategoriesPanel.c | 12 +- ColumnsPanel.c | 30 ++- ColumnsPanel.h | 7 +- CommandLine.c | 12 +- DisplayOptionsPanel.c | 7 +- FunctionBar.c | 11 +- FunctionBar.h | 4 +- Header.c | 3 + IncSet.c | 37 ++- IncSet.h | 3 +- InfoScreen.c | 12 +- ListItem.c | 14 +- ListItem.h | 8 + MainPanel.c | 22 +- Makefile.am | 2 + Panel.c | 21 +- Panel.h | 10 + Process.c | 10 +- ProcessList.c | 18 +- ScreenManager.c | 90 ++++++-- ScreenManager.h | 5 +- ScreensPanel.c | 308 +++++++++++++++++++++++++ ScreensPanel.h | 49 ++++ Settings.c | 293 +++++++++++++++++------ Settings.h | 47 ++-- darwin/DarwinProcess.c | 2 +- darwin/Platform.c | 10 + darwin/Platform.h | 4 +- dragonflybsd/DragonFlyBSDProcessList.c | 2 +- dragonflybsd/Platform.c | 9 +- dragonflybsd/Platform.h | 4 +- freebsd/FreeBSDProcessList.c | 2 +- freebsd/Platform.c | 9 +- freebsd/Platform.h | 4 +- linux/LinuxProcessList.c | 31 +-- linux/Platform.c | 15 +- linux/Platform.h | 4 +- netbsd/NetBSDProcessList.c | 2 +- netbsd/Platform.c | 10 + netbsd/Platform.h | 4 + openbsd/OpenBSDProcessList.c | 2 +- openbsd/Platform.c | 10 +- openbsd/Platform.h | 4 +- pcp/PCPProcessList.c | 26 +-- pcp/Platform.c | 15 ++ pcp/Platform.h | 4 + solaris/Platform.c | 12 +- solaris/Platform.h | 6 +- solaris/SolarisProcessList.c | 2 +- unsupported/Platform.c | 12 +- unsupported/Platform.h | 6 +- unsupported/UnsupportedProcessList.c | 2 +- 56 files changed, 1113 insertions(+), 249 deletions(-) create mode 100644 ScreensPanel.c create mode 100644 ScreensPanel.h diff --git a/Action.c b/Action.c index 92145330..4e564540 100644 --- a/Action.c +++ b/Action.c @@ -58,7 +58,7 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) header->pl->following = pid; unfollow = true; } - ScreenManager_run(scr, &panelFocus, &ch); + ScreenManager_run(scr, &panelFocus, &ch, NULL); if (unfollow) { header->pl->following = -1; } @@ -85,7 +85,7 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) static void Action_runSetup(State* st) { ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true); CategoriesPanel_new(scr, st->settings, st->header, st->pl); - ScreenManager_run(scr, NULL, NULL); + ScreenManager_run(scr, NULL, NULL, "Setup"); ScreenManager_delete(scr); if (st->settings->changed) { Header_writeBackToSettings(st->header); @@ -154,7 +154,7 @@ static bool collapseIntoParent(Panel* panel) { } Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) { - Settings_setSortKey(settings, sortKey); + ScreenSettings_setSortKey(settings->ss, sortKey); return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING; } @@ -164,8 +164,9 @@ 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 ")); Panel_setHeader(sortPanel, "Sort by"); - const ProcessField* fields = st->settings->fields; - Hashtable* dynamicColumns = st->settings->dynamicColumns; + const Settings* settings = st->settings; + const ProcessField* fields = settings->ss->fields; + Hashtable* dynamicColumns = settings->dynamicColumns; for (int i = 0; fields[i]; i++) { char* name = NULL; if (fields[i] >= LAST_PROCESSFIELD) { @@ -177,7 +178,7 @@ static Htop_Reaction actionSetSortColumn(State* st) { name = String_trim(Process_fields[fields[i]].name); } Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); - if (fields[i] == Settings_getActiveSortKey(st->settings)) + if (fields[i] == ScreenSettings_getActiveSortKey(settings->ss)) Panel_setSelected(sortPanel, i); free(name); @@ -231,16 +232,18 @@ static Htop_Reaction actionToggleMergedCommand(State* st) { } static Htop_Reaction actionToggleTreeView(State* st) { - st->settings->treeView = !st->settings->treeView; + ScreenSettings* ss = st->settings->ss; + ss->treeView = !ss->treeView; - if (!st->settings->allBranchesCollapsed) + if (!ss->allBranchesCollapsed) ProcessList_expandTree(st->pl); return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) { - st->settings->allBranchesCollapsed = !st->settings->allBranchesCollapsed; - if (st->settings->allBranchesCollapsed) + ScreenSettings* ss = st->settings->ss; + ss->allBranchesCollapsed = !ss->allBranchesCollapsed; + if (ss->allBranchesCollapsed) ProcessList_collapseAllBranches(st->pl); else ProcessList_expandTree(st->pl); @@ -277,7 +280,7 @@ static Htop_Reaction actionLowerPriority(State* st) { } static Htop_Reaction actionInvertSortOrder(State* st) { - Settings_invertSortOrder(st->settings); + ScreenSettings_invertSortOrder(st->settings->ss); if (st->pauseProcessUpdate) ProcessList_sort(st->pl); return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; @@ -289,7 +292,7 @@ static Htop_Reaction actionExpandOrCollapse(State* st) { } static Htop_Reaction actionCollapseIntoParent(State* st) { - if (!st->settings->treeView) { + if (!st->settings->ss->treeView) { return HTOP_OK; } bool changed = collapseIntoParent((Panel*)st->mainPanel); @@ -297,7 +300,46 @@ static Htop_Reaction actionCollapseIntoParent(State* st) { } static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) { - return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); + return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); +} + +static Htop_Reaction actionNextScreen(State* st) { + Settings* settings = st->settings; + settings->ssIndex++; + if (settings->ssIndex == settings->nScreens) { + settings->ssIndex = 0; + } + settings->ss = settings->screens[settings->ssIndex]; + return HTOP_REFRESH; +} + +static Htop_Reaction actionPrevScreen(State* st) { + Settings* settings = st->settings; + if (settings->ssIndex == 0) { + settings->ssIndex = settings->nScreens - 1; + } else { + settings->ssIndex--; + } + settings->ss = settings->screens[settings->ssIndex]; + return HTOP_REFRESH; +} + +Htop_Reaction Action_setScreenTab(Settings* settings, int x) { + 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); + if (x <= s + len + 1) { + settings->ssIndex = i; + settings->ss = settings->screens[i]; + return HTOP_REFRESH; + } + s += len + 3; + } + return 0; } static Htop_Reaction actionQuit(ATTR_UNUSED State* st) { @@ -714,4 +756,6 @@ void Action_setBindings(Htop_Action* keys) { keys[KEY_F(10)] = actionQuit; keys[KEY_F(18)] = actionExpandCollapseOrSortColumn; keys[KEY_RECLICK] = actionExpandOrCollapse; + keys[KEY_SHIFT_TAB] = actionPrevScreen; + keys['\t'] = actionNextScreen; } diff --git a/Action.h b/Action.h index e1d14e4f..06af1886 100644 --- a/Action.h +++ b/Action.h @@ -57,6 +57,8 @@ 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_follow(State* st); void Action_setBindings(Htop_Action* keys); diff --git a/CRT.c b/CRT.c index 24c8c65c..30236a3b 100644 --- a/CRT.c +++ b/CRT.c @@ -193,6 +193,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Magenta, Black), [CPU_STEAL] = ColorPair(Cyan, Black), [CPU_GUEST] = ColorPair(Cyan, Black), + [PANEL_EDIT] = ColorPair(White, Blue), + [SCREENS_OTH_BORDER] = ColorPair(Blue, Blue), + [SCREENS_OTH_TEXT] = ColorPair(Black, Blue), + [SCREENS_CUR_BORDER] = ColorPair(Green, Green), + [SCREENS_CUR_TEXT] = ColorPair(Black, Green), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Cyan, Black), [PRESSURE_STALL_SIXTY] = A_BOLD | ColorPair(Cyan, Black), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Black), @@ -295,6 +300,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = A_BOLD, [CPU_STEAL] = A_DIM, [CPU_GUEST] = A_DIM, + [PANEL_EDIT] = A_BOLD, + [SCREENS_OTH_BORDER] = A_DIM, + [SCREENS_OTH_TEXT] = A_DIM, + [SCREENS_CUR_BORDER] = A_REVERSE, + [SCREENS_CUR_TEXT] = A_REVERSE, [PRESSURE_STALL_THREEHUNDRED] = A_DIM, [PRESSURE_STALL_SIXTY] = A_NORMAL, [PRESSURE_STALL_TEN] = A_BOLD, @@ -397,6 +407,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Blue, White), [CPU_STEAL] = ColorPair(Cyan, White), [CPU_GUEST] = ColorPair(Cyan, White), + [PANEL_EDIT] = ColorPair(White,Blue), + [SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Black,White), + [SCREENS_OTH_TEXT] = A_BOLD | ColorPair(Black,White), + [SCREENS_CUR_BORDER] = ColorPair(Green,Green), + [SCREENS_CUR_TEXT] = ColorPair(Black,Green), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, White), [PRESSURE_STALL_SIXTY] = ColorPair(Black, White), [PRESSURE_STALL_TEN] = ColorPair(Black, White), @@ -499,6 +514,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Blue, Black), [CPU_STEAL] = ColorPair(Black, Black), [CPU_GUEST] = ColorPair(Black, Black), + [PANEL_EDIT] = ColorPair(White,Blue), + [SCREENS_OTH_BORDER] = ColorPair(Blue,Black), + [SCREENS_OTH_TEXT] = ColorPair(Blue,Black), + [SCREENS_CUR_BORDER] = ColorPair(Green,Green), + [SCREENS_CUR_TEXT] = ColorPair(Black,Green), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Black, Black), [PRESSURE_STALL_TEN] = ColorPair(Black, Black), @@ -601,6 +621,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Black, Blue), [CPU_STEAL] = ColorPair(White, Blue), [CPU_GUEST] = ColorPair(White, Blue), + [PANEL_EDIT] = ColorPair(White,Blue), + [SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Yellow,Blue), + [SCREENS_OTH_TEXT] = ColorPair(Cyan,Blue), + [SCREENS_CUR_BORDER] = ColorPair(Cyan,Cyan), + [SCREENS_CUR_TEXT] = ColorPair(Black,Cyan), [PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black, Blue), [PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White, Blue), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Blue), @@ -701,6 +726,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Blue, Black), [CPU_STEAL] = ColorPair(Cyan, Black), [CPU_GUEST] = ColorPair(Cyan, Black), + [PANEL_EDIT] = ColorPair(White,Cyan), + [SCREENS_OTH_BORDER] = ColorPair(White,Black), + [SCREENS_OTH_TEXT] = ColorPair(Cyan,Black), + [SCREENS_CUR_BORDER] = A_BOLD | ColorPair(White,Black), + [SCREENS_CUR_TEXT] = A_BOLD | ColorPair(Green,Black), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Green, Black), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green, Black), @@ -725,8 +755,6 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. }; -int CRT_cursorX = 0; - int CRT_scrollHAmount = 5; int CRT_scrollWheelVAmount = 10; @@ -915,6 +943,7 @@ IGNORE_WCASTQUAL_BEGIN define_key("\033[14~", KEY_F(4)); define_key("\033[14;2~", KEY_F(15)); define_key("\033[17;2~", KEY_F(18)); + define_key("\033[Z", KEY_SHIFT_TAB); char sequence[3] = "\033a"; for (char c = 'a'; c <= 'z'; c++) { sequence[1] = c; @@ -925,6 +954,9 @@ IGNORE_WCASTQUAL_END #undef define_key #endif } + if (termType && (String_startsWith(termType, "rxvt"))) { + define_key("\033[Z", KEY_SHIFT_TAB); + } CRT_installSignalHandlers(); diff --git a/CRT.h b/CRT.h index c437e659..0b6302fc 100644 --- a/CRT.h +++ b/CRT.h @@ -120,6 +120,11 @@ typedef enum ColorElements_ { CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, + PANEL_EDIT, + SCREENS_OTH_BORDER, + SCREENS_OTH_TEXT, + SCREENS_CUR_BORDER, + SCREENS_CUR_TEXT, PRESSURE_STALL_TEN, PRESSURE_STALL_SIXTY, PRESSURE_STALL_THREEHUNDRED, @@ -150,6 +155,7 @@ void CRT_handleSIGSEGV(int signal) ATTR_NORETURN; #define KEY_WHEELUP KEY_F(30) #define KEY_WHEELDOWN KEY_F(31) #define KEY_RECLICK KEY_F(32) +#define KEY_SHIFT_TAB KEY_F(33) #define KEY_ALT(x) (KEY_F(64 - 26) + ((x) - 'A')) extern const char* CRT_degreeSign; diff --git a/CategoriesPanel.c b/CategoriesPanel.c index 30867ee9..6e905ce9 100644 --- a/CategoriesPanel.c +++ b/CategoriesPanel.c @@ -14,7 +14,6 @@ in the source distribution for its full text. #include "AvailableColumnsPanel.h" #include "AvailableMetersPanel.h" #include "ColorsPanel.h" -#include "ColumnsPanel.h" #include "DisplayOptionsPanel.h" #include "FunctionBar.h" #include "Header.h" @@ -25,6 +24,7 @@ in the source distribution for its full text. #include "MetersPanel.h" #include "Object.h" #include "ProvideCurses.h" +#include "ScreensPanel.h" #include "Vector.h" #include "XUtils.h" @@ -69,9 +69,11 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) { ScreenManager_add(this->scr, colors, -1); } -static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) { - Panel* columns = (Panel*) ColumnsPanel_new(this->settings); +static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) { + Panel* screens = (Panel*) ScreensPanel_new(this->settings); + Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns; Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns, this->settings->dynamicColumns); + ScreenManager_add(this->scr, screens, 20); ScreenManager_add(this->scr, columns, 20); ScreenManager_add(this->scr, availableColumns, -1); } @@ -91,7 +93,7 @@ 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 = "Screens", .ctor = CategoriesPanel_makeScreensPage }, { .name = "Colors", .ctor = CategoriesPanel_makeColorsPage }, }; @@ -157,7 +159,7 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea this->settings = settings; this->header = header; this->pl = pl; - Panel_setHeader(super, "Setup"); + Panel_setHeader(super, "Categories"); for (size_t i = 0; i < ARRAYSIZE(categoriesPanelPages); i++) Panel_add(super, (Object*) ListItem_new(categoriesPanelPages[i].name, 0)); diff --git a/ColumnsPanel.c b/ColumnsPanel.c index a1450bb0..24826936 100644 --- a/ColumnsPanel.c +++ b/ColumnsPanel.c @@ -138,20 +138,26 @@ static void ColumnsPanel_add(Panel* super, unsigned int key, Hashtable* columns) Panel_add(super, (Object*) ListItem_new(name, key)); } -ColumnsPanel* ColumnsPanel_new(Settings* settings) { +void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss, Hashtable* columns) { + Panel* super = (Panel*) this; + Panel_prune(super); + for (const ProcessField* fields = ss->fields; *fields; fields++) + ColumnsPanel_add(super, *fields, columns); + this->ss = ss; +} + +ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, Hashtable* columns, bool* changed) { ColumnsPanel* this = AllocThis(ColumnsPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL); Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); - this->settings = settings; + this->ss = ss; + this->changed = changed; this->moving = false; Panel_setHeader(super, "Active Columns"); - Hashtable* dynamicColumns = settings->dynamicColumns; - const ProcessField* fields = settings->fields; - for (; *fields; fields++) - ColumnsPanel_add(super, *fields, dynamicColumns); + ColumnsPanel_fill(this, ss, columns); return this; } @@ -159,14 +165,14 @@ ColumnsPanel* ColumnsPanel_new(Settings* settings) { void ColumnsPanel_update(Panel* super) { ColumnsPanel* this = (ColumnsPanel*) super; int size = Panel_size(super); - this->settings->changed = true; - this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size + 1)); - this->settings->flags = 0; + *(this->changed) = true; + this->ss->fields = xRealloc(this->ss->fields, sizeof(ProcessField) * (size + 1)); + this->ss->flags = 0; for (int i = 0; i < size; i++) { int key = ((ListItem*) Panel_get(super, i))->key; - this->settings->fields[i] = key; + this->ss->fields[i] = key; if (key < LAST_PROCESSFIELD) - this->settings->flags |= Process_fields[key].flags; + this->ss->flags |= Process_fields[key].flags; } - this->settings->fields[size] = 0; + this->ss->fields[size] = 0; } diff --git a/ColumnsPanel.h b/ColumnsPanel.h index d9360f46..63f6f92b 100644 --- a/ColumnsPanel.h +++ b/ColumnsPanel.h @@ -15,14 +15,17 @@ in the source distribution for its full text. typedef struct ColumnsPanel_ { Panel super; + ScreenSettings* ss; + bool* changed; - Settings* settings; bool moving; } ColumnsPanel; extern const PanelClass ColumnsPanel_class; -ColumnsPanel* ColumnsPanel_new(Settings* settings); +ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, Hashtable* columns, bool* changed); + +void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss, Hashtable* columns); void ColumnsPanel_update(Panel* super); diff --git a/CommandLine.c b/CommandLine.c index 3a45d820..4416864b 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -328,7 +328,7 @@ int CommandLine_run(const char* name, int argc, char** argv) { settings->enableMouse = false; #endif if (flags.treeView) - settings->treeView = true; + settings->ss->treeView = true; if (flags.highlightChanges) settings->highlightChanges = true; if (flags.highlightDelaySecs != -1) @@ -337,9 +337,9 @@ int CommandLine_run(const char* name, int argc, char** argv) { // -t -s means "tree sorted by key" // -s means "list sorted by key" (previous existing behavior) if (!flags.treeView) { - settings->treeView = false; + settings->ss->treeView = false; } - Settings_setSortKey(settings, flags.sortKey); + ScreenSettings_setSortKey(settings->ss, flags.sortKey); } CRT_init(settings, flags.allowUnicode); @@ -347,7 +347,7 @@ int CommandLine_run(const char* name, int argc, char** argv) { MainPanel* panel = MainPanel_new(); ProcessList_setPanel(pl, (Panel*) panel); - MainPanel_updateTreeFunctions(panel, settings->treeView); + MainPanel_updateTreeFunctions(panel, settings->ss->treeView); State state = { .settings = settings, @@ -370,10 +370,10 @@ int CommandLine_run(const char* name, int argc, char** argv) { CommandLine_delay(pl, 75); ProcessList_scan(pl, false); - if (settings->allBranchesCollapsed) + if (settings->ss->allBranchesCollapsed) ProcessList_collapseAllBranches(pl); - ScreenManager_run(scr, NULL, NULL); + ScreenManager_run(scr, NULL, NULL, NULL); Platform_done(); diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index 82121204..25e6d759 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -97,9 +97,10 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* this->scr = scr; Panel_setHeader(super, "Display options"); - Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView))); - Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID))); - Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->allBranchesCollapsed))); + Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->ss->treeView))); + Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->ss->treeViewAlwaysByPID))); + Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->ss->allBranchesCollapsed))); + Panel_add(super, (Object*) CheckItem_newByRef("Show tabs for screens", &(settings->screenTabs))); Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers))); Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads))); diff --git a/FunctionBar.c b/FunctionBar.c index fc3304ae..08500373 100644 --- a/FunctionBar.c +++ b/FunctionBar.c @@ -88,11 +88,12 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) { } } -void FunctionBar_draw(const FunctionBar* this) { - FunctionBar_drawExtra(this, NULL, -1, false); +int FunctionBar_draw(const FunctionBar* this) { + return FunctionBar_drawExtra(this, NULL, -1, false); } -void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) { +int FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) { + int cursorX = 0; attrset(CRT_colors[FUNCTION_BAR]); mvhline(LINES - 1, 0, ' ', COLS); int x = 0; @@ -113,18 +114,20 @@ void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr } mvaddstr(LINES - 1, x, buffer); x += strlen(buffer); + cursorX = x; } attrset(CRT_colors[RESET_COLOR]); if (setCursor) { - CRT_cursorX = x; curs_set(1); } else { curs_set(0); } currentLen = x; + + return cursorX; } void FunctionBar_append(const char* buffer, int attr) { diff --git a/FunctionBar.h b/FunctionBar.h index ebe405f3..f01a5ef5 100644 --- a/FunctionBar.h +++ b/FunctionBar.h @@ -29,9 +29,9 @@ void FunctionBar_delete(FunctionBar* this); void FunctionBar_setLabel(FunctionBar* this, int event, const char* text); -void FunctionBar_draw(const FunctionBar* this); +int FunctionBar_draw(const FunctionBar* this); -void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor); +int FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor); void FunctionBar_append(const char* buffer, int attr); diff --git a/Header.c b/Header.c index c557a450..976ccf22 100644 --- a/Header.c +++ b/Header.c @@ -283,6 +283,9 @@ int Header_calculateHeight(Header* this) { } maxHeight = MAXIMUM(maxHeight, height); } + if (this->settings->screenTabs) { + maxHeight++; + } this->height = maxHeight; this->pad = pad; return maxHeight; diff --git a/IncSet.c b/IncSet.c index 56f9c32f..22c5b259 100644 --- a/IncSet.c +++ b/IncSet.c @@ -105,10 +105,10 @@ static void updateWeakPanel(const IncSet* this, Panel* panel, Vector* lines) { } } -static bool search(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) { +static bool search(const IncSet* this, Panel* panel, IncMode_GetPanelValue getPanelValue) { int size = Panel_size(panel); for (int i = 0; i < size; i++) { - if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { + if (String_contains_i(getPanelValue(panel, i), this->active->buffer)) { Panel_setSelected(panel, i); return true; } @@ -117,6 +117,21 @@ static bool search(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getP return false; } +void IncSet_activate(IncSet* this, IncType type, Panel* panel) { + this->active = &(this->modes[type]); + panel->currentBar = this->active->bar; + panel->cursorOn = true; + this->panel = panel; + IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]); +} + +static void IncSet_deactivate(IncSet* this, Panel* panel) { + this->active = NULL; + Panel_setDefaultBar(panel); + panel->cursorOn = false; + FunctionBar_draw(this->defaultBar); +} + static bool IncMode_find(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) { int size = Panel_size(panel); int here = Panel_getSelectedIndex(panel); @@ -194,12 +209,11 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue IncMode_reset(mode); } } - this->active = NULL; - Panel_setDefaultBar(panel); + IncSet_deactivate(this, panel); doSearch = false; } if (doSearch) { - this->found = search(mode, panel, getPanelValue); + this->found = search(this, panel, getPanelValue); } if (filterChanged && lines) { updateWeakPanel(this, panel, lines); @@ -212,14 +226,13 @@ const char* IncSet_getListItemValue(Panel* panel, int i) { return l ? l->value : ""; } -void IncSet_activate(IncSet* this, IncType type, Panel* panel) { - this->active = &(this->modes[type]); - panel->currentBar = this->active->bar; -} - -void IncSet_drawBar(const IncSet* this) { +void IncSet_drawBar(const IncSet* this, int attr) { if (this->active) { - FunctionBar_drawExtra(this->active->bar, this->active->buffer, (this->active->isFilter || this->found) ? -1 : CRT_colors[FAILED_SEARCH], true); + if (!this->active->isFilter && !this->found) + attr = CRT_colors[FAILED_SEARCH]; + int cursorX = FunctionBar_drawExtra(this->active->bar, this->active->buffer, attr, true); + this->panel->cursorY = LINES - 1; + this->panel->cursorX = cursorX; } else { FunctionBar_draw(this->defaultBar); } diff --git a/IncSet.h b/IncSet.h index a98e7f8d..15b5d5d2 100644 --- a/IncSet.h +++ b/IncSet.h @@ -32,6 +32,7 @@ typedef struct IncMode_ { typedef struct IncSet_ { IncMode modes[2]; IncMode* active; + Panel* panel; FunctionBar* defaultBar; bool filtering; bool found; @@ -57,7 +58,7 @@ const char* IncSet_getListItemValue(Panel* panel, int i); void IncSet_activate(IncSet* this, IncType type, Panel* panel); -void IncSet_drawBar(const IncSet* this); +void IncSet_drawBar(const IncSet* this, int attr); int IncSet_synthesizeEvent(IncSet* this, int x); diff --git a/InfoScreen.c b/InfoScreen.c index f431f799..eea09aa2 100644 --- a/InfoScreen.c +++ b/InfoScreen.c @@ -57,7 +57,7 @@ void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) { attrset(CRT_colors[DEFAULT_COLOR]); Panel_draw(this->display, true, true, true, false); - IncSet_drawBar(this->inc); + IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]); } void InfoScreen_addLine(InfoScreen* this, const char* line) { @@ -89,15 +89,9 @@ void InfoScreen_run(InfoScreen* this) { while (looping) { Panel_draw(panel, false, true, true, false); - IncSet_drawBar(this->inc); + IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]); - if (this->inc->active) { - (void) move(LINES - 1, CRT_cursorX); - } -#ifdef HAVE_SET_ESCDELAY - set_escdelay(25); -#endif - int ch = getch(); + int ch = Panel_getCh(panel); if (ch == ERR) { if (As_InfoScreen(this)->onErr) { diff --git a/ListItem.c b/ListItem.c index 9161a1cc..246bc5d0 100644 --- a/ListItem.c +++ b/ListItem.c @@ -18,13 +18,13 @@ in the source distribution for its full text. #include "XUtils.h" -static void ListItem_delete(Object* cast) { +void ListItem_delete(Object* cast) { ListItem* this = (ListItem*)cast; free(this->value); free(this); } -static void ListItem_display(const Object* cast, RichString* out) { +void ListItem_display(const Object* cast, RichString* out) { const ListItem* const this = (const ListItem*)cast; assert (this != NULL); @@ -38,11 +38,15 @@ static void ListItem_display(const Object* cast, RichString* out) { RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value); } -ListItem* ListItem_new(const char* value, int key) { - ListItem* this = AllocThis(ListItem); +void ListItem_init(ListItem* this, const char* value, int key) { this->value = xStrdup(value); this->key = key; this->moving = false; +} + +ListItem* ListItem_new(const char* value, int key) { + ListItem* this = AllocThis(ListItem); + ListItem_init(this, value, key); return this; } @@ -55,7 +59,7 @@ void ListItem_append(ListItem* this, const char* text) { this->value[newLen] = '\0'; } -static int ListItem_compare(const void* cast1, const void* cast2) { +int ListItem_compare(const void* cast1, const void* cast2) { const ListItem* obj1 = (const ListItem*) cast1; const ListItem* obj2 = (const ListItem*) cast2; return strcmp(obj1->value, obj2->value); diff --git a/ListItem.h b/ListItem.h index 2c7ae4ea..b2c3e061 100644 --- a/ListItem.h +++ b/ListItem.h @@ -21,10 +21,18 @@ typedef struct ListItem_ { extern const ObjectClass ListItem_class; +void ListItem_delete(Object* cast); + +void ListItem_display(const Object* cast, RichString* out); + +void ListItem_init(ListItem* this, const char* value, int key); + ListItem* ListItem_new(const char* value, int key); void ListItem_append(ListItem* this, const char* text); +int ListItem_compare(const void* cast1, const void* cast2); + static inline const char* ListItem_getRef(const ListItem* this) { return this->value; } diff --git a/MainPanel.c b/MainPanel.c index 07dc6319..aeabe753 100644 --- a/MainPanel.c +++ b/MainPanel.c @@ -71,23 +71,29 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { if (needReset) this->state->hideProcessSelection = false; + Settings* settings = this->state->settings; + ScreenSettings* ss = settings->ss; + if (EVENT_IS_HEADER_CLICK(ch)) { int x = EVENT_HEADER_CLICK_GET_X(ch); const ProcessList* pl = this->state->pl; - Settings* settings = this->state->settings; int hx = super->scrollH + x + 1; ProcessField field = ProcessList_keyAt(pl, hx); - if (settings->treeView && settings->treeViewAlwaysByPID) { - settings->treeView = false; - settings->direction = 1; + if (ss->treeView && ss->treeViewAlwaysByPID) { + ss->treeView = false; + ss->direction = 1; reaction |= Action_setSortKey(settings, field); - } else if (field == Settings_getActiveSortKey(settings)) { - Settings_invertSortOrder(settings); + } else if (field == ScreenSettings_getActiveSortKey(ss)) { + ScreenSettings_invertSortOrder(ss); } else { reaction |= Action_setSortKey(settings, field); } reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS; result = HANDLED; + } else if (EVENT_IS_SCREEN_TAB_CLICK(ch)) { + int x = EVENT_SCREEN_TAB_GET_X(ch); + reaction |= Action_setScreenTab(settings, x); + result = HANDLED; } else if (ch != ERR && this->inc->active) { bool filterChanged = IncSet_handleKey(this->inc, ch, super, MainPanel_getValue, NULL); if (filterChanged) { @@ -116,7 +122,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { } if (reaction & HTOP_REDRAW_BAR) { - MainPanel_updateTreeFunctions(this, this->state->settings->treeView); + MainPanel_updateTreeFunctions(this, settings->ss->treeView); } if (reaction & HTOP_RESIZE) { result |= RESIZE; @@ -182,7 +188,7 @@ static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) { if (hideFunctionBar && !this->inc->active) return; - IncSet_drawBar(this->inc); + IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]); if (this->state->pauseProcessUpdate) { FunctionBar_append("PAUSED", CRT_colors[PAUSED]); } diff --git a/Makefile.am b/Makefile.am index 2a9cc29f..8af1864a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -74,6 +74,7 @@ myhtopsources = \ ProcessLocksScreen.c \ RichString.c \ ScreenManager.c \ + ScreensPanel.c \ Settings.c \ SignalsPanel.c \ SwapMeter.c \ @@ -135,6 +136,7 @@ myhtopheaders = \ ProvideCurses.h \ RichString.h \ ScreenManager.h \ + ScreensPanel.h \ Settings.h \ SignalsPanel.h \ SwapMeter.h \ diff --git a/Panel.c b/Panel.c index a5773d52..01ddfac9 100644 --- a/Panel.c +++ b/Panel.c @@ -49,6 +49,8 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type this->y = y; this->w = w; this->h = h; + this->cursorX = 0; + this->cursorY = 0; this->eventHandlerState = NULL; this->items = Vector_new(type, owner, DEFAULT_SIZE); this->scrollV = 0; @@ -72,6 +74,11 @@ void Panel_done(Panel* this) { RichString_delete(&this->header); } +void Panel_setCursorToSelection(Panel* this) { + this->cursorY = this->y + this->selected - this->scrollV + 1; + this->cursorX = this->x + this->selectedLen - this->scrollH; +} + void Panel_setSelectionColor(Panel* this, ColorElements colorId) { this->selectionColorId = colorId; } @@ -330,7 +337,6 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect this->oldSelected = this->selected; this->wasFocus = focus; this->needsRedraw = false; - move(0, 0); } static int Panel_headerHeight(const Panel* this) { @@ -485,3 +491,16 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) { return IGNORED; } + +int Panel_getCh(Panel* this) { + if (this->cursorOn) { + move(this->cursorY, this->cursorX); + curs_set(1); + } else { + curs_set(0); + } +#ifdef HAVE_SET_ESCDELAY + set_escdelay(25); +#endif + return getch(); +} diff --git a/Panel.h b/Panel.h index 9bb4c77e..33d532e8 100644 --- a/Panel.h +++ b/Panel.h @@ -39,6 +39,10 @@ typedef enum HandlerResult_ { #define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000) #define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000) +#define EVENT_SCREEN_TAB_CLICK(x_) (-20000 + (x_)) +#define EVENT_IS_SCREEN_TAB_CLICK(ev_) ((ev_) >= -20000 && (ev_) < -10000) +#define EVENT_SCREEN_TAB_GET_X(ev_) ((ev_) + 20000) + typedef HandlerResult (*Panel_EventHandler)(Panel*, int); typedef void (*Panel_DrawFunctionBar)(Panel*, bool); typedef void (*Panel_PrintHeader)(Panel*); @@ -61,6 +65,7 @@ typedef struct PanelClass_ { struct Panel_ { Object super; int x, y, w, h; + int cursorX, cursorY; Vector* items; int selected; int oldSelected; @@ -69,6 +74,7 @@ struct Panel_ { int scrollV; int scrollH; bool needsRedraw; + bool cursorOn; bool wasFocus; FunctionBar* currentBar; FunctionBar* defaultBar; @@ -90,6 +96,8 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type void Panel_done(Panel* this); +void Panel_setCursorToSelection(Panel* this); + void Panel_setSelectionColor(Panel* this, ColorElements colorId); void Panel_setHeader(Panel* this, const char* header); @@ -130,4 +138,6 @@ bool Panel_onKey(Panel* this, int key); HandlerResult Panel_selectByTyping(Panel* this, int ch); +int Panel_getCh(Panel* this); + #endif diff --git a/Process.c b/Process.c index cef5af33..47c3ef72 100644 --- a/Process.c +++ b/Process.c @@ -784,7 +784,8 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field attr = CRT_colors[PROCESS_THREAD]; baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; } - if (!this->settings->treeView || this->indent == 0) { + const ScreenSettings* ss = this->settings->ss; + if (!ss->treeView || this->indent == 0) { Process_writeCommand(this, attr, baseattr, str); return; } @@ -974,7 +975,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field void Process_display(const Object* cast, RichString* out) { const Process* this = (const Process*) cast; - const ProcessField* fields = this->settings->fields; + const ProcessField* fields = this->settings->ss->fields; for (int i = 0; fields[i]; i++) As_Process(this)->writeField(this, out, fields[i]); @@ -1092,8 +1093,9 @@ int Process_compare(const void* v1, const void* v2) { const Process* p2 = (const Process*)v2; const Settings* settings = p1->settings; + const ScreenSettings* ss = settings->ss; - ProcessField key = Settings_getActiveSortKey(settings); + ProcessField key = ScreenSettings_getActiveSortKey(ss); int result = Process_compareByKey(p1, p2, key); @@ -1101,7 +1103,7 @@ int Process_compare(const void* v1, const void* v2) { if (!result) return SPACESHIP_NUMBER(p1->pid, p2->pid); - return (Settings_getActiveDirection(settings) == 1) ? result : -result; + return (ScreenSettings_getActiveDirection(ss) == 1) ? result : -result; } int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) { diff --git a/ProcessList.c b/ProcessList.c index c4c759da..1df1990d 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -124,13 +124,14 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header) { RichString_rewind(header, RichString_size(header)); const Settings* settings = this->settings; - const ProcessField* fields = settings->fields; + const ScreenSettings* ss = settings->ss; + const ProcessField* fields = ss->fields; - ProcessField key = Settings_getActiveSortKey(settings); + ProcessField key = ScreenSettings_getActiveSortKey(ss); for (int i = 0; fields[i]; i++) { int color; - if (settings->treeView && settings->treeViewAlwaysByPID) { + if (ss->treeView && ss->treeViewAlwaysByPID) { color = CRT_colors[PANEL_HEADER_FOCUS]; } else if (key == fields[i]) { color = CRT_colors[PANEL_SELECTION_FOCUS]; @@ -140,10 +141,11 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header) { RichString_appendWide(header, color, alignedProcessFieldTitle(this, fields[i])); if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') { + bool ascending = ScreenSettings_getActiveDirection(ss) == 1; RichString_rewind(header, 1); // rewind to override space RichString_appendnWide(header, CRT_colors[PANEL_SELECTION_FOCUS], - CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_ASC : TREE_STR_DESC], + CRT_treeStr[ascending ? TREE_STR_ASC : TREE_STR_DESC], 1); } if (COMM == fields[i] && settings->showMergedCommand) { @@ -402,7 +404,7 @@ static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) { static void ProcessList_buildTree(ProcessList* this) { int node_counter = 1; int node_index = 0; - int direction = Settings_getActiveDirection(this->settings); + int direction = ScreenSettings_getActiveDirection(this->settings->ss); // Sort by PID Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID); @@ -488,7 +490,7 @@ static void ProcessList_buildTree(ProcessList* this) { } void ProcessList_sort(ProcessList* this) { - if (this->settings->treeView) { + if (this->settings->ss->treeView) { ProcessList_updateTreeSet(this); Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompare); } else { @@ -498,7 +500,7 @@ void ProcessList_sort(ProcessList* this) { ProcessField ProcessList_keyAt(const ProcessList* this, int at) { int x = 0; - const ProcessField* fields = this->settings->fields; + const ProcessField* fields = this->settings->ss->fields; ProcessField field; for (int i = 0; (field = fields[i]); i++) { int len = strlen(alignedProcessFieldTitle(this, field)); @@ -661,7 +663,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { // Set UID column width based on max UID. Process_setUidColumnWidth(maxUid); - if (this->settings->treeView) { + if (this->settings->ss->treeView) { // Clear out the hashtable to avoid any left-over processes from previous build // // The sorting algorithm relies on the fact that diff --git a/ScreenManager.c b/ScreenManager.c index 96e9c47e..d45f3afc 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -49,27 +49,43 @@ inline int ScreenManager_size(const ScreenManager* this) { } void ScreenManager_add(ScreenManager* this, Panel* item, int size) { + ScreenManager_insert(this, item, size, Vector_size(this->panels)); +} + +void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx) { int lastX = 0; - if (this->panelCount > 0) { - const Panel* last = (const Panel*) Vector_get(this->panels, this->panelCount - 1); + if (idx > 0) { + const Panel* last = (const Panel*) Vector_get(this->panels, idx - 1); lastX = last->x + last->w + 1; } int height = LINES - this->y1 - (this->header ? this->header->height : 0) + this->y2; - if (size > 0) { - Panel_resize(item, size, height); - } else { - Panel_resize(item, COLS - this->x1 + this->x2 - lastX, height); + if (size <= 0) { + size = COLS - this->x1 + this->x2 - lastX; } + Panel_resize(item, size, height); Panel_move(item, lastX, this->y1 + (this->header ? this->header->height : 0)); - Vector_add(this->panels, item); + if (idx < this->panelCount) { + for (int i = idx + 1; i <= this->panelCount; i++) { + Panel* p = (Panel*) Vector_get(this->panels, i); + Panel_move(p, p->x + size, p->y); + } + } + Vector_insert(this->panels, idx, item); item->needsRedraw = true; this->panelCount++; } Panel* ScreenManager_remove(ScreenManager* this, int idx) { assert(this->panelCount > idx); + int w = ((Panel*) Vector_get(this->panels, idx))->w; Panel* panel = (Panel*) Vector_remove(this->panels, idx); this->panelCount--; + if (idx < this->panelCount) { + for (int i = idx; i < this->panelCount; i++) { + Panel* p = (Panel*) Vector_get(this->panels, i); + Panel_move(p, p->x - w, p->y); + } + } return panel; } @@ -108,7 +124,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi ProcessList_scan(pl, this->state->pauseProcessUpdate); // always update header, especially to avoid gaps in graph meters Header_updateData(this->header); - if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) { + if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->ss->treeView)) { ProcessList_sort(pl); *sortTimeout = 1; } @@ -125,7 +141,53 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi *rescan = false; } +static inline bool drawTab(int* y, int* x, int l, const char* name, bool cur) { + attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]); + mvaddch(*y, *x, '['); + (*x)++; + if (*x >= l) + return false; + int nameLen = strlen(name); + int n = MINIMUM(l - *x, nameLen); + attrset(CRT_colors[cur ? SCREENS_CUR_TEXT : SCREENS_OTH_TEXT]); + mvaddnstr(*y, *x, name, n); + *x += n; + if (*x >= l) + return false; + attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]); + mvaddch(*y, *x, ']'); + *x += 2; + if (*x >= l) + return false; + return true; +} + +static void ScreenManager_drawScreenTabs(ScreenManager* this) { + ScreenSettings** screens = this->settings->screens; + int cur = this->settings->ssIndex; + int l = COLS; + Panel* panel = (Panel*) Vector_get(this->panels, 0); + int y = panel->y - 1; + int x = 2; + + if (this->name) { + drawTab(&y, &x, l, this->name, true); + return; + } + + for (int s = 0; screens[s]; s++) { + bool ok = drawTab(&y, &x, l, screens[s]->name, s == cur); + if (!ok) { + break; + } + } + attrset(CRT_colors[RESET_COLOR]); +} + static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) { + if (this->settings->screenTabs) { + ScreenManager_drawScreenTabs(this); + } const int nPanels = this->panelCount; for (int i = 0; i < nPanels; i++) { Panel* panel = (Panel*) Vector_get(this->panels, i); @@ -138,7 +200,7 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_ } } -void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { +void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, const char* name) { bool quit = false; int focus = 0; @@ -156,6 +218,8 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { int sortTimeout = 0; int resetSortTimeout = 5; + this->name = name; + while (!quit) { if (this->header) { checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut, &force_redraw); @@ -167,10 +231,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { } int prevCh = ch; -#ifdef HAVE_SET_ESCDELAY - set_escdelay(25); -#endif - ch = getch(); + ch = Panel_getCh(panelFocus); HandlerResult result = IGNORED; #ifdef HAVE_GETMOUSE @@ -189,6 +250,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { if (mevent.y == panel->y) { ch = EVENT_HEADER_CLICK(mevent.x - panel->x); break; + } else if (this->settings->screenTabs && mevent.y == panel->y - 1) { + ch = EVENT_SCREEN_TAB_CLICK(mevent.x); + break; } else if (mevent.y > panel->y && mevent.y <= panel->y + panel->h) { ch = KEY_MOUSE; if (panel == panelFocus || this->allowFocusChange) { diff --git a/ScreenManager.h b/ScreenManager.h index 455be705..978b524b 100644 --- a/ScreenManager.h +++ b/ScreenManager.h @@ -22,6 +22,7 @@ typedef struct ScreenManager_ { int x2; int y2; Vector* panels; + const char* name; int panelCount; Header* header; const Settings* settings; @@ -37,10 +38,12 @@ int ScreenManager_size(const ScreenManager* this); void ScreenManager_add(ScreenManager* this, Panel* item, int size); +void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx); + Panel* ScreenManager_remove(ScreenManager* this, int idx); void ScreenManager_resize(ScreenManager* this); -void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey); +void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, const char* name); #endif diff --git a/ScreensPanel.c b/ScreensPanel.c new file mode 100644 index 00000000..e809c062 --- /dev/null +++ b/ScreensPanel.c @@ -0,0 +1,308 @@ +/* +htop - ScreensPanel.c +(C) 2004-2011 Hisham H. Muhammad +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "ScreensPanel.h" +#include "Platform.h" + +#include "CRT.h" +#include "XUtils.h" + +#include +#include +#include +#include + + +ObjectClass ScreenListItem_class = { + .extends = Class(ListItem), + .display = ListItem_display, + .delete = ListItem_delete, + .compare = ListItem_compare +}; + +ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss) { + ScreenListItem* this = AllocThis(ScreenListItem); + ListItem_init((ListItem*)this, value, 0); + this->ss = ss; + return this; +} + +static const char* const ScreensFunctions[] = {" ", "Rename", " ", " ", "New ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL}; + +static void ScreensPanel_delete(Object* object) { + Panel* super = (Panel*) object; + ScreensPanel* this = (ScreensPanel*) object; + Panel_done(super); + free(this); +} + +static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) { + ScreensPanel* const this = (ScreensPanel*) super; + + if (ch >= 32 && ch < 127 && ch != 61) { + if (this->cursor < SCREEN_NAME_LEN - 1) { + this->buffer[this->cursor] = ch; + this->cursor++; + super->selectedLen = strlen(this->buffer); + Panel_setCursorToSelection(super); + } + } else { + switch(ch) { + case 127: + case KEY_BACKSPACE: + { + if (this->cursor > 0) { + this->cursor--; + this->buffer[this->cursor] = '\0'; + super->selectedLen = strlen(this->buffer); + Panel_setCursorToSelection(super); + } + break; + } + case 0x0a: + case 0x0d: + case KEY_ENTER: + { + ListItem* item = (ListItem*) Panel_getSelected(super); + if (!item) + break; + free(this->saved); + item->value = xStrdup(this->buffer); + this->renaming = false; + super->cursorOn = false; + Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]); + ScreensPanel_update(super); + break; + } + case 27: // Esc + { + ListItem* item = (ListItem*) Panel_getSelected(super); + if (!item) + break; + item->value = this->saved; + this->renaming = false; + super->cursorOn = false; + Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]); + break; + } + } + } + return HANDLED; +} + +static void startRenaming(Panel* super) { + ScreensPanel* const this = (ScreensPanel*) super; + + ListItem* item = (ListItem*) Panel_getSelected(super); + if (item == NULL) + return; + this->renaming = true; + super->cursorOn = true; + char* name = item->value; + this->saved = name; + strncpy(this->buffer, name, SCREEN_NAME_LEN); + this->buffer[SCREEN_NAME_LEN] = '\0'; + this->cursor = strlen(this->buffer); + item->value = this->buffer; + Panel_setSelectionColor(super, CRT_colors[PANEL_EDIT]); + super->selectedLen = strlen(this->buffer); + Panel_setCursorToSelection(super); +} + +static void rebuildSettingsArray(Panel* super) { + ScreensPanel* const this = (ScreensPanel*) super; + + int n = Panel_size(super); + free(this->settings->screens); + this->settings->screens = xMalloc(sizeof(ScreenSettings*) * (n + 1)); + this->settings->screens[n] = NULL; + for (int i = 0; i < n; i++) { + ScreenListItem* item = (ScreenListItem*) Panel_get(super, i); + this->settings->screens[i] = item->ss; + } + this->settings->nScreens = n; +} + +static void addNewScreen(Panel* super) { + ScreensPanel* const this = (ScreensPanel*) super; + + const char* name = "New"; + ScreenSettings* ss = Settings_newScreen(this->settings, name, "PID Command"); + ScreenListItem* item = ScreenListItem_new(name, ss); + int idx = Panel_getSelectedIndex(super); + Panel_insert(super, idx + 1, (Object*) item); + Panel_setSelected(super, idx + 1); +} + +static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) { + ScreensPanel* const this = (ScreensPanel*) super; + + int selected = Panel_getSelectedIndex(super); + ScreenListItem* oldFocus = (ScreenListItem*) Panel_getSelected(super); + bool shouldRebuildArray = false; + HandlerResult result = IGNORED; + switch(ch) { + case 0x0a: + case 0x0d: + case KEY_ENTER: + case KEY_MOUSE: + case KEY_RECLICK: + { + this->moving = !(this->moving); + Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]); + ListItem* item = (ListItem*) Panel_getSelected(super); + if (item) + item->moving = this->moving; + result = HANDLED; + break; + } + case EVENT_SET_SELECTED: + result = HANDLED; + break; + case KEY_NPAGE: + case KEY_PPAGE: + case KEY_HOME: + case KEY_END: { + Panel_onKey(super, ch); + break; + } + case KEY_F(2): + case KEY_CTRL('R'): + { + startRenaming(super); + result = HANDLED; + break; + } + case KEY_F(5): + case KEY_CTRL('N'): + { + addNewScreen(super); + startRenaming(super); + shouldRebuildArray = true; + result = HANDLED; + break; + } + case KEY_UP: + { + if (!this->moving) { + Panel_onKey(super, ch); + break; + } + /* else fallthrough */ + } /* FALLTHRU */ + case KEY_F(7): + case '[': + case '-': + { + Panel_moveSelectedUp(super); + shouldRebuildArray = true; + result = HANDLED; + break; + } + case KEY_DOWN: + { + if (!this->moving) { + Panel_onKey(super, ch); + break; + } + /* else fallthrough */ + } /* FALLTHRU */ + case KEY_F(8): + case ']': + case '+': + { + Panel_moveSelectedDown(super); + shouldRebuildArray = true; + result = HANDLED; + break; + } + case KEY_F(9): + //case KEY_DC: + { + if (Panel_size(super) > 1) { + Panel_remove(super, selected); + } + shouldRebuildArray = true; + result = HANDLED; + break; + } + default: + { + if (ch < 255 && isalpha(ch)) + result = Panel_selectByTyping(super, ch); + if (result == BREAK_LOOP) + result = IGNORED; + break; + } + } + ScreenListItem* newFocus = (ScreenListItem*) Panel_getSelected(super); + if (newFocus && oldFocus != newFocus) { + ColumnsPanel_fill(this->columns, newFocus->ss, this->settings->dynamicColumns); + result = HANDLED; + } + if (shouldRebuildArray) + rebuildSettingsArray(super); + if (result == HANDLED) + ScreensPanel_update(super); + return result; +} + +static HandlerResult ScreensPanel_eventHandler(Panel* super, int ch) { + ScreensPanel* const this = (ScreensPanel*) super; + + if (this->renaming) { + return ScreensPanel_eventHandlerRenaming(super, ch); + } else { + return ScreensPanel_eventHandlerNormal(super, ch); + } +} + +PanelClass ScreensPanel_class = { + .super = { + .extends = Class(Panel), + .delete = ScreensPanel_delete + }, + .eventHandler = ScreensPanel_eventHandler +}; + +ScreensPanel* ScreensPanel_new(Settings* settings) { + ScreensPanel* this = AllocThis(ScreensPanel); + Panel* super = (Panel*) this; + Hashtable* columns = settings->dynamicColumns; + FunctionBar* fuBar = FunctionBar_new(ScreensFunctions, NULL, NULL); + Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); + + this->settings = settings; + this->columns = ColumnsPanel_new(settings->screens[0], columns, &(settings->changed)); + this->moving = false; + this->renaming = false; + super->cursorOn = false; + this->cursor = 0; + Panel_setHeader(super, "Screens"); + + for (unsigned int i = 0; i < settings->nScreens; i++) { + ScreenSettings* ss = settings->screens[i]; + char* name = ss->name; + Panel_add(super, (Object*) ScreenListItem_new(name, ss)); + } + return this; +} + +void ScreensPanel_update(Panel* super) { + ScreensPanel* this = (ScreensPanel*) super; + int size = Panel_size(super); + this->settings->changed = true; + this->settings->screens = xRealloc(this->settings->screens, sizeof(char*) * (size+1)); + for (int i = 0; i < size; i++) { + ScreenListItem* item = (ScreenListItem*) Panel_get(super, i); + ScreenSettings* ss = item->ss; + free(ss->name); + this->settings->screens[i] = ss; + ss->name = xStrdup(((ListItem*) item)->value); + } + this->settings->screens[size] = NULL; +} diff --git a/ScreensPanel.h b/ScreensPanel.h new file mode 100644 index 00000000..52c5fff3 --- /dev/null +++ b/ScreensPanel.h @@ -0,0 +1,49 @@ +#ifndef HEADER_ScreensPanel +#define HEADER_ScreensPanel +/* +htop - ScreensPanel.h +(C) 2004-2011 Hisham H. Muhammad +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Panel.h" +#include "ScreenManager.h" +#include "ColumnsPanel.h" +#include "Settings.h" +#include "ListItem.h" + +#ifndef SCREEN_NAME_LEN +#define SCREEN_NAME_LEN 20 +#endif + +typedef struct ScreensPanel_ { + Panel super; + + ScreenManager* scr; + Settings* settings; + ColumnsPanel* columns; + char buffer[SCREEN_NAME_LEN + 1]; + char* saved; + int cursor; + bool moving; + bool renaming; +} ScreensPanel; + +typedef struct ScreenListItem_ { + ListItem super; + ScreenSettings* ss; +} ScreenListItem; + + +extern ObjectClass ScreenListItem_class; + +ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss); + +extern PanelClass ScreensPanel_class; + +ScreensPanel* ScreensPanel_new(Settings* settings); + +void ScreensPanel_update(Panel* super); + +#endif diff --git a/Settings.c b/Settings.c index 1f13b5a3..d4aa0b8e 100644 --- a/Settings.c +++ b/Settings.c @@ -24,14 +24,62 @@ in the source distribution for its full text. #include "XUtils.h" +/* + +static char** readQuotedList(char* line) { + int n = 0; + char** list = xCalloc(sizeof(char*), 1); + int start = 0; + for (;;) { + while (line[start] && line[start] == ' ') { + start++; + } + if (line[start] != '"') { + break; + } + start++; + int close = start; + while (line[close] && line[close] != '"') { + close++; + } + int len = close - start; + char* item = xMalloc(len + 1); + strncpy(item, line + start, len); + item[len] = '\0'; + list[n] = item; + n++; + list = xRealloc(list, sizeof(char*) * (n + 1)); + start = close + 1; + } + list[n] = NULL; + return list; +} + +static void writeQuotedList(FILE* fd, char** list) { + const char* sep = ""; + for (int i = 0; list[i]; i++) { + fprintf(fd, "%s\"%s\"", sep, list[i]); + sep = " "; + } + fprintf(fd, "\n"); +} + +*/ + void Settings_delete(Settings* this) { free(this->filename); - free(this->fields); for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) { String_freeArray(this->hColumns[i].names); free(this->hColumns[i].modes); } free(this->hColumns); + if (this->screens) { + for (unsigned int i = 0; this->screens[i]; i++) { + free(this->screens[i]->name); + free(this->screens[i]->fields); + } + free(this->screens); + } free(this); } @@ -148,46 +196,98 @@ static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount) this->hColumns[1].modes[r++] = TEXT_METERMODE; } -static void Settings_readFields(Settings* settings, const char* line) { +static const char* toFieldName(Hashtable* columns, int id) { + if (id < 0) + return NULL; + if (id >= LAST_PROCESSFIELD) { + const DynamicColumn* column = DynamicColumn_lookup(columns, id); + return column->name; + } + return Process_fields[id].name; +} + +static int toFieldIndex(Hashtable* columns, const char* str) { + if (isdigit(str[0])) { + // This "+1" is for compatibility with the older enum format. + int id = atoi(str) + 1; + if (toFieldName(columns, id)) { + return id; + } + } else { + // Dynamically-defined columns are always stored by-name. + char dynamic[32] = {0}; + if (sscanf(str, "Dynamic(%30s)", dynamic)) { + char* end; + if ((end = strrchr(dynamic, ')')) != NULL) { + bool success; + unsigned int key; + *end = '\0'; + success = DynamicColumn_search(columns, dynamic, &key) != NULL; + *end = ')'; + if (success) + return key; + } + } + // Fallback to iterative scan of table of fields by-name. + for (int p = 1; p < LAST_PROCESSFIELD; p++) { + const char* pName = toFieldName(columns, p); + if (pName && strcmp(pName, str) == 0) + return p; + } + } + return -1; +} + +static void ScreenSettings_readFields(ScreenSettings* ss, Hashtable* columns, const char* line) { char* trim = String_trim(line); char** ids = String_split(trim, ' ', NULL); free(trim); - settings->flags = 0; - unsigned int i, j; for (j = 0, i = 0; ids[i]; i++) { if (j >= UINT_MAX / sizeof(ProcessField)) continue; if (j >= LAST_PROCESSFIELD) { - settings->fields = xRealloc(settings->fields, j * sizeof(ProcessField)); - memset(&settings->fields[j], 0, sizeof(ProcessField)); - } - - // Dynamically-defined columns are always stored by-name. - char dynamic[32] = {0}; - if (sscanf(ids[i], "Dynamic(%30s)", dynamic)) { - char* end; - if ((end = strrchr(dynamic, ')')) == NULL) - continue; - *end = '\0'; - unsigned int key; - if (!DynamicColumn_search(settings->dynamicColumns, dynamic, &key)) - continue; - settings->fields[j++] = key; - continue; - } - // This "+1" is for compatibility with the older enum format. - int id = atoi(ids[i]) + 1; - if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) { - settings->flags |= Process_fields[id].flags; - settings->fields[j++] = id; + ss->fields = xRealloc(ss->fields, (j+1) * sizeof(ProcessField)); + memset(&ss->fields[j], 0, sizeof(ProcessField)); } + int id = toFieldIndex(columns, ids[i]); + if (id >= 0) + ss->fields[j] = id; + if (id > 0 && id < LAST_PROCESSFIELD) + ss->flags |= Process_fields[id].flags; + j++; } - settings->fields[j] = NULL_PROCESSFIELD; String_freeArray(ids); } +ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line) { + ScreenSettings* ss = xCalloc(sizeof(ScreenSettings), 1); + ss->name = xStrdup(name); + ss->fields = xCalloc(LAST_PROCESSFIELD, sizeof(ProcessField)); + ss->flags = 0; + ss->direction = 1; + ss->treeDirection = 1; + ss->treeSortKey = 1; + ss->treeView = false; + ss->treeViewAlwaysByPID = false; + ss->allBranchesCollapsed = false; + ScreenSettings_readFields(ss, this->dynamicColumns, line); + this->screens[this->nScreens] = ss; + this->nScreens++; + this->screens = xRealloc(this->screens, sizeof(ScreenSettings*) * (this->nScreens + 1)); + this->screens[this->nScreens] = NULL; + return ss; +} + +static void Settings_defaultScreens(Settings* this) { + for (unsigned int i = 0; i < Platform_numberOfDefaultScreens; i++) { + ScreenDefaults* defaults = &Platform_defaultScreens[i]; + Settings_newScreen(this, defaults->name, defaults->columns); + this->screens[i]->sortKey = toFieldIndex(this->dynamicColumns, defaults->sortKey); + } +} + static bool Settings_read(Settings* this, const char* fileName, unsigned int initialCpuCount) { FILE* fd = fopen(fileName, "r"); if (!fd) @@ -220,23 +320,33 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini return false; } } else if (String_eq(option[0], "fields")) { - Settings_readFields(this, option[1]); + // old (no screen) naming also supported for backwards compatibility + if (!this->nScreens) + Settings_defaultScreens(this); + ScreenSettings_readFields(this->screens[0], this->dynamicColumns, option[1]); } else if (String_eq(option[0], "sort_key")) { + // old (no screen) naming also supported for backwards compatibility // This "+1" is for compatibility with the older enum format. - this->sortKey = atoi(option[1]) + 1; + this->screens[0]->sortKey = atoi(option[1]) + 1; } else if (String_eq(option[0], "tree_sort_key")) { + // old (no screen) naming also supported for backwards compatibility // This "+1" is for compatibility with the older enum format. - this->treeSortKey = atoi(option[1]) + 1; + this->screens[0]->treeSortKey = atoi(option[1]) + 1; } else if (String_eq(option[0], "sort_direction")) { - this->direction = atoi(option[1]); + // old (no screen) naming also supported for backwards compatibility + this->screens[0]->direction = atoi(option[1]); } else if (String_eq(option[0], "tree_sort_direction")) { - this->treeDirection = atoi(option[1]); + // old (no screen) naming also supported for backwards compatibility + this->screens[0]->treeDirection = atoi(option[1]); } else if (String_eq(option[0], "tree_view")) { - this->treeView = atoi(option[1]); + // old (no screen) naming also supported for backwards compatibility + this->screens[0]->treeView = atoi(option[1]); } else if (String_eq(option[0], "tree_view_always_by_pid")) { - this->treeViewAlwaysByPID = atoi(option[1]); + // old (no screen) naming also supported for backwards compatibility + this->screens[0]->treeViewAlwaysByPID = atoi(option[1]); } else if (String_eq(option[0], "all_branches_collapsed")) { - this->allBranchesCollapsed = atoi(option[1]); + // old (no screen) naming also supported for backwards compatibility + this->screens[0]->allBranchesCollapsed = atoi(option[1]); } else if (String_eq(option[0], "hide_kernel_threads")) { this->hideKernelThreads = atoi(option[1]); } else if (String_eq(option[0], "hide_userland_threads")) { @@ -267,6 +377,8 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini this->showMergedCommand = atoi(option[1]); } else if (String_eq(option[0], "header_margin")) { this->headerMargin = atoi(option[1]); + } else if (String_eq(option[0], "screen_tabs")) { + this->screenTabs = atoi(option[1]); } else if (String_eq(option[0], "expand_system_time")) { // Compatibility option. this->detailedCPUTime = atoi(option[1]); @@ -332,23 +444,45 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini } else if (String_eq(option[0], "topology_affinity")) { this->topologyAffinity = !!atoi(option[1]); #endif + } else if (strncmp(option[0], "screen:", 7) == 0) { + Settings_newScreen(this, option[0] + 7, option[1]); + } else if (String_eq(option[0], ".sort_key")) { + if (this->nScreens) + this->screens[this->nScreens - 1]->sortKey = toFieldIndex(this->dynamicColumns, option[1]); + } else if (String_eq(option[0], ".tree_sort_key")) { + if (this->nScreens) + this->screens[this->nScreens - 1]->treeSortKey = toFieldIndex(this->dynamicColumns, option[1]); + } else if (String_eq(option[0], ".sort_direction")) { + if (this->nScreens) + this->screens[this->nScreens - 1]->direction = atoi(option[1]); + } else if (String_eq(option[0], ".tree_sort_direction")) { + if (this->nScreens) + this->screens[this->nScreens - 1]->treeDirection = atoi(option[1]); + } else if (String_eq(option[0], ".tree_view")) { + if (this->nScreens) + this->screens[this->nScreens - 1]->treeView = atoi(option[1]); + } else if (String_eq(option[0], ".tree_view_always_by_pid")) { + if (this->nScreens) + this->screens[this->nScreens - 1]->treeViewAlwaysByPID = atoi(option[1]); + } else if (String_eq(option[0], ".all_branches_collapsed")) { + if (this->nScreens) + this->screens[this->nScreens - 1]->allBranchesCollapsed = atoi(option[1]); } String_freeArray(option); } fclose(fd); - if (!didReadMeters || !Settings_validateMeters(this)) { + if (!didReadMeters || !Settings_validateMeters(this)) Settings_defaultMeters(this, initialCpuCount); - } + if (!this->nScreens) + Settings_defaultScreens(this); return didReadAny; } -static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns, const char* name, char separator) { - fprintf(fd, "%s=", name); +static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns, bool byName, char separator) { const char* sep = ""; for (unsigned int i = 0; fields[i]; i++) { - if (fields[i] >= LAST_PROCESSFIELD) { - const DynamicColumn* column = DynamicColumn_lookup(columns, fields[i]); - fprintf(fd, "%sDynamic(%s)", sep, column->name); + if (fields[i] >= LAST_PROCESSFIELD || byName) { + fprintf(fd, "%s%s", sep, toFieldName(columns, i)); } else { // This "-1" is for compatibility with the older enum format. fprintf(fd, "%s%d", sep, (int) fields[i] - 1); @@ -358,15 +492,19 @@ static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns fputc(separator, fd); } -static void writeMeters(const Settings* this, FILE* fd, char separator, unsigned int column) { +static void writeList(FILE* fd, char** list, int len, char separator) { const char* sep = ""; - for (size_t i = 0; i < this->hColumns[column].len; i++) { - fprintf(fd, "%s%s", sep, this->hColumns[column].names[i]); + for (int i = 0; i < len; i++) { + fprintf(fd, "%s%s", sep, list[i]); sep = " "; } fputc(separator, fd); } +static void writeMeters(const Settings* this, FILE* fd, char separator, unsigned int column) { + writeList(fd, this->hColumns[column].names, this->hColumns[column].len, separator); +} + static void writeMeterModes(const Settings* this, FILE* fd, char separator, unsigned int column) { const char* sep = ""; for (size_t i = 0; i < this->hColumns[column].len; i++) { @@ -400,12 +538,7 @@ int Settings_write(const Settings* this, bool onCrash) { } printSettingString("htop_version", VERSION); printSettingInteger("config_reader_min_version", CONFIG_READER_MIN_VERSION); - writeFields(fd, this->fields, this->dynamicColumns, "fields", separator); - // This "-1" is for compatibility with the older enum format. - printSettingInteger("sort_key", this->sortKey - 1); - printSettingInteger("sort_direction", this->direction); - printSettingInteger("tree_sort_key", this->treeSortKey - 1); - printSettingInteger("tree_sort_direction", this->treeDirection); + fprintf(fd, "fields="); writeFields(fd, this->screens[0]->fields, this->dynamicColumns, false, separator); printSettingInteger("hide_kernel_threads", this->hideKernelThreads); printSettingInteger("hide_userland_threads", this->hideUserlandThreads); printSettingInteger("shadow_other_users", this->shadowOtherUsers); @@ -420,10 +553,8 @@ int Settings_write(const Settings* this, bool onCrash) { printSettingInteger("find_comm_in_cmdline", this->findCommInCmdline); printSettingInteger("strip_exe_from_cmdline", this->stripExeFromCmdline); printSettingInteger("show_merged_command", this->showMergedCommand); - printSettingInteger("tree_view", this->treeView); - printSettingInteger("tree_view_always_by_pid", this->treeViewAlwaysByPID); - printSettingInteger("all_branches_collapsed", this->allBranchesCollapsed); printSettingInteger("header_margin", this->headerMargin); + printSettingInteger("screen_tabs", this->screenTabs); printSettingInteger("detailed_cpu_time", this->detailedCPUTime); printSettingInteger("cpu_count_from_one", this->countCPUsFromOne); printSettingInteger("show_cpu_usage", this->showCPUUsage); @@ -452,6 +583,31 @@ int Settings_write(const Settings* this, bool onCrash) { writeMeterModes(this, fd, separator, i); } + // Legacy compatibility with older versions of htop + printSettingInteger("tree_view", this->screens[0]->treeView); + // This "-1" is for compatibility with the older enum format. + printSettingInteger("sort_key", this->screens[0]->sortKey - 1); + printSettingInteger("tree_sort_key", this->screens[0]->treeSortKey - 1); + printSettingInteger("sort_direction", this->screens[0]->direction); + printSettingInteger("tree_sort_direction", this->screens[0]->treeDirection); + printSettingInteger("tree_view_always_by_pid", this->screens[0]->treeViewAlwaysByPID); + printSettingInteger("all_branches_collapsed", this->screens[0]->allBranchesCollapsed); + + if (this->screens && this->screens[0]) { + for (unsigned int i = 0; i < this->nScreens; i++) { + ScreenSettings* ss = this->screens[i]; + fprintf(fd, "screen:%s=", ss->name); + writeFields(fd, ss->fields, this->dynamicColumns, true, separator); + printSettingString(".sort_key", toFieldName(this->dynamicColumns, ss->sortKey)); + printSettingString(".tree_sort_key", toFieldName(this->dynamicColumns, ss->treeSortKey)); + printSettingInteger(".tree_view", ss->treeView); + printSettingInteger(".tree_view_always_by_pid", ss->treeViewAlwaysByPID); + printSettingInteger(".sort_direction", ss->direction); + printSettingInteger(".tree_sort_direction", ss->treeDirection); + printSettingInteger(".all_branches_collapsed", ss->allBranchesCollapsed); + } + } + #undef printSettingString #undef printSettingInteger @@ -475,16 +631,11 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) this->dynamicColumns = dynamicColumns; this->hLayout = HF_TWO_50_50; this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting)); - this->sortKey = PERCENT_CPU; - this->treeSortKey = PID; - this->direction = -1; - this->treeDirection = 1; + this->shadowOtherUsers = false; this->showThreadNames = false; this->hideKernelThreads = true; this->hideUserlandThreads = false; - this->treeView = false; - this->allBranchesCollapsed = false; this->highlightBaseName = false; this->highlightDeletedExe = true; this->highlightMegabytes = true; @@ -506,18 +657,13 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) this->showMergedCommand = false; this->hideFunctionBar = 0; this->headerMargin = true; + this->screenTabs = true; #ifdef HAVE_LIBHWLOC this->topologyAffinity = false; #endif - this->fields = xCalloc(LAST_PROCESSFIELD + 1, sizeof(ProcessField)); - // TODO: turn 'fields' into a Vector, - // (and ProcessFields into proper objects). - this->flags = 0; - const ProcessField* defaults = Platform_defaultFields; - for (int i = 0; defaults[i]; i++) { - this->fields[i] = defaults[i]; - this->flags |= Process_fields[defaults[i]].flags; - } + + this->screens = xCalloc(Platform_numberOfDefaultScreens * sizeof(ScreenSettings*), 1); + this->nScreens = 0; char* legacyDotfile = NULL; const char* rcfile = getenv("HTOPRC"); @@ -578,16 +724,21 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) } if (!ok) { Settings_defaultMeters(this, initialCpuCount); + Settings_defaultScreens(this); } + + this->ssIndex = 0; + this->ss = this->screens[this->ssIndex]; + return this; } -void Settings_invertSortOrder(Settings* this) { +void ScreenSettings_invertSortOrder(ScreenSettings* this) { int* attr = (this->treeView) ? &(this->treeDirection) : &(this->direction); *attr = (*attr == 1) ? -1 : 1; } -void Settings_setSortKey(Settings* this, ProcessField sortKey) { +void ScreenSettings_setSortKey(ScreenSettings* this, ProcessField sortKey) { if (this->treeViewAlwaysByPID || !this->treeView) { this->sortKey = sortKey; this->direction = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1; diff --git a/Settings.h b/Settings.h index a1f70402..2ca8034d 100644 --- a/Settings.h +++ b/Settings.h @@ -19,7 +19,13 @@ in the source distribution for its full text. #define DEFAULT_DELAY 15 -#define CONFIG_READER_MIN_VERSION 2 +#define CONFIG_READER_MIN_VERSION 3 + +typedef struct { + const char* name; + const char* columns; + const char* sortKey; +} ScreenDefaults; typedef struct { size_t len; @@ -27,6 +33,19 @@ typedef struct { int* modes; } MeterColumnSetting; +typedef struct { + char* name; + ProcessField* fields; + uint32_t flags; + int direction; + int treeDirection; + ProcessField sortKey; + ProcessField treeSortKey; + bool treeView; + bool treeViewAlwaysByPID; + bool allBranchesCollapsed; +} ScreenSettings; + typedef struct Settings_ { char* filename; int config_version; @@ -34,16 +53,14 @@ typedef struct Settings_ { MeterColumnSetting* hColumns; Hashtable* dynamicColumns; - ProcessField* fields; - uint32_t flags; + ScreenSettings** screens; + unsigned int nScreens; + unsigned int ssIndex; + ScreenSettings* ss; + int colorScheme; int delay; - int direction; - int treeDirection; - ProcessField sortKey; - ProcessField treeSortKey; - bool countCPUsFromOne; bool detailedCPUTime; bool showCPUUsage; @@ -52,9 +69,6 @@ typedef struct Settings_ { bool showCPUTemperature; bool degreeFahrenheit; #endif - bool treeView; - bool treeViewAlwaysByPID; - bool allBranchesCollapsed; bool showProgramPath; bool shadowOtherUsers; bool showThreadNames; @@ -72,6 +86,7 @@ typedef struct Settings_ { bool updateProcessNames; bool accountGuestInCPUMeter; bool headerMargin; + bool screenTabs; #ifdef HAVE_GETMOUSE bool enableMouse; #endif @@ -85,13 +100,13 @@ typedef struct Settings_ { #define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu)) -static inline ProcessField Settings_getActiveSortKey(const Settings* this) { +static inline ProcessField ScreenSettings_getActiveSortKey(const ScreenSettings* this) { return (this->treeView) ? (this->treeViewAlwaysByPID ? PID : this->treeSortKey) : this->sortKey; } -static inline int Settings_getActiveDirection(const Settings* this) { +static inline int ScreenSettings_getActiveDirection(const ScreenSettings* this) { return this->treeView ? this->treeDirection : this->direction; } @@ -101,9 +116,11 @@ int Settings_write(const Settings* this, bool onCrash); Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns); -void Settings_invertSortOrder(Settings* this); +ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line); -void Settings_setSortKey(Settings* this, ProcessField sortKey); +void ScreenSettings_invertSortOrder(ScreenSettings* this); + +void ScreenSettings_setSortKey(ScreenSettings* this, ProcessField sortKey); void Settings_enableReadonly(void); diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index 95ae960f..231ca143 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -322,7 +322,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, DarwinProcess_updateExe(ep->p_pid, proc); DarwinProcess_updateCmdLine(ps, proc); - if (proc->settings->flags & PROCESS_FLAG_CWD) { + if (proc->settings->ss->flags & PROCESS_FLAG_CWD) { DarwinProcess_updateCwd(ep->p_pid, proc); } } diff --git a/darwin/Platform.c b/darwin/Platform.c index 152f617b..7fda8309 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -49,6 +49,16 @@ in the source distribution for its full text. #endif +ScreenDefaults Platform_defaultScreens[] = { + { + .name = "Main", + .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME COMM", + .sortKey = "PERCENT_CPU", + }, +}; + +const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens); + const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const SignalItem Platform_signals[] = { diff --git a/darwin/Platform.h b/darwin/Platform.h index fe75db06..0022478f 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -26,7 +26,9 @@ in the source distribution for its full text. #include "generic/uname.h" -extern const ProcessField Platform_defaultFields[]; +extern ScreenDefaults Platform_defaultScreens[]; + +extern const unsigned int Platform_numberOfDefaultScreens; extern const SignalItem Platform_signals[]; diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c index 86586a83..1dec068f 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.c +++ b/dragonflybsd/DragonFlyBSDProcessList.c @@ -481,7 +481,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { DragonFlyBSDProcessList_updateExe(kproc, proc); DragonFlyBSDProcessList_updateProcessName(dfpl->kd, kproc, proc); - if (settings->flags & PROCESS_FLAG_CWD) { + if (settings->ss->flags & PROCESS_FLAG_CWD) { DragonFlyBSDProcessList_updateCwd(kproc, proc); } diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 49414451..6672a776 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -32,8 +32,15 @@ in the source distribution for its full text. #include "dragonflybsd/DragonFlyBSDProcess.h" #include "dragonflybsd/DragonFlyBSDProcessList.h" +ScreenDefaults Platform_defaultScreens[] = { + { + .name = "Main", + .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME COMM", + .sortKey = "PERCENT_CPU", + }, +}; -const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; +const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens); const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h index 281a7ee0..f40d5a83 100644 --- a/dragonflybsd/Platform.h +++ b/dragonflybsd/Platform.h @@ -29,7 +29,9 @@ in the source distribution for its full text. #include "generic/uname.h" -extern const ProcessField Platform_defaultFields[]; +extern ScreenDefaults Platform_defaultScreens[]; + +extern const unsigned int Platform_numberOfDefaultScreens; extern const SignalItem Platform_signals[]; diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index 507f480a..a4b6b837 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -516,7 +516,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { FreeBSDProcessList_updateExe(kproc, proc); FreeBSDProcessList_updateProcessName(fpl->kd, kproc, proc); - if (settings->flags & PROCESS_FLAG_CWD) { + if (settings->ss->flags & PROCESS_FLAG_CWD) { FreeBSDProcessList_updateCwd(kproc, proc); } diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 6c82bc24..eff30855 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -50,8 +50,15 @@ in the source distribution for its full text. #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" +ScreenDefaults Platform_defaultScreens[] = { + { + .name = "Main", + .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME COMM", + .sortKey = "PERCENT_CPU", + }, +}; -const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; +const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens); const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, diff --git a/freebsd/Platform.h b/freebsd/Platform.h index a1aa31bc..f5ab27df 100644 --- a/freebsd/Platform.h +++ b/freebsd/Platform.h @@ -25,7 +25,9 @@ in the source distribution for its full text. #include "generic/uname.h" -extern const ProcessField Platform_defaultFields[]; +extern ScreenDefaults Platform_defaultScreens[]; + +extern const unsigned int Platform_numberOfDefaultScreens; extern const SignalItem Platform_signals[]; diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 3bfe7db5..a055c13e 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -1370,6 +1370,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ ProcessList* pl = (ProcessList*) this; const struct dirent* entry; const Settings* settings = pl->settings; + const ScreenSettings* ss = settings->ss; #ifdef HAVE_OPENAT int dirFd = openat(parentFd, dirname, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); @@ -1463,7 +1464,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ continue; } - if (settings->flags & PROCESS_FLAG_IO) + if (ss->flags & PROCESS_FLAG_IO) LinuxProcessList_readIoFile(lp, procFd, pl->realtimeMs); if (!LinuxProcessList_readStatmFile(lp, procFd)) @@ -1472,7 +1473,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ { bool prev = proc->usesDeletedLib; - if ((settings->flags & PROCESS_FLAG_LINUX_LRS_FIX) || + if ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && !proc->isKernelThread && !proc->isUserlandThread)) { // Check if we really should recalculate the M_LRS value for this process uint64_t passedTimeInMs = pl->realtimeMs - lp->last_mlrs_calctime; @@ -1481,7 +1482,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ if (passedTimeInMs > recheck) { lp->last_mlrs_calctime = pl->realtimeMs; - LinuxProcessList_readMaps(lp, procFd, settings->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe); + LinuxProcessList_readMaps(lp, procFd, ss->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe); } } else { /* Copy from process structure in threads and reset if setting got disabled */ @@ -1491,7 +1492,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ proc->mergedCommand.exeChanged |= prev ^ proc->usesDeletedLib; } - if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) { + if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) { if (!parent) { // Read smaps file of each process only every second pass to improve performance static int smaps_flag = 0; @@ -1521,7 +1522,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ proc->tty_name = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr); } - if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO) { + if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO) { LinuxProcess_updateIOPriority(lp); } @@ -1536,13 +1537,13 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ if (!preExisting) { #ifdef HAVE_OPENVZ - if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) { + if (ss->flags & PROCESS_FLAG_LINUX_OPENVZ) { LinuxProcessList_readOpenVZData(lp, procFd); } #endif #ifdef HAVE_VSERVER - if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) { + if (ss->flags & PROCESS_FLAG_LINUX_VSERVER) { LinuxProcessList_readVServerData(lp, procFd); } #endif @@ -1567,32 +1568,32 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } #ifdef HAVE_DELAYACCT - if (settings->flags & PROCESS_FLAG_LINUX_DELAYACCT) { + if (ss->flags & PROCESS_FLAG_LINUX_DELAYACCT) { LinuxProcessList_readDelayAcctData(this, lp); } #endif - if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) { + if (ss->flags & PROCESS_FLAG_LINUX_CGROUP) { LinuxProcessList_readCGroupFile(lp, procFd); } - if (settings->flags & PROCESS_FLAG_LINUX_OOM) { + if (ss->flags & PROCESS_FLAG_LINUX_OOM) { LinuxProcessList_readOomData(lp, procFd); } - if (settings->flags & PROCESS_FLAG_LINUX_CTXT) { + if (ss->flags & PROCESS_FLAG_LINUX_CTXT) { LinuxProcessList_readCtxtData(lp, procFd); } - if (settings->flags & PROCESS_FLAG_LINUX_SECATTR) { + if (ss->flags & PROCESS_FLAG_LINUX_SECATTR) { LinuxProcessList_readSecattrData(lp, procFd); } - if (settings->flags & PROCESS_FLAG_CWD) { + if (ss->flags & PROCESS_FLAG_CWD) { LinuxProcessList_readCwd(lp, procFd); } - if ((settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP) && this->haveAutogroup) { + if ((ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) && this->haveAutogroup) { LinuxProcessList_readAutogroup(lp, procFd); } @@ -2146,7 +2147,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { return; } - if (settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP) { + if (settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) { // Refer to sched(7) 'autogroup feature' section // The kernel feature can be enabled/disabled through procfs at // any time, so check for it at the start of each sample - only diff --git a/linux/Platform.c b/linux/Platform.c index 93953e86..1e412be0 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -79,7 +79,20 @@ enum CapMode { }; #endif -const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; +ScreenDefaults Platform_defaultScreens[] = { + { + .name = "Main", + .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME COMM", + .sortKey = "PERCENT_CPU", + }, + { + .name = "I/O", + .columns = "PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY COMM", + .sortKey = "IO_RATE", + }, +}; + +const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens); const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, diff --git a/linux/Platform.h b/linux/Platform.h index 2e2fb3e6..5c16e70f 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -38,7 +38,9 @@ in the source distribution for its full text. #endif -extern const ProcessField Platform_defaultFields[]; +extern ScreenDefaults Platform_defaultScreens[]; + +extern const unsigned int Platform_numberOfDefaultScreens; extern const SignalItem Platform_signals[]; diff --git a/netbsd/NetBSDProcessList.c b/netbsd/NetBSDProcessList.c index ab0f0b75..22124fe9 100644 --- a/netbsd/NetBSDProcessList.c +++ b/netbsd/NetBSDProcessList.c @@ -307,7 +307,7 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { } } - if (settings->flags & PROCESS_FLAG_CWD) { + if (settings->ss->flags & PROCESS_FLAG_CWD) { NetBSDProcessList_updateCwd(kproc, proc); } diff --git a/netbsd/Platform.c b/netbsd/Platform.c index 3b27548d..724ab0de 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -68,6 +68,16 @@ in the source distribution for its full text. const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; +ScreenDefaults Platform_defaultScreens[] = { + { + .name = "Main", + .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME COMM", + .sortKey = "PERCENT_CPU", + }, +}; + +const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens); + /* * See /usr/include/sys/signal.h */ diff --git a/netbsd/Platform.h b/netbsd/Platform.h index e650bcda..407a56f9 100644 --- a/netbsd/Platform.h +++ b/netbsd/Platform.h @@ -36,6 +36,10 @@ in the source distribution for its full text. extern const ProcessField Platform_defaultFields[]; +extern ScreenDefaults Platform_defaultScreens[]; + +extern const unsigned int Platform_numberOfDefaultScreens; + /* see /usr/include/sys/signal.h */ extern const SignalItem Platform_signals[]; diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c index af7879e3..9a21b225 100644 --- a/openbsd/OpenBSDProcessList.c +++ b/openbsd/OpenBSDProcessList.c @@ -309,7 +309,7 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { OpenBSDProcessList_updateProcessName(this->kd, kproc, proc); - if (settings->flags & PROCESS_FLAG_CWD) { + if (settings->ss->flags & PROCESS_FLAG_CWD) { OpenBSDProcessList_updateCwd(kproc, proc); } diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 15467e13..b8e7ce91 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -46,7 +46,15 @@ in the source distribution for its full text. #include "openbsd/OpenBSDProcessList.h" -const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; +ScreenDefaults Platform_defaultScreens[] = { + { + .name = "Main", + .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME COMM", + .sortKey = "PERCENT_CPU", + }, +}; + +const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens); /* * See /usr/include/sys/signal.h diff --git a/openbsd/Platform.h b/openbsd/Platform.h index fd6a6575..b3612cc6 100644 --- a/openbsd/Platform.h +++ b/openbsd/Platform.h @@ -26,7 +26,9 @@ in the source distribution for its full text. #include "generic/uname.h" -extern const ProcessField Platform_defaultFields[]; +extern ScreenDefaults Platform_defaultScreens[]; + +extern const unsigned int Platform_numberOfDefaultScreens; /* see /usr/include/sys/signal.h */ extern const SignalItem Platform_signals[]; diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index cae097fd..0664a162 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -384,12 +384,12 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, continue; } - if (settings->flags & PROCESS_FLAG_IO) + if (settings->ss->flags & PROCESS_FLAG_IO) PCPProcessList_updateIO(pp, pid, offset, now); PCPProcessList_updateMemory(pp, pid, offset); - if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && + if ((settings->ss->flags & PROCESS_FLAG_LINUX_SMAPS) && (Process_isKernelThread(proc) == false)) { if (PCPMetric_enabled(PCP_PROC_SMAPS_PSS)) PCPProcessList_updateSmaps(pp, pid, offset); @@ -419,22 +419,22 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, PCPProcessList_updateCmdline(proc, pid, offset, command); } - if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) + if (settings->ss->flags & PROCESS_FLAG_LINUX_CGROUP) PCPProcessList_readCGroups(pp, pid, offset); - if (settings->flags & PROCESS_FLAG_LINUX_OOM) + if (settings->ss->flags & PROCESS_FLAG_LINUX_OOM) PCPProcessList_readOomData(pp, pid, offset); - if (settings->flags & PROCESS_FLAG_LINUX_CTXT) + if (settings->ss->flags & PROCESS_FLAG_LINUX_CTXT) PCPProcessList_readCtxtData(pp, pid, offset); - if (settings->flags & PROCESS_FLAG_LINUX_SECATTR) + if (settings->ss->flags & PROCESS_FLAG_LINUX_SECATTR) PCPProcessList_readSecattrData(pp, pid, offset); - if (settings->flags & PROCESS_FLAG_CWD) + if (settings->ss->flags & PROCESS_FLAG_CWD) PCPProcessList_readCwd(pp, pid, offset); - if (settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP) + if (settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) PCPProcessList_readAutogroup(pp, pid, offset); if (proc->state == ZOMBIE && !proc->cmdline && command[0]) { @@ -676,16 +676,16 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { for (int metric = PCP_PROC_PID; metric < PCP_METRIC_COUNT; metric++) PCPMetric_enable(metric, enabled); - flagged = settings->flags & PROCESS_FLAG_LINUX_CGROUP; + flagged = settings->ss->flags & PROCESS_FLAG_LINUX_CGROUP; PCPMetric_enable(PCP_PROC_CGROUPS, flagged && enabled); - flagged = settings->flags & PROCESS_FLAG_LINUX_OOM; + flagged = settings->ss->flags & PROCESS_FLAG_LINUX_OOM; PCPMetric_enable(PCP_PROC_OOMSCORE, flagged && enabled); - flagged = settings->flags & PROCESS_FLAG_LINUX_CTXT; + flagged = settings->ss->flags & PROCESS_FLAG_LINUX_CTXT; PCPMetric_enable(PCP_PROC_VCTXSW, flagged && enabled); PCPMetric_enable(PCP_PROC_NVCTXSW, flagged && enabled); - flagged = settings->flags & PROCESS_FLAG_LINUX_SECATTR; + flagged = settings->ss->flags & PROCESS_FLAG_LINUX_SECATTR; PCPMetric_enable(PCP_PROC_LABELS, flagged && enabled); - flagged = settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP; + flagged = settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP; PCPMetric_enable(PCP_PROC_AUTOGROUP_ID, flagged && enabled); PCPMetric_enable(PCP_PROC_AUTOGROUP_NICE, flagged && enabled); diff --git a/pcp/Platform.c b/pcp/Platform.c index 150660af..f6de7d9c 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -54,6 +54,21 @@ in the source distribution for its full text. Platform* pcp; +ScreenDefaults Platform_defaultScreens[] = { + { + .name = "Main", + .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME COMM", + .sortKey = "PERCENT_CPU", + }, + { + .name = "I/O", + .columns = "PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY COMM", + .sortKey = "IO_RATE", + }, +}; + +const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens); + ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, (int)M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; int Platform_numberOfFields = LAST_PROCESSFIELD; diff --git a/pcp/Platform.h b/pcp/Platform.h index ad38cbbd..f080df28 100644 --- a/pcp/Platform.h +++ b/pcp/Platform.h @@ -58,6 +58,10 @@ typedef struct Platform_ { unsigned int ncpu; /* maximum processor count configured */ } Platform; +extern ScreenDefaults Platform_defaultScreens[]; + +extern const unsigned int Platform_numberOfDefaultScreens; + extern ProcessField Platform_defaultFields[]; extern int Platform_numberOfFields; diff --git a/solaris/Platform.c b/solaris/Platform.c index cdc79ae4..15917c94 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -40,6 +40,16 @@ in the source distribution for its full text. #include "SolarisProcessList.h" +ScreenDefaults Platform_defaultScreens[] = { + { + .name = "Default", + .columns = "PID LWPID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME COMM", + .sortKey = "PERCENT_CPU", + }, +}; + +const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens); + const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, { .name = " 1 SIGHUP", .number = 1 }, @@ -87,8 +97,6 @@ const SignalItem Platform_signals[] = { const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); -const ProcessField Platform_defaultFields[] = { PID, LWPID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; - const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, diff --git a/solaris/Platform.h b/solaris/Platform.h index b140788d..6bd8179b 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -52,12 +52,14 @@ typedef struct envAccum_ { char* env; } envAccum; +extern ScreenDefaults Platform_defaultScreens[]; + +extern const unsigned int Platform_numberOfDefaultScreens; + extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; -extern const ProcessField Platform_defaultFields[]; - extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c index 71e85fcb..3e006fcb 100644 --- a/solaris/SolarisProcessList.c +++ b/solaris/SolarisProcessList.c @@ -451,7 +451,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, Process_updateComm(proc, _psinfo->pr_fname); Process_updateCmdline(proc, _psinfo->pr_psargs, 0, 0); - if (proc->settings->flags & PROCESS_FLAG_CWD) { + if (proc->settings->ss->flags & PROCESS_FLAG_CWD) { SolarisProcessList_updateCwd(_psinfo->pr_pid, proc); } } diff --git a/unsupported/Platform.c b/unsupported/Platform.c index e55de4ab..db484511 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -27,14 +27,22 @@ in the source distribution for its full text. #include "UptimeMeter.h" +const ScreenDefaults Platform_defaultScreens[] = { + { + .name = "Main", + .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME COMM", + .sortKey = "PERCENT_CPU", + }, +}; + +const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens); + const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, }; const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); -const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; - const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, diff --git a/unsupported/Platform.h b/unsupported/Platform.h index 7f4ad9ab..5c874d48 100644 --- a/unsupported/Platform.h +++ b/unsupported/Platform.h @@ -20,12 +20,14 @@ in the source distribution for its full text. #include "unsupported/UnsupportedProcess.h" +extern const ScreenDefaults Platform_defaultScreens[]; + +extern const unsigned int Platform_numberOfDefaultScreens; + extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; -extern const ProcessField Platform_defaultFields[]; - extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c index b64de411..0fbe7032 100644 --- a/unsupported/UnsupportedProcessList.c +++ b/unsupported/UnsupportedProcessList.c @@ -51,7 +51,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { Process_updateCmdline(proc, "", 0, 0); Process_updateExe(proc, "/path/to/executable"); - if (proc->settings->flags & PROCESS_FLAG_CWD) { + if (proc->settings->ss->flags & PROCESS_FLAG_CWD) { free_and_xStrdup(&proc->procCwd, "/current/working/directory"); }