summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2022-01-06 22:47:39 +0100
committerCanop <cano.petrole@gmail.com>2022-01-06 22:47:39 +0100
commite89fb172d81fe99dd4d8a95b8784b2ed25a8ba37 (patch)
tree0a20fa8a00bf5f84a0be5fcb5bb6ee74b4d27465 /src
parentbe168b0de914ed04193ab734de0772615ae89493 (diff)
determine when to draw/keep/erase kitty images
Benefits: - fix background not erased - fix flickering - fix many redraws when computing sizes (or any background task)
Diffstat (limited to 'src')
-rw-r--r--src/app/app.rs20
-rw-r--r--src/app/display_context.rs1
-rw-r--r--src/image/image_view.rs61
-rw-r--r--src/kitty/image_renderer.rs17
-rw-r--r--src/kitty/image_set.rs37
-rw-r--r--src/kitty/mod.rs91
-rw-r--r--src/preview/preview.rs11
-rw-r--r--src/preview/preview_state.rs3
8 files changed, 140 insertions, 101 deletions
diff --git a/src/app/app.rs b/src/app/app.rs
index 921ff58..d7d1839 100644
--- a/src/app/app.rs
+++ b/src/app/app.rs
@@ -6,7 +6,9 @@ use {
conf::Conf,
display::{Areas, Screen, W},
errors::ProgramError,
- file_sum, git,
+ file_sum,
+ git,
+ kitty,
launchable::Launchable,
path::closest_dir,
skin::*,
@@ -63,6 +65,9 @@ pub struct App {
/// receiver to listen to the sequence channel
rx_seqs: Receiver<Sequence>,
+
+ /// counter incremented at every draw
+ drawing_count: usize,
}
impl App {
@@ -99,6 +104,7 @@ impl App {
shared_root: None,
tx_seqs,
rx_seqs,
+ drawing_count: 0,
})
}
@@ -194,15 +200,12 @@ impl App {
app_state: &AppState,
con: &AppContext,
) -> Result<(), ProgramError> {
- // if some images are displayed by kitty, we'll erase them,
- // but only after having displayed the new ones (if any)
- // to prevent some flickerings
- let previous_images = crate::kitty::take_current_images();
-
+ self.drawing_count += 1;
for (idx, panel) in self.panels.as_mut_slice().iter_mut().enumerate() {
let active = idx == self.active_panel_idx;
let panel_skin = if active { &skin.focused } else { &skin.unfocused };
let disc = DisplayContext {
+ count: self.drawing_count,
active,
screen: self.screen,
panel_skin,
@@ -215,10 +218,7 @@ impl App {
panel.display(w, &disc)?,
);
}
-
- if let Some(previous_images) = previous_images {
- previous_images.erase(w)?;
- }
+ kitty::manager().lock().unwrap().erase_images_before(w, self.drawing_count)?;
w.flush()?;
Ok(())
}
diff --git a/src/app/display_context.rs b/src/app/display_context.rs
index 775bfe1..318c335 100644
--- a/src/app/display_context.rs
+++ b/src/app/display_context.rs
@@ -10,6 +10,7 @@ use {
/// short lived wrapping of a few things which are needed for displaying
/// panels
pub struct DisplayContext<'c> {
+ pub count: usize,
pub active: bool,
pub screen: Screen,
pub state_area: Area,
diff --git a/src/image/image_view.rs b/src/image/image_view.rs
index 9ec8708..f7cc687 100644
--- a/src/image/image_view.rs
+++ b/src/image/image_view.rs
@@ -1,9 +1,10 @@
use {
super::double_line::DoubleLine,
crate::{
- app::AppContext,
+ app::*,
display::{Screen, W},
errors::ProgramError,
+ kitty::{self, KittyImageId},
skin::PanelSkin,
},
crossterm::{
@@ -24,6 +25,19 @@ use {
termimad::{fill_bg, Area},
};
+#[derive(Debug)]
+struct DrawingInfo {
+ drawing_count: usize,
+ area: Area,
+}
+
+impl DrawingInfo {
+ pub fn follows_in_place(&self, previous: &DrawingInfo) -> bool {
+ self.drawing_count == previous.drawing_count + 1
+ && self.area == previous.area
+ }
+}
+
/// an already resized image, with the dimensions it
/// was computed for (which may be different from the
/// dimensions we got)
@@ -39,6 +53,8 @@ pub struct ImageView {
path: PathBuf,
source_img: DynamicImage,
display_img: Option<CachedImage>,
+ last_drawing: Option<DrawingInfo>,
+ kitty_image_id: Option<KittyImageId>,
}
impl ImageView {
@@ -52,6 +68,8 @@ impl ImageView {
path: path.to_path_buf(),
source_img,
display_img: None,
+ last_drawing: None,
+ kitty_image_id: None,
})
}
pub fn is_png(&self) -> bool {
@@ -63,17 +81,46 @@ impl ImageView {
pub fn display(
&mut self,
w: &mut W,
- _screen: Screen,
- panel_skin: &PanelSkin,
+ disc: &DisplayContext,
area: &Area,
- con: &AppContext,
) -> Result<(), ProgramError> {
- let styles = &panel_skin.styles;
+
+ let styles = &disc.panel_skin.styles;
let bg = styles.preview.get_bg()
.or_else(|| styles.default.get_bg())
.unwrap_or(Color::AnsiValue(238));
- if crate::kitty::try_print_image(w, &self.source_img, area)? {
+ // we avoid drawing when we were just diplayed
+ // on the last drawing_count and the area is the same.
+ let drawing_info = DrawingInfo {
+ drawing_count: disc.count,
+ area: area.clone(),
+ };
+ let must_draw = self.last_drawing
+ .as_ref()
+ .map_or(true, |previous| !drawing_info.follows_in_place(previous));
+ if must_draw {
+ debug!("image_view must be cleared");
+ } else {
+ debug!("no need to clear image_view");
+ }
+ self.last_drawing = Some(drawing_info);
+
+ let mut kitty_manager = kitty::manager().lock().unwrap();
+
+ if !must_draw {
+ if let Some(kitty_image_id) = self.kitty_image_id {
+ // we tell the manager the images must be kept, otherwise
+ // they would be erased at end of drawing, as obsolete
+ kitty_manager.keep(kitty_image_id, disc.count);
+ }
+ return Ok(());
+ }
+
+ self.kitty_image_id = kitty_manager
+ .try_print_image(w, &self.source_img, area, bg, disc.count)?;
+
+ if self.kitty_image_id.is_some() {
return Ok(());
}
@@ -101,7 +148,7 @@ impl ImageView {
let (width, height) = img.dimensions();
debug!("resized image dimensions: {},{}", width, height);
debug_assert!(width <= area.width as u32);
- let mut double_line = DoubleLine::new(width as usize, con.true_colors);
+ let mut double_line = DoubleLine::new(width as usize, disc.con.true_colors);
let mut y = area.top;
let img_top_offset = (area.height - (height / 2) as u16) / 2;
for _ in 0..img_top_offset {
diff --git a/src/kitty/image_renderer.rs b/src/kitty/image_renderer.rs
index 58dab57..1402a65 100644
--- a/src/kitty/image_renderer.rs
+++ b/src/kitty/image_renderer.rs
@@ -12,6 +12,7 @@ use {
crossterm::{
cursor,
QueueableCommand,
+ style::Color,
},
image::{
DynamicImage,
@@ -23,7 +24,7 @@ use {
io::{self, Write},
},
tempfile,
- termimad::Area,
+ termimad::{fill_bg, Area},
};
/// How to send the image to kitty
@@ -95,7 +96,7 @@ pub struct KittyImageRenderer {
cell_width: u32,
cell_height: u32,
next_id: usize,
- pub transmission_medium: TransmissionMedium,
+ transmission_medium: TransmissionMedium,
}
/// An image prepared for a precise area on screen
@@ -217,14 +218,22 @@ impl KittyImageRenderer {
self.next_id += 1;
new_id
}
- /// Print the dynamicImage and return the KittyImageId
- /// for later removal from screen
+ /// Clean the area, then print the dynamicImage and
+ /// return the KittyImageId for later removal from screen
pub fn print(
&mut self,
w: &mut W,
src: &DynamicImage,
area: &Area,
+ bg: Color,
) -> Result<usize, ProgramError> {
+
+ // clean the background below (and around) the image
+ for y in area.top..area.top + area.height {
+ w.queue(cursor::MoveTo(area.left, y))?;
+ fill_bg(w, area.width as usize, bg)?;
+ }
+
let img = KittyImage::new(src, area, self);
debug!("transmission medium: {:?}", self.transmission_medium);
match self.transmission_medium {
diff --git a/src/kitty/image_set.rs b/src/kitty/image_set.rs
deleted file mode 100644
index b818eaf..0000000
--- a/src/kitty/image_set.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use {
- crate::{
- display::W,
- errors::ProgramError,
- },
- std::io::Write,
-};
-
-#[derive(Debug, Default)]
-pub struct KittyImageSet {
- ids: Vec<usize>,
-}
-
-impl KittyImageSet {
- pub fn erase(
- self,
- w: &mut W,
- ) -> Result<(), ProgramError> {
- for id in &self.ids {
- debug!("erase kitty image {}", id);
- write!(w, "\u{1b}_Ga=d,d=I,i={}\u{1b}\\", id)?;
- }
- Ok(())
- }
- /// erase all kitty images, even the forgetted ones
- ///
- /// (this is currently unused)
- pub fn erase_all(
- w: &mut W,
- ) -> Result<(), ProgramError> {
- write!(w, "\u{1b}_Ga=d,d=A\u{1b}\\")?;
- Ok(())
- }
- pub fn push(&mut self, new_id: usize) {
- self.ids.push(new_id);
- }
-}
diff --git a/src/kitty/mod.rs b/src/kitty/mod.rs
index adc9e45..907acbe 100644
--- a/src/kitty/mod.rs
+++ b/src/kitty/mod.rs
@@ -1,10 +1,8 @@
mod detect_support;
mod image_renderer;
-mod image_set;
pub use {
image_renderer::*,
- image_set::*,
};
use {
@@ -12,51 +10,43 @@ use {
display::W,
errors::ProgramError,
},
+ crossterm::style::Color,
image::DynamicImage,
once_cell::sync::Lazy,
- std::sync::Mutex,
+ std::{
+ io::Write,
+ sync::Mutex,
+ },
termimad::Area,
};
-/// Give the current images, so that they can be removed
-/// (which should be done only after a new content has been
-/// displayed)
-pub fn take_current_images() -> Option<KittyImageSet> {
- manager().lock().unwrap().take_current_images()
-}
-/// Try print the image in the specified area.
-///
-/// Return Ok(true) if it went well, Ok(false) if
-/// the terminal doesn't appear compatible with the Kitty
-/// graphics protocol, or an error if somebody went wrong.
-pub fn try_print_image(
- w: &mut W,
- src: &DynamicImage,
- area: &Area,
-) -> Result<bool, ProgramError> {
- let mut manager = manager().lock().unwrap();
- manager.try_print_image(w, src, area)
-}
+pub type KittyImageId = usize;
static MANAGER: Lazy<Mutex<KittyManager>> = Lazy::new(|| {
let manager = KittyManager {
- current_images: None,
+ rendered_images: Vec::new(),
renderer: MaybeRenderer::Untested,
};
Mutex::new(manager)
});
-fn manager() -> &'static Mutex<KittyManager> {
+pub fn manager() -> &'static Mutex<KittyManager> {
&*MANAGER
}
#[derive(Debug)]
-struct KittyManager {
- current_images: Option<KittyImageSet>,
+pub struct KittyManager {
+ rendered_images: Vec<RenderedImage>,
renderer: MaybeRenderer,
}
#[derive(Debug)]
+struct RenderedImage {
+ image_id: KittyImageId,
+ drawing_count: usize,
+}
+
+#[derive(Debug)]
enum MaybeRenderer {
Untested,
Disabled,
@@ -66,9 +56,6 @@ enum MaybeRenderer {
}
impl KittyManager {
- pub fn take_current_images(&mut self) -> Option<KittyImageSet> {
- self.current_images.take()
- }
/// return the renderer if it's already checked and enabled, none if
/// it's disabled or if it hasn't been tested yet
pub fn renderer_if_tested(&mut self) -> Option<&mut KittyImageRenderer> {
@@ -96,20 +83,52 @@ impl KittyManager {
}
}
}
+ pub fn keep(
+ &mut self,
+ kept_id: KittyImageId,
+ drawing_count: usize,
+ ) {
+ for image in self.rendered_images.iter_mut() {
+ if image.image_id == kept_id {
+ image.drawing_count = drawing_count;
+ }
+ }
+ }
pub fn try_print_image(
&mut self,
w: &mut W,
src: &DynamicImage,
area: &Area,
- ) -> Result<bool, ProgramError> {
+ bg: Color,
+ drawing_count: usize,
+ ) -> Result<Option<KittyImageId>, ProgramError> {
if let Some(renderer) = self.renderer() {
- let new_id = renderer.print(w, src, area)?;
- self.current_images
- .get_or_insert_with(KittyImageSet::default)
- .push(new_id);
- Ok(true)
+ let new_id = renderer.print(w, src, area, bg)?;
+ self.rendered_images.push(RenderedImage {
+ image_id: new_id,
+ drawing_count,
+ });
+ Ok(Some(new_id))
} else {
- Ok(false)
+ Ok(None)
+ }
+ }
+ pub fn erase_images_before(
+ &mut self,
+ w: &mut W,
+ drawing_count: usize,
+ ) -> Result<(), ProgramError> {
+ let mut kept_images = Vec::new();
+ for image in self.rendered_images.drain(..) {
+ if image.drawing_count >= drawing_count {
+ kept_images.push(image);
+ } else {
+ let id = image.image_id;
+ debug!("erase kitty image {}", id);
+ write!(w, "\u{1b}_Ga=d,d=I,i={}\u{1b}\\", id)?;
+ }
}
+ self.rendered_images = kept_images;
+ Ok(())
}
}
diff --git a/src/preview/preview.rs b/src/preview/preview.rs
index a80c4e3..d38d8a6 100644
--- a/src/preview/preview.rs
+++ b/src/preview/preview.rs
@@ -1,7 +1,7 @@
use {
super::*,
crate::{
- app::{AppContext, LineNumber},
+ app::*,
command::ScrollCommand,
display::*,
errors::ProgramError,
@@ -225,13 +225,14 @@ impl Preview {
pub fn display(
&mut self,
w: &mut W,
- screen: Screen,
- panel_skin: &PanelSkin,
+ disc: &DisplayContext,
area: &Area,
- con: &AppContext,
) -> Result<(), ProgramError> {
+ let panel_skin = &disc.panel_skin;
+ let screen = disc.screen;
+ let con = &disc.con;
match self {
- Self::Image(iv) => iv.display(w, screen, panel_skin, area, con),
+ Self::Image(iv) => iv.display(w, disc, area),
Self::Syntactic(sv) => sv.display(w, screen, panel_skin, area, con),
Self::ZeroLen(zlv) => zlv.display(w, screen, panel_skin, area),
Self::Hex(hv) => hv.display(w, screen, panel_skin, area),
diff --git a/src/preview/preview_state.rs b/src/preview/preview_state.rs
index 34a5aae..99249b5 100644
--- a/src/preview/preview_state.rs
+++ b/src/preview/preview_state.rs
@@ -228,7 +228,6 @@ impl PanelState for PreviewState {
w: &mut W,
disc: &DisplayContext,
) -> Result<(), ProgramError> {
- let con = &disc.con;
let state_area = &disc.state_area;
if state_area.height < 3 {
warn!("area too small for preview");
@@ -264,7 +263,7 @@ impl PanelState for PreviewState {
cw.fill(&styles.preview_title, &SPACE_FILLING)?;
let preview = self.filtered_preview.as_mut().unwrap_or(&mut self.preview);
preview.display_info(w, disc.screen, disc.panel_skin, &info_area)?;
- if let Err(err) = preview.display(w, disc.screen, disc.panel_skin, &self.preview_area, con) {
+ if let Err(err) = preview.display(w, disc, &self.preview_area) {
warn!("error while displaying file: {:?}", &err);
if preview.get_mode().is_some() {
// means it's not an error already