1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
use std::io::Write;
use std::{fs, process, thread, time};

use abstutil::Timer;

use crate::runner::State;
use crate::{Prerender, SharedAppState};

pub(crate) fn screenshot_everything<A: SharedAppState>(
    state: &mut State<A>,
    dir_path: &str,
    prerender: &Prerender,
    zoom: f64,
    max_x: f64,
    max_y: f64,
) {
    let mut timer = Timer::new("capturing screen");
    let num_tiles_x = (max_x * zoom / state.canvas.window_width).ceil() as usize;
    let num_tiles_y = (max_y * zoom / state.canvas.window_height).ceil() as usize;
    let orig_zoom = state.canvas.cam_zoom;
    let orig_x = state.canvas.cam_x;
    let orig_y = state.canvas.cam_y;

    timer.start_iter("capturing images", num_tiles_x * num_tiles_y);
    let mut filenames: Vec<String> = Vec::new();
    state.canvas.cam_zoom = zoom;
    fs::create_dir_all(dir_path).unwrap();

    for tile_y in 0..num_tiles_y {
        for tile_x in 0..num_tiles_x {
            timer.next();
            state.canvas.cam_x = (tile_x as f64) * state.canvas.window_width;
            state.canvas.cam_y = (tile_y as f64) * state.canvas.window_height;

            let suffix = state.draw(prerender, true).unwrap_or_else(String::new);
            let filename = format!("{:02}x{:02}{}.gif", tile_x + 1, tile_y + 1, suffix);

            // TODO Is vsync or something else causing the above redraw to not actually show up in
            // time for scrot to see it? This is slow (30s total for Montlake), but stable.
            thread::sleep(time::Duration::from_millis(100));

            if screencap(&format!("{}/{}", dir_path, filename)) {
                filenames.push(filename);
            } else {
                // Abort early.
                return;
            }
        }
    }

    state.canvas.cam_zoom = orig_zoom;
    state.canvas.cam_x = orig_x;
    state.canvas.cam_y = orig_y;
    finish(dir_path, filenames, num_tiles_x, num_tiles_y);
}

fn screencap(filename: &str) -> bool {
    if !process::Command::new("scrot")
        .args(&[
            "--quality",
            "100",
            "--focused",
            "--silent",
            "screenshot.png",
        ])
        .status()
        .map(|s| s.success())
        .unwrap_or(false)
    {
        println!("Screencapping failed; you probably don't have scrot (https://en.wikipedia.org/wiki/Scrot) installed");
        return false;
    }
    if !process::Command::new("convert")
        .arg("screenshot.png")
        .arg(filename)
        .status()
        .map(|s| s.success())
        .unwrap_or(false)
    {
        println!(
            "Screencapping failed; you probably don't have convert (https://imagemagick.org) \
             installed"
        );
        return false;
    }
    process::Command::new("rm")
        .arg("screenshot.png")
        .status()
        .unwrap();

    true
}

fn finish(dir_path: &str, filenames: Vec<String>, num_tiles_x: usize, num_tiles_y: usize) {
    let mut args = filenames;
    args.push("-mode".to_string());
    args.push("Concatenate".to_string());
    args.push("-tile".to_string());
    args.push(format!("{}x{}", num_tiles_x, num_tiles_y));
    args.push("full.gif".to_string());

    let mut file = fs::File::create(format!("{}/combine.sh", dir_path)).unwrap();
    writeln!(file, "#!/bin/bash\n").unwrap();
    writeln!(file, "montage {}", args.join(" ")).unwrap();
    writeln!(file, "rm -f combine.sh").unwrap();
}