#![allow(non_snake_case)]
#![allow(non_camel_case_types)] #![allow(dead_code)]
use crate::ported::categoriespanel::CategoriesPanel_new;
use crate::ported::crt::{
CRT_enableDelay, CRT_setMouse, ColorElements, KEY_DOWN, KEY_F, KEY_RECLICK, KEY_SHIFT_TAB,
};
use crate::ported::commandscreen::{CommandScreen_delete, CommandScreen_new};
use crate::ported::processlocksscreen::{ProcessLocksScreen_delete, ProcessLocksScreen_new};
use crate::ported::envscreen::{EnvScreen_delete, EnvScreen_new};
use crate::ported::infoscreen::InfoScreen_run;
use crate::ported::openfilesscreen::{OpenFilesScreen_delete, OpenFilesScreen_new};
use crate::ported::tracescreen::{TraceScreen_delete, TraceScreen_forkTracer, TraceScreen_new};
use crate::ported::dynamiccolumn::DynamicColumn;
use crate::ported::functionbar::{FunctionBar_newEnterEsc, Ncurses};
use crate::ported::hashtable::Hashtable_get;
use crate::ported::header::{Header, Header_writeBackToSettings};
use crate::ported::incset::{IncSet_activate, IncSet_filter, IncSet_reset, IncType};
use crate::ported::linux::linuxprocess::{Process_fields, LAST_PROCESSFIELD};
use crate::ported::listitem::{ListItem, ListItem_getRef, ListItem_new};
use crate::ported::machine::{Machine, Machine_scanTables};
use crate::ported::mainpanel::{
MainPanel, MainPanel_foreachRow, MainPanel_selectedRow, MainPanel_setFunctionBar,
};
use crate::ported::object::{Arg, Object};
use crate::ported::panel::{
Panel, PanelClass, PanelItem, Panel_add, Panel_draw, Panel_getSelected, Panel_insert,
Panel_move, Panel_new, Panel_onKey, Panel_resize, Panel_setHeader, Panel_setSelected,
Panel_setSelectionColor, Panel_size,
};
use crate::ported::userstable::{UsersTable, UsersTable_foreach};
use crate::ported::process::{
Process, ProcessField, Process_rowChangePriorityBy, Process_rowSendSignal,
};
use crate::ported::signalspanel::{SignalsPanel_new, SIGNALSPANEL_INITSELECTEDSIGNAL};
use crate::ported::row::{Row, Row_getGroupOrParent, Row_isChildOf, Row_toggleTag};
use crate::ported::screenmanager::{
ScreenManager_add, ScreenManager_delete, ScreenManager_new, ScreenManager_remove,
ScreenManager_run,
};
use crate::ported::settings::{
RowField, ScreenSettings_getActiveSortKey, ScreenSettings_invertSortOrder,
ScreenSettings_setSortKey, Settings, Settings_isReadonly,
};
use crate::ported::table::{Table_collapseAllBranches, Table_expandTree};
use crate::ported::xutils::String_trim;
const ROW_DYNAMIC_FIELDS: i32 = LAST_PROCESSFIELD as i32;
const SCREEN_TAB_MARGIN_LEFT: i32 = 2;
const SCREEN_TAB_COLUMN_GAP: i32 = 1;
pub type Htop_Reaction = u32;
pub const HTOP_OK: Htop_Reaction = 0x00;
pub const HTOP_REFRESH: Htop_Reaction = 0x01;
pub const HTOP_RECALCULATE: Htop_Reaction = 0x02 | HTOP_REFRESH;
pub const HTOP_SAVE_SETTINGS: Htop_Reaction = 0x04;
pub const HTOP_KEEP_FOLLOWING: Htop_Reaction = 0x08;
pub const HTOP_QUIT: Htop_Reaction = 0x10;
pub const HTOP_REDRAW_BAR: Htop_Reaction = 0x20;
pub const HTOP_UPDATE_PANELHDR: Htop_Reaction = 0x40 | HTOP_REFRESH;
pub const HTOP_RESIZE: Htop_Reaction = 0x80 | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
pub type Htop_Action = fn(&mut State) -> Htop_Reaction;
pub struct State {
pub host: *mut Machine,
pub mainPanel: *mut MainPanel,
pub header: *mut Header,
pub failedUpdate: Option<String>,
pub pauseUpdate: bool,
pub hideSelection: bool,
pub hideMeters: bool,
}
pub fn Action_pickFromVector(
st: &mut State,
list: Box<dyn PanelClass>,
x: i32,
follow: bool,
) -> Option<Box<dyn Object>> {
let mainPanel = st.mainPanel;
let header = st.header;
let host = st.host;
let st_ptr: *mut State = st;
let y = unsafe { (*mainPanel).super_.y };
let mut scr = ScreenManager_new(header, host, st_ptr);
scr.allowFocusChange = false;
let mut list = list;
ScreenManager_add(&mut scr, list, x);
let mp_box: Box<MainPanel> = unsafe { Box::from_raw(mainPanel) };
ScreenManager_add(&mut scr, mp_box, -1);
let mut panelFocus: usize = 0;
let mut ch: i32 = 0;
let mut unfollow = false;
let row = if follow {
MainPanel_selectedRow(unsafe { &*mainPanel })
} else {
-1
};
if follow {
let at = unsafe {
(*host)
.activeTable
.expect("Action_pickFromVector: host->activeTable is NULL")
};
if unsafe { (*at).following } == -1 {
unsafe {
(*at).following = row;
}
unfollow = true;
}
}
ScreenManager_run(&mut scr, Some(&mut panelFocus), Some(&mut ch), None);
if unfollow {
let at = unsafe {
(*host)
.activeTable
.expect("Action_pickFromVector: host->activeTable is NULL")
};
unsafe {
(*at).following = -1;
}
}
let mp_reclaimed = ScreenManager_remove(&mut scr, 1);
let _leaked: *mut dyn PanelClass = Box::into_raw(mp_reclaimed);
list = ScreenManager_remove(&mut scr, 0);
ScreenManager_delete(scr);
Panel_move(unsafe { &mut (*mainPanel).super_ }, 0, y);
Panel_resize(
unsafe { &mut (*mainPanel).super_ },
Ncurses::cols(),
Ncurses::lines() - y - 1,
);
if panelFocus == 0 && ch == 13 {
let return_selection = if follow {
let matches = match Panel_getSelected(unsafe { &(*mainPanel).super_ }) {
Some(o) => {
let any: &dyn core::any::Any = o;
any.downcast_ref::<Row>().is_some_and(|r| r.id == row)
}
None => false,
};
matches
} else {
true
};
if return_selection {
let panel = list.as_panel_mut();
let sel = panel.selected;
if sel >= 0 && (sel as usize) < panel.items.len() {
if let PanelItem::Owned(b) = panel.items.remove(sel as usize) {
return Some(b);
}
}
}
}
None
}
pub fn Action_runSetup(st: &mut State) {
let host = st.host;
let header = st.header;
let st_ptr: *mut State = st;
let mut scr = ScreenManager_new(header, host, st_ptr);
CategoriesPanel_new(&mut scr, header, host);
ScreenManager_run(&mut scr, None, None, Some("Setup"));
ScreenManager_delete(scr);
let changed = unsafe {
(*host)
.settings
.as_ref()
.expect("Action_runSetup: host->settings is NULL")
.changed
};
if changed {
let enableMouse = unsafe { (*host).settings.as_ref().unwrap().enableMouse };
CRT_setMouse(enableMouse);
let settings = unsafe { (*host).settings.as_mut().unwrap() };
Header_writeBackToSettings(unsafe { &*header }, settings);
}
}
pub fn changePriority(panel: &mut MainPanel, delta: i32) -> bool {
let mut anyTagged = false;
let ok = MainPanel_foreachRow(
panel,
Process_rowChangePriorityBy,
Arg::I(delta),
Some(&mut anyTagged),
);
if !ok {
let mut out = std::io::stdout().lock();
Ncurses::beep(&mut out);
}
anyTagged
}
pub fn addUserToVector(key: i32, user: &str, panel: &mut Panel) {
Panel_add(panel, Box::new(ListItem_new(user, key)));
}
pub fn Action_setUserOnly(userName: &str, userId: &mut libc::uid_t) -> bool {
let c_userName = match std::ffi::CString::new(userName) {
Ok(s) => s,
Err(_) => {
*userId = libc::uid_t::MAX;
return false;
}
};
let user = unsafe { libc::getpwnam(c_userName.as_ptr()) };
if !user.is_null() {
*userId = unsafe { (*user).pw_uid };
return true;
}
*userId = libc::uid_t::MAX;
false
}
pub fn tagAllChildren(panel: &mut Panel, parent_idx: i32) {
let parent_id = {
let obj: &mut dyn Object = panel.items[parent_idx as usize].object_mut();
let any: &mut dyn core::any::Any = obj;
let parent = any
.downcast_mut::<Row>()
.expect("tagAllChildren operates on the mainPanel, whose items are Rows");
parent.tag = true;
parent.id
};
let size = Panel_size(panel);
for i in 0..size {
let recurse = {
let obj: &dyn Object = panel.items[i as usize].object();
let any: &dyn core::any::Any = obj;
let row = any
.downcast_ref::<Row>()
.expect("tagAllChildren operates on the mainPanel, whose items are Rows");
!row.tag && Row_isChildOf(row, parent_id)
};
if recurse {
tagAllChildren(panel, i);
}
}
}
pub fn expandCollapse(panel: &mut Panel) -> bool {
if panel.items.is_empty() {
return false;
}
let idx = panel.selected as usize;
let obj: &mut dyn Object = panel.items[idx].object_mut();
let any: &mut dyn core::any::Any = obj;
let row = any
.downcast_mut::<Row>()
.expect("expandCollapse operates on the mainPanel, whose items are Rows");
row.showChildren = !row.showChildren;
true
}
pub fn collapseIntoParent(panel: &mut Panel) -> bool {
if panel.items.is_empty() {
return false;
}
let parent_id = {
let obj: &dyn Object = panel.items[panel.selected as usize].object();
let any: &dyn core::any::Any = obj;
let r = any
.downcast_ref::<Row>()
.expect("collapseIntoParent operates on the mainPanel, whose items are Rows");
Row_getGroupOrParent(r)
};
let size = Panel_size(panel);
for i in 0..size {
let id = {
let obj: &dyn Object = panel.items[i as usize].object();
let any: &dyn core::any::Any = obj;
any.downcast_ref::<Row>()
.expect("collapseIntoParent operates on the mainPanel, whose items are Rows")
.id
};
if id == parent_id {
let obj: &mut dyn Object = panel.items[i as usize].object_mut();
let any: &mut dyn core::any::Any = obj;
any.downcast_mut::<Row>()
.expect("collapseIntoParent operates on the mainPanel, whose items are Rows")
.showChildren = false;
Panel_setSelected(panel, i);
return true;
}
}
false
}
pub fn Action_setSortKey(settings: &mut Settings, sortKey: RowField) -> Htop_Reaction {
ScreenSettings_setSortKey(&mut settings.screens[settings.ssIndex as usize], sortKey);
HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING
}
pub fn Action_writeableProcess(st: &State) -> bool {
let settings = unsafe {
(*st.host)
.settings
.as_ref()
.expect("Action_writeableProcess: host->settings is NULL")
};
let readonly = Settings_isReadonly()
|| settings.screens[settings.ssIndex as usize]
.dynamic
.is_some();
!readonly
}
pub fn Action_readableProcess(st: &State) -> bool {
let settings = unsafe {
(*st.host)
.settings
.as_ref()
.expect("Action_readableProcess: host->settings is NULL")
};
settings.screens[settings.ssIndex as usize]
.dynamic
.is_none()
}
pub fn actionSetSortColumn(st: &mut State) -> Htop_Reaction {
let mut reaction: Htop_Reaction = HTOP_OK;
let mut sortPanel = Panel_new(0, 0, 0, 0, Some(FunctionBar_newEnterEsc("Sort ", "Cancel ")));
Panel_setHeader(&mut sortPanel, "Sort by");
let host = st.host;
let (fields, dynamicColumns, activeSortKey) = unsafe {
let settings = (*host)
.settings
.as_ref()
.expect("actionSetSortColumn: host->settings is NULL");
let ss = &settings.screens[settings.ssIndex as usize];
(
ss.fields.clone(),
settings.dynamicColumns,
ScreenSettings_getActiveSortKey(ss),
)
};
for i in 0..fields.len() {
let field = fields[i];
if field == 0 {
break; }
let name: String;
if field >= ROW_DYNAMIC_FIELDS {
let column = match dynamicColumns {
Some(dc) => Hashtable_get(unsafe { &*dc }, field as u32).and_then(|o| {
let any: &dyn core::any::Any = o;
any.downcast_ref::<DynamicColumn>()
}),
None => None,
};
let column = match column {
Some(c) => c,
None => continue,
};
name = column
.caption
.clone()
.unwrap_or_else(|| column.name.clone());
} else {
name = String_trim(Process_fields[field as usize].name);
}
Panel_add(&mut sortPanel, Box::new(ListItem_new(&name, field)));
if field == activeSortKey {
Panel_setSelected(&mut sortPanel, i as i32);
}
}
let picked = Action_pickFromVector(st, Box::new(sortPanel), 14, false);
if let Some(obj) = picked {
let any: &dyn core::any::Any = &*obj;
if let Some(item) = any.downcast_ref::<ListItem>() {
let settings = unsafe {
(*host)
.settings
.as_mut()
.expect("actionSetSortColumn: host->settings is NULL")
};
reaction |= Action_setSortKey(settings, item.key);
}
}
let at = unsafe {
(*host)
.activeTable
.expect("actionSetSortColumn: host->activeTable is NULL")
};
unsafe {
(*at).needsSort = true;
}
reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR
}
pub fn actionSortByPID(st: &mut State) -> Htop_Reaction {
let settings = unsafe {
(*st.host)
.settings
.as_mut()
.expect("actionSortByPID: host->settings is NULL")
};
Action_setSortKey(settings, ProcessField::PID as RowField)
}
pub fn actionSortByMemory(st: &mut State) -> Htop_Reaction {
let settings = unsafe {
(*st.host)
.settings
.as_mut()
.expect("actionSortByMemory: host->settings is NULL")
};
Action_setSortKey(settings, ProcessField::PERCENT_MEM as RowField)
}
pub fn actionSortByCPU(st: &mut State) -> Htop_Reaction {
let settings = unsafe {
(*st.host)
.settings
.as_mut()
.expect("actionSortByCPU: host->settings is NULL")
};
Action_setSortKey(settings, ProcessField::PERCENT_CPU as RowField)
}
pub fn actionSortByTime(st: &mut State) -> Htop_Reaction {
let settings = unsafe {
(*st.host)
.settings
.as_mut()
.expect("actionSortByTime: host->settings is NULL")
};
Action_setSortKey(settings, ProcessField::TIME as RowField)
}
pub fn actionToggleKernelThreads(st: &mut State) -> Htop_Reaction {
let settings = unsafe {
(*st.host)
.settings
.as_mut()
.expect("actionToggleKernelThreads: host->settings is NULL")
};
settings.hideKernelThreads = !settings.hideKernelThreads;
settings.lastUpdate += 1;
Machine_scanTables(unsafe { &mut *st.host });
HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING
}
pub fn actionToggleUserlandThreads(st: &mut State) -> Htop_Reaction {
let settings = unsafe {
(*st.host)
.settings
.as_mut()
.expect("actionToggleUserlandThreads: host->settings is NULL")
};
settings.hideUserlandThreads = !settings.hideUserlandThreads;
settings.lastUpdate += 1;
Machine_scanTables(unsafe { &mut *st.host });
HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING
}
pub fn actionToggleRunningInContainer(st: &mut State) -> Htop_Reaction {
let settings = unsafe {
(*st.host)
.settings
.as_mut()
.expect("actionToggleRunningInContainer: host->settings is NULL")
};
settings.hideRunningInContainer = !settings.hideRunningInContainer;
settings.lastUpdate += 1;
HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING
}
pub fn actionToggleProgramPath(st: &mut State) -> Htop_Reaction {
let settings = unsafe {
(*st.host)
.settings
.as_mut()
.expect("actionToggleProgramPath: host->settings is NULL")
};
settings.showProgramPath = !settings.showProgramPath;
settings.lastUpdate += 1;
HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING
}
pub fn actionToggleMergedCommand(st: &mut State) -> Htop_Reaction {
let settings = unsafe {
(*st.host)
.settings
.as_mut()
.expect("actionToggleMergedCommand: host->settings is NULL")
};
settings.showMergedCommand = !settings.showMergedCommand;
settings.lastUpdate += 1;
HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_UPDATE_PANELHDR
}
pub fn actionToggleTreeView(st: &mut State) -> Htop_Reaction {
let host = st.host;
unsafe {
let ssidx = (*host)
.settings
.as_ref()
.expect("actionToggleTreeView: host->settings is NULL")
.ssIndex as usize;
{
let ss = &mut (*host).settings.as_mut().unwrap().screens[ssidx];
ss.treeView = !ss.treeView;
}
let all_collapsed = (*host).settings.as_ref().unwrap().screens[ssidx].allBranchesCollapsed;
let at = (*host)
.activeTable
.expect("actionToggleTreeView: host->activeTable is NULL");
if !all_collapsed {
Table_expandTree(&mut *at);
}
(*at).needsSort = true;
}
HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR
}
pub fn actionToggleHideMeters(st: &mut State) -> Htop_Reaction {
st.hideMeters = !st.hideMeters;
HTOP_RESIZE | HTOP_KEEP_FOLLOWING
}
pub fn actionExpandOrCollapseAllBranches(st: &mut State) -> Htop_Reaction {
let host = st.host;
unsafe {
let ssidx = (*host)
.settings
.as_ref()
.expect("actionExpandOrCollapseAllBranches: host->settings is NULL")
.ssIndex as usize;
if !(*host).settings.as_ref().unwrap().screens[ssidx].treeView {
return HTOP_OK;
}
let collapsed = {
let ss = &mut (*host).settings.as_mut().unwrap().screens[ssidx];
ss.allBranchesCollapsed = !ss.allBranchesCollapsed;
ss.allBranchesCollapsed
};
let at = (*host)
.activeTable
.expect("actionExpandOrCollapseAllBranches: host->activeTable is NULL");
if collapsed {
Table_collapseAllBranches(&mut *at);
} else {
Table_expandTree(&mut *at);
}
}
HTOP_REFRESH | HTOP_SAVE_SETTINGS
}
pub fn actionIncFilter(st: &mut State) -> Htop_Reaction {
let mp = unsafe { &mut *st.mainPanel };
IncSet_activate(&mut mp.inc, IncType::INC_FILTER, &mut mp.super_);
let filter = IncSet_filter(&mp.inc).map(|s| s.to_string());
let host = unsafe { &mut *st.host };
let at = host
.activeTable
.expect("actionIncFilter: host->activeTable is NULL");
unsafe {
(*at).incFilter = filter;
}
HTOP_REFRESH | HTOP_KEEP_FOLLOWING
}
pub fn actionIncSearch(st: &mut State) -> Htop_Reaction {
let mp = unsafe { &mut *st.mainPanel };
IncSet_reset(&mut mp.inc, IncType::INC_SEARCH);
IncSet_activate(&mut mp.inc, IncType::INC_SEARCH, &mut mp.super_);
HTOP_REFRESH | HTOP_KEEP_FOLLOWING
}
pub fn actionHigherPriority(st: &mut State) -> Htop_Reaction {
if !Action_writeableProcess(st) {
return HTOP_OK;
}
let changed = changePriority(unsafe { &mut *st.mainPanel }, -1);
if changed {
HTOP_REFRESH
} else {
HTOP_OK
}
}
pub fn actionLowerPriority(st: &mut State) -> Htop_Reaction {
if !Action_writeableProcess(st) {
return HTOP_OK;
}
let changed = changePriority(unsafe { &mut *st.mainPanel }, 1);
if changed {
HTOP_REFRESH
} else {
HTOP_OK
}
}
pub fn actionInvertSortOrder(st: &mut State) -> Htop_Reaction {
let host = st.host;
unsafe {
let ssidx = (*host)
.settings
.as_ref()
.expect("actionInvertSortOrder: host->settings is NULL")
.ssIndex as usize;
ScreenSettings_invertSortOrder(&mut (*host).settings.as_mut().unwrap().screens[ssidx]);
let at = (*host)
.activeTable
.expect("actionInvertSortOrder: host->activeTable is NULL");
(*at).needsSort = true;
}
HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_UPDATE_PANELHDR
}
pub fn actionExpandOrCollapse(st: &mut State) -> Htop_Reaction {
let treeView = unsafe {
let s = (*st.host)
.settings
.as_ref()
.expect("actionExpandOrCollapse: host->settings is NULL");
s.screens[s.ssIndex as usize].treeView
};
if !treeView {
return HTOP_OK;
}
let changed = expandCollapse(unsafe { &mut (*st.mainPanel).super_ });
if changed {
HTOP_RECALCULATE
} else {
HTOP_OK
}
}
pub fn actionCollapseIntoParent(st: &mut State) -> Htop_Reaction {
let treeView = unsafe {
let s = (*st.host)
.settings
.as_ref()
.expect("actionCollapseIntoParent: host->settings is NULL");
s.screens[s.ssIndex as usize].treeView
};
if !treeView {
return HTOP_OK;
}
let changed = collapseIntoParent(unsafe { &mut (*st.mainPanel).super_ });
if changed {
HTOP_RECALCULATE
} else {
HTOP_OK
}
}
pub fn actionExpandCollapseOrSortColumn(st: &mut State) -> Htop_Reaction {
let treeView = unsafe {
let s = (*st.host)
.settings
.as_ref()
.expect("actionExpandCollapseOrSortColumn: host->settings is NULL");
s.screens[s.ssIndex as usize].treeView
};
if treeView {
actionExpandOrCollapse(st)
} else {
actionSetSortColumn(st)
}
}
pub fn setActiveScreen(st: &State, ssIdx: u32) {
let host = unsafe { &mut *st.host };
let settings = host
.settings
.as_mut()
.expect("setActiveScreen: host->settings is NULL");
debug_assert_eq!(settings.ssIndex, ssIdx);
let idx = ssIdx as usize;
let processTable = host.processTable;
let settings = host.settings.as_mut().unwrap();
if settings.screens[idx].table.is_none() {
settings.screens[idx].table = processTable;
}
let active = settings.screens[idx].table;
host.activeTable = active;
let readonly = Settings_isReadonly() || (active != processTable);
let mp = unsafe { &mut *st.mainPanel };
MainPanel_setFunctionBar(mp, readonly);
}
pub fn actionNextScreen(st: &mut State) -> Htop_Reaction {
let idx = unsafe {
let settings = (*st.host)
.settings
.as_mut()
.expect("actionNextScreen: host->settings is NULL");
let nScreens = settings.screens.len() as u32;
settings.ssIndex += 1;
if settings.ssIndex == nScreens {
settings.ssIndex = 0;
}
settings.ssIndex
};
setActiveScreen(st, idx);
HTOP_UPDATE_PANELHDR | HTOP_REFRESH | HTOP_REDRAW_BAR
}
pub fn actionPrevScreen(st: &mut State) -> Htop_Reaction {
let idx = unsafe {
let settings = (*st.host)
.settings
.as_mut()
.expect("actionPrevScreen: host->settings is NULL");
if settings.ssIndex == 0 {
settings.ssIndex = settings.screens.len() as u32 - 1;
} else {
settings.ssIndex -= 1;
}
settings.ssIndex
};
setActiveScreen(st, idx);
HTOP_UPDATE_PANELHDR | HTOP_REFRESH | HTOP_REDRAW_BAR
}
pub fn Action_setScreenTab(st: &State, x: i32) -> Htop_Reaction {
let host = st.host;
let bracketWidth: i32 = 2;
if x < SCREEN_TAB_MARGIN_LEFT {
return HTOP_OK;
}
let nScreens = unsafe {
(*host)
.settings
.as_ref()
.expect("Action_setScreenTab: host->settings is NULL")
.screens
.len()
};
let mut rem = x - SCREEN_TAB_MARGIN_LEFT;
for i in 0..nScreens {
let width = if rem >= bracketWidth {
let n = (rem - bracketWidth + 1) as usize;
unsafe {
let heading = (*host).settings.as_ref().unwrap().screens[i]
.heading
.as_deref()
.unwrap_or("");
heading.len().min(n) as i32
}
} else {
0
};
if width >= rem - bracketWidth + 1 {
unsafe {
(*host).settings.as_mut().unwrap().ssIndex = i as u32;
}
setActiveScreen(st, i as u32);
return HTOP_UPDATE_PANELHDR | HTOP_REFRESH | HTOP_REDRAW_BAR;
}
rem -= bracketWidth + width;
if rem < SCREEN_TAB_COLUMN_GAP {
return HTOP_OK;
}
rem -= SCREEN_TAB_COLUMN_GAP;
}
HTOP_OK
}
pub fn actionQuit(_st: &mut State) -> Htop_Reaction {
HTOP_QUIT
}
pub fn actionSetAffinity(st: &mut State) -> Htop_Reaction {
if !Action_writeableProcess(st) {
return HTOP_OK;
}
let host = unsafe { &*st.host };
if host.activeCPUs == 1 {
return HTOP_OK;
}
HTOP_OK
}
pub fn actionSetSchedPolicy(_st: &mut State) -> Htop_Reaction {
todo!("port of Action.c:480 — #ifdef SCHEDULER_SUPPORT (Linux-only, not compiled on darwin) + unported Scheduling_* panels")
}
static PRE_SELECTED_SIGNAL: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(SIGNALSPANEL_INITSELECTEDSIGNAL);
pub fn actionKill(st: &mut State) -> Htop_Reaction {
use std::sync::atomic::Ordering;
if !Action_writeableProcess(st) {
return HTOP_OK;
}
let pre = PRE_SELECTED_SIGNAL.load(Ordering::Relaxed);
#[cfg(target_os = "macos")]
let signals = crate::ported::darwin::platform::Platform_signals;
#[cfg(not(target_os = "macos"))]
let signals: &[crate::ported::signalspanel::SignalItem] = &[];
let signalsPanel = SignalsPanel_new(pre, signals);
let picked = Action_pickFromVector(st, Box::new(signalsPanel), 14, true);
if let Some(obj) = picked {
let any: &dyn core::any::Any = obj.as_ref();
if let Some(sgn) = any.downcast_ref::<ListItem>() {
if sgn.key != 0 {
PRE_SELECTED_SIGNAL.store(sgn.key, Ordering::Relaxed);
let mp = unsafe { &mut (*st.mainPanel).super_ };
Panel_setHeader(mp, "Sending...");
Panel_draw(mp, false, true, true, false);
{
let mut out = std::io::stdout().lock();
Ncurses::refresh(&mut out);
}
let ok = MainPanel_foreachRow(
unsafe { &mut *st.mainPanel },
Process_rowSendSignal,
Arg::I(sgn.key),
None,
);
if !ok {
let mut out = std::io::stdout().lock();
Ncurses::beep(&mut out);
}
Ncurses::napms(500);
}
}
}
HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR
}
pub fn actionFilterByUser(st: &mut State) -> Htop_Reaction {
let mut usersPanel = Panel_new(
0,
0,
0,
0,
Some(FunctionBar_newEnterEsc("Show ", "Cancel ")),
);
Panel_setHeader(&mut usersPanel, "Show processes of:");
let usersTable = unsafe { (*st.host).usersTable };
if let Some(ptr) = usersTable {
let ut = unsafe { &*(ptr as *const UsersTable) };
UsersTable_foreach(ut, &mut |uid, name| {
addUserToVector(uid as i32, name, &mut usersPanel);
});
}
usersPanel.items.sort_by(|a, b| {
let name = |it: &PanelItem| -> String {
match it {
PanelItem::Owned(o) => (o.as_ref() as &dyn core::any::Any)
.downcast_ref::<ListItem>()
.map(|li| ListItem_getRef(li).to_string())
.unwrap_or_default(),
PanelItem::Borrowed(_) => String::new(),
}
};
name(a).cmp(&name(b))
});
Panel_insert(&mut usersPanel, 0, Box::new(ListItem_new("All users", -1)));
let picked = Action_pickFromVector(st, Box::new(usersPanel), 19, false);
if let Some(obj) = picked {
if let Some(li) = (obj.as_ref() as &dyn core::any::Any).downcast_ref::<ListItem>() {
if li.key == -1 {
unsafe {
(*st.host).userId = u32::MAX; }
} else {
let name = ListItem_getRef(li).to_string();
Action_setUserOnly(&name, unsafe { &mut (*st.host).userId });
}
}
}
HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR
}
pub fn Action_follow(st: &mut State) -> Htop_Reaction {
let sel = MainPanel_selectedRow(unsafe { &*st.mainPanel });
let host = unsafe { &mut *st.host };
let at = host
.activeTable
.expect("Action_follow: host->activeTable is NULL");
unsafe {
(*at).following = sel;
}
Panel_setSelectionColor(
unsafe { &mut (*st.mainPanel).super_ },
ColorElements::PANEL_SELECTION_FOLLOW,
);
HTOP_KEEP_FOLLOWING
}
pub fn actionSetup(st: &mut State) -> Htop_Reaction {
Action_runSetup(st);
HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR | HTOP_RESIZE
}
pub fn actionLsof(st: &mut State) -> Htop_Reaction {
if !Action_writeableProcess(st) {
return HTOP_OK;
}
let mainpanel = st.mainPanel;
if mainpanel.is_null() {
return HTOP_OK;
}
let mut ofs = {
let panel = unsafe { &(*mainpanel).super_ };
let p = match Panel_getSelected(panel).and_then(|o| o.as_process()) {
Some(p) => p,
None => return HTOP_OK,
};
OpenFilesScreen_new(p)
};
InfoScreen_run(&mut ofs);
OpenFilesScreen_delete(ofs);
let mut out = std::io::stdout().lock();
Ncurses::clear(&mut out);
Ncurses::refresh(&mut out);
CRT_enableDelay();
HTOP_REFRESH | HTOP_REDRAW_BAR
}
pub fn actionShowLocks(st: &mut State) -> Htop_Reaction {
let panel = unsafe { &(*st.mainPanel).super_ };
let p: *const Process = match Panel_getSelected(panel).and_then(|o| o.as_process()) {
Some(pr) => pr as *const Process,
None => return HTOP_OK,
};
let mut pls = ProcessLocksScreen_new(unsafe { &*p });
InfoScreen_run(&mut pls);
ProcessLocksScreen_delete(pls);
{
let mut out = std::io::stdout().lock();
Ncurses::clear(&mut out);
}
CRT_enableDelay();
HTOP_REFRESH | HTOP_REDRAW_BAR
}
pub fn actionBacktrace(_st: &mut State) -> Htop_Reaction {
todo!("port of Action.c:616 — #if HAVE_BACKTRACE_SCREEN (Linux-only, not compiled on darwin) + unported BacktracePanel_new/Vector")
}
pub fn actionStrace(st: &mut State) -> Htop_Reaction {
if !Action_writeableProcess(st) {
return HTOP_OK;
}
let mainpanel = st.mainPanel;
if mainpanel.is_null() {
return HTOP_OK;
}
let mut ts = {
let panel = unsafe { &(*mainpanel).super_ };
let p = match Panel_getSelected(panel).and_then(|o| o.as_process()) {
Some(p) => p,
None => return HTOP_OK,
};
TraceScreen_new(p)
};
if TraceScreen_forkTracer(&mut ts) {
InfoScreen_run(&mut ts);
}
TraceScreen_delete(&mut ts);
let mut out = std::io::stdout().lock();
Ncurses::clear(&mut out);
Ncurses::refresh(&mut out);
CRT_enableDelay();
HTOP_REFRESH | HTOP_REDRAW_BAR
}
pub fn actionTag(st: &mut State) -> Htop_Reaction {
let panel = unsafe { &mut (*st.mainPanel).super_ };
let sel = panel.selected;
if sel < 0 || sel as usize >= panel.items.len() {
return HTOP_OK;
}
{
let obj: &mut dyn Object = panel.items[sel as usize].object_mut();
if let Some(r) = obj.as_row_mut() {
Row_toggleTag(r);
}
}
Panel_onKey(panel, KEY_DOWN);
HTOP_OK
}
pub fn actionRedraw(_st: &mut State) -> Htop_Reaction {
let mut out = std::io::stdout().lock();
Ncurses::clear(&mut out);
Ncurses::refresh(&mut out);
HTOP_RECALCULATE | HTOP_REFRESH | HTOP_REDRAW_BAR
}
pub fn actionTogglePauseUpdate(st: &mut State) -> Htop_Reaction {
st.pauseUpdate = !st.pauseUpdate;
HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_KEEP_FOLLOWING
}
pub fn addattrstr<W: std::io::Write>(out: &mut W, attr: i32, str_: &str) {
Ncurses::attrset(out, attr);
Ncurses::addstr(out, str_);
}
pub fn actionHelp(_st: &mut State) -> Htop_Reaction {
todo!(
"port of Action.c:751 — clear/attrset/mvaddstr/mvhline/refresh ncurses substrate unported"
)
}
pub fn actionUntagAll(st: &mut State) -> Htop_Reaction {
let panel = unsafe { &mut (*st.mainPanel).super_ };
let size = Panel_size(panel);
for i in 0..size {
let obj: &mut dyn Object = panel.items[i as usize].object_mut();
let any: &mut dyn core::any::Any = obj;
if let Some(row) = any.downcast_mut::<Row>() {
row.tag = false;
}
}
HTOP_REFRESH
}
pub fn actionTagAllChildren(st: &mut State) -> Htop_Reaction {
let panel = unsafe { &mut (*st.mainPanel).super_ };
let sel = panel.selected;
if sel < 0 || sel as usize >= panel.items.len() {
return HTOP_OK;
}
tagAllChildren(panel, sel);
HTOP_OK
}
pub fn actionShowEnvScreen(st: &mut State) -> Htop_Reaction {
if !Action_readableProcess(st) {
return HTOP_OK;
}
let mainpanel = st.mainPanel;
if mainpanel.is_null() {
return HTOP_OK;
}
let mut es = {
let panel = unsafe { &(*mainpanel).super_ };
let p = match Panel_getSelected(panel).and_then(|o| o.as_process()) {
Some(p) => p,
None => return HTOP_OK,
};
EnvScreen_new(p)
};
InfoScreen_run(&mut es);
EnvScreen_delete(es);
let mut out = std::io::stdout().lock();
Ncurses::clear(&mut out);
Ncurses::refresh(&mut out);
CRT_enableDelay();
HTOP_REFRESH | HTOP_REDRAW_BAR
}
pub fn actionShowCommandScreen(st: &mut State) -> Htop_Reaction {
if !Action_readableProcess(st) {
return HTOP_OK;
}
let mainpanel = st.mainPanel;
if mainpanel.is_null() {
return HTOP_OK;
}
let mut cmd_scr = {
let panel = unsafe { &(*mainpanel).super_ };
let p = match Panel_getSelected(panel).and_then(|o| o.as_process()) {
Some(p) => p as *const Process,
None => return HTOP_OK,
};
CommandScreen_new(p)
};
InfoScreen_run(&mut cmd_scr);
CommandScreen_delete(cmd_scr);
let mut out = std::io::stdout().lock();
Ncurses::clear(&mut out);
Ncurses::refresh(&mut out);
CRT_enableDelay();
HTOP_REFRESH | HTOP_REDRAW_BAR
}
pub fn Action_setBindings(keys: &mut [Option<Htop_Action>]) {
keys[b' ' as usize] = Some(actionTag);
keys[b'#' as usize] = Some(actionToggleHideMeters);
keys[b'*' as usize] = Some(actionExpandOrCollapseAllBranches);
keys[b'+' as usize] = Some(actionExpandOrCollapse);
keys[b',' as usize] = Some(actionSetSortColumn);
keys[b'-' as usize] = Some(actionExpandOrCollapse);
keys[b'.' as usize] = Some(actionSetSortColumn);
keys[b'/' as usize] = Some(actionIncSearch);
keys[b'<' as usize] = Some(actionSetSortColumn);
keys[b'=' as usize] = Some(actionExpandOrCollapse);
keys[b'>' as usize] = Some(actionSetSortColumn);
keys[b'?' as usize] = Some(actionHelp);
keys[b'C' as usize] = Some(actionSetup);
keys[b'F' as usize] = Some(Action_follow);
keys[b'H' as usize] = Some(actionToggleUserlandThreads);
keys[b'I' as usize] = Some(actionInvertSortOrder);
keys[b'K' as usize] = Some(actionToggleKernelThreads);
keys[b'M' as usize] = Some(actionSortByMemory);
keys[b'N' as usize] = Some(actionSortByPID);
keys[b'O' as usize] = Some(actionToggleRunningInContainer);
keys[b'P' as usize] = Some(actionSortByCPU);
keys[b'S' as usize] = Some(actionSetup);
keys[b'T' as usize] = Some(actionSortByTime);
keys[b'U' as usize] = Some(actionUntagAll);
keys[b'Z' as usize] = Some(actionTogglePauseUpdate);
keys[b'[' as usize] = Some(actionLowerPriority);
keys[0o14] = Some(actionRedraw); keys[0o177] = Some(actionCollapseIntoParent); keys[b'\\' as usize] = Some(actionIncFilter);
keys[b']' as usize] = Some(actionHigherPriority);
keys[b'a' as usize] = Some(actionSetAffinity);
keys[b'c' as usize] = Some(actionTagAllChildren);
keys[b'e' as usize] = Some(actionShowEnvScreen);
keys[b'h' as usize] = Some(actionHelp);
keys[b'k' as usize] = Some(actionKill);
keys[b'l' as usize] = Some(actionLsof);
keys[b'm' as usize] = Some(actionToggleMergedCommand);
keys[b'p' as usize] = Some(actionToggleProgramPath);
keys[b'q' as usize] = Some(actionQuit);
keys[b's' as usize] = Some(actionStrace);
keys[b't' as usize] = Some(actionToggleTreeView);
keys[b'u' as usize] = Some(actionFilterByUser);
keys[b'w' as usize] = Some(actionShowCommandScreen);
keys[b'x' as usize] = Some(actionShowLocks);
keys[KEY_F(1) as usize] = Some(actionHelp);
keys[KEY_F(2) as usize] = Some(actionSetup);
keys[KEY_F(3) as usize] = Some(actionIncSearch);
keys[KEY_F(4) as usize] = Some(actionIncFilter);
keys[KEY_F(5) as usize] = Some(actionToggleTreeView);
keys[KEY_F(6) as usize] = Some(actionSetSortColumn);
keys[KEY_F(7) as usize] = Some(actionHigherPriority);
keys[KEY_F(8) as usize] = Some(actionLowerPriority);
keys[KEY_F(9) as usize] = Some(actionKill);
keys[KEY_F(10) as usize] = Some(actionQuit);
keys[KEY_F(18) as usize] = Some(actionExpandCollapseOrSortColumn);
keys[KEY_RECLICK as usize] = Some(actionExpandOrCollapse);
keys[KEY_SHIFT_TAB as usize] = Some(actionPrevScreen);
keys[b'\t' as usize] = Some(actionNextScreen);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::panel::{Panel_add, Panel_get, Panel_getSelectedIndex, Panel_new};
fn row(id: i32, group: i32, parent: i32) -> Row {
Row {
id,
group,
parent,
..Row::default()
}
}
fn show_children_at(p: &Panel, i: i32) -> bool {
let obj: &dyn Object = Panel_get(p, i);
let any: &dyn core::any::Any = obj;
any.downcast_ref::<Row>().unwrap().showChildren
}
#[test]
fn expand_collapse_toggles_selected_show_children() {
let mut p = Panel_new(0, 0, 0, 0, None);
Panel_add(&mut p, Box::new(row(1, 1, 0)));
Panel_add(&mut p, Box::new(row(2, 2, 0)));
Panel_setSelected(&mut p, 1);
assert!(expandCollapse(&mut p));
assert!(show_children_at(&p, 1));
assert!(!show_children_at(&p, 0));
assert!(expandCollapse(&mut p));
assert!(!show_children_at(&p, 1));
}
#[test]
fn expand_collapse_empty_panel_returns_false() {
let mut p = Panel_new(0, 0, 0, 0, None);
assert!(!expandCollapse(&mut p));
}
#[test]
fn collapse_into_parent_finds_parent_and_moves_selection() {
let mut p = Panel_new(0, 0, 0, 0, None);
let mut parent = row(10, 10, 0);
parent.showChildren = true;
Panel_add(&mut p, Box::new(parent)); Panel_add(&mut p, Box::new(row(11, 10, 10))); Panel_setSelected(&mut p, 1);
assert!(collapseIntoParent(&mut p));
assert_eq!(Panel_getSelectedIndex(&p), 0);
assert!(!show_children_at(&p, 0));
}
#[test]
fn collapse_into_parent_no_match_returns_false() {
let mut p = Panel_new(0, 0, 0, 0, None);
Panel_add(&mut p, Box::new(row(1, 99, 0)));
Panel_add(&mut p, Box::new(row(2, 2, 0)));
Panel_setSelected(&mut p, 0);
assert!(!collapseIntoParent(&mut p));
}
#[test]
fn collapse_into_parent_empty_panel_returns_false() {
let mut p = Panel_new(0, 0, 0, 0, None);
assert!(!collapseIntoParent(&mut p));
}
#[test]
fn reaction_flag_composites_match_c() {
assert_eq!(HTOP_OK, 0x00);
assert_eq!(HTOP_REFRESH, 0x01);
assert_eq!(HTOP_RECALCULATE, 0x03); assert_eq!(HTOP_QUIT, 0x10);
assert_eq!(HTOP_UPDATE_PANELHDR, 0x41); assert_eq!(HTOP_RESIZE, 0xE1);
}
#[test]
fn action_quit_returns_htop_quit() {
let mut st = State {
host: core::ptr::null_mut(),
mainPanel: core::ptr::null_mut(),
header: core::ptr::null_mut(),
failedUpdate: None,
pauseUpdate: false,
hideSelection: false,
hideMeters: false,
};
assert_eq!(actionQuit(&mut st), HTOP_QUIT);
assert_eq!(actionQuit(&mut st), 0x10);
}
#[test]
fn action_toggle_hide_meters_flips_and_returns_resize() {
let mut st = State {
host: core::ptr::null_mut(),
mainPanel: core::ptr::null_mut(),
header: core::ptr::null_mut(),
failedUpdate: None,
pauseUpdate: false,
hideSelection: false,
hideMeters: false,
};
let r = actionToggleHideMeters(&mut st);
assert!(st.hideMeters);
assert_eq!(r, 0xE1 | 0x08); let r2 = actionToggleHideMeters(&mut st);
assert!(!st.hideMeters);
assert_eq!(r2, r);
assert!(!st.pauseUpdate);
assert!(!st.hideSelection);
}
#[test]
fn action_toggle_pause_update_flips_and_returns_refresh() {
let mut st = State {
host: core::ptr::null_mut(),
mainPanel: core::ptr::null_mut(),
header: core::ptr::null_mut(),
failedUpdate: None,
pauseUpdate: false,
hideSelection: false,
hideMeters: false,
};
let r = actionTogglePauseUpdate(&mut st);
assert!(st.pauseUpdate);
assert_eq!(r, 0x01 | 0x20 | 0x08);
let r2 = actionTogglePauseUpdate(&mut st);
assert!(!st.pauseUpdate);
assert_eq!(r2, r);
assert!(!st.hideMeters);
}
}