You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1181 lines
42 KiB

4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
5 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
  1. /*
  2. * meli
  3. *
  4. * Copyright 2017-2018 Manos Pitsidianakis
  5. *
  6. * This file is part of meli.
  7. *
  8. * meli is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * meli is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with meli. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. /*! The application's state.
  22. The UI crate has an Box<dyn Component>-Component-System design. The System part, is also the application's state, so they're both merged in the `State` struct.
  23. `State` owns all the Components of the UI. In the application's main event loop, input is handed to the state in the form of `UIEvent` objects which traverse the component graph. Components decide to handle each input or not.
  24. Input is received in the main loop from threads which listen on the stdin for user input, observe folders for file changes etc. The relevant struct is `ThreadEvent`.
  25. */
  26. use super::*;
  27. use crate::plugins::PluginManager;
  28. use melib::backends::{AccountHash, MailboxHash, NotifyFn};
  29. use crate::jobs::JobExecutor;
  30. use crossbeam::channel::{unbounded, Receiver, Sender};
  31. use smallvec::SmallVec;
  32. use std::collections::HashMap;
  33. use std::env;
  34. use std::io::Write;
  35. use std::os::unix::io::RawFd;
  36. use std::sync::Arc;
  37. use std::thread;
  38. use termion::raw::IntoRawMode;
  39. use termion::screen::AlternateScreen;
  40. use termion::{clear, cursor};
  41. pub type StateStdout = termion::screen::AlternateScreen<termion::raw::RawTerminal<std::io::Stdout>>;
  42. struct InputHandler {
  43. pipe: (RawFd, RawFd),
  44. rx: Receiver<InputCommand>,
  45. tx: Sender<InputCommand>,
  46. state_tx: Sender<ThreadEvent>,
  47. control: std::sync::Weak<()>,
  48. }
  49. impl InputHandler {
  50. fn restore(&mut self) {
  51. let working = Arc::new(());
  52. let control = Arc::downgrade(&working);
  53. /* Clear channel without blocking. switch_to_main_screen() issues a kill when
  54. * returning from a fork and there's no input thread, so the newly created thread will
  55. * receive it and die. */
  56. //let _ = self.rx.try_iter().count();
  57. let rx = self.rx.clone();
  58. let pipe = self.pipe.0;
  59. let tx = self.state_tx.clone();
  60. thread::Builder::new()
  61. .name("input-thread".to_string())
  62. .spawn(move || {
  63. get_events(
  64. |i| {
  65. tx.send(ThreadEvent::Input(i)).unwrap();
  66. },
  67. &rx,
  68. pipe,
  69. working,
  70. )
  71. })
  72. .unwrap();
  73. self.control = control;
  74. }
  75. fn kill(&self) {
  76. let _ = nix::unistd::write(self.pipe.1, &[1]);
  77. self.tx.send(InputCommand::Kill).unwrap();
  78. }
  79. fn check(&mut self) {
  80. match self.control.upgrade() {
  81. Some(_) => {}
  82. None => {
  83. debug!("restarting input_thread");
  84. self.restore();
  85. }
  86. }
  87. }
  88. }
  89. /// A context container for loaded settings, accounts, UI changes, etc.
  90. pub struct Context {
  91. pub accounts: Vec<Account>,
  92. pub account_hashes: HashMap<AccountHash, usize>,
  93. pub settings: Settings,
  94. pub runtime_settings: Settings,
  95. /// Areas of the screen that must be redrawn in the next render
  96. pub dirty_areas: VecDeque<Area>,
  97. /// Events queue that components send back to the state
  98. pub replies: VecDeque<UIEvent>,
  99. sender: Sender<ThreadEvent>,
  100. receiver: Receiver<ThreadEvent>,
  101. input_thread: InputHandler,
  102. work_controller: WorkController,
  103. job_executor: Arc<JobExecutor>,
  104. pub children: Vec<std::process::Child>,
  105. pub plugin_manager: PluginManager,
  106. pub temp_files: Vec<File>,
  107. }
  108. impl Context {
  109. pub fn replies(&mut self) -> smallvec::SmallVec<[UIEvent; 8]> {
  110. self.replies.drain(0..).collect()
  111. }
  112. pub fn input_kill(&self) {
  113. self.input_thread.kill();
  114. }
  115. pub fn restore_input(&mut self) {
  116. self.input_thread.restore();
  117. }
  118. pub fn is_online(&mut self, account_pos: usize) -> Result<()> {
  119. let Context {
  120. ref mut accounts,
  121. ref mut replies,
  122. ..
  123. } = self;
  124. let was_online = accounts[account_pos].is_online.is_ok();
  125. let ret = accounts[account_pos].is_online();
  126. if ret.is_ok() {
  127. if !was_online {
  128. debug!("inserting mailbox hashes:");
  129. for mailbox_node in accounts[account_pos].list_mailboxes() {
  130. debug!(
  131. "hash & mailbox: {:?} {}",
  132. mailbox_node.hash,
  133. accounts[account_pos][&mailbox_node.hash].name()
  134. );
  135. }
  136. accounts[account_pos].watch();
  137. replies.push_back(UIEvent::AccountStatusChange(account_pos));
  138. }
  139. }
  140. if ret.is_ok() != was_online {
  141. replies.push_back(UIEvent::AccountStatusChange(account_pos));
  142. }
  143. ret
  144. }
  145. pub fn work_controller(&self) -> &WorkController {
  146. &self.work_controller
  147. }
  148. }
  149. /// A State object to manage and own components and components of the UI. `State` is responsible for
  150. /// managing the terminal and interfacing with `melib`
  151. pub struct State {
  152. cols: usize,
  153. rows: usize,
  154. grid: CellBuffer,
  155. overlay_grid: CellBuffer,
  156. draw_rate_limit: RateLimit,
  157. stdout: Option<StateStdout>,
  158. child: Option<ForkType>,
  159. draw_horizontal_segment_fn: fn(&mut CellBuffer, &mut StateStdout, usize, usize, usize) -> (),
  160. pub mode: UIMode,
  161. overlay: Vec<Box<dyn Component>>,
  162. components: Vec<Box<dyn Component>>,
  163. pub context: Context,
  164. timer: thread::JoinHandle<()>,
  165. display_messages: SmallVec<[DisplayMessage; 8]>,
  166. display_messages_expiration_start: Option<UnixTimestamp>,
  167. display_messages_active: bool,
  168. display_messages_pos: usize,
  169. }
  170. #[derive(Debug)]
  171. struct DisplayMessage {
  172. timestamp: UnixTimestamp,
  173. msg: String,
  174. }
  175. impl Drop for State {
  176. fn drop(&mut self) {
  177. // When done, restore the defaults to avoid messing with the terminal.
  178. self.switch_to_main_screen();
  179. use nix::sys::wait::{waitpid, WaitPidFlag};
  180. for child in self.context.children.iter_mut() {
  181. if let Err(err) = waitpid(
  182. nix::unistd::Pid::from_raw(child.id() as i32),
  183. Some(WaitPidFlag::WNOHANG),
  184. ) {
  185. debug!("Failed to wait on subprocess {}: {}", child.id(), err);
  186. }
  187. }
  188. if let Some(ForkType::Embed(child_pid)) = self.child.take() {
  189. /* Try wait, we don't want to block */
  190. if let Err(e) = waitpid(child_pid, Some(WaitPidFlag::WNOHANG)) {
  191. debug!("Failed to wait on subprocess {}: {}", child_pid, e);
  192. }
  193. }
  194. }
  195. }
  196. impl State {
  197. pub fn new(
  198. settings: Option<Settings>,
  199. sender: Sender<ThreadEvent>,
  200. receiver: Receiver<ThreadEvent>,
  201. ) -> Result<Self> {
  202. /*
  203. * Create async channel to block the input-thread if we need to fork and stop it from reading
  204. * stdin, see get_events() for details
  205. * */
  206. let input_thread = unbounded();
  207. let input_thread_pipe = nix::unistd::pipe()
  208. .map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>)?;
  209. let mut backends = Backends::new();
  210. let settings = if let Some(settings) = settings {
  211. settings
  212. } else {
  213. Settings::new()?
  214. };
  215. let mut plugin_manager = PluginManager::new();
  216. for (_, p) in settings.plugins.clone() {
  217. if crate::plugins::PluginKind::Backend == p.kind() {
  218. debug!("registering {:?}", &p);
  219. crate::plugins::backend::PluginBackend::register(
  220. plugin_manager.listener(),
  221. p.clone(),
  222. &mut backends,
  223. );
  224. }
  225. plugin_manager.register(p)?;
  226. }
  227. let termsize = termion::terminal_size()?;
  228. let cols = termsize.0 as usize;
  229. let rows = termsize.1 as usize;
  230. let mut account_hashes = HashMap::with_capacity_and_hasher(1, Default::default());
  231. let work_controller = WorkController::new(sender.clone());
  232. let job_executor = Arc::new(JobExecutor::new(sender.clone()));
  233. let accounts: Vec<Account> = {
  234. let mut file_accs = settings
  235. .accounts
  236. .iter()
  237. .collect::<Vec<(&String, &AccountConf)>>();
  238. file_accs.sort_by(|a, b| a.0.cmp(&b.0));
  239. file_accs
  240. .into_iter()
  241. .enumerate()
  242. .map(|(index, (n, a_s))| {
  243. let sender = sender.clone();
  244. let account_hash = {
  245. use std::collections::hash_map::DefaultHasher;
  246. use std::hash::Hasher;
  247. let mut hasher = DefaultHasher::new();
  248. hasher.write(n.as_bytes());
  249. hasher.finish()
  250. };
  251. account_hashes.insert(account_hash, index);
  252. Account::new(
  253. index,
  254. account_hash,
  255. n.to_string(),
  256. a_s.clone(),
  257. &backends,
  258. work_controller.get_context(),
  259. job_executor.clone(),
  260. sender.clone(),
  261. NotifyFn::new(Box::new(move |f: MailboxHash| {
  262. sender
  263. .send(ThreadEvent::UIEvent(UIEvent::WorkerProgress(
  264. account_hash,
  265. f,
  266. )))
  267. .unwrap();
  268. })),
  269. )
  270. })
  271. .collect::<Result<Vec<Account>>>()?
  272. };
  273. let timer = {
  274. let sender = sender.clone();
  275. thread::Builder::new().spawn(move || {
  276. let sender = sender;
  277. loop {
  278. thread::park();
  279. sender.send(ThreadEvent::Pulse).unwrap();
  280. thread::sleep(std::time::Duration::from_millis(100));
  281. }
  282. })
  283. }?;
  284. timer.thread().unpark();
  285. let working = Arc::new(());
  286. let control = Arc::downgrade(&working);
  287. let mut s = State {
  288. cols,
  289. rows,
  290. grid: CellBuffer::new(cols, rows, Cell::with_char(' ')),
  291. overlay_grid: CellBuffer::new(cols, rows, Cell::with_char(' ')),
  292. stdout: None,
  293. child: None,
  294. mode: UIMode::Normal,
  295. components: Vec::with_capacity(8),
  296. overlay: Vec::new(),
  297. timer,
  298. draw_rate_limit: RateLimit::new(1, 3),
  299. draw_horizontal_segment_fn: if settings.terminal.use_color() {
  300. State::draw_horizontal_segment
  301. } else {
  302. State::draw_horizontal_segment_no_color
  303. },
  304. display_messages: SmallVec::new(),
  305. display_messages_expiration_start: None,
  306. display_messages_pos: 0,
  307. display_messages_active: false,
  308. context: Context {
  309. accounts,
  310. account_hashes,
  311. settings: settings.clone(),
  312. runtime_settings: settings,
  313. dirty_areas: VecDeque::with_capacity(5),
  314. replies: VecDeque::with_capacity(5),
  315. temp_files: Vec::new(),
  316. work_controller,
  317. job_executor,
  318. children: vec![],
  319. plugin_manager,
  320. input_thread: InputHandler {
  321. pipe: input_thread_pipe,
  322. rx: input_thread.1,
  323. tx: input_thread.0,
  324. control,
  325. state_tx: sender.clone(),
  326. },
  327. sender,
  328. receiver,
  329. },
  330. };
  331. s.draw_rate_limit
  332. .timer
  333. .set_value(std::time::Duration::from_millis(3));
  334. if s.context.settings.terminal.ascii_drawing {
  335. s.grid.set_ascii_drawing(true);
  336. s.overlay_grid.set_ascii_drawing(true);
  337. }
  338. s.switch_to_alternate_screen();
  339. for i in 0..s.context.accounts.len() {
  340. if !s.context.accounts[i].backend_capabilities.is_remote {
  341. s.context.accounts[i].watch();
  342. }
  343. if s.context.is_online(i).is_ok() && s.context.accounts[i].is_empty() {
  344. //return Err(MeliError::new(format!(
  345. // "Account {} has no mailboxes configured.",
  346. // s.context.accounts[i].name()
  347. //)));
  348. }
  349. }
  350. s.context.restore_input();
  351. Ok(s)
  352. }
  353. /*
  354. * When we receive a mailbox hash from a watcher thread,
  355. * we match the hash to the index of the mailbox, request a reload
  356. * and startup a thread to remind us to poll it every now and then till it's finished.
  357. */
  358. pub fn refresh_event(&mut self, event: RefreshEvent) {
  359. let account_hash = event.account_hash();
  360. let mailbox_hash = event.mailbox_hash();
  361. if let Some(&idxa) = self.context.account_hashes.get(&account_hash) {
  362. if self.context.accounts[idxa]
  363. .mailbox_entries
  364. .contains_key(&mailbox_hash)
  365. {
  366. if self.context.accounts[idxa].load(mailbox_hash).is_err() {
  367. self.context.replies.push_back(UIEvent::from(event));
  368. return;
  369. }
  370. let Context {
  371. ref mut accounts, ..
  372. } = &mut self.context;
  373. if let Some(notification) = accounts[idxa].reload(event, mailbox_hash) {
  374. if let UIEvent::Notification(_, _, _) = notification {
  375. self.rcv_event(UIEvent::MailboxUpdate((idxa, mailbox_hash)));
  376. }
  377. self.rcv_event(notification);
  378. }
  379. } else {
  380. if let melib::backends::RefreshEventKind::Failure(err) = event.kind() {
  381. debug!(err);
  382. }
  383. }
  384. }
  385. }
  386. pub fn new_thread(&mut self, id: thread::ThreadId, name: String) {
  387. self.context
  388. .work_controller
  389. .static_threads
  390. .lock()
  391. .unwrap()
  392. .insert(id, name.into());
  393. }
  394. /// Switch back to the terminal's main screen (The command line the user sees before opening
  395. /// the application)
  396. pub fn switch_to_main_screen(&mut self) {
  397. write!(
  398. self.stdout(),
  399. "{}{}{}{}",
  400. termion::screen::ToMainScreen,
  401. cursor::Show,
  402. RestoreWindowTitleIconFromStack,
  403. BracketModeEnd,
  404. )
  405. .unwrap();
  406. self.flush();
  407. self.stdout = None;
  408. }
  409. pub fn switch_to_alternate_screen(&mut self) {
  410. let s = std::io::stdout();
  411. let mut stdout = AlternateScreen::from(s.into_raw_mode().unwrap());
  412. write!(
  413. &mut stdout,
  414. "{save_title_to_stack}{}{}{}{window_title}{}{}",
  415. termion::screen::ToAlternateScreen,
  416. cursor::Hide,
  417. clear::All,
  418. cursor::Goto(1, 1),
  419. BracketModeStart,
  420. save_title_to_stack = SaveWindowTitleIconToStack,
  421. window_title = if let Some(ref title) = self.context.settings.terminal.window_title {
  422. format!("\x1b]2;{}\x07", title)
  423. } else {
  424. String::new()
  425. },
  426. )
  427. .unwrap();
  428. self.stdout = Some(stdout);
  429. self.flush();
  430. }
  431. pub fn receiver(&self) -> Receiver<ThreadEvent> {
  432. self.context.receiver.clone()
  433. }
  434. pub fn sender(&self) -> Sender<ThreadEvent> {
  435. self.context.sender.clone()
  436. }
  437. pub fn restore_input(&mut self) {
  438. self.context.restore_input();
  439. }
  440. /// On `SIGWNICH` the `State` redraws itself according to the new terminal size.
  441. pub fn update_size(&mut self) {
  442. let termsize = termion::terminal_size().ok();
  443. let termcols = termsize.map(|(w, _)| w);
  444. let termrows = termsize.map(|(_, h)| h);
  445. if termcols.unwrap_or(72) as usize != self.cols
  446. || termrows.unwrap_or(120) as usize != self.rows
  447. {
  448. debug!(
  449. "Size updated, from ({}, {}) -> ({:?}, {:?})",
  450. self.cols, self.rows, termcols, termrows
  451. );
  452. }
  453. self.cols = termcols.unwrap_or(72) as usize;
  454. self.rows = termrows.unwrap_or(120) as usize;
  455. self.grid.resize(self.cols, self.rows, Cell::with_char(' '));
  456. self.overlay_grid
  457. .resize(self.cols, self.rows, Cell::with_char(' '));
  458. self.rcv_event(UIEvent::Resize);
  459. // Invalidate dirty areas.
  460. self.context.dirty_areas.clear();
  461. }
  462. /// Force a redraw for all dirty components.
  463. pub fn redraw(&mut self) {
  464. if !self.draw_rate_limit.tick() {
  465. return;
  466. }
  467. for i in 0..self.components.len() {
  468. self.draw_component(i);
  469. }
  470. let mut areas: smallvec::SmallVec<[Area; 8]> =
  471. self.context.dirty_areas.drain(0..).collect();
  472. if self.display_messages_active {
  473. let now = melib::datetime::now();
  474. if self
  475. .display_messages_expiration_start
  476. .map(|t| t + 5 < now)
  477. .unwrap_or(false)
  478. {
  479. self.display_messages_active = false;
  480. self.display_messages_expiration_start = None;
  481. areas.push((
  482. (0, 0),
  483. (self.cols.saturating_sub(1), self.rows.saturating_sub(1)),
  484. ));
  485. }
  486. }
  487. /* Sort by x_start, ie upper_left corner's x coordinate */
  488. areas.sort_by(|a, b| (a.0).0.partial_cmp(&(b.0).0).unwrap());
  489. /* draw each dirty area */
  490. let rows = self.rows;
  491. for y in 0..rows {
  492. let mut segment = None;
  493. for ((x_start, y_start), (x_end, y_end)) in &areas {
  494. if y < *y_start || y > *y_end {
  495. continue;
  496. }
  497. if let Some((x_start, x_end)) = segment.take() {
  498. (self.draw_horizontal_segment_fn)(
  499. &mut self.grid,
  500. self.stdout.as_mut().unwrap(),
  501. x_start,
  502. x_end,
  503. y,
  504. );
  505. }
  506. match segment {
  507. ref mut s @ None => {
  508. *s = Some((*x_start, *x_end));
  509. }
  510. ref mut s @ Some(_) if s.unwrap().1 < *x_start => {
  511. (self.draw_horizontal_segment_fn)(
  512. &mut self.grid,
  513. self.stdout.as_mut().unwrap(),
  514. s.unwrap().0,
  515. s.unwrap().1,
  516. y,
  517. );
  518. *s = Some((*x_start, *x_end));
  519. }
  520. ref mut s @ Some(_) if s.unwrap().1 < *x_end => {
  521. (self.draw_horizontal_segment_fn)(
  522. &mut self.grid,
  523. self.stdout.as_mut().unwrap(),
  524. s.unwrap().0,
  525. s.unwrap().1,
  526. y,
  527. );
  528. *s = Some((s.unwrap().1, *x_end));
  529. }
  530. Some((_, ref mut x)) => {
  531. *x = *x_end;
  532. }
  533. }
  534. }
  535. if let Some((x_start, x_end)) = segment {
  536. (self.draw_horizontal_segment_fn)(
  537. &mut self.grid,
  538. self.stdout.as_mut().unwrap(),
  539. x_start,
  540. x_end,
  541. y,
  542. );
  543. }
  544. }
  545. if self.display_messages_active {
  546. if let Some(DisplayMessage {
  547. ref timestamp,
  548. ref msg,
  549. ..
  550. }) = self.display_messages.get(self.display_messages_pos)
  551. {
  552. let noto_colors = crate::conf::value(&self.context, "status.notification");
  553. use crate::melib::text_processing::{Reflow, TextProcessing};
  554. let msg_lines = msg.split_lines_reflow(Reflow::All, Some(self.cols / 3));
  555. let width = msg_lines
  556. .iter()
  557. .map(|line| line.grapheme_len() + 4)
  558. .max()
  559. .unwrap_or(0);
  560. let displ_area = place_in_area(
  561. (
  562. (0, 0),
  563. (self.cols.saturating_sub(1), self.rows.saturating_sub(1)),
  564. ),
  565. (width, std::cmp::min(self.rows, msg_lines.len() + 4)),
  566. false,
  567. false,
  568. );
  569. for row in self.overlay_grid.bounds_iter(displ_area) {
  570. for c in row {
  571. self.overlay_grid[c]
  572. .set_ch(' ')
  573. .set_fg(noto_colors.fg)
  574. .set_bg(noto_colors.bg)
  575. .set_attrs(noto_colors.attrs);
  576. }
  577. }
  578. let ((x, mut y), box_displ_area_bottom_right) =
  579. create_box(&mut self.overlay_grid, displ_area);
  580. for line in msg_lines
  581. .into_iter()
  582. .chain(Some(String::new()))
  583. .chain(Some(melib::datetime::timestamp_to_string(*timestamp, None)))
  584. {
  585. write_string_to_grid(
  586. &line,
  587. &mut self.overlay_grid,
  588. noto_colors.fg,
  589. noto_colors.bg,
  590. noto_colors.attrs,
  591. ((x, y), box_displ_area_bottom_right),
  592. Some(x),
  593. );
  594. y += 1;
  595. }
  596. if self.display_messages.len() > 1 {
  597. write_string_to_grid(
  598. if self.display_messages_pos == 0 {
  599. "Next: >"
  600. } else if self.display_messages_pos + 1 == self.display_messages.len() {
  601. "Prev: <"
  602. } else {
  603. "Prev: <, Next: >"
  604. },
  605. &mut self.overlay_grid,
  606. noto_colors.fg,
  607. noto_colors.bg,
  608. noto_colors.attrs,
  609. ((x, y), box_displ_area_bottom_right),
  610. Some(x),
  611. );
  612. }
  613. for y in get_y(upper_left!(displ_area))..=get_y(bottom_right!(displ_area)) {
  614. (self.draw_horizontal_segment_fn)(
  615. &mut self.overlay_grid,
  616. self.stdout.as_mut().unwrap(),
  617. get_x(upper_left!(displ_area)),
  618. get_x(bottom_right!(displ_area)),
  619. y,
  620. );
  621. }
  622. }
  623. }
  624. if !self.overlay.is_empty() {
  625. let area = center_area(
  626. (
  627. (0, 0),
  628. (self.cols.saturating_sub(1), self.rows.saturating_sub(1)),
  629. ),
  630. (
  631. if self.cols / 3 > 30 {
  632. self.cols / 3
  633. } else {
  634. self.cols
  635. },
  636. if self.rows / 5 > 10 {
  637. self.rows / 5
  638. } else {
  639. self.rows
  640. },
  641. ),
  642. );
  643. copy_area(&mut self.overlay_grid, &self.grid, area, area);
  644. self.overlay
  645. .get_mut(0)
  646. .unwrap()
  647. .draw(&mut self.overlay_grid, area, &mut self.context);
  648. for y in get_y(upper_left!(area))..=get_y(bottom_right!(area)) {
  649. (self.draw_horizontal_segment_fn)(
  650. &mut self.overlay_grid,
  651. self.stdout.as_mut().unwrap(),
  652. get_x(upper_left!(area)),
  653. get_x(bottom_right!(area)),
  654. y,
  655. );
  656. }
  657. }
  658. self.flush();
  659. }
  660. /// Draw only a specific `area` on the screen.
  661. fn draw_horizontal_segment(
  662. grid: &mut CellBuffer,
  663. stdout: &mut StateStdout,
  664. x_start: usize,
  665. x_end: usize,
  666. y: usize,
  667. ) {
  668. write!(
  669. stdout,
  670. "{}",
  671. cursor::Goto(x_start as u16 + 1, (y + 1) as u16)
  672. )
  673. .unwrap();
  674. let mut current_fg = Color::Default;
  675. let mut current_bg = Color::Default;
  676. let mut current_attrs = Attr::DEFAULT;
  677. write!(stdout, "\x1B[m").unwrap();
  678. for x in x_start..=x_end {
  679. let c = &grid[(x, y)];
  680. if c.attrs() != current_attrs {
  681. c.attrs().write(current_attrs, stdout).unwrap();
  682. current_attrs = c.attrs();
  683. }
  684. if c.bg() != current_bg {
  685. c.bg().write_bg(stdout).unwrap();
  686. current_bg = c.bg();
  687. }
  688. if c.fg() != current_fg {
  689. c.fg().write_fg(stdout).unwrap();
  690. current_fg = c.fg();
  691. }
  692. if !c.empty() {
  693. write!(stdout, "{}", c.ch()).unwrap();
  694. }
  695. }
  696. }
  697. fn draw_horizontal_segment_no_color(
  698. grid: &mut CellBuffer,
  699. stdout: &mut StateStdout,
  700. x_start: usize,
  701. x_end: usize,
  702. y: usize,
  703. ) {
  704. write!(
  705. stdout,
  706. "{}",
  707. cursor::Goto(x_start as u16 + 1, (y + 1) as u16)
  708. )
  709. .unwrap();
  710. let mut current_attrs = Attr::DEFAULT;
  711. write!(stdout, "\x1B[m").unwrap();
  712. for x in x_start..=x_end {
  713. let c = &grid[(x, y)];
  714. if c.attrs() != current_attrs {
  715. c.attrs().write(current_attrs, stdout).unwrap();
  716. current_attrs = c.attrs();
  717. }
  718. if !c.empty() {
  719. write!(stdout, "{}", c.ch()).unwrap();
  720. }
  721. }
  722. }
  723. /// Draw the entire screen from scratch.
  724. pub fn render(&mut self) {
  725. self.update_size();
  726. let cols = self.cols;
  727. let rows = self.rows;
  728. self.context
  729. .dirty_areas
  730. .push_back(((0, 0), (cols - 1, rows - 1)));
  731. self.redraw();
  732. }
  733. pub fn draw_component(&mut self, idx: usize) {
  734. let component = &mut self.components[idx];
  735. let upper_left = (0, 0);
  736. let bottom_right = (self.cols - 1, self.rows - 1);
  737. if component.is_dirty() {
  738. component.draw(
  739. &mut self.grid,
  740. (upper_left, bottom_right),
  741. &mut self.context,
  742. );
  743. }
  744. }
  745. pub fn can_quit_cleanly(&mut self) -> bool {
  746. let State {
  747. ref mut components,
  748. ref context,
  749. ..
  750. } = self;
  751. components.iter_mut().all(|c| c.can_quit_cleanly(context))
  752. }
  753. pub fn register_component(&mut self, component: Box<dyn Component>) {
  754. self.components.push(component);
  755. }
  756. /// Convert user commands to actions/method calls.
  757. fn exec_command(&mut self, cmd: Action) {
  758. match cmd {
  759. SetEnv(key, val) => {
  760. env::set_var(key.as_str(), val.as_str());
  761. }
  762. PrintEnv(key) => {
  763. self.context
  764. .replies
  765. .push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(
  766. env::var(key.as_str()).unwrap_or_else(|e| e.to_string()),
  767. )));
  768. }
  769. Mailbox(account_name, op) => {
  770. if let Some(account) = self
  771. .context
  772. .accounts
  773. .iter_mut()
  774. .find(|a| a.name() == account_name)
  775. {
  776. match account.mailbox_operation(op) {
  777. Err(err) => {
  778. self.context.replies.push_back(UIEvent::StatusEvent(
  779. StatusEvent::DisplayMessage(err.to_string()),
  780. ));
  781. }
  782. Ok(msg) => {
  783. self.context.replies.push_back(UIEvent::StatusEvent(
  784. StatusEvent::DisplayMessage(format!("`{}`: {}", account_name, msg)),
  785. ));
  786. }
  787. }
  788. } else {
  789. self.context.replies.push_back(UIEvent::StatusEvent(
  790. StatusEvent::DisplayMessage(format!(
  791. "Account with name `{}` not found.",
  792. account_name
  793. )),
  794. ));
  795. }
  796. }
  797. #[cfg(feature = "sqlite3")]
  798. AccountAction(ref account_name, ReIndex) => {
  799. let account_index = if let Some(a) = self
  800. .context
  801. .accounts
  802. .iter()
  803. .position(|acc| acc.name() == account_name)
  804. {
  805. a
  806. } else {
  807. self.context.replies.push_back(UIEvent::Notification(
  808. None,
  809. format!("Account {} was not found.", account_name),
  810. Some(NotificationType::ERROR),
  811. ));
  812. return;
  813. };
  814. if *self.context.accounts[account_index]
  815. .settings
  816. .conf
  817. .search_backend()
  818. != crate::conf::SearchBackend::Sqlite3
  819. {
  820. self.context.replies.push_back(UIEvent::Notification(
  821. None,
  822. format!(
  823. "Account {} doesn't have an sqlite3 search backend.",
  824. account_name
  825. ),
  826. Some(NotificationType::ERROR),
  827. ));
  828. return;
  829. }
  830. match crate::sqlite3::index(&mut self.context, account_index) {
  831. Ok(job) => {
  832. let (channel, handle, job_id) =
  833. self.context.job_executor.spawn_blocking(job);
  834. self.context.accounts[account_index].active_jobs.insert(
  835. job_id,
  836. crate::conf::accounts::JobRequest::Generic {
  837. name: "Message index rebuild".into(),
  838. handle,
  839. channel,
  840. },
  841. );
  842. self.context.replies.push_back(UIEvent::Notification(
  843. None,
  844. "Message index rebuild started.".to_string(),
  845. Some(NotificationType::INFO),
  846. ));
  847. }
  848. Err(e) => {
  849. self.context.replies.push_back(UIEvent::Notification(
  850. None,
  851. format!("Message index rebuild failed: {}.", e),
  852. Some(NotificationType::ERROR),
  853. ));
  854. }
  855. }
  856. }
  857. #[cfg(not(feature = "sqlite3"))]
  858. AccountAction(ref account_name, ReIndex) => {
  859. self.context.replies.push_back(UIEvent::Notification(
  860. None,
  861. "Message index rebuild failed: meli is not built with sqlite3 support."
  862. .to_string(),
  863. Some(NotificationType::ERROR),
  864. ));
  865. }
  866. AccountAction(ref account_name, PrintAccountSetting(ref setting)) => {
  867. let path = setting.split(".").collect::<SmallVec<[&str; 16]>>();
  868. if let Some(pos) = self
  869. .context
  870. .accounts
  871. .iter()
  872. .position(|a| a.name() == account_name)
  873. {
  874. self.context.replies.push_back(UIEvent::StatusEvent(
  875. StatusEvent::UpdateStatus(format!(
  876. "{}",
  877. self.context.accounts[pos]
  878. .settings
  879. .lookup("settings", &path)
  880. .unwrap_or_else(|err| err.to_string())
  881. )),
  882. ));
  883. } else {
  884. self.context.replies.push_back(UIEvent::Notification(
  885. None,
  886. format!("Account {} was not found.", account_name),
  887. Some(NotificationType::ERROR),
  888. ));
  889. return;
  890. }
  891. }
  892. PrintSetting(ref setting) => {
  893. let path = setting.split(".").collect::<SmallVec<[&str; 16]>>();
  894. self.context
  895. .replies
  896. .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(format!(
  897. "{}",
  898. self.context
  899. .settings
  900. .lookup("settings", &path)
  901. .unwrap_or_else(|err| err.to_string())
  902. ))));
  903. }
  904. v => {
  905. self.rcv_event(UIEvent::Action(v));
  906. }
  907. }
  908. }
  909. /// The application's main loop sends `UIEvents` to state via this method.
  910. pub fn rcv_event(&mut self, mut event: UIEvent) {
  911. if let UIEvent::Input(_) = event {
  912. if self.display_messages_expiration_start.is_none() {
  913. self.display_messages_expiration_start = Some(melib::datetime::now());
  914. }
  915. }
  916. match event {
  917. // Command type is handled only by State.
  918. UIEvent::Command(cmd) => {
  919. if let Ok(action) = parse_command(&cmd.as_bytes()) {
  920. if action.needs_confirmation() {
  921. self.overlay.push(Box::new(UIConfirmationDialog::new(
  922. "You sure?",
  923. vec![(true, "yes".to_string()), (false, "no".to_string())],
  924. true,
  925. Some(Box::new(move |id: ComponentId, result: bool| {
  926. Some(UIEvent::FinishedUIDialog(
  927. id,
  928. Box::new(if result { Some(action) } else { None }),
  929. ))
  930. })),
  931. &mut self.context,
  932. )));
  933. } else {
  934. self.exec_command(action);
  935. }
  936. } else {
  937. self.context.replies.push_back(UIEvent::StatusEvent(
  938. StatusEvent::DisplayMessage("invalid command".to_string()),
  939. ));
  940. }
  941. return;
  942. }
  943. UIEvent::Fork(ForkType::Finished) => {
  944. /*
  945. * Fork has finished in the past.
  946. * We're back in the AlternateScreen, but the cursor is reset to Shown, so fix
  947. * it.
  948. write!(self.stdout(), "{}", cursor::Hide,).unwrap();
  949. self.flush();
  950. */
  951. self.switch_to_main_screen();
  952. self.switch_to_alternate_screen();
  953. self.context.restore_input();
  954. return;
  955. }
  956. UIEvent::Fork(ForkType::Generic(child)) => {
  957. self.context.children.push(child);
  958. return;
  959. }
  960. UIEvent::Fork(child) => {
  961. self.mode = UIMode::Fork;
  962. self.child = Some(child);
  963. return;
  964. }
  965. UIEvent::WorkerProgress(account_hash, mailbox_hash) => {
  966. if let Some(&account_idx) = self.context.account_hashes.get(&account_hash) {
  967. let _ = self.context.accounts[account_idx].load(mailbox_hash);
  968. }
  969. return;
  970. }
  971. UIEvent::ChangeMode(m) => {
  972. self.context
  973. .sender
  974. .send(ThreadEvent::UIEvent(UIEvent::ChangeMode(m)))
  975. .unwrap();
  976. }
  977. UIEvent::Timer(id) if id == self.draw_rate_limit.id() => {
  978. self.draw_rate_limit.reset();
  979. self.redraw();
  980. return;
  981. }
  982. UIEvent::Input(Key::Alt('<')) => {
  983. self.display_messages_expiration_start = Some(melib::datetime::now());
  984. self.display_messages_active = true;
  985. self.display_messages_pos = self.display_messages_pos.saturating_sub(1);
  986. return;
  987. }
  988. UIEvent::Input(Key::Alt('>')) => {
  989. self.display_messages_expiration_start = Some(melib::datetime::now());
  990. self.display_messages_active = true;
  991. self.display_messages_pos = std::cmp::min(
  992. self.display_messages.len().saturating_sub(1),
  993. self.display_messages_pos + 1,
  994. );
  995. return;
  996. }
  997. UIEvent::StatusEvent(StatusEvent::DisplayMessage(ref msg)) => {
  998. self.display_messages.push(DisplayMessage {
  999. timestamp: melib::datetime::now(),
  1000. msg: msg.clone(),
  1001. });
  1002. self.display_messages_active = true;
  1003. self.display_messages_expiration_start = None;
  1004. self.display_messages_pos = self.display_messages.len() - 1;
  1005. self.redraw();
  1006. }
  1007. UIEvent::FinishedUIDialog(ref id, ref mut results)
  1008. if self.overlay.iter().any(|c| c.id() == *id) =>
  1009. {
  1010. if let Some(Some(ref mut action)) = results.downcast_mut::<Option<Action>>() {
  1011. self.exec_command(std::mem::replace(action, Action::ToggleThreadSnooze));
  1012. let pos = self.overlay.iter().position(|c| c.id() == *id).unwrap();
  1013. self.overlay.remove(pos);
  1014. return;
  1015. }
  1016. }
  1017. UIEvent::GlobalUIDialog(dialog) => {
  1018. self.overlay.push(dialog);
  1019. return;
  1020. }
  1021. _ => {}
  1022. }
  1023. let Self {
  1024. ref mut components,
  1025. ref mut context,
  1026. ref mut overlay,
  1027. ..
  1028. } = self;
  1029. /* inform each component */
  1030. for c in overlay.iter_mut().chain(components.iter_mut()) {
  1031. if c.process_event(&mut event, context) {
  1032. break;
  1033. }
  1034. }
  1035. if !self.context.replies.is_empty() {
  1036. let replies: smallvec::SmallVec<[UIEvent; 8]> =
  1037. self.context.replies.drain(0..).collect();
  1038. // Pass replies to self and call count on the map iterator to force evaluation
  1039. replies.into_iter().map(|r| self.rcv_event(r)).count();
  1040. }
  1041. }
  1042. pub fn try_wait_on_child(&mut self) -> Option<bool> {
  1043. let should_return_flag = match self.child {
  1044. Some(ForkType::NewDraft(_, ref mut c)) => {
  1045. let w = c.try_wait();
  1046. match w {
  1047. Ok(Some(_)) => true,
  1048. Ok(None) => false,
  1049. Err(e) => {
  1050. log(
  1051. format!("Failed to wait on editor process: {}", e.to_string()),
  1052. ERROR,
  1053. );
  1054. return None;
  1055. }
  1056. }
  1057. }
  1058. Some(ForkType::Generic(ref mut c)) => {
  1059. let w = c.try_wait();
  1060. match w {
  1061. Ok(Some(_)) => true,
  1062. Ok(None) => false,
  1063. Err(e) => {
  1064. log(
  1065. format!("Failed to wait on child process: {}", e.to_string()),
  1066. ERROR,
  1067. );
  1068. return None;
  1069. }
  1070. }
  1071. }
  1072. Some(ForkType::Finished) => {
  1073. /* Fork has already finished */
  1074. std::mem::replace(&mut self.child, None);
  1075. return None;
  1076. }
  1077. _ => {
  1078. return None;
  1079. }
  1080. };
  1081. if should_return_flag {
  1082. return Some(true);
  1083. }
  1084. Some(false)
  1085. }
  1086. fn flush(&mut self) {
  1087. if let Some(s) = self.stdout.as_mut() {
  1088. s.flush().unwrap();
  1089. }
  1090. }
  1091. fn stdout(&mut self) -> &mut StateStdout {
  1092. self.stdout.as_mut().unwrap()
  1093. }
  1094. pub fn check_accounts(&mut self) {
  1095. let mut ctr = 0;
  1096. for i in 0..self.context.accounts.len() {
  1097. if self.context.is_online(i).is_ok() {
  1098. ctr += 1;
  1099. }
  1100. }
  1101. if ctr != self.context.accounts.len() {
  1102. self.timer.thread().unpark();
  1103. }
  1104. self.context.input_thread.check();
  1105. }
  1106. }