summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-03-27 11:36:40 -0700
committerIrene Knapp <ireneista@irenes.space>2026-03-27 11:36:40 -0700
commita52b286d6c8e2000c416f23eece0b930fe8ec4e9 (patch)
treec40b556fd6c63a9cad0df542c975779e84e4bc08 /src
parent46b955afbce8997c39bda4e83abcaed2e954e65c (diff)
scrolling now uses the terminal scroll feature when possible
it doesn't redraw the newly blank lines in that case though, so it's broken, but that can come next

Force-Push: yes
Change-Id: I52c90249852331e5f9bbcdee4d645ee33e4c671a
Diffstat (limited to 'src')
-rw-r--r--src/main.rs15
-rw-r--r--src/terminal.rs45
2 files changed, 51 insertions, 9 deletions
diff --git a/src/main.rs b/src/main.rs
index 2f93a2e..b94b274 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -161,7 +161,8 @@ impl Ivy {
       terminal.stdout.write_all("\n~".as_bytes()).await?;
     }
 
-    terminal.stdout.write_all("\n        status goes here".as_bytes()).await?;
+    terminal.do_cursor_position(0, height).await?;
+    terminal.stdout.write_all("        status goes here".as_bytes()).await?;
 
     let window = self.window.read().await;
     terminal.do_cursor_position(*window.cursor_column.read().await,
@@ -385,9 +386,15 @@ impl Ivy {
     }
 
     let new_scroll_top = *self.window.read().await.scroll_top.read().await;
-    if old_scroll_top != new_scroll_top {
-      self.terminal.write().await.clear().await?;
-      self.draw().await?;
+    let height = *self.terminal.read().await.height.read().await - 1;
+    if new_scroll_top != old_scroll_top {
+      let difference = new_scroll_top as isize - old_scroll_top as isize;
+      if (difference.abs() as usize) < height {
+        self.terminal.write().await.scroll(difference).await?;
+      } else {
+        self.terminal.write().await.clear().await?;
+        self.draw().await?;
+      }
     }
 
     Ok(())
diff --git a/src/terminal.rs b/src/terminal.rs
index 9188c43..1ba2b01 100644
--- a/src/terminal.rs
+++ b/src/terminal.rs
@@ -81,10 +81,18 @@ impl Terminal {
     *self.width.write().await = width.try_into().unwrap();
     *self.height.write().await = height.try_into().unwrap();
 
+    self.set_scroll_region(0, height - 1).await?;
+
     Ok(())
   }
 
   pub async fn zap_full_screen(&mut self) -> Result<()> {
+    let (width, height) = self.do_report_size().await?;
+    *self.width.write().await = width.try_into().unwrap();
+    *self.height.write().await = height.try_into().unwrap();
+
+    self.set_scroll_region(0, height).await?;
+
     self.do_end_alternate_screen().await?;
     self.stdout.flush().await?;
 
@@ -113,7 +121,7 @@ impl Terminal {
   }
 
   pub async fn read_report(&mut self, escape_type: EscapeType)
-      -> Result<(char, Vec<u64>)>
+      -> Result<(char, Vec<usize>)>
   {
     let mut expected = escape_type.intro();
 
@@ -126,7 +134,7 @@ impl Terminal {
 
     let mut values = Vec::new();
 
-    let mut number: u64 = 0;
+    let mut number: usize = 0;
     let mut in_number = false;
     let mut after_semicolon = false;
 
@@ -140,9 +148,11 @@ impl Terminal {
         if c.is_ascii_digit() {
           if in_number {
             number = number * 10
-                     + <u32 as Into<u64>>::into(c.to_digit(10).unwrap());
+                     + <u32 as TryInto<usize>>::try_into(
+                           c.to_digit(10).unwrap()).unwrap();
           } else {
-            number = c.to_digit(10).unwrap().into();
+            number = <u32 as TryInto<usize>>::try_into(
+                         c.to_digit(10).unwrap()).unwrap();
 
             in_number = true;
             after_semicolon = false;
@@ -195,7 +205,7 @@ impl Terminal {
   }
 
   // dtterm? xterm
-  pub async fn do_report_size(&mut self) -> Result<(u64, u64)> {
+  pub async fn do_report_size(&mut self) -> Result<(usize, usize)> {
     self.do_escape(EscapeType::CSI, "t", &[18]).await?;
     self.stdout.flush().await?;
     let (code, values) = self.read_report(EscapeType::CSI).await?;
@@ -210,6 +220,31 @@ impl Terminal {
   pub async fn clear(&mut self) -> Result<()> {
     self.do_escape(EscapeType::CSI, "J", &[2]).await
   }
+
+  // vt420, ECMA-48
+  pub async fn scroll_up(&mut self, distance: usize) -> Result<()> {
+    self.do_escape(EscapeType::CSI, "S", &[distance]).await
+  }
+
+  // vt420, ECMA-48
+  pub async fn scroll_down(&mut self, distance: usize) -> Result<()> {
+    self.do_escape(EscapeType::CSI, "T", &[distance]).await
+  }
+
+  pub async fn scroll(&mut self, distance_up: isize) -> Result<()> {
+    if distance_up >= 0 {
+      self.scroll_up(distance_up as usize).await
+    } else {
+      self.scroll_down(-distance_up as usize).await
+    }
+  }
+
+  // vt100
+  pub async fn set_scroll_region(&mut self, top: usize, bottom: usize)
+      -> Result<()>
+  {
+    self.do_escape(EscapeType::CSI, "r", &[top, bottom]).await
+  }
 }