feature: support ignoring all keypresses (#1719)

* refactor

* add no key setting

* do it here instead of in main loop
This commit is contained in:
Clement Tsang 2025-08-29 21:30:50 -04:00 committed by GitHub
parent 43e1b34899
commit d799c656aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 34 additions and 9 deletions

View File

@ -25,6 +25,7 @@ That said, these are more guidelines rather than hardset rules, though the proje
### Features ### Features
- [#1793](https://github.com/ClementTsang/bottom/pull/1793): Add support for threads in Linux. - [#1793](https://github.com/ClementTsang/bottom/pull/1793): Add support for threads in Linux.
- [#1719](https://github.com/ClementTsang/bottom/pull/1719): Support ignoring all keypresses.
### Bug Fixes ### Bug Fixes

View File

@ -51,6 +51,7 @@ pub struct AppConfigFields {
pub use_old_network_legend: bool, pub use_old_network_legend: bool,
pub table_gap: u16, pub table_gap: u16,
pub disable_click: bool, pub disable_click: bool,
pub disable_keys: bool,
pub enable_gpu: bool, pub enable_gpu: bool,
pub enable_cache_memory: bool, pub enable_cache_memory: bool,
pub show_table_scroll_position: bool, pub show_table_scroll_position: bool,

View File

@ -325,6 +325,9 @@ pub(crate) const CONFIG_TEXT: &str = r#"# This is a default config file for bott
# Disable mouse clicks # Disable mouse clicks
#disable_click = false #disable_click = false
# Disable keyboard shortcuts
#disable_keys = false
# Show memory values in the processes widget as values by default # Show memory values in the processes widget as values by default
#process_memory_as_value = false #process_memory_as_value = false

View File

@ -145,7 +145,10 @@ fn panic_hook(panic_info: &PanicHookInfo<'_>) {
/// Create a thread to poll for user inputs and forward them to the main thread. /// Create a thread to poll for user inputs and forward them to the main thread.
fn create_input_thread( fn create_input_thread(
sender: Sender<BottomEvent>, cancellation_token: Arc<CancellationToken>, sender: Sender<BottomEvent>, cancellation_token: Arc<CancellationToken>,
app_config_fields: &AppConfigFields,
) -> JoinHandle<()> { ) -> JoinHandle<()> {
let keys_disabled = app_config_fields.disable_keys;
thread::spawn(move || { thread::spawn(move || {
let mut mouse_timer = Instant::now(); let mut mouse_timer = Instant::now();
@ -177,7 +180,9 @@ fn create_input_thread(
break; break;
} }
} }
Event::Key(key) if key.kind == KeyEventKind::Press => { Event::Key(key)
if !keys_disabled && key.kind == KeyEventKind::Press =>
{
// For now, we only care about key down events. This may change in // For now, we only care about key down events. This may change in
// the future. // the future.
if sender.send(BottomEvent::KeyInput(key)).is_err() { if sender.send(BottomEvent::KeyInput(key)).is_err() {
@ -325,7 +330,11 @@ pub fn start_bottom(enable_error_hook: &mut bool) -> anyhow::Result<()> {
); );
// Set up the input handling loop thread. // Set up the input handling loop thread.
let _input_thread = create_input_thread(sender.clone(), cancellation_token.clone()); let _input_thread = create_input_thread(
sender.clone(),
cancellation_token.clone(),
&app.app_config_fields,
);
// Set up the cleaning loop thread. // Set up the cleaning loop thread.
let _cleaning_thread = { let _cleaning_thread = {

View File

@ -306,6 +306,7 @@ pub(crate) fn init_app(args: BottomArgs, config: Config) -> Result<(App, BottomL
use_old_network_legend: is_flag_enabled!(use_old_network_legend, args.network, config), use_old_network_legend: is_flag_enabled!(use_old_network_legend, args.network, config),
table_gap: u16::from(!(is_flag_enabled!(hide_table_gap, args.general, config))), table_gap: u16::from(!(is_flag_enabled!(hide_table_gap, args.general, config))),
disable_click: is_flag_enabled!(disable_click, args.general, config), disable_click: is_flag_enabled!(disable_click, args.general, config),
disable_keys: is_flag_enabled!(disable_keys, args.general, config),
enable_gpu: get_enable_gpu(args, config), enable_gpu: get_enable_gpu(args, config),
enable_cache_memory: get_enable_cache_memory(args, config), enable_cache_memory: get_enable_cache_memory(args, config),
show_table_scroll_position: is_flag_enabled!( show_table_scroll_position: is_flag_enabled!(
@ -1038,7 +1039,7 @@ mod test {
app::App, app::App,
args::BottomArgs, args::BottomArgs,
options::{ options::{
config::flags::FlagConfig, get_default_time_value, get_retention, get_update_rate, config::flags::GeneralConfig, get_default_time_value, get_retention, get_update_rate,
try_parse_ms, try_parse_ms,
}, },
}; };
@ -1117,7 +1118,7 @@ mod test {
let args = BottomArgs::parse_from(["btm"]); let args = BottomArgs::parse_from(["btm"]);
let mut config = Config::default(); let mut config = Config::default();
let flags = FlagConfig { let flags = GeneralConfig {
time_delta: Some("2 min".to_string().into()), time_delta: Some("2 min".to_string().into()),
default_time_value: Some("300s".to_string().into()), default_time_value: Some("300s".to_string().into()),
rate: Some("1s".to_string().into()), rate: Some("1s".to_string().into()),
@ -1147,7 +1148,7 @@ mod test {
let args = BottomArgs::parse_from(["btm"]); let args = BottomArgs::parse_from(["btm"]);
let mut config = Config::default(); let mut config = Config::default();
let flags = FlagConfig { let flags = GeneralConfig {
time_delta: Some("120000".to_string().into()), time_delta: Some("120000".to_string().into()),
default_time_value: Some("300000".to_string().into()), default_time_value: Some("300000".to_string().into()),
rate: Some("1000".to_string().into()), rate: Some("1000".to_string().into()),
@ -1177,7 +1178,7 @@ mod test {
let args = BottomArgs::parse_from(["btm"]); let args = BottomArgs::parse_from(["btm"]);
let mut config = Config::default(); let mut config = Config::default();
let flags = FlagConfig { let flags = GeneralConfig {
time_delta: Some(120000.into()), time_delta: Some(120000.into()),
default_time_value: Some(300000.into()), default_time_value: Some(300000.into()),
rate: Some(1000.into()), rate: Some(1000.into()),

View File

@ -207,6 +207,15 @@ pub struct GeneralArgs {
)] )]
pub disable_click: bool, pub disable_click: bool,
#[arg(
long,
action = ArgAction::SetTrue,
help = "Disables keypresses.",
long_help = "Disables keypresses from interacting with bottom. Note this includes keyboard shortcuts to quit bottom.",
alias = "disable-keys"
)]
pub disable_keys: bool,
// TODO: Change this to accept a string with the type of marker. // TODO: Change this to accept a string with the type of marker.
#[arg( #[arg(
short = 'm', short = 'm',

View File

@ -9,7 +9,7 @@ pub mod style;
pub mod temperature; pub mod temperature;
use disk::DiskConfig; use disk::DiskConfig;
use flags::FlagConfig; use flags::GeneralConfig;
use network::NetworkConfig; use network::NetworkConfig;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use style::StyleConfig; use style::StyleConfig;
@ -23,7 +23,7 @@ use self::{cpu::CpuConfig, layout::Row, process::ProcessesConfig};
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))] #[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
pub struct Config { pub struct Config {
pub(crate) flags: Option<FlagConfig>, pub(crate) flags: Option<GeneralConfig>,
pub(crate) styles: Option<StyleConfig>, pub(crate) styles: Option<StyleConfig>,
pub(crate) row: Option<Vec<Row>>, pub(crate) row: Option<Vec<Row>>,
pub(crate) processes: Option<ProcessesConfig>, pub(crate) processes: Option<ProcessesConfig>,

View File

@ -6,7 +6,7 @@ use super::StringOrNum;
#[derive(Clone, Debug, Default, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))] #[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
pub(crate) struct FlagConfig { pub(crate) struct GeneralConfig {
pub(crate) hide_avg_cpu: Option<bool>, pub(crate) hide_avg_cpu: Option<bool>,
pub(crate) dot_marker: Option<bool>, pub(crate) dot_marker: Option<bool>,
pub(crate) temperature_type: Option<String>, pub(crate) temperature_type: Option<String>,
@ -30,6 +30,7 @@ pub(crate) struct FlagConfig {
pub(crate) hide_table_gap: Option<bool>, pub(crate) hide_table_gap: Option<bool>,
pub(crate) battery: Option<bool>, pub(crate) battery: Option<bool>,
pub(crate) disable_click: Option<bool>, pub(crate) disable_click: Option<bool>,
pub(crate) disable_keys: Option<bool>,
pub(crate) no_write: Option<bool>, pub(crate) no_write: Option<bool>,
pub(crate) network_legend: Option<String>, pub(crate) network_legend: Option<String>,
pub(crate) memory_legend: Option<String>, pub(crate) memory_legend: Option<String>,