htop: support screens configuration
`htop` has changed the singular fields list to a configuration of multiple screens. Each screen has a name and its own set of fields to show, plus some options to control sorting and view style (tree or flat). Screen names can be shown with the `screen_tabs` setting. Fields in screens are set with their names (as strings) rather than numbers. Extend the htop module to allow configuring screens with a defined set of options. Order the screens with dag topology. Configurations without screens are converted automatically by `htop` on load. This feature was requested in #3616.
This commit is contained in:
parent
017b12de5b
commit
8e1ed125fe
|
@ -76,21 +76,6 @@ let
|
|||
M_PSSWP = 120;
|
||||
};
|
||||
|
||||
defaultFields = with fields; [
|
||||
PID
|
||||
USER
|
||||
PRIORITY
|
||||
NICE
|
||||
M_SIZE
|
||||
M_RESIDENT
|
||||
M_SHARE
|
||||
STATE
|
||||
PERCENT_CPU
|
||||
PERCENT_MEM
|
||||
TIME
|
||||
COMM
|
||||
];
|
||||
|
||||
modes = {
|
||||
Bar = 1;
|
||||
Text = 2;
|
||||
|
@ -106,6 +91,48 @@ let
|
|||
led = meter modes.LED;
|
||||
blank = text "Blank";
|
||||
|
||||
screenOptions = {
|
||||
name = mkOption { type = types.str; };
|
||||
|
||||
fields = mkOption {
|
||||
type = types.oneOf [ types.str (types.listOf types.str) ];
|
||||
default = "PID USER M_VIRT STATE PERCENT_CPU PERCENT_MEM TIME Command";
|
||||
};
|
||||
|
||||
all_branches_collapsed = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
sort_direction = mkOption {
|
||||
type = types.enum [ (-1) 1 ];
|
||||
default = -1;
|
||||
};
|
||||
sort_key = mkOption {
|
||||
type = types.str;
|
||||
example = "PERCENT_MEM";
|
||||
};
|
||||
|
||||
tree_sort_direction = mkOption {
|
||||
type = types.enum [ (-1) 1 ];
|
||||
default = -1;
|
||||
};
|
||||
tree_sort_key = mkOption {
|
||||
type = types.str;
|
||||
example = "PERCENT_MEM";
|
||||
};
|
||||
|
||||
tree_view = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
tree_view_always_by_pid = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
meta.maintainers = [ hm.maintainers.bjpbakker ];
|
||||
|
||||
|
@ -155,6 +182,38 @@ in {
|
|||
'';
|
||||
};
|
||||
|
||||
screens = mkOption {
|
||||
type = hm.types.dagOf (types.submodule ({ dagName, ... }: {
|
||||
options = screenOptions;
|
||||
config.name = mkDefault dagName;
|
||||
}));
|
||||
default = { };
|
||||
example = literalExpression ''
|
||||
{
|
||||
"Main" = {
|
||||
fields = "PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command";
|
||||
sort_key = "PERCENT_MEM";
|
||||
tree_sort_key = "PERCENT_MEM";
|
||||
tree_view = false;
|
||||
tree_view_always_by_pid = false;
|
||||
sort_direction = -1;
|
||||
tree_sort_direction = -1;
|
||||
all_branches_collapsed = false;
|
||||
};
|
||||
"I/O" = lib.hm.dag.entryAfter ["Main"] {
|
||||
fields = "PID STATE STARTTIME M_RESIDENT COMM EXE USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE";
|
||||
sort_key = "IO_RATE";
|
||||
tree_sort_key = "PID";
|
||||
tree_view = false;
|
||||
tree_view_always_by_pid = false;
|
||||
sort_direction = -1;
|
||||
tree_sort_direction = -1;
|
||||
all_branches_collapsed = false;
|
||||
};
|
||||
};
|
||||
'';
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.htop;
|
||||
|
@ -171,25 +230,55 @@ in {
|
|||
home.packages = [ cfg.package ];
|
||||
|
||||
xdg.configFile."htop/htoprc" = let
|
||||
defaults = {
|
||||
fields = if isDarwin then
|
||||
remove fields.M_SHARE defaultFields
|
||||
else
|
||||
defaultFields;
|
||||
};
|
||||
|
||||
before = optionalAttrs (cfg.settings ? header_layout) {
|
||||
inherit (cfg.settings) header_layout;
|
||||
};
|
||||
|
||||
settings = defaults // (removeAttrs cfg.settings (attrNames before));
|
||||
|
||||
formatOptions = mapAttrsToList formatOption;
|
||||
|
||||
in mkIf (cfg.settings != { }) {
|
||||
text =
|
||||
concatStringsSep "\n" (formatOptions before ++ formatOptions settings)
|
||||
+ "\n";
|
||||
hasScreens = cfg.screens != { };
|
||||
|
||||
settings = let
|
||||
# old (no screen) configuration support
|
||||
defaultFields = let
|
||||
defaults = with fields; [
|
||||
PID
|
||||
USER
|
||||
PRIORITY
|
||||
NICE
|
||||
M_SIZE
|
||||
M_RESIDENT
|
||||
M_SHARE
|
||||
STATE
|
||||
PERCENT_CPU
|
||||
PERCENT_MEM
|
||||
TIME
|
||||
COMM
|
||||
];
|
||||
in if isDarwin then remove fields.M_SHARE defaults else defaults;
|
||||
|
||||
oldDefaults = optionalAttrs (!hasScreens) { fields = defaultFields; };
|
||||
|
||||
leading = optionalAttrs (cfg.settings ? header_layout) {
|
||||
inherit (cfg.settings) header_layout;
|
||||
};
|
||||
|
||||
settings' = oldDefaults
|
||||
// (removeAttrs cfg.settings (attrNames leading));
|
||||
in formatOptions leading ++ formatOptions settings';
|
||||
|
||||
screens = let
|
||||
formatOption' = k: formatOption ".${k}";
|
||||
formatScreen = { name, fields, ... }@screen:
|
||||
let
|
||||
options = removeAttrs screen [ "fields" "name" ];
|
||||
newScreen = "screen:${formatOption name fields}";
|
||||
in [ newScreen ] ++ mapAttrsToList formatOption' options;
|
||||
|
||||
screens' = let sorted = hm.dag.topoSort cfg.screens;
|
||||
in sorted.result or (abort
|
||||
"Dependency cycle in htop screens: ${builtins.toJSON sorted}");
|
||||
|
||||
in concatMap (x: formatScreen x.data) screens';
|
||||
|
||||
in mkIf (cfg.settings != { } || hasScreens) {
|
||||
text = concatStringsSep "\n" (settings ++ screens) + "\n";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
htop-empty-settings = ./empty-settings.nix;
|
||||
htop-example-settings = ./example-settings.nix;
|
||||
htop-header_layout = ./header_layout.nix;
|
||||
htop-screens = ./screens.nix;
|
||||
htop-settings-without-fields = ./settings-without-fields.nix;
|
||||
}
|
||||
|
|
39
tests/modules/programs/htop/screens.nix
Normal file
39
tests/modules/programs/htop/screens.nix
Normal file
|
@ -0,0 +1,39 @@
|
|||
{ lib, ... }:
|
||||
|
||||
{
|
||||
programs.htop.enable = true;
|
||||
programs.htop.settings = { screen_tabs = true; };
|
||||
programs.htop.screens = {
|
||||
"Main" = {
|
||||
fields =
|
||||
"PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command";
|
||||
sort_key = "PERCENT_MEM";
|
||||
tree_sort_key = "PERCENT_MEM";
|
||||
tree_view = false;
|
||||
tree_view_always_by_pid = false;
|
||||
sort_direction = -1;
|
||||
tree_sort_direction = -1;
|
||||
all_branches_collapsed = false;
|
||||
};
|
||||
"I/O" = lib.hm.dag.entryAfter [ "Main" ] {
|
||||
fields =
|
||||
"PID STATE STARTTIME M_RESIDENT COMM EXE USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE";
|
||||
sort_key = "IO_RATE";
|
||||
tree_sort_key = "PID";
|
||||
tree_view = false;
|
||||
tree_view_always_by_pid = false;
|
||||
sort_direction = -1;
|
||||
tree_sort_direction = -1;
|
||||
all_branches_collapsed = false;
|
||||
};
|
||||
};
|
||||
|
||||
test.stubs.htop = { };
|
||||
|
||||
nmt.script = ''
|
||||
htoprc=home-files/.config/htop/htoprc
|
||||
assertFileExists $htoprc
|
||||
assertFileContent $htoprc ${./screens.txt}
|
||||
'';
|
||||
|
||||
}
|
17
tests/modules/programs/htop/screens.txt
Normal file
17
tests/modules/programs/htop/screens.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
screen_tabs=1
|
||||
screen:Main=PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command
|
||||
.all_branches_collapsed=0
|
||||
.sort_direction=-1
|
||||
.sort_key=PERCENT_MEM
|
||||
.tree_sort_direction=-1
|
||||
.tree_sort_key=PERCENT_MEM
|
||||
.tree_view=0
|
||||
.tree_view_always_by_pid=0
|
||||
screen:I/O=PID STATE STARTTIME M_RESIDENT COMM EXE USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE
|
||||
.all_branches_collapsed=0
|
||||
.sort_direction=-1
|
||||
.sort_key=IO_RATE
|
||||
.tree_sort_direction=-1
|
||||
.tree_sort_key=PID
|
||||
.tree_view=0
|
||||
.tree_view_always_by_pid=0
|
Loading…
Reference in a new issue