Run get_disks() in the background using a thread
Allows app interaction while the scan is running
This commit is contained in:
parent
7d4f28f950
commit
c62c6c751c
7 changed files with 55 additions and 40 deletions
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"keybindings": {
|
"keybindings": {
|
||||||
"ScanDisks": {
|
"ScanDisks": {
|
||||||
"<Enter>": "Process",
|
|
||||||
"<q>": "Quit", // Quit the application
|
"<q>": "Quit", // Quit the application
|
||||||
"<Ctrl-d>": "Quit", // Another way to quit
|
"<Ctrl-d>": "Quit", // Another way to quit
|
||||||
"<Ctrl-c>": "Quit", // Yet another way to quit
|
"<Ctrl-c>": "Quit", // Yet another way to quit
|
||||||
|
|
|
||||||
36
src/app.rs
36
src/app.rs
|
|
@ -13,7 +13,11 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
|
// along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
use std::iter::zip;
|
use std::{
|
||||||
|
iter::zip,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::KeyEvent;
|
||||||
|
|
@ -32,6 +36,7 @@ use crate::{
|
||||||
Component,
|
Component,
|
||||||
},
|
},
|
||||||
config::Config,
|
config::Config,
|
||||||
|
system::disk::{get_disks, Disk},
|
||||||
tui::{Event, Tui},
|
tui::{Event, Tui},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -40,6 +45,7 @@ pub struct App {
|
||||||
tick_rate: f64,
|
tick_rate: f64,
|
||||||
frame_rate: f64,
|
frame_rate: f64,
|
||||||
components: Vec<Box<dyn Component>>,
|
components: Vec<Box<dyn Component>>,
|
||||||
|
disks: Arc<Mutex<Vec<Disk>>>,
|
||||||
should_quit: bool,
|
should_quit: bool,
|
||||||
should_suspend: bool,
|
should_suspend: bool,
|
||||||
cur_mode: Mode,
|
cur_mode: Mode,
|
||||||
|
|
@ -74,6 +80,7 @@ impl App {
|
||||||
Box::new(Footer::new()),
|
Box::new(Footer::new()),
|
||||||
Box::new(Popup::new()),
|
Box::new(Popup::new()),
|
||||||
],
|
],
|
||||||
|
disks: Arc::new(Mutex::new(Vec::new())),
|
||||||
should_quit: false,
|
should_quit: false,
|
||||||
should_suspend: false,
|
should_suspend: false,
|
||||||
config: Config::new()?,
|
config: Config::new()?,
|
||||||
|
|
@ -114,6 +121,8 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self) -> Result<()> {
|
pub async fn run(&mut self) -> Result<()> {
|
||||||
|
let disk_wrapper = Arc::clone(&self.disks);
|
||||||
|
let _ = lazy_get_disks(disk_wrapper);
|
||||||
let mut tui = Tui::new()?
|
let mut tui = Tui::new()?
|
||||||
// .mouse(true) // uncomment this line to enable mouse support
|
// .mouse(true) // uncomment this line to enable mouse support
|
||||||
.tick_rate(self.tick_rate)
|
.tick_rate(self.tick_rate)
|
||||||
|
|
@ -203,6 +212,12 @@ impl App {
|
||||||
match action {
|
match action {
|
||||||
Action::Tick => {
|
Action::Tick => {
|
||||||
self.last_tick_key_events.drain(..);
|
self.last_tick_key_events.drain(..);
|
||||||
|
// Continue to next screen if shared disks has been set
|
||||||
|
if self.cur_mode == Mode::ScanDisks {
|
||||||
|
if let Ok(_) = &self.disks.try_lock() {
|
||||||
|
self.action_tx.send(Action::NextScreen)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Action::Quit => self.should_quit = true,
|
Action::Quit => self.should_quit = true,
|
||||||
Action::Suspend => self.should_suspend = true,
|
Action::Suspend => self.should_suspend = true,
|
||||||
|
|
@ -212,13 +227,17 @@ impl App {
|
||||||
Action::Render => self.render(tui)?,
|
Action::Render => self.render(tui)?,
|
||||||
Action::PrevScreen => {
|
Action::PrevScreen => {
|
||||||
self.action_tx.send(Action::SetMode(self.prev_mode))?;
|
self.action_tx.send(Action::SetMode(self.prev_mode))?;
|
||||||
self.action_tx.send(Action::Select(None, None))?;
|
|
||||||
}
|
}
|
||||||
Action::NextScreen => {
|
Action::NextScreen => {
|
||||||
if let Some(mode) = self.next_mode() {
|
if let Some(mode) = self.next_mode() {
|
||||||
self.action_tx.send(Action::SetMode(mode))?;
|
self.action_tx.send(Action::SetMode(mode))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Action::ScanDisks => {
|
||||||
|
let disk_wrapper = Arc::clone(&self.disks);
|
||||||
|
let _ = lazy_get_disks(disk_wrapper);
|
||||||
|
self.action_tx.send(Action::SetMode(Mode::ScanDisks))?;
|
||||||
|
}
|
||||||
Action::Select(one, two) => {
|
Action::Select(one, two) => {
|
||||||
self.selections[0] = one;
|
self.selections[0] = one;
|
||||||
self.selections[1] = two;
|
self.selections[1] = two;
|
||||||
|
|
@ -228,8 +247,10 @@ impl App {
|
||||||
match new_mode {
|
match new_mode {
|
||||||
Mode::ScanDisks => {
|
Mode::ScanDisks => {
|
||||||
self.prev_mode = self.cur_mode;
|
self.prev_mode = self.cur_mode;
|
||||||
self.action_tx.send(Action::Select(None, None))?;
|
}
|
||||||
self.action_tx.send(Action::ScanDisks)?;
|
Mode::SelectDisks | Mode::SelectParts => {
|
||||||
|
let disks = self.disks.lock().unwrap();
|
||||||
|
self.action_tx.send(Action::UpdateDiskList(disks.clone()))?;
|
||||||
}
|
}
|
||||||
Mode::Done => {
|
Mode::Done => {
|
||||||
self.action_tx.send(Action::DisplayPopup(String::from(
|
self.action_tx.send(Action::DisplayPopup(String::from(
|
||||||
|
|
@ -330,3 +351,10 @@ fn get_chunks(r: Rect) -> Vec<Rect> {
|
||||||
// Done
|
// Done
|
||||||
chunks
|
chunks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lazy_get_disks(disk_wrapper: Arc<Mutex<Vec<Disk>>>) -> JoinHandle<()> {
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut disks = disk_wrapper.lock().unwrap();
|
||||||
|
*disks = get_disks();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ pub struct Footer {
|
||||||
impl Footer {
|
impl Footer {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
footer_text: String::from("(Enter) to select / (b) to go back / (q) to quit"),
|
footer_text: String::from("(q) to quit"),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ impl Component for Footer {
|
||||||
}
|
}
|
||||||
Action::SetMode(new_mode) => {
|
Action::SetMode(new_mode) => {
|
||||||
self.footer_text = match new_mode {
|
self.footer_text = match new_mode {
|
||||||
Mode::ScanDisks => String::from("(Enter) to start / (q) to quit"),
|
Mode::ScanDisks => String::from("(q) to quit"),
|
||||||
Mode::SelectDisks => String::from(
|
Mode::SelectDisks => String::from(
|
||||||
"(Enter) to select / / (i) to install driver / (r) to rescan / (q) to quit",
|
"(Enter) to select / / (i) to install driver / (r) to rescan / (q) to quit",
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -69,11 +69,6 @@ impl Component for Left {
|
||||||
Action::KeyUp => self.item_list.previous(),
|
Action::KeyUp => self.item_list.previous(),
|
||||||
Action::KeyDown => self.item_list.next(),
|
Action::KeyDown => self.item_list.next(),
|
||||||
Action::Process => match self.mode {
|
Action::Process => match self.mode {
|
||||||
Mode::ScanDisks => {
|
|
||||||
if let Some(command_tx) = self.command_tx.clone() {
|
|
||||||
command_tx.send(Action::ScanDisks)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Mode::Confirm => {
|
Mode::Confirm => {
|
||||||
if let Some(command_tx) = self.command_tx.clone() {
|
if let Some(command_tx) = self.command_tx.clone() {
|
||||||
command_tx.send(Action::NextScreen)?;
|
command_tx.send(Action::NextScreen)?;
|
||||||
|
|
@ -123,17 +118,22 @@ impl Component for Left {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Action::Select(None, None) => self.selections = vec![None, None],
|
|
||||||
Action::Select(Some(index), None) => self.selections[0] = Some(index),
|
Action::Select(Some(index), None) => self.selections[0] = Some(index),
|
||||||
Action::SetMode(new_mode) => {
|
Action::SetMode(new_mode) => {
|
||||||
let prev_mode = self.mode;
|
let prev_mode = self.mode;
|
||||||
self.mode = new_mode;
|
self.mode = new_mode;
|
||||||
match new_mode {
|
match new_mode {
|
||||||
Mode::ScanDisks => self.title_text = String::from(""),
|
Mode::ScanDisks => {
|
||||||
|
self.title_text = String::from("");
|
||||||
|
self.item_list.clear_items();
|
||||||
|
}
|
||||||
Mode::SelectDisks => {
|
Mode::SelectDisks => {
|
||||||
self.selections[0] = None;
|
self.selections[0] = None;
|
||||||
self.selections[1] = None;
|
self.selections[1] = None;
|
||||||
self.title_text = String::from("Select Source and Destination Disks")
|
self.title_text = String::from("Select Source and Destination Disks");
|
||||||
|
if let Some(command_tx) = self.command_tx.clone() {
|
||||||
|
command_tx.send(Action::DismissPopup)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Mode::SelectParts => {
|
Mode::SelectParts => {
|
||||||
self.selections[0] = None;
|
self.selections[0] = None;
|
||||||
|
|
@ -171,7 +171,7 @@ impl Component for Left {
|
||||||
|
|
||||||
// Body (scan disks)
|
// Body (scan disks)
|
||||||
if self.item_list.items.is_empty() {
|
if self.item_list.items.is_empty() {
|
||||||
let paragraph = Paragraph::new(String::from("Press Enter to start!")).block(
|
let paragraph = Paragraph::new(String::new()).block(
|
||||||
Block::default()
|
Block::default()
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.padding(Padding::new(1, 1, 1, 1)),
|
.padding(Padding::new(1, 1, 1, 1)),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use clap::command;
|
|
||||||
// This file is part of Deja-vu.
|
// This file is part of Deja-vu.
|
||||||
//
|
//
|
||||||
// Deja-vu is free software: you can redistribute it and/or modify it
|
// Deja-vu is free software: you can redistribute it and/or modify it
|
||||||
|
|
@ -19,18 +18,12 @@ use ratatui::{prelude::*, widgets::*};
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use super::Component;
|
use super::Component;
|
||||||
use crate::{
|
use crate::{action::Action, app::Mode, config::Config};
|
||||||
action::Action,
|
|
||||||
app::Mode,
|
|
||||||
config::Config,
|
|
||||||
system::disk::{get_disks, Disk},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Popup {
|
pub struct Popup {
|
||||||
command_tx: Option<UnboundedSender<Action>>,
|
command_tx: Option<UnboundedSender<Action>>,
|
||||||
config: Config,
|
config: Config,
|
||||||
disks: Vec<Disk>,
|
|
||||||
popup_text: String,
|
popup_text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,18 +57,7 @@ impl Component for Popup {
|
||||||
}
|
}
|
||||||
Action::DismissPopup => self.popup_text.clear(),
|
Action::DismissPopup => self.popup_text.clear(),
|
||||||
Action::DisplayPopup(new_text) => self.popup_text = String::from(new_text),
|
Action::DisplayPopup(new_text) => self.popup_text = String::from(new_text),
|
||||||
Action::ScanDisks => {
|
Action::SetMode(Mode::ScanDisks) => self.popup_text = String::from("Scanning Disks..."),
|
||||||
if let Some(command_tx) = self.command_tx.clone() {
|
|
||||||
self.disks = get_disks();
|
|
||||||
command_tx.send(Action::NextScreen)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Action::SetMode(Mode::SelectDisks) => {
|
|
||||||
self.popup_text.clear();
|
|
||||||
if let Some(command_tx) = self.command_tx.clone() {
|
|
||||||
command_tx.send(Action::UpdateDiskList(self.disks.clone()))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,14 @@ use ratatui::{prelude::*, widgets::*};
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use super::Component;
|
use super::Component;
|
||||||
use crate::{action::Action, app::Mode, config::Config};
|
use crate::{action::Action, app::Mode, config::Config, system::disk::Disk};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Right {
|
pub struct Right {
|
||||||
command_tx: Option<UnboundedSender<Action>>,
|
command_tx: Option<UnboundedSender<Action>>,
|
||||||
config: Config,
|
config: Config,
|
||||||
cur_mode: Mode,
|
cur_mode: Mode,
|
||||||
|
disks: Vec<Disk>,
|
||||||
prev_mode: Mode,
|
prev_mode: Mode,
|
||||||
all_modes: Vec<Mode>,
|
all_modes: Vec<Mode>,
|
||||||
selections: Vec<Option<usize>>,
|
selections: Vec<Option<usize>>,
|
||||||
|
|
@ -76,6 +77,7 @@ impl Component for Right {
|
||||||
self.cur_mode = new_mode;
|
self.cur_mode = new_mode;
|
||||||
self.all_modes.push(new_mode);
|
self.all_modes.push(new_mode);
|
||||||
}
|
}
|
||||||
|
Action::UpdateDiskList(disks) => self.disks = disks,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
@ -95,8 +97,12 @@ impl Component for Right {
|
||||||
|
|
||||||
// Body
|
// Body
|
||||||
let paragraph = Paragraph::new(format!(
|
let paragraph = Paragraph::new(format!(
|
||||||
"Prev Mode: {:?} // Cur Mode: {:?}\n{:?}\n{:?}",
|
"Prev Mode: {:?} // Cur Mode: {:?}\n{:?}\n{:?}\nDisks: {}",
|
||||||
self.prev_mode, self.cur_mode, self.all_modes, self.selections,
|
self.prev_mode,
|
||||||
|
self.cur_mode,
|
||||||
|
self.all_modes,
|
||||||
|
self.selections,
|
||||||
|
self.disks.len(),
|
||||||
))
|
))
|
||||||
.block(
|
.block(
|
||||||
Block::default()
|
Block::default()
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ impl fmt::Display for PartitionTableType {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_disks() -> Vec<Disk> {
|
pub fn get_disks() -> Vec<Disk> {
|
||||||
let disks;
|
let disks: Vec<Disk>;
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
disks = diskpart::get_disks();
|
disks = diskpart::get_disks();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue