screenshot/
screenshot.rs

1use std::{
2    assert_eq, env, io,
3    process::{Command, Stdio},
4};
5
6use ratatui::{
7    backend::CrosstermBackend,
8    crossterm::{
9        execute,
10        terminal::{
11            disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, SetSize,
12        },
13    },
14    layout::Rect,
15    widgets::{Block, Borders, Paragraph},
16    Frame, Terminal,
17};
18use ratatui_image::{picker::Picker, protocol::Protocol, Image, Resize};
19struct App {
20    image: Protocol,
21}
22
23const ASSERT_FONT_SIZE: (u16, u16) = (9, 18);
24const SCREEN_SIZE: (u16, u16) = (46, 12);
25
26fn main() -> Result<(), Box<dyn std::error::Error>> {
27    // setup terminal
28    enable_raw_mode()?;
29    let mut stdout = io::stdout();
30    execute!(
31        stdout,
32        EnterAlternateScreen,
33        SetSize(SCREEN_SIZE.0, SCREEN_SIZE.1)
34    )?;
35    let backend = CrosstermBackend::new(stdout);
36    let mut terminal = Terminal::new(backend)?;
37
38    let mut picker = Picker::from_query_stdio()?;
39    picker.set_background_color([255, 0, 255, 255]);
40    if false {
41        assert_eq!(
42            ASSERT_FONT_SIZE,
43            picker.font_size(),
44            "Font size must be fixed to a specific size: {ASSERT_FONT_SIZE:?}",
45        );
46    }
47    let dyn_img = image::io::Reader::open("./assets/Ada.png")?.decode()?;
48    let image = picker.new_protocol(
49        dyn_img,
50        Rect::new(0, 0, SCREEN_SIZE.0 - 10, SCREEN_SIZE.1 - 4),
51        Resize::Fit(None),
52    )?;
53    let mut app = App { image };
54
55    terminal.draw(|f| ui(f, &mut app))?;
56    std::thread::sleep(std::time::Duration::from_secs(1)); // let the terminal actually draw.
57    let xwd = Command::new("xwd")
58        .args(["-root", "-silent"])
59        .stdout(Stdio::piped())
60        .spawn()
61        .expect("Failed to start xwd command");
62    let screenshot_term = env::var("SCREENSHOT_TERM_NAME").unwrap_or("unknown".to_string());
63    std::process::Command::new("convert")
64        .args([
65            "xwd:-",
66            &format!("png:./target/screenshot_{screenshot_term}.png"),
67        ])
68        .stdin(xwd.stdout.expect("failed to get stdout"))
69        .stdout(std::process::Stdio::null())
70        .stderr(std::process::Stdio::null())
71        .spawn()
72        .and_then(|mut child| child.wait())?;
73
74    // restore terminal
75    disable_raw_mode()?;
76    execute!(terminal.backend_mut(), LeaveAlternateScreen,)?;
77    terminal.show_cursor()?;
78
79    Ok(())
80}
81
82fn ui(f: &mut Frame<'_>, app: &mut App) {
83    let area = Rect::new(0, 0, SCREEN_SIZE.0, SCREEN_SIZE.1);
84    let block = Block::default()
85        .borders(Borders::ALL)
86        .title("Screenshot test");
87
88    f.render_widget(
89        Paragraph::new("PartiallyHiddenScreenshotParagraphBackground\n".repeat(10)),
90        block.inner(area),
91    );
92
93    let image = Image::new(&mut app.image);
94    f.render_widget(image, block.inner(area));
95    f.render_widget(block, area);
96}