1 use {
2     super::*,
3     crate::{
4         browser::BrowserState,
5         command::{Command, Sequence},
6         conf::Conf,
7         display::{Areas, Screen, W},
8         errors::ProgramError,
9         file_sum, git,
10         launchable::Launchable,
11         path::closest_dir,
12         skin::*,
13         stage::Stage,
14         task_sync::{Dam, Either},
15         verb::Internal,
16     },
17     crossbeam::channel::{
18         Receiver,
19         Sender,
20         unbounded,
21     },
22     crossterm::event::Event,
23     std::{
24         io::Write,
25         path::PathBuf,
26         sync::{Arc, Mutex},
27     },
28     strict::NonEmptyVec,
29     termimad::EventSource,
30 };
31 
32 
33 /// The GUI
34 pub struct App {
35     /// dimensions of the screen
36     screen: Screen,
37 
38     /// the panels of the application, at least one
39     panels: NonEmptyVec<Panel>,
40 
41     /// index of the currently focused panel
42     active_panel_idx: usize,
43 
44     /// whether the app is in the (uncancellable) process of quitting
45     quitting: bool,
46 
47     /// what must be done after having closed the TUI
48     launch_at_end: Option<Launchable>,
49 
50     /// a count of all panels created
51     created_panels_count: usize,
52 
53     /// the panel dedicated to preview, if any
54     preview_panel: Option<PanelId>,
55 
56     stage_panel: Option<PanelId>,
57 
58     /// an optional copy of the root for the --server
59     shared_root: Option<Arc<Mutex<PathBuf>>>,
60 
61     /// sender to the sequence channel
62     tx_seqs: Sender<Sequence>,
63 
64     /// receiver to listen to the sequence channel
65     rx_seqs: Receiver<Sequence>,
66 }
67 
68 impl App {
69 
new( con: &AppContext, ) -> Result<App, ProgramError>70     pub fn new(
71         con: &AppContext,
72     ) -> Result<App, ProgramError> {
73         let screen = Screen::new(con)?;
74         let panel = Panel::new(
75             PanelId::from(0),
76             Box::new(
77                 BrowserState::new(
78                     con.launch_args.root.clone(),
79                     con.launch_args.tree_options.clone(),
80                     screen,
81                     con,
82                     &Dam::unlimited(),
83                 )?
84                 .expect("Failed to create BrowserState"),
85             ),
86             Areas::create(&mut Vec::new(), 0, screen, false)?,
87             con,
88         );
89         let (tx_seqs, rx_seqs) = unbounded::<Sequence>();
90         Ok(App {
91             screen,
92             active_panel_idx: 0,
93             panels: panel.into(),
94             quitting: false,
95             launch_at_end: None,
96             created_panels_count: 1,
97             preview_panel: None,
98             stage_panel: None,
99             shared_root: None,
100             tx_seqs,
101             rx_seqs,
102         })
103     }
104 
panel_ref_to_idx(&self, panel_ref: PanelReference) -> Option<usize>105     fn panel_ref_to_idx(&self, panel_ref: PanelReference) -> Option<usize> {
106         match panel_ref {
107             PanelReference::Active => Some(self.active_panel_idx),
108             PanelReference::Leftest => Some(0),
109             PanelReference::Rightest => Some(self.panels.len().get() - 1),
110             PanelReference::Id(id) => self.panel_id_to_idx(id),
111             PanelReference::Preview => self.preview_panel.and_then(|id| self.panel_id_to_idx(id)),
112         }
113     }
114 
115     /// return the current index of the panel whith given id
panel_id_to_idx(&self, id: PanelId) -> Option<usize>116     fn panel_id_to_idx(&self, id: PanelId) -> Option<usize> {
117         self.panels.iter().position(|panel| panel.id == id)
118     }
119 
state(&self) -> &dyn PanelState120     fn state(&self) -> &dyn PanelState {
121         self.panels[self.active_panel_idx].state()
122     }
mut_state(&mut self) -> &mut dyn PanelState123     fn mut_state(&mut self) -> &mut dyn PanelState {
124         self.panels[self.active_panel_idx].mut_state()
125     }
panel(&self) -> &Panel126     fn panel(&self) -> &Panel {
127         &self.panels[self.active_panel_idx]
128     }
mut_panel(&mut self) -> &mut Panel129     fn mut_panel(&mut self) -> &mut Panel {
130         unsafe {
131             self.panels
132                 .as_mut_slice()
133                 .get_unchecked_mut(self.active_panel_idx)
134         }
135     }
136 
137     /// close the panel if it's not the last one
138     ///
139     /// Return true when the panel has been removed (ie it wasn't the last one)
close_panel(&mut self, panel_idx: usize) -> bool140     fn close_panel(&mut self, panel_idx: usize) -> bool {
141         let active_panel_id = self.panels[self.active_panel_idx].id;
142         if let Some(preview_id) = self.preview_panel {
143             if self.panels.has_len(2) && self.panels[panel_idx].id != preview_id {
144                 // we don't want to stay with just the preview
145                 return false;
146             }
147         }
148         if let Some(stage_id) = self.stage_panel {
149             if self.panels.has_len(2) && self.panels[panel_idx].id != stage_id {
150                 // we don't want to stay with just the stage
151                 return false;
152             }
153         }
154         if let Ok(removed_panel) = self.panels.remove(panel_idx) {
155             if self.preview_panel == Some(removed_panel.id) {
156                 self.preview_panel = None;
157             }
158             if self.stage_panel == Some(removed_panel.id) {
159                 self.stage_panel = None;
160             }
161             Areas::resize_all(
162                 self.panels.as_mut_slice(),
163                 self.screen,
164                 self.preview_panel.is_some(),
165             )
166             .expect("removing a panel should be easy");
167             self.active_panel_idx = self
168                 .panels
169                 .iter()
170                 .position(|p| p.id == active_panel_id)
171                 .unwrap_or(self.panels.len().get() - 1);
172             true
173         } else {
174             false // there's no other panel to go to
175         }
176     }
177 
178     /// remove the top state of the current panel
179     ///
180     /// Close the panel too if that was its only state.
181     /// Close nothing and return false if there's not
182     /// at least two states in the app.
remove_state(&mut self) -> bool183     fn remove_state(&mut self) -> bool {
184         self.panels[self.active_panel_idx].remove_state()
185             || self.close_panel(self.active_panel_idx)
186     }
187 
188     /// redraw the whole screen. All drawing
189     /// are supposed to happen here, and only here.
display_panels( &mut self, w: &mut W, skin: &AppSkin, app_state: &AppState, con: &AppContext, ) -> Result<(), ProgramError>190     fn display_panels(
191         &mut self,
192         w: &mut W,
193         skin: &AppSkin,
194         app_state: &AppState,
195         con: &AppContext,
196     ) -> Result<(), ProgramError> {
197         // if some images are displayed by kitty, we'll erase it,
198         // but only after having displayed the new ones (if any)
199         // to prevent some flickerings
200         #[cfg(unix)]
201         let previous_images = crate::kitty::image_renderer()
202             .as_ref()
203             .and_then(|renderer| {
204                 let mut renderer = renderer.lock().unwrap();
205                 renderer.take_current_images()
206             });
207         for (idx, panel) in self.panels.as_mut_slice().iter_mut().enumerate() {
208             let active = idx == self.active_panel_idx;
209             let panel_skin = if active { &skin.focused } else { &skin.unfocused };
210             let disc = DisplayContext {
211                 active,
212                 screen: self.screen,
213                 panel_skin,
214                 state_area: panel.areas.state.clone(),
215                 app_state,
216                 con,
217             };
218             time!(
219                 "display panel",
220                 panel.display(w, &disc)?,
221             );
222         }
223         #[cfg(unix)]
224         if let Some(previous_images) = previous_images {
225             if let Some(renderer) = crate::kitty::image_renderer().as_ref() {
226                 let mut renderer = renderer.lock().unwrap();
227                 renderer.erase(w, previous_images)?;
228             }
229         }
230         w.flush()?;
231         Ok(())
232     }
233 
234     /// if there are exactly two non preview panels, return the selection
235     /// in the non focused panel
get_other_panel_path(&self) -> Option<PathBuf>236     fn get_other_panel_path(&self) -> Option<PathBuf> {
237         let len = self.panels.len().get();
238         if len == 3 {
239             if let Some(preview_id) = self.preview_panel {
240                 for (idx, panel) in self.panels.iter().enumerate() {
241                     if self.active_panel_idx != idx && panel.id != preview_id {
242                         return panel.state().selected_path()
243                             .map(|p| p.to_path_buf());
244                     }
245                 }
246             }
247             None
248         } else if self.panels.len().get() == 2 && self.preview_panel.is_none() {
249             let non_focused_panel_idx = if self.active_panel_idx == 0 { 1 } else { 0 };
250             self.panels[non_focused_panel_idx].state().selected_path()
251                 .map(|p| p.to_path_buf())
252         } else {
253             None
254         }
255     }
256 
257     /// apply a command. Change the states but don't redraw on screen.
apply_command( &mut self, w: &mut W, cmd: Command, panel_skin: &PanelSkin, app_state: &mut AppState, con: &AppContext, ) -> Result<(), ProgramError>258     fn apply_command(
259         &mut self,
260         w: &mut W,
261         cmd: Command,
262         panel_skin: &PanelSkin,
263         app_state: &mut AppState,
264         con: &AppContext,
265     ) -> Result<(), ProgramError> {
266         use CmdResult::*;
267         let mut error: Option<String> = None;
268         let is_input_invocation = cmd.is_verb_invocated_from_input();
269         let app_cmd_context = AppCmdContext {
270             panel_skin,
271             preview_panel: self.preview_panel,
272             stage_panel: self.stage_panel,
273             screen: self.screen, // it can't change in this function
274             con,
275         };
276         match self.mut_panel().apply_command(w, &cmd, app_state, &app_cmd_context)? {
277             ApplyOnPanel { id } => {
278                 if let Some(idx) = self.panel_id_to_idx(id) {
279                     if let DisplayError(txt) = self.panels[idx].apply_command(
280                         w, &cmd, app_state, &app_cmd_context
281                     )? {
282                         // we should probably handle other results
283                         // which implies the possibility of a recursion
284                         error = Some(txt);
285                     } else if is_input_invocation {
286                         self.mut_panel().clear_input();
287                     }
288                 } else {
289                     warn!("no panel found for ApplyOnPanel");
290                 }
291             }
292             ClosePanel { validate_purpose, panel_ref } => {
293                 if is_input_invocation {
294                     self.mut_panel().clear_input_invocation(con);
295                 }
296                 let close_idx = self.panel_ref_to_idx(panel_ref)
297                     .unwrap_or_else(||
298                         // when there's a preview panel, we close it rather than the app
299                         if self.panels.len().get()==2 && self.preview_panel.is_some() {
300                             1
301                         } else {
302                             self.active_panel_idx
303                         }
304                     );
305                 let mut new_arg = None;
306                 if validate_purpose {
307                     let purpose = &self.panels[close_idx].purpose;
308                     if let PanelPurpose::ArgEdition { .. } = purpose {
309                         new_arg = self.panels[close_idx]
310                             .state()
311                             .selected_path()
312                             .map(|p| p.to_string_lossy().to_string());
313                     }
314                 }
315                 if self.close_panel(close_idx) {
316                     let screen = self.screen;
317                     self.mut_state().refresh(screen, con);
318                     if let Some(new_arg) = new_arg {
319                         self.mut_panel().set_input_arg(new_arg);
320                         let new_input = self.panel().get_input_content();
321                         let cmd = Command::from_raw(new_input, false);
322                         let app_cmd_context = AppCmdContext {
323                             panel_skin,
324                             preview_panel: self.preview_panel,
325                             stage_panel: self.stage_panel,
326                             screen,
327                             con,
328                         };
329                         self.mut_panel().apply_command(w, &cmd, app_state, &app_cmd_context)?;
330                     }
331                 } else {
332                     self.quitting = true;
333                 }
334             }
335             DisplayError(txt) => {
336                 error = Some(txt);
337             }
338             ExecuteSequence { sequence } => {
339                 self.tx_seqs.send(sequence).unwrap();
340             }
341             HandleInApp(internal) => {
342                 match internal {
343                     Internal::panel_left | Internal::panel_right => {
344                         let new_active_panel_idx = if internal == Internal::panel_left {
345                             // we're not here to create a panel, this is handled in the state
346                             // we're here because the state wants us to either move to the panel
347                             // to the left, or close the rightest one
348                             if self.active_panel_idx == 0 {
349                                 self.close_panel(self.panels.len().get()-1);
350                                 None
351                             } else {
352                                 Some(self.active_panel_idx - 1)
353                             }
354                         } else { // panel_right
355                             // we're not here to create panels (it's done in the state).
356                             // So we either move to the right or close the leftes panel
357                             if self.active_panel_idx + 1 == self.panels.len().get() {
358                                 self.close_panel(0);
359                                 None
360                             } else {
361                                 Some(self.active_panel_idx + 1)
362                             }
363                         };
364                         if let Some(idx) = new_active_panel_idx {
365                             if is_input_invocation {
366                                 self.mut_panel().clear_input();
367                             }
368                             self.active_panel_idx = idx;
369                             let app_cmd_context = AppCmdContext {
370                                 panel_skin,
371                                 preview_panel: self.preview_panel,
372                                 stage_panel: self.stage_panel,
373                                 screen: self.screen,
374                                 con,
375                             };
376                             self.mut_panel().refresh_input_status(app_state, &app_cmd_context);
377                         }
378                     }
379                     Internal::toggle_second_tree => {
380                         let panels_count = self.panels.len().get();
381                         let trees_count = self.panels.iter()
382                             .filter(|p| p.state().get_type() == PanelStateType::Tree)
383                             .count();
384                         if trees_count < 2 {
385                             // we open a tree, closing a (non tree) panel if necessary
386                             if panels_count >= con.max_panels_count {
387                                 for i in (0..panels_count).rev() {
388                                     if self.panels[i].state().get_type() != PanelStateType::Tree {
389                                         self.close_panel(i);
390                                         break;
391                                     }
392                                 }
393                             }
394                             if let Some(selected_path) = self.state().selected_path() {
395                                 let dir = closest_dir(selected_path);
396                                 if let Ok(Some(new_state)) = BrowserState::new(
397                                     dir,
398                                     self.state().tree_options().without_pattern(),
399                                     self.screen,
400                                     con,
401                                     &Dam::unlimited(),
402                                 ) {
403                                     if let Err(s) = self.new_panel(
404                                         Box::new(new_state),
405                                         PanelPurpose::None,
406                                         HDir::Right,
407                                         is_input_invocation,
408                                         con,
409                                     ) {
410                                         error = Some(s);
411                                     }
412                                 }
413                             }
414                         } else {
415                             // we close the rightest inactive tree
416                             for i in (0..panels_count).rev() {
417                                 if self.panels[i].state().get_type() == PanelStateType::Tree {
418                                     if i == self.active_panel_idx {
419                                         continue;
420                                     }
421                                     self.close_panel(i);
422                                     break;
423                                 }
424                             }
425                         }
426                     }
427                     _ => {
428                         info!("unhandled propagated internal. cmd={:?}", &cmd);
429                     }
430                 }
431             }
432             Keep => {
433                 if is_input_invocation {
434                     self.mut_panel().clear_input_invocation(con);
435                 }
436             }
437             Launch(launchable) => {
438                 self.launch_at_end = Some(*launchable);
439                 self.quitting = true;
440             }
441             NewPanel {
442                 state,
443                 purpose,
444                 direction,
445             } => {
446                 if let Err(s) = self.new_panel(state, purpose, direction, is_input_invocation, con) {
447                     error = Some(s);
448                 }
449             }
450             NewState(state) => {
451                 self.mut_panel().clear_input();
452                 self.mut_panel().push_state(state);
453                 self.mut_panel().refresh_input_status(app_state, &app_cmd_context);
454             }
455             PopState => {
456                 if is_input_invocation {
457                     self.mut_panel().clear_input();
458                 }
459                 if self.remove_state() {
460                     self.mut_state().refresh(app_cmd_context.screen, con);
461                     self.mut_panel().refresh_input_status(app_state, &app_cmd_context);
462                 } else if con.quit_on_last_cancel {
463                     self.quitting = true;
464                 }
465             }
466             PopStateAndReapply => {
467                 if is_input_invocation {
468                     self.mut_panel().clear_input();
469                 }
470                 if self.remove_state() {
471                     let app_cmd_context = AppCmdContext {
472                         panel_skin,
473                         preview_panel: self.preview_panel,
474                         stage_panel: self.stage_panel,
475                         screen: self.screen,
476                         con,
477                     };
478                     self.mut_panel().apply_command(w, &cmd, app_state, &app_cmd_context)?;
479                 } else if con.quit_on_last_cancel {
480                     self.quitting = true;
481                 }
482             }
483             Quit => {
484                 self.quitting = true;
485             }
486             RefreshState { clear_cache } => {
487                 if is_input_invocation {
488                     self.mut_panel().clear_input_invocation(con);
489                 }
490                 if clear_cache {
491                     clear_caches();
492                 }
493                 app_state.stage.refresh();
494                 for i in 0..self.panels.len().get() {
495                     self.panels[i].mut_state().refresh(self.screen, con);
496                 }
497             }
498         }
499         if let Some(text) = error {
500             self.mut_panel().set_error(text);
501         }
502 
503         app_state.other_panel_path = self.get_other_panel_path();
504         if let Some(path) = self.state().tree_root() {
505             app_state.root = path.to_path_buf();
506         }
507 
508 
509         if let Some(shared_root) = &mut self.shared_root {
510             if let Ok(mut root) = shared_root.lock() {
511                 *root = app_state.root.clone();
512             }
513         }
514 
515         self.update_preview(con);
516 
517         Ok(())
518     }
519 
520     /// update the state of the preview, if there's some
update_preview(&mut self, con: &AppContext)521     fn update_preview(&mut self, con: &AppContext) {
522         let preview_idx = self.preview_panel.and_then(|id| self.panel_id_to_idx(id));
523         if let Some(preview_idx) = preview_idx {
524             if let Some(path) = self.state().selected_path() {
525                 let old_path = self.panels[preview_idx].state().selected_path();
526                 if Some(path) != old_path && path.is_file() {
527                     let path = path.to_path_buf();
528                     self.panels[preview_idx].mut_state().set_selected_path(path, con);
529                 }
530             }
531         }
532     }
533 
534     /// get the index of the panel at x
clicked_panel_index(&self, x: u16, _y: u16) -> usize535     fn clicked_panel_index(&self, x: u16, _y: u16) -> usize {
536         let len = self.panels.len().get();
537         (len * x as usize) / (self.screen.width as usize + 1)
538     }
539 
540     /// handle CmdResult::NewPanel
new_panel( &mut self, state: Box<dyn PanelState>, purpose: PanelPurpose, direction: HDir, is_input_invocation: bool, con: &AppContext, ) -> Result<(), String>541     fn new_panel(
542         &mut self,
543         state: Box<dyn PanelState>,
544         purpose: PanelPurpose,
545         direction: HDir,
546         is_input_invocation: bool, // if true we clean the input
547         con: &AppContext,
548     ) -> Result<(), String> {
549         match state.get_type() {
550             PanelStateType::Preview if self.preview_panel.is_some() => {
551                 return Err("There can be only one preview panel".to_owned());
552                 // todo replace instead ?
553             }
554             PanelStateType::Stage if self.stage_panel.is_some() => {
555                 return Err("There can be only one stage panel".to_owned());
556                 // todo replace instead ?
557             }
558             _ => {}
559         }
560         if is_input_invocation {
561             self.mut_panel().clear_input_invocation(con);
562         }
563         let insertion_idx = if purpose.is_preview() {
564             self.panels.len().get()
565         } else if direction == HDir::Right {
566             self.active_panel_idx + 1
567         } else {
568             self.active_panel_idx
569         };
570         let with_preview = purpose.is_preview() || self.preview_panel.is_some();
571         match Areas::create(
572             self.panels.as_mut_slice(),
573             insertion_idx,
574             self.screen,
575             with_preview,
576         ) {
577             Ok(areas) => {
578                 let panel_id = self.created_panels_count.into();
579                 match state.get_type() {
580                     PanelStateType::Preview => {
581                         self.preview_panel = Some(panel_id);
582                     }
583                     PanelStateType::Stage => {
584                         self.stage_panel = Some(panel_id);
585                     }
586                     _ => {
587                         self.active_panel_idx = insertion_idx;
588                     }
589                 }
590                 let mut panel = Panel::new(panel_id, state, areas, con);
591                 panel.purpose = purpose;
592                 self.created_panels_count += 1;
593                 self.panels.insert(insertion_idx, panel);
594                 Ok(())
595             }
596             Err(e) => Err(e.to_string())
597         }
598     }
599 
600     /// do the pending tasks, if any, and refresh the screen accordingly
do_pending_tasks( &mut self, w: &mut W, skin: &AppSkin, dam: &mut Dam, app_state: &mut AppState, con: &AppContext, ) -> Result<(), ProgramError>601     fn do_pending_tasks(
602         &mut self,
603         w: &mut W,
604         skin: &AppSkin,
605         dam: &mut Dam,
606         app_state: &mut AppState,
607         con: &AppContext,
608     ) -> Result<(), ProgramError> {
609         while self.has_pending_task() && !dam.has_event() {
610             if self.do_pending_task(app_state, con, dam) {
611                 self.update_preview(con); // the selection may have changed
612                 let app_cmd_context = AppCmdContext {
613                     panel_skin: &skin.focused,
614                     preview_panel: self.preview_panel,
615                     stage_panel: self.stage_panel,
616                     screen: self.screen,
617                     con,
618                 };
619                 self.mut_panel().refresh_input_status(app_state, &app_cmd_context);
620                 self.display_panels(w, skin, app_state, con)?;
621             } else {
622                 warn!("unexpected lack of update on do_pending_task");
623                 return Ok(());
624             }
625         }
626         Ok(())
627     }
628 
629     /// do the next pending task
do_pending_task( &mut self, app_state: &AppState, con: &AppContext, dam: &mut Dam, ) -> bool630     fn do_pending_task(
631         &mut self,
632         app_state: &AppState,
633         con: &AppContext,
634         dam: &mut Dam,
635     ) -> bool {
636         let screen = self.screen;
637         // we start with the focused panel
638         if self.panel().has_pending_task() {
639             self.mut_panel().do_pending_task(&app_state.stage, screen, con, dam);
640             return true;
641         }
642         // then the other ones
643         for idx in 0..self.panels.len().get() {
644             if idx != self.active_panel_idx {
645                 let panel = &mut self.panels[idx];
646                 if panel.has_pending_task() {
647                     panel.do_pending_task(&app_state.stage, screen, con, dam);
648                     return true;
649                 }
650             }
651         }
652         false
653     }
654 
has_pending_task(&mut self) -> bool655     fn has_pending_task(&mut self) -> bool {
656         self.panels.iter().any(|p| p.has_pending_task())
657     }
658 
659     /// This is the main loop of the application
run( mut self, w: &mut W, con: &AppContext, conf: &Conf, ) -> Result<Option<Launchable>, ProgramError>660     pub fn run(
661         mut self,
662         w: &mut W,
663         con: &AppContext,
664         conf: &Conf,
665     ) -> Result<Option<Launchable>, ProgramError> {
666         #[cfg(feature = "clipboard")]
667         {
668             // different systems have different clipboard capabilities
669             // and it may be useful to know which one we have
670             debug!("Clipboard backend: {:?}", terminal_clipboard::get_type());
671         }
672 
673         // we listen for events in a separate thread so that we can go on listening
674         // when a long search is running, and interrupt it if needed
675         let event_source = EventSource::new()?;
676         let rx_events = event_source.receiver();
677         let mut dam = Dam::from(rx_events);
678         let skin = AppSkin::new(conf, con.launch_args.color == Some(false));
679         let mut app_state = AppState {
680             stage: Stage::default(),
681             root: con.launch_args.root.clone(),
682             other_panel_path: None,
683         };
684 
685         self.screen.clear_bottom_right_char(w, &skin.focused)?;
686 
687         if let Some(raw_sequence) = &con.launch_args.commands {
688             self.tx_seqs
689                 .send(Sequence::new_local(raw_sequence.to_string()))
690                 .unwrap();
691         }
692 
693         #[cfg(unix)]
694         let _server = con.launch_args.listen.as_ref()
695             .map(|server_name| {
696                 let shared_root = Arc::new(Mutex::new(app_state.root.clone()));
697                 let server = crate::net::Server::new(
698                     server_name,
699                     self.tx_seqs.clone(),
700                     Arc::clone(&shared_root),
701                 );
702                 self.shared_root = Some(shared_root);
703                 server
704             })
705             .transpose()?;
706 
707         loop {
708             if !self.quitting {
709                 self.display_panels(w, &skin, &app_state, con)?;
710                 time!(
711                     Info,
712                     "pending_tasks",
713                     self.do_pending_tasks(w, &skin, &mut dam, &mut app_state, con)?,
714                 );
715             }
716             match dam.next(&self.rx_seqs) {
717                 Either::First(Some(event)) => {
718                     info!("event: {:?}", &event);
719                     let mut handled = false;
720 
721                     // app level handling
722                     if let Some((x, y)) = event.as_click() {
723                         if self.clicked_panel_index(x, y) != self.active_panel_idx {
724                             // panel activation click
725                             self.active_panel_idx = self.clicked_panel_index(x, y);
726                             handled = true;
727                         }
728                     } else if let Event::Resize(w, h) = event.event {
729                         self.screen.set_terminal_size(w, h, con);
730                         Areas::resize_all(
731                             self.panels.as_mut_slice(),
732                             self.screen,
733                             self.preview_panel.is_some(),
734                         )?;
735                         for panel in &mut self.panels {
736                             panel.mut_state().refresh(self.screen, con);
737                         }
738                         handled = true;
739                     }
740 
741                     // event handled by the panel
742                     if !handled {
743                         let cmd = self.mut_panel().add_event(w, event, &app_state, con)?;
744                         debug!("command after add_event: {:?}", &cmd);
745                         self.apply_command(w, cmd, &skin.focused, &mut app_state, con)?;
746 
747                     }
748 
749                     event_source.unblock(self.quitting);
750                 }
751                 Either::First(None) => {
752                     // this is how we quit the application,
753                     // when the input thread is properly closed
754                     break;
755                 }
756                 Either::Second(Some(raw_sequence)) => {
757                     debug!("got command sequence: {:?}", &raw_sequence);
758                     for (input, arg_cmd) in raw_sequence.parse(con)? {
759                         self.mut_panel().set_input_content(&input);
760                         self.apply_command(w, arg_cmd, &skin.focused, &mut app_state, con)?;
761                         if self.quitting {
762                             // is that a 100% safe way of quitting ?
763                             return Ok(self.launch_at_end.take());
764                         } else {
765                             self.display_panels(w, &skin, &app_state, con)?;
766                             time!(
767                                 "sequence pending tasks",
768                                 self.do_pending_tasks(w, &skin, &mut dam, &mut app_state, con)?,
769                             );
770                         }
771                     }
772                 }
773                 Either::Second(None) => {
774                     warn!("I didn't expect a None to occur here");
775                 }
776             }
777         }
778 
779         Ok(self.launch_at_end.take())
780     }
781 }
782 
783 /// clear the file sizes and git stats cache.
784 /// This should be done on Refresh actions and after any external
785 /// command.
clear_caches()786 fn clear_caches() {
787     file_sum::clear_cache();
788     git::clear_status_computer_cache();
789     #[cfg(unix)]
790     crate::filesystems::clear_cache();
791 }
792