#![allow(non_snake_case)]
#![allow(dead_code)]
use crate::ported::functionbar::Ncurses;
use crate::ported::incset::IncSet_new;
use crate::ported::infoscreen::{
InfoScreen, InfoScreenClass, InfoScreen_addLine, InfoScreen_done, InfoScreen_drawTitled,
InfoScreen_init,
};
use crate::ported::listitem::ListItem_new;
use crate::ported::object::Object;
use crate::ported::panel::{Panel_getSelectedIndex, Panel_new, Panel_prune, Panel_setSelected};
use crate::ported::process::{Process, Process_getCommand, Process_getPid};
use crate::ported::vector::Vector_new;
const VECTOR_DEFAULT_SIZE: core::ffi::c_int = 10;
pub struct CommandScreen {
pub super_: InfoScreen,
}
impl InfoScreenClass for CommandScreen {
fn super_InfoScreen(&mut self) -> &mut InfoScreen {
&mut self.super_
}
fn draw(&mut self) {
CommandScreen_draw(&mut self.super_);
}
fn scan(&mut self) {
CommandScreen_scan(&mut self.super_);
}
fn has_scan(&self) -> bool {
true
}
}
pub fn CommandScreen_scan(this: &mut InfoScreen) {
let idx = Panel_getSelectedIndex(&this.display).max(0);
Panel_prune(&mut this.display);
let p = Process_getCommand(unsafe { &*this.process });
let bytes: &[u8] = match p {
Some(b) => b,
None => &[],
};
let cols = Ncurses::cols();
let line_maxlen: usize = if cols < 40 { 40 } else { cols as usize };
let mut line_offset: usize = 0;
let mut last_space: usize = 0;
let mut line: Vec<u8> = vec![0u8; line_maxlen + 1];
for &ch in bytes {
if line_offset >= line_maxlen {
debug_assert!(line_offset <= line_maxlen);
debug_assert!(last_space <= line_maxlen);
let line_len = if last_space == 0 {
line_offset
} else {
last_space
};
{
let s = String::from_utf8_lossy(&line[..line_len]);
InfoScreen_addLine(this, &s);
}
debug_assert!(line_len <= line_offset);
let old_offset = line_offset;
line_offset -= line_len;
line.copy_within(line_len..old_offset, 0);
last_space = 0;
}
line[line_offset] = ch;
line_offset += 1;
if ch == b' ' {
last_space = line_offset;
}
}
if line_offset > 0 {
let s = String::from_utf8_lossy(&line[..line_offset]);
InfoScreen_addLine(this, &s);
}
Panel_setSelected(&mut this.display, idx);
}
pub fn CommandScreen_draw(this: &mut InfoScreen) {
let pid = Process_getPid(unsafe { &*this.process });
let cmd = Process_getCommand(unsafe { &*this.process });
let cmd = match cmd {
Some(b) => String::from_utf8_lossy(b).into_owned(),
None => String::new(),
};
let title = format!("Command of process {} - {}", pid, cmd);
InfoScreen_drawTitled(this, &title);
}
pub fn CommandScreen_new(process: *const Process) -> CommandScreen {
let list_item_class = ListItem_new("", 0).klass();
let mut this = CommandScreen {
super_: InfoScreen {
process: core::ptr::null(),
display: Panel_new(0, 0, 0, 0, None),
inc: IncSet_new(None),
lines: Vector_new(list_item_class, true, VECTOR_DEFAULT_SIZE),
},
};
InfoScreen_init(&mut this.super_, process, None, Ncurses::lines() - 2, " ");
this
}
pub fn CommandScreen_delete(this: CommandScreen) {
let CommandScreen { super_ } = this;
InfoScreen_done(super_);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::functionbar::FunctionBar_new;
use crate::ported::incset::IncSet_filter;
use crate::ported::panel::{Panel_headerHeight, Panel_size};
use crate::ported::vector::Vector_size;
const INFO_FUNCTIONS: [&str; 4] = ["Search ", "Filter ", "Refresh", "Done "];
#[test]
fn new_initializes_infoscreen_base() {
let cs = CommandScreen_new(core::ptr::null());
assert!(cs.super_.process.is_null());
assert_eq!(Vector_size(&cs.super_.lines), 0);
assert_eq!(Panel_size(&cs.super_.display), 0);
assert!(IncSet_filter(&cs.super_.inc).is_none());
}
#[test]
fn new_geometry_matches_c_panel_new_args() {
let cs = CommandScreen_new(core::ptr::null());
assert_eq!(cs.super_.display.x, 0);
assert_eq!(cs.super_.display.y, 1);
assert_eq!(cs.super_.display.w, Ncurses::cols());
assert_eq!(cs.super_.display.h, Ncurses::lines() - 2);
assert_eq!(Panel_headerHeight(&cs.super_.display), 1);
}
#[test]
fn new_builds_default_infoscreen_bar() {
let cs = CommandScreen_new(core::ptr::null());
let bar = cs
.super_
.display
.defaultBar
.as_ref()
.expect("default bar built");
assert_eq!(bar.functions, INFO_FUNCTIONS.to_vec());
let inc_bar = cs.super_.inc.defaultBar.as_ref().expect("inc default bar");
assert_eq!(inc_bar.functions, INFO_FUNCTIONS.to_vec());
}
#[test]
fn new_stores_nonnull_process_backpointer() {
let sentinel = 0xdead_beef_usize as *const Process;
let cs = CommandScreen_new(sentinel);
assert_eq!(cs.super_.process, sentinel);
}
#[test]
fn info_functions_match_functionbar_labels() {
let bar = FunctionBar_new(Some(&INFO_FUNCTIONS[..]), None, None);
assert_eq!(bar.functions, INFO_FUNCTIONS.to_vec());
}
}