mirror of
https://github.com/cloudhead/rx.git
synced 2024-10-03 18:48:54 +03:00
Change rustfmt max-width to 100
This commit is contained in:
parent
3a894921c7
commit
d35ed16a54
1
.rustfmt.toml
Normal file
1
.rustfmt.toml
Normal file
@ -0,0 +1 @@
|
||||
max_width = 100
|
37
src/brush.rs
37
src/brush.rs
@ -129,12 +129,7 @@ impl Brush {
|
||||
}
|
||||
|
||||
/// Start drawing. Called when input is first pressed.
|
||||
pub fn start_drawing(
|
||||
&mut self,
|
||||
p: ViewCoords<i32>,
|
||||
color: Rgba8,
|
||||
extent: ViewExtent,
|
||||
) {
|
||||
pub fn start_drawing(&mut self, p: ViewCoords<i32>, color: Rgba8, extent: ViewExtent) {
|
||||
self.state = BrushState::DrawStarted(extent);
|
||||
self.color = color;
|
||||
self.stroke = Vec::with_capacity(32);
|
||||
@ -177,11 +172,7 @@ impl Brush {
|
||||
}
|
||||
|
||||
/// Expand a point into all brush heads.
|
||||
pub fn expand(
|
||||
&self,
|
||||
p: ViewCoords<i32>,
|
||||
extent: ViewExtent,
|
||||
) -> Vec<ViewCoords<i32>> {
|
||||
pub fn expand(&self, p: ViewCoords<i32>, extent: ViewExtent) -> Vec<ViewCoords<i32>> {
|
||||
let mut pixels = vec![*p];
|
||||
let ViewExtent { fw, fh, nframes } = extent;
|
||||
|
||||
@ -190,9 +181,7 @@ impl Brush {
|
||||
let frame_index = p.x / fw as i32;
|
||||
|
||||
pixels.push(Point2::new(
|
||||
(frame_index + 1) * fw as i32
|
||||
- (p.x - frame_index * fw as i32)
|
||||
- 1,
|
||||
(frame_index + 1) * fw as i32 - (p.x - frame_index * fw as i32) - 1,
|
||||
p.y,
|
||||
));
|
||||
}
|
||||
@ -215,13 +204,7 @@ impl Brush {
|
||||
}
|
||||
|
||||
/// Return the brush's output strokes as shapes.
|
||||
pub fn output(
|
||||
&self,
|
||||
stroke: Stroke,
|
||||
fill: Fill,
|
||||
scale: f32,
|
||||
origin: Origin,
|
||||
) -> Vec<Shape> {
|
||||
pub fn output(&self, stroke: Stroke, fill: Fill, scale: f32, origin: Origin) -> Vec<Shape> {
|
||||
match self.state {
|
||||
BrushState::DrawStarted(extent)
|
||||
| BrushState::Drawing(extent)
|
||||
@ -230,8 +213,7 @@ impl Brush {
|
||||
|
||||
for p in &self.stroke {
|
||||
pixels.extend_from_slice(
|
||||
self.expand(ViewCoords::new(p.x, p.y), extent)
|
||||
.as_slice(),
|
||||
self.expand(ViewCoords::new(p.x, p.y), extent).as_slice(),
|
||||
);
|
||||
}
|
||||
pixels
|
||||
@ -277,8 +259,7 @@ impl Brush {
|
||||
};
|
||||
|
||||
Shape::Rectangle(
|
||||
Rect::new(x, y, x + size * scale, y + size * scale)
|
||||
- Vector2::new(offset, offset),
|
||||
Rect::new(x, y, x + size * scale, y + size * scale) - Vector2::new(offset, offset),
|
||||
z,
|
||||
Rotation::ZERO,
|
||||
stroke,
|
||||
@ -289,11 +270,7 @@ impl Brush {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Draw a line between two points. Uses Bresenham's line algorithm.
|
||||
fn line(
|
||||
mut p0: Point2<i32>,
|
||||
p1: Point2<i32>,
|
||||
canvas: &mut Vec<Point2<i32>>,
|
||||
) {
|
||||
fn line(mut p0: Point2<i32>, p1: Point2<i32>, canvas: &mut Vec<Point2<i32>>) {
|
||||
let dx = i32::abs(p1.x - p0.x);
|
||||
let dy = i32::abs(p1.y - p0.y);
|
||||
let sx = if p0.x < p1.x { 1 } else { -1 };
|
||||
|
66
src/cmd.rs
66
src/cmd.rs
@ -96,14 +96,10 @@ impl fmt::Display for Command {
|
||||
Self::Map(_) => write!(f, "Map a key combination to a command"),
|
||||
Self::Mode(m) => write!(f, "Switch session mode to {}", m),
|
||||
Self::AddFrame => write!(f, "Add a blank frame to the view"),
|
||||
Self::CloneFrame(i) => {
|
||||
write!(f, "Clone frame {} and add it to the view", i)
|
||||
}
|
||||
Self::CloneFrame(i) => write!(f, "Clone frame {} and add it to the view", i),
|
||||
Self::RemoveFrame => write!(f, "Remove the last frame of the view"),
|
||||
Self::Noop => write!(f, "No-op"),
|
||||
Self::PaletteAdd(c) => {
|
||||
write!(f, "Add {color} to palette", color = c)
|
||||
}
|
||||
Self::PaletteAdd(c) => write!(f, "Add {color} to palette", color = c),
|
||||
Self::PaletteClear => write!(f, "Clear palette"),
|
||||
Self::PaletteSample => write!(f, "Sample palette from view"),
|
||||
Self::Pan(x, 0) if *x > 0 => write!(f, "Pan workspace right"),
|
||||
@ -119,18 +115,12 @@ impl fmt::Display for Command {
|
||||
Self::Tool(Tool::Brush(_)) => write!(f, "Brush tool"),
|
||||
Self::Tool(Tool::Sampler) => write!(f, "Color sampler tool"),
|
||||
Self::ToolPrev => write!(f, "Switch to previous tool"),
|
||||
Self::Set(s, v) => {
|
||||
write!(f, "Set {setting} to {val}", setting = s, val = v)
|
||||
}
|
||||
Self::Set(s, v) => write!(f, "Set {setting} to {val}", setting = s, val = v),
|
||||
Self::Slice(Some(n)) => write!(f, "Slice view into {} frame(s)", n),
|
||||
Self::Slice(None) => write!(f, "Reset view slices"),
|
||||
Self::Source(_) => write!(f, "Source an rx script (eg. a palette)"),
|
||||
Self::SwapColors => {
|
||||
write!(f, "Swap foreground & background colors")
|
||||
}
|
||||
Self::Toggle(s) => {
|
||||
write!(f, "Toggle {setting} on/off", setting = s)
|
||||
}
|
||||
Self::SwapColors => write!(f, "Swap foreground & background colors"),
|
||||
Self::Toggle(s) => write!(f, "Toggle {setting} on/off", setting = s),
|
||||
Self::Undo => write!(f, "Undo view edit"),
|
||||
Self::ViewCenter => write!(f, "Center active view"),
|
||||
Self::ViewNext => write!(f, "Go to next view"),
|
||||
@ -141,30 +131,18 @@ impl fmt::Display for Command {
|
||||
Self::Zoom(Op::Incr) => write!(f, "Zoom in view"),
|
||||
Self::Zoom(Op::Decr) => write!(f, "Zoom out view"),
|
||||
Self::Zoom(Op::Set(z)) => write!(f, "Set view zoom to {:.1}", z),
|
||||
Self::SelectionFill(None) => {
|
||||
write!(f, "Fill selection with foreground color")
|
||||
}
|
||||
Self::SelectionFill(None) => write!(f, "Fill selection with foreground color"),
|
||||
Self::SelectionYank => write!(f, "Yank/copy selection"),
|
||||
Self::SelectionDelete => write!(f, "Delete/cut selection"),
|
||||
Self::SelectionPaste => write!(f, "Paste selection"),
|
||||
Self::SelectionExpand => write!(f, "Expand selection to frame"),
|
||||
Self::SelectionOffset(1, 1) => write!(f, "Outset selection"),
|
||||
Self::SelectionOffset(-1, -1) => write!(f, "Inset selection"),
|
||||
Self::SelectionOffset(x, y) => {
|
||||
write!(f, "Offset selection by {:2},{:2}", x, y)
|
||||
}
|
||||
Self::SelectionMove(x, 0) if *x > 0 => {
|
||||
write!(f, "Move selection right")
|
||||
}
|
||||
Self::SelectionMove(x, 0) if *x < 0 => {
|
||||
write!(f, "Move selection left")
|
||||
}
|
||||
Self::SelectionMove(0, y) if *y > 0 => {
|
||||
write!(f, "Move selection up")
|
||||
}
|
||||
Self::SelectionMove(0, y) if *y < 0 => {
|
||||
write!(f, "Move selection down")
|
||||
}
|
||||
Self::SelectionOffset(x, y) => write!(f, "Offset selection by {:2},{:2}", x, y),
|
||||
Self::SelectionMove(x, 0) if *x > 0 => write!(f, "Move selection right"),
|
||||
Self::SelectionMove(x, 0) if *x < 0 => write!(f, "Move selection left"),
|
||||
Self::SelectionMove(0, y) if *y > 0 => write!(f, "Move selection up"),
|
||||
Self::SelectionMove(0, y) if *y < 0 => write!(f, "Move selection down"),
|
||||
Self::SelectionJump(Direction::Forward) => {
|
||||
write!(f, "Move selection forward by one frame")
|
||||
}
|
||||
@ -258,9 +236,7 @@ impl<'a> Parse<'a> for Key {
|
||||
"tab" => platform::Key::Tab,
|
||||
"end" => platform::Key::End,
|
||||
"esc" => platform::Key::Escape,
|
||||
other => {
|
||||
return Err(Error::new(format!("unknown key <{}>", other)))
|
||||
}
|
||||
other => return Err(Error::new(format!("unknown key <{}>", other))),
|
||||
};
|
||||
Ok((Key::Virtual(virt), p))
|
||||
} else {
|
||||
@ -647,12 +623,10 @@ impl<'a> Parse<'a> for Command {
|
||||
"parsing failed: `f/new` has been renamed to `f/add`",
|
||||
)),
|
||||
"f/add" => Ok((Command::AddFrame, p)),
|
||||
"f/clone" => {
|
||||
match p.clone().parse::<i32>().or_else(|_| Ok((-1, p))) {
|
||||
Ok((index, p)) => Ok((Command::CloneFrame(index), p)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
"f/clone" => match p.clone().parse::<i32>().or_else(|_| Ok((-1, p))) {
|
||||
Ok((index, p)) => Ok((Command::CloneFrame(index), p)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
"f/remove" => Ok((Command::RemoveFrame, p)),
|
||||
"f/resize" => {
|
||||
let ((w, h), p) = p.parse::<(u32, u32)>()?;
|
||||
@ -661,12 +635,8 @@ impl<'a> Parse<'a> for Command {
|
||||
"tool" => {
|
||||
let (t, p) = p.word()?;
|
||||
match t {
|
||||
"pan" => {
|
||||
Ok((Command::Tool(Tool::Pan(PanState::default())), p))
|
||||
}
|
||||
"brush" => {
|
||||
Ok((Command::Tool(Tool::Brush(Brush::default())), p))
|
||||
}
|
||||
"pan" => Ok((Command::Tool(Tool::Pan(PanState::default())), p)),
|
||||
"brush" => Ok((Command::Tool(Tool::Brush(Brush::default())), p)),
|
||||
"sampler" => Ok((Command::Tool(Tool::Sampler), p)),
|
||||
_ => Err(Error::new(format!("unknown tool {:?}", t))),
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
//! Data included in the `rx` binary.
|
||||
|
||||
/// Initial (default) configuration for rx.
|
||||
pub const CONFIG: &[u8] =
|
||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/config/init.rx"));
|
||||
pub const CONFIG: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/config/init.rx"));
|
||||
|
||||
/// Cursor sprites.
|
||||
pub const CURSORS: &[u8] =
|
||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/cursors.png"));
|
||||
|
||||
/// Glyphs used for font rendering.
|
||||
pub const GLYPHS: &[u8] =
|
||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/glyphs.png"));
|
||||
pub const GLYPHS: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/glyphs.png"));
|
||||
|
26
src/event.rs
26
src/event.rs
@ -67,26 +67,14 @@ pub enum Event {
|
||||
impl From<Event> for String {
|
||||
fn from(event: Event) -> String {
|
||||
match event {
|
||||
Event::MouseInput(_, platform::InputState::Pressed) => {
|
||||
format!("mouse/input pressed")
|
||||
}
|
||||
Event::MouseInput(_, platform::InputState::Released) => {
|
||||
format!("mouse/input released")
|
||||
}
|
||||
Event::MouseInput(_, platform::InputState::Repeated) => {
|
||||
unreachable!()
|
||||
}
|
||||
Event::MouseWheel(delta) => {
|
||||
format!("mouse/wheel {} {}", delta.x, delta.y)
|
||||
}
|
||||
Event::MouseInput(_, platform::InputState::Pressed) => format!("mouse/input pressed"),
|
||||
Event::MouseInput(_, platform::InputState::Released) => format!("mouse/input released"),
|
||||
Event::MouseInput(_, platform::InputState::Repeated) => unreachable!(),
|
||||
Event::MouseWheel(delta) => format!("mouse/wheel {} {}", delta.x, delta.y),
|
||||
Event::CursorMoved(platform::LogicalPosition { x, y }) => {
|
||||
format!("cursor/moved {} {}", x, y)
|
||||
}
|
||||
Event::KeyboardInput(platform::KeyboardInput {
|
||||
key,
|
||||
state,
|
||||
..
|
||||
}) => {
|
||||
Event::KeyboardInput(platform::KeyboardInput { key, state, .. }) => {
|
||||
let state = match state {
|
||||
platform::InputState::Pressed => "pressed",
|
||||
platform::InputState::Released => "released",
|
||||
@ -119,9 +107,7 @@ impl FromStr for Event {
|
||||
"cursor/moved" => {
|
||||
let ((x, y), p) = p.parse::<(f64, f64)>()?;
|
||||
Ok((
|
||||
Event::CursorMoved(platform::LogicalPosition::new(
|
||||
x as f64, y as f64,
|
||||
)),
|
||||
Event::CursorMoved(platform::LogicalPosition::new(x as f64, y as f64)),
|
||||
p,
|
||||
))
|
||||
}
|
||||
|
@ -50,9 +50,8 @@ impl DigestState {
|
||||
let r = io::BufReader::new(f);
|
||||
for line in r.lines() {
|
||||
let line = line?;
|
||||
let hash = Hash::from_str(line.as_str()).map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::InvalidInput, e)
|
||||
})?;
|
||||
let hash = Hash::from_str(line.as_str())
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
|
||||
frames.push(hash);
|
||||
}
|
||||
}
|
||||
@ -128,10 +127,7 @@ impl Execution {
|
||||
}
|
||||
|
||||
/// Create a recording.
|
||||
pub fn recording<P: AsRef<Path>>(
|
||||
path: P,
|
||||
mode: DigestMode,
|
||||
) -> io::Result<Self> {
|
||||
pub fn recording<P: AsRef<Path>>(path: P, mode: DigestMode) -> io::Result<Self> {
|
||||
use io::{Error, ErrorKind};
|
||||
|
||||
let path = path.as_ref();
|
||||
@ -143,10 +139,7 @@ impl Execution {
|
||||
))?
|
||||
.as_ref();
|
||||
|
||||
let digest = DigestState::from(
|
||||
mode,
|
||||
path.join(file_name).with_extension("digest"),
|
||||
)?;
|
||||
let digest = DigestState::from(mode, path.join(file_name).with_extension("digest"))?;
|
||||
|
||||
Ok(Self::Recording {
|
||||
events: Vec::new(),
|
||||
@ -157,10 +150,7 @@ impl Execution {
|
||||
}
|
||||
|
||||
/// Create a replay.
|
||||
pub fn replaying<P: AsRef<Path>>(
|
||||
path: P,
|
||||
mode: DigestMode,
|
||||
) -> io::Result<Self> {
|
||||
pub fn replaying<P: AsRef<Path>>(path: P, mode: DigestMode) -> io::Result<Self> {
|
||||
use io::{Error, ErrorKind};
|
||||
|
||||
let mut events = VecDeque::new();
|
||||
@ -174,10 +164,7 @@ impl Execution {
|
||||
))?
|
||||
.as_ref();
|
||||
|
||||
let digest = DigestState::from(
|
||||
mode,
|
||||
path.join(file_name).with_extension("digest"),
|
||||
)?;
|
||||
let digest = DigestState::from(mode, path.join(file_name).with_extension("digest"))?;
|
||||
|
||||
let events_path = path.join(file_name).with_extension("events");
|
||||
match File::open(&events_path) {
|
||||
@ -188,12 +175,7 @@ impl Execution {
|
||||
let ev = TimedEvent::from_str(&line).map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!(
|
||||
"{}:{}: {}",
|
||||
events_path.display(),
|
||||
i + 1,
|
||||
e
|
||||
),
|
||||
format!("{}:{}: {}", events_path.display(), i + 1, e),
|
||||
)
|
||||
})?;
|
||||
events.push_back(ev);
|
||||
@ -283,8 +265,7 @@ impl Execution {
|
||||
))?
|
||||
.as_ref();
|
||||
|
||||
let mut f =
|
||||
File::create(path.join(file_name.with_extension("events")))?;
|
||||
let mut f = File::create(path.join(file_name.with_extension("events")))?;
|
||||
for ev in events.clone() {
|
||||
writeln!(&mut f, "{}", String::from(ev))?;
|
||||
}
|
||||
@ -330,10 +311,7 @@ impl Execution {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn write_digest<P: AsRef<Path>>(
|
||||
recorder: &FrameRecorder,
|
||||
path: P,
|
||||
) -> io::Result<()> {
|
||||
fn write_digest<P: AsRef<Path>>(recorder: &FrameRecorder, path: P) -> io::Result<()> {
|
||||
use std::io::Write;
|
||||
|
||||
let path = path.as_ref();
|
||||
|
16
src/font.rs
16
src/font.rs
@ -15,12 +15,7 @@ pub struct Font {
|
||||
}
|
||||
|
||||
impl Font {
|
||||
pub fn new(
|
||||
texture: gfx::Texture,
|
||||
binding: gfx::BindingGroup,
|
||||
gw: f32,
|
||||
gh: f32,
|
||||
) -> Font {
|
||||
pub fn new(texture: gfx::Texture, binding: gfx::BindingGroup, gw: f32, gh: f32) -> Font {
|
||||
let width = texture.w as f32;
|
||||
let height = texture.h as f32;
|
||||
|
||||
@ -52,14 +47,7 @@ impl TextBatch {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(
|
||||
&mut self,
|
||||
text: &str,
|
||||
mut sx: f32,
|
||||
sy: f32,
|
||||
z: ZDepth,
|
||||
color: Rgba8,
|
||||
) {
|
||||
pub fn add(&mut self, text: &str, mut sx: f32, sy: f32, z: ZDepth, color: Rgba8) {
|
||||
let offset: f32 = 32.;
|
||||
|
||||
let gw = self.gw;
|
||||
|
@ -16,10 +16,7 @@ impl<'a> core::AbstractPipeline<'a> for Pipeline {
|
||||
|
||||
fn description() -> core::PipelineDescription<'a> {
|
||||
core::PipelineDescription {
|
||||
vertex_layout: &[
|
||||
core::VertexFormat::Float4,
|
||||
core::VertexFormat::Float2,
|
||||
],
|
||||
vertex_layout: &[core::VertexFormat::Float4, core::VertexFormat::Float2],
|
||||
pipeline_layout: &[
|
||||
Set(&[Binding {
|
||||
binding: BindingType::UniformBuffer,
|
||||
@ -49,8 +46,7 @@ impl<'a> core::AbstractPipeline<'a> for Pipeline {
|
||||
fn setup(pipeline: core::Pipeline, dev: &core::Device) -> Self {
|
||||
let m: Matrix4<f32> = Matrix4::identity();
|
||||
let buf = dev.create_uniform_buffer(&[m]);
|
||||
let bindings =
|
||||
dev.create_binding_group(&pipeline.layout.sets[0], &[&buf]);
|
||||
let bindings = dev.create_binding_group(&pipeline.layout.sets[0], &[&buf]);
|
||||
|
||||
Self {
|
||||
pipeline,
|
||||
@ -79,10 +75,9 @@ impl Pipeline {
|
||||
framebuffer: &core::Framebuffer,
|
||||
sampler: &core::Sampler,
|
||||
) -> core::BindingGroup {
|
||||
renderer.device.create_binding_group(
|
||||
&self.pipeline.layout.sets[2],
|
||||
&[framebuffer, sampler],
|
||||
)
|
||||
renderer
|
||||
.device
|
||||
.create_binding_group(&self.pipeline.layout.sets[2], &[framebuffer, sampler])
|
||||
}
|
||||
|
||||
pub fn vertex_buffer(
|
||||
|
@ -56,12 +56,7 @@ impl TransformBuffer {
|
||||
(0..max).step_by(AlignedBuffer::ALIGNMENT as usize)
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
transforms: &[Matrix4<f32>],
|
||||
r: &core::Renderer,
|
||||
f: &mut core::Frame,
|
||||
) {
|
||||
pub fn update(&mut self, transforms: &[Matrix4<f32>], r: &core::Renderer, f: &mut core::Frame) {
|
||||
let len = transforms.len();
|
||||
assert!(len <= self.cap, "fatal: capacity exceeded");
|
||||
|
||||
|
59
src/lib.rs
59
src/lib.rs
@ -91,10 +91,7 @@ impl Default for Options {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init<P: AsRef<Path>>(
|
||||
paths: &[P],
|
||||
options: Options,
|
||||
) -> std::io::Result<()> {
|
||||
pub fn init<P: AsRef<Path>>(paths: &[P], options: Options) -> std::io::Result<()> {
|
||||
use std::io;
|
||||
|
||||
debug!("options: {:?}", options);
|
||||
@ -103,8 +100,7 @@ pub fn init<P: AsRef<Path>>(
|
||||
WindowHint::Resizable(options.resizable),
|
||||
WindowHint::Visible(!options.headless),
|
||||
];
|
||||
let (win, events) =
|
||||
platform::init("rx", options.width, options.height, hints)?;
|
||||
let (win, events) = platform::init("rx", options.width, options.height, hints)?;
|
||||
|
||||
let hidpi_factor = win.hidpi_factor();
|
||||
let win_size = win.size();
|
||||
@ -114,19 +110,15 @@ pub fn init<P: AsRef<Path>>(
|
||||
info!("hidpi factor: {}", hidpi_factor);
|
||||
|
||||
let resources = ResourceManager::new();
|
||||
let base_dirs =
|
||||
dirs::ProjectDirs::from("org", "void", "rx").ok_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::NotFound, "home directory not found")
|
||||
})?;
|
||||
let mut session =
|
||||
Session::new(win_w, win_h, hidpi_factor, resources.clone(), base_dirs)
|
||||
.init(options.source.clone())?;
|
||||
let base_dirs = dirs::ProjectDirs::from("org", "void", "rx")
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "home directory not found"))?;
|
||||
let mut session = Session::new(win_w, win_h, hidpi_factor, resources.clone(), base_dirs)
|
||||
.init(options.source.clone())?;
|
||||
|
||||
// When working with digests, certain settings need to be overwritten
|
||||
// to ensure things work correctly.
|
||||
match &options.exec {
|
||||
Execution::Replaying { digest, .. }
|
||||
| Execution::Recording { digest, .. }
|
||||
Execution::Replaying { digest, .. } | Execution::Recording { digest, .. }
|
||||
if digest.mode != DigestMode::Ignore =>
|
||||
{
|
||||
session
|
||||
@ -151,10 +143,7 @@ pub fn init<P: AsRef<Path>>(
|
||||
let mut renderer = Renderer::new(&mut r, win_size, resources);
|
||||
|
||||
if let Err(e) = session.edit(paths) {
|
||||
session.message(
|
||||
format!("Error loading path(s): {}", e),
|
||||
MessageType::Error,
|
||||
);
|
||||
session.message(format!("Error loading path(s): {}", e), MessageType::Error);
|
||||
}
|
||||
if session.views.is_empty() {
|
||||
session.blank(
|
||||
@ -168,11 +157,7 @@ pub fn init<P: AsRef<Path>>(
|
||||
|
||||
let physical = win_size.to_physical(hidpi_factor);
|
||||
let mut logical = win_size;
|
||||
let mut swap_chain = r.swap_chain(
|
||||
physical.width as u32,
|
||||
physical.height as u32,
|
||||
present_mode,
|
||||
);
|
||||
let mut swap_chain = r.swap_chain(physical.width as u32, physical.height as u32, present_mode);
|
||||
|
||||
let mut render_timer = FrameTimer::new();
|
||||
let mut update_timer = FrameTimer::new();
|
||||
@ -225,11 +210,8 @@ pub fn init<P: AsRef<Path>>(
|
||||
present_mode,
|
||||
);
|
||||
} else {
|
||||
let input_delay: f64 =
|
||||
session.settings["input/delay"].float64();
|
||||
std::thread::sleep(time::Duration::from_micros(
|
||||
(input_delay * 1000.) as u64,
|
||||
));
|
||||
let input_delay: f64 = session.settings["input/delay"].float64();
|
||||
std::thread::sleep(time::Duration::from_micros((input_delay * 1000.) as u64));
|
||||
}
|
||||
|
||||
let delta = last.elapsed();
|
||||
@ -241,14 +223,8 @@ pub fn init<P: AsRef<Path>>(
|
||||
return platform::ControlFlow::Wait;
|
||||
}
|
||||
|
||||
let effects = update_timer.run(|avg| {
|
||||
session.update(
|
||||
&mut session_events,
|
||||
execution.clone(),
|
||||
delta,
|
||||
avg,
|
||||
)
|
||||
});
|
||||
let effects = update_timer
|
||||
.run(|avg| session.update(&mut session_events, execution.clone(), delta, avg));
|
||||
render_timer.run(|avg| {
|
||||
renderer.frame(
|
||||
&session,
|
||||
@ -344,14 +320,9 @@ fn resize(
|
||||
present_mode: core::PresentMode,
|
||||
) {
|
||||
let scale: f64 = session.settings["scale"].float64();
|
||||
let logical_size =
|
||||
platform::LogicalSize::new(size.width / scale, size.height / scale);
|
||||
let logical_size = platform::LogicalSize::new(size.width / scale, size.height / scale);
|
||||
session.handle_resized(logical_size);
|
||||
|
||||
let physical = size.to_physical(hidpi_factor);
|
||||
*swap_chain = r.swap_chain(
|
||||
physical.width as u32,
|
||||
physical.height as u32,
|
||||
present_mode,
|
||||
);
|
||||
*swap_chain = r.swap_chain(physical.width as u32, physical.height as u32, present_mode);
|
||||
}
|
||||
|
22
src/main.rs
22
src/main.rs
@ -38,9 +38,7 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(
|
||||
mut args: pico_args::Arguments,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn execute(mut args: pico_args::Arguments) -> Result<(), Box<dyn std::error::Error>> {
|
||||
rx::ALLOCATOR.reset();
|
||||
|
||||
let default = rx::Options::default();
|
||||
@ -64,10 +62,7 @@ fn execute(
|
||||
let source = args.opt_value_from_str::<_, PathBuf>("-u")?;
|
||||
let replay = args.opt_value_from_str::<_, PathBuf>("--replay")?;
|
||||
let record = args.opt_value_from_str::<_, PathBuf>("--record")?;
|
||||
let resizable = width.is_none()
|
||||
&& height.is_none()
|
||||
&& replay.is_none()
|
||||
&& record.is_none();
|
||||
let resizable = width.is_none() && height.is_none() && replay.is_none() && record.is_none();
|
||||
|
||||
if replay.is_some() && record.is_some() {
|
||||
return Err("'--replay' and '--record' can't both be specified".into());
|
||||
@ -80,10 +75,7 @@ fn execute(
|
||||
} else if !verify_digests && !record_digests {
|
||||
DigestMode::Ignore
|
||||
} else {
|
||||
return Err(
|
||||
"'--record-digests' and '--verify-digests' can't both be specified"
|
||||
.into(),
|
||||
);
|
||||
return Err("'--record-digests' and '--verify-digests' can't both be specified".into());
|
||||
};
|
||||
|
||||
let log = match args
|
||||
@ -120,10 +112,8 @@ fn execute(
|
||||
|
||||
match args.free() {
|
||||
Ok(paths) => rx::init(&paths, options).map_err(|e| e.into()),
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!("{}\n{}", e, HELP),
|
||||
)
|
||||
.into()),
|
||||
Err(e) => {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, format!("{}\n{}", e, HELP)).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::platform::{
|
||||
ControlFlow, InputState, Key, KeyboardInput, LogicalDelta, LogicalPosition,
|
||||
LogicalSize, ModifiersState, MouseButton, WindowEvent, WindowHint,
|
||||
ControlFlow, InputState, Key, KeyboardInput, LogicalDelta, LogicalPosition, LogicalSize,
|
||||
ModifiersState, MouseButton, WindowEvent, WindowHint,
|
||||
};
|
||||
|
||||
use glfw;
|
||||
@ -15,8 +15,8 @@ pub fn init<T>(
|
||||
h: u32,
|
||||
hints: &[WindowHint],
|
||||
) -> io::Result<(Window<T>, Events)> {
|
||||
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
let mut glfw =
|
||||
glfw::init(glfw::FAIL_ON_ERRORS).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
glfw.window_hint(glfw::WindowHint::Resizable(true));
|
||||
glfw.window_hint(glfw::WindowHint::Visible(true));
|
||||
@ -30,9 +30,7 @@ pub fn init<T>(
|
||||
|
||||
let (mut window, events) = glfw
|
||||
.create_window(w, h, title, glfw::WindowMode::Windowed)
|
||||
.ok_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::Other, "glfw: error creating window")
|
||||
})?;
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "glfw: error creating window"))?;
|
||||
|
||||
window.set_all_polling(true);
|
||||
|
||||
@ -66,11 +64,7 @@ where
|
||||
|
||||
if win.redraw_requested {
|
||||
win.redraw_requested = false;
|
||||
win.send_event(
|
||||
WindowEvent::RedrawRequested,
|
||||
&mut callback,
|
||||
&mut glfw,
|
||||
);
|
||||
win.send_event(WindowEvent::RedrawRequested, &mut callback, &mut glfw);
|
||||
}
|
||||
}
|
||||
callback(&mut win, WindowEvent::Destroyed);
|
||||
@ -122,12 +116,8 @@ impl<T> Window<T> {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn send_event<F>(
|
||||
&mut self,
|
||||
event: WindowEvent,
|
||||
callback: &mut F,
|
||||
glfw: &mut glfw::Glfw,
|
||||
) where
|
||||
fn send_event<F>(&mut self, event: WindowEvent, callback: &mut F, glfw: &mut glfw::Glfw)
|
||||
where
|
||||
F: 'static + FnMut(&mut Window<T>, WindowEvent) -> ControlFlow<T>,
|
||||
{
|
||||
match callback(self, event) {
|
||||
@ -184,24 +174,18 @@ impl From<glfw::WindowEvent> for WindowEvent {
|
||||
match event {
|
||||
// We care about logical ("screen") coordinates, so we
|
||||
// use this event instead of the framebuffer size event.
|
||||
Glfw::Size(w, h) => {
|
||||
WindowEvent::Resized(LogicalSize::new(w as f64, h as f64))
|
||||
}
|
||||
Glfw::Size(w, h) => WindowEvent::Resized(LogicalSize::new(w as f64, h as f64)),
|
||||
Glfw::FramebufferSize(_, _) => WindowEvent::Noop,
|
||||
Glfw::Iconify(true) => WindowEvent::Minimized,
|
||||
Glfw::Iconify(false) => WindowEvent::Restored,
|
||||
Glfw::Close => WindowEvent::CloseRequested,
|
||||
Glfw::Refresh => WindowEvent::RedrawRequested,
|
||||
Glfw::Pos(x, y) => {
|
||||
WindowEvent::Moved(LogicalPosition::new(x as f64, y as f64))
|
||||
}
|
||||
Glfw::MouseButton(button, action, modifiers) => {
|
||||
WindowEvent::MouseInput {
|
||||
state: action.into(),
|
||||
button: button.into(),
|
||||
modifiers: modifiers.into(),
|
||||
}
|
||||
}
|
||||
Glfw::Pos(x, y) => WindowEvent::Moved(LogicalPosition::new(x as f64, y as f64)),
|
||||
Glfw::MouseButton(button, action, modifiers) => WindowEvent::MouseInput {
|
||||
state: action.into(),
|
||||
button: button.into(),
|
||||
modifiers: modifiers.into(),
|
||||
},
|
||||
Glfw::Scroll(x, y) => WindowEvent::MouseWheel {
|
||||
delta: LogicalDelta { x, y },
|
||||
},
|
||||
@ -211,13 +195,11 @@ impl From<glfw::WindowEvent> for WindowEvent {
|
||||
position: LogicalPosition::new(x, y),
|
||||
},
|
||||
Glfw::Char(c) => WindowEvent::ReceivedCharacter(c),
|
||||
Glfw::Key(key, _, action, modifiers) => {
|
||||
WindowEvent::KeyboardInput(KeyboardInput {
|
||||
key: Some(key.into()),
|
||||
state: action.into(),
|
||||
modifiers: modifiers.into(),
|
||||
})
|
||||
}
|
||||
Glfw::Key(key, _, action, modifiers) => WindowEvent::KeyboardInput(KeyboardInput {
|
||||
key: Some(key.into()),
|
||||
state: action.into(),
|
||||
modifiers: modifiers.into(),
|
||||
}),
|
||||
Glfw::Focus(b) => WindowEvent::Focused(b),
|
||||
Glfw::ContentScale(x, y) => {
|
||||
if (x - y).abs() > 0.1 {
|
||||
|
@ -26,11 +26,7 @@ pub fn init<T>(
|
||||
}
|
||||
|
||||
/// Run the main event loop.
|
||||
pub fn run<F, T>(
|
||||
win: backend::Window<T>,
|
||||
events: backend::Events,
|
||||
callback: F,
|
||||
) -> T
|
||||
pub fn run<F, T>(win: backend::Window<T>, events: backend::Events, callback: F) -> T
|
||||
where
|
||||
F: 'static + FnMut(&mut backend::Window<T>, WindowEvent) -> ControlFlow<T>,
|
||||
T: Default,
|
||||
@ -350,10 +346,7 @@ impl LogicalPosition {
|
||||
LogicalPosition { x, y }
|
||||
}
|
||||
|
||||
pub fn from_physical<T: Into<PhysicalPosition>>(
|
||||
physical: T,
|
||||
dpi_factor: f64,
|
||||
) -> Self {
|
||||
pub fn from_physical<T: Into<PhysicalPosition>>(physical: T, dpi_factor: f64) -> Self {
|
||||
physical.into().to_logical(dpi_factor)
|
||||
}
|
||||
|
||||
@ -376,10 +369,7 @@ impl PhysicalPosition {
|
||||
PhysicalPosition { x, y }
|
||||
}
|
||||
|
||||
pub fn from_logical<T: Into<LogicalPosition>>(
|
||||
logical: T,
|
||||
dpi_factor: f64,
|
||||
) -> Self {
|
||||
pub fn from_logical<T: Into<LogicalPosition>>(logical: T, dpi_factor: f64) -> Self {
|
||||
logical.into().to_physical(dpi_factor)
|
||||
}
|
||||
|
||||
@ -402,10 +392,7 @@ impl LogicalSize {
|
||||
LogicalSize { width, height }
|
||||
}
|
||||
|
||||
pub fn from_physical<T: Into<PhysicalSize>>(
|
||||
physical: T,
|
||||
dpi_factor: f64,
|
||||
) -> Self {
|
||||
pub fn from_physical<T: Into<PhysicalSize>>(physical: T, dpi_factor: f64) -> Self {
|
||||
physical.into().to_logical(dpi_factor)
|
||||
}
|
||||
|
||||
@ -445,10 +432,7 @@ impl PhysicalSize {
|
||||
PhysicalSize { width, height }
|
||||
}
|
||||
|
||||
pub fn from_logical<T: Into<LogicalSize>>(
|
||||
logical: T,
|
||||
dpi_factor: f64,
|
||||
) -> Self {
|
||||
pub fn from_logical<T: Into<LogicalSize>>(logical: T, dpi_factor: f64) -> Self {
|
||||
logical.into().to_physical(dpi_factor)
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::platform::{
|
||||
ControlFlow, InputState, Key, KeyboardInput, LogicalDelta, LogicalPosition,
|
||||
LogicalSize, ModifiersState, MouseButton, WindowEvent, WindowHint,
|
||||
ControlFlow, InputState, Key, KeyboardInput, LogicalDelta, LogicalPosition, LogicalSize,
|
||||
ModifiersState, MouseButton, WindowEvent, WindowHint,
|
||||
};
|
||||
|
||||
use winit;
|
||||
@ -30,9 +30,7 @@ where
|
||||
}
|
||||
}
|
||||
winit::event::Event::EventsCleared => {
|
||||
if let ControlFlow::Exit(r) =
|
||||
callback(&mut win, WindowEvent::Ready)
|
||||
{
|
||||
if let ControlFlow::Exit(r) = callback(&mut win, WindowEvent::Ready) {
|
||||
*control_flow = winit::event_loop::ControlFlow::Exit;
|
||||
exit = r;
|
||||
}
|
||||
@ -183,9 +181,7 @@ impl From<winit::event::WindowEvent> for WindowEvent {
|
||||
position: position.into(),
|
||||
},
|
||||
Winit::ReceivedCharacter(c) => WindowEvent::ReceivedCharacter(c),
|
||||
Winit::KeyboardInput { input, .. } => {
|
||||
WindowEvent::KeyboardInput(input.into())
|
||||
}
|
||||
Winit::KeyboardInput { input, .. } => WindowEvent::KeyboardInput(input.into()),
|
||||
Winit::Focused(b) => WindowEvent::Focused(b),
|
||||
Winit::HiDpiFactorChanged(n) => WindowEvent::HiDpiFactorChanged(n),
|
||||
|
||||
@ -298,9 +294,7 @@ impl From<winit::event::MouseScrollDelta> for LogicalDelta {
|
||||
x: x as f64,
|
||||
y: y as f64,
|
||||
},
|
||||
winit::event::MouseScrollDelta::PixelDelta(pos) => {
|
||||
LogicalDelta { x: pos.x, y: pos.y }
|
||||
}
|
||||
winit::event::MouseScrollDelta::PixelDelta(pos) => LogicalDelta { x: pos.x, y: pos.y },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
186
src/renderer.rs
186
src/renderer.rs
@ -198,17 +198,12 @@ impl Renderer {
|
||||
const HELP_LAYER: ZDepth = ZDepth(-0.3);
|
||||
const CURSOR_LAYER: ZDepth = ZDepth(-0.2);
|
||||
|
||||
pub fn new(
|
||||
r: &mut core::Renderer,
|
||||
window: LogicalSize,
|
||||
resources: ResourceManager,
|
||||
) -> Self {
|
||||
pub fn new(r: &mut core::Renderer, window: LogicalSize, resources: ResourceManager) -> Self {
|
||||
let (win_w, win_h) = (window.width as u32, window.height as u32);
|
||||
|
||||
let sprite2d: kit::sprite2d::Pipeline = r.pipeline(Blending::default());
|
||||
let shape2d: kit::shape2d::Pipeline = r.pipeline(Blending::default());
|
||||
let framebuffer2d: framebuffer2d::Pipeline =
|
||||
r.pipeline(Blending::default());
|
||||
let framebuffer2d: framebuffer2d::Pipeline = r.pipeline(Blending::default());
|
||||
let screen2d: screen2d::Pipeline = r.pipeline(Blending::default());
|
||||
|
||||
let sampler = r.sampler(Filter::Nearest, Filter::Nearest);
|
||||
@ -226,12 +221,7 @@ impl Renderer {
|
||||
let binding = sprite2d.binding(r, &texture, &sampler);
|
||||
|
||||
(
|
||||
Font::new(
|
||||
texture,
|
||||
binding,
|
||||
self::GLYPH_WIDTH,
|
||||
self::GLYPH_HEIGHT,
|
||||
),
|
||||
Font::new(texture, binding, self::GLYPH_WIDTH, self::GLYPH_HEIGHT),
|
||||
img,
|
||||
)
|
||||
};
|
||||
@ -312,21 +302,11 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(
|
||||
&mut self,
|
||||
effects: Vec<Effect>,
|
||||
views: &ViewManager,
|
||||
r: &mut core::Renderer,
|
||||
) {
|
||||
pub fn init(&mut self, effects: Vec<Effect>, views: &ViewManager, r: &mut core::Renderer) {
|
||||
self.handle_effects(effects, &views, r);
|
||||
}
|
||||
|
||||
fn render_help(
|
||||
&self,
|
||||
session: &Session,
|
||||
r: &mut core::Renderer,
|
||||
p: &mut core::Pass,
|
||||
) {
|
||||
fn render_help(&self, session: &Session, r: &mut core::Renderer, p: &mut core::Pass) {
|
||||
let win_buf = shape2d::Batch::singleton(Shape::Rectangle(
|
||||
Rect::origin(self.window.width as f32, self.window.height as f32),
|
||||
Renderer::HELP_LAYER,
|
||||
@ -361,8 +341,7 @@ impl Renderer {
|
||||
.filter_map(|kb| kb.display.as_ref().map(|d| (d, kb)))
|
||||
.partition(|(_, kb)| kb.modes.contains(&Mode::Normal));
|
||||
|
||||
let mut line = (0..(self.window.height as usize
|
||||
- self::LINE_HEIGHT as usize * 4))
|
||||
let mut line = (0..(self.window.height as usize - self::LINE_HEIGHT as usize * 4))
|
||||
.rev()
|
||||
.step_by(self::LINE_HEIGHT as usize);
|
||||
|
||||
@ -415,8 +394,7 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
for (i, l) in session::HELP.lines().enumerate() {
|
||||
let y =
|
||||
self.window.height as f32 - (i + 4) as f32 * self::LINE_HEIGHT;
|
||||
let y = self.window.height as f32 - (i + 4) as f32 * self::LINE_HEIGHT;
|
||||
|
||||
text.add(
|
||||
l,
|
||||
@ -456,16 +434,10 @@ impl Renderer {
|
||||
let mut ui_batch = shape2d::Batch::new();
|
||||
let mut text_batch = TextBatch::new(&self.font);
|
||||
let mut overlay_batch = TextBatch::new(&self.font);
|
||||
let mut cursor_batch = sprite2d::Batch::new(
|
||||
self.cursors.texture.w,
|
||||
self.cursors.texture.h,
|
||||
);
|
||||
let mut paste_batch =
|
||||
sprite2d::Batch::new(self.paste.texture.w, self.paste.texture.h);
|
||||
let mut checker_batch = sprite2d::Batch::new(
|
||||
self.checker.texture.w,
|
||||
self.checker.texture.h,
|
||||
);
|
||||
let mut cursor_batch = sprite2d::Batch::new(self.cursors.texture.w, self.cursors.texture.h);
|
||||
let mut paste_batch = sprite2d::Batch::new(self.paste.texture.w, self.paste.texture.h);
|
||||
let mut checker_batch =
|
||||
sprite2d::Batch::new(self.checker.texture.w, self.checker.texture.h);
|
||||
|
||||
// Handle view operations.
|
||||
for v in session.views.values() {
|
||||
@ -512,12 +484,7 @@ impl Renderer {
|
||||
let mut f = r.frame();
|
||||
|
||||
self.update_view_animations(session, r);
|
||||
self.update_view_transforms(
|
||||
session.views.values(),
|
||||
session.offset,
|
||||
&r,
|
||||
&mut f,
|
||||
);
|
||||
self.update_view_transforms(session.views.values(), session.offset, &r, &mut f);
|
||||
|
||||
let v = session.active_view();
|
||||
let view_data = self
|
||||
@ -525,8 +492,7 @@ impl Renderer {
|
||||
.get(&v.id)
|
||||
.expect("the view data for the active view must exist");
|
||||
let view_ortho = kit::ortho(v.width(), v.height());
|
||||
let ortho =
|
||||
kit::ortho(self.window.width as u32, self.window.height as u32);
|
||||
let ortho = kit::ortho(self.window.width as u32, self.window.height as u32);
|
||||
|
||||
if self.cache.ortho.map_or(true, |m| m != ortho) {
|
||||
r.update_pipeline(&self.shape2d, ortho, &mut f);
|
||||
@ -551,18 +517,11 @@ impl Renderer {
|
||||
// Always clear the active view staging buffer. We do this because
|
||||
// it may not get drawn to this frame, and hence may remain dirty
|
||||
// from a previous frame.
|
||||
let mut p = f.pass(
|
||||
PassOp::Clear(Rgba::TRANSPARENT),
|
||||
&view_data.staging_fb,
|
||||
);
|
||||
let mut p = f.pass(PassOp::Clear(Rgba::TRANSPARENT), &view_data.staging_fb);
|
||||
|
||||
// Render brush strokes to view staging framebuffers.
|
||||
if let Some(buf) = &staging_buf {
|
||||
self.render_brush_strokes(
|
||||
buf,
|
||||
&Blending::default(),
|
||||
&mut p,
|
||||
);
|
||||
self.render_brush_strokes(buf, &Blending::default(), &mut p);
|
||||
}
|
||||
// Draw paste buffer to view staging buffer.
|
||||
if let Some(buf) = paste_buf {
|
||||
@ -601,8 +560,7 @@ impl Renderer {
|
||||
}
|
||||
|
||||
{
|
||||
let mut p =
|
||||
f.pass(PassOp::Clear(Rgba::TRANSPARENT), &self.screen_fb);
|
||||
let mut p = f.pass(PassOp::Clear(Rgba::TRANSPARENT), &self.screen_fb);
|
||||
|
||||
// Draw view checkers to screen framebuffer.
|
||||
if session.settings["checker"].is_set() {
|
||||
@ -642,9 +600,7 @@ impl Renderer {
|
||||
p.set_binding(&self.screen_binding, &[]);
|
||||
p.draw_buffer(&self.screen_vb);
|
||||
|
||||
if session.settings["debug"].is_set()
|
||||
|| !execution.borrow().is_normal()
|
||||
{
|
||||
if session.settings["debug"].is_set() || !execution.borrow().is_normal() {
|
||||
p.set_pipeline(&self.sprite2d);
|
||||
p.draw(&overlay_buf, &self.font.binding);
|
||||
}
|
||||
@ -732,8 +688,7 @@ impl Renderer {
|
||||
//
|
||||
// Either way, we handle it equally, by re-creating the view-data and restoring
|
||||
// the current snapshot.
|
||||
let view_data =
|
||||
ViewData::new(vw, vh, &self.framebuffer2d, &self.sprite2d, r);
|
||||
let view_data = ViewData::new(vw, vh, &self.framebuffer2d, &self.sprite2d, r);
|
||||
|
||||
// We don't want the lock to be held when `submit` is called below,
|
||||
// because in some cases it'll trigger the read-back which claims
|
||||
@ -808,23 +763,17 @@ impl Renderer {
|
||||
|
||||
buffer.extend_from_slice(row);
|
||||
}
|
||||
let mut pixels: Vec<Rgba8> =
|
||||
Vec::with_capacity(buffer.len());
|
||||
let mut pixels: Vec<Rgba8> = Vec::with_capacity(buffer.len());
|
||||
for c in buffer.into_iter() {
|
||||
pixels.push(c.into());
|
||||
}
|
||||
assert!(pixels.len() == w * h);
|
||||
|
||||
if self.paste.texture.w != w as u32
|
||||
|| self.paste.texture.h != h as u32
|
||||
{
|
||||
if self.paste.texture.w != w as u32 || self.paste.texture.h != h as u32 {
|
||||
self.paste.ready = false;
|
||||
self.paste.texture = r.texture(w as u32, h as u32);
|
||||
self.paste.binding = self.paste2d.binding(
|
||||
r,
|
||||
&self.paste.texture,
|
||||
&self.sampler,
|
||||
);
|
||||
self.paste.binding =
|
||||
self.paste2d.binding(r, &self.paste.texture, &self.sampler);
|
||||
}
|
||||
pixels
|
||||
};
|
||||
@ -855,8 +804,7 @@ impl Renderer {
|
||||
let (s, pixels) = resources.get_snapshot(*id);
|
||||
let (w, h) = (s.width(), s.height());
|
||||
|
||||
let view_data =
|
||||
ViewData::new(w, h, &self.framebuffer2d, &self.sprite2d, r);
|
||||
let view_data = ViewData::new(w, h, &self.framebuffer2d, &self.sprite2d, r);
|
||||
|
||||
debug_assert!(!pixels.is_empty());
|
||||
r.submit(&[
|
||||
@ -869,11 +817,7 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_ui(
|
||||
session: &Session,
|
||||
canvas: &mut shape2d::Batch,
|
||||
text: &mut TextBatch,
|
||||
) {
|
||||
fn draw_ui(session: &Session, canvas: &mut shape2d::Batch, text: &mut TextBatch) {
|
||||
let view = session.active_view();
|
||||
|
||||
if let Some(selection) = session.selection {
|
||||
@ -904,13 +848,7 @@ impl Renderer {
|
||||
} else {
|
||||
(s.y2) as f32 * z - self::LINE_HEIGHT + 1.
|
||||
};
|
||||
text.add(
|
||||
&t,
|
||||
x + offset.x,
|
||||
y + offset.y,
|
||||
Renderer::TEXT_LAYER,
|
||||
stroke,
|
||||
);
|
||||
text.add(&t, x + offset.x, y + offset.y, Renderer::TEXT_LAYER, stroke);
|
||||
}
|
||||
|
||||
// Selection stroke.
|
||||
@ -924,8 +862,7 @@ impl Renderer {
|
||||
// Selection fill.
|
||||
if r.intersects(view.bounds()) {
|
||||
canvas.add(Shape::Rectangle(
|
||||
r.intersection(view.bounds()).map(|n| n as f32) * view.zoom
|
||||
+ offset,
|
||||
r.intersection(view.bounds()).map(|n| n as f32) * view.zoom + offset,
|
||||
Renderer::UI_LAYER,
|
||||
Rotation::ZERO,
|
||||
Stroke::NONE,
|
||||
@ -953,21 +890,16 @@ impl Renderer {
|
||||
let border_color = if session.is_active(*id) {
|
||||
match session.mode {
|
||||
// TODO: (rgx) Use `Rgba8::alpha`.
|
||||
Mode::Visual(_) => Rgba8::new(
|
||||
color::RED.r,
|
||||
color::RED.g,
|
||||
color::RED.b,
|
||||
0xdd,
|
||||
)
|
||||
.into(),
|
||||
Mode::Visual(_) => {
|
||||
Rgba8::new(color::RED.r, color::RED.g, color::RED.b, 0xdd).into()
|
||||
}
|
||||
_ => color::WHITE.into(),
|
||||
}
|
||||
} else {
|
||||
Rgba::new(0.5, 0.5, 0.5, 1.0)
|
||||
};
|
||||
canvas.add(Shape::Rectangle(
|
||||
Rect::new(r.x1 - 1., r.y1 - 1., r.x2 + 1., r.y2 + 1.)
|
||||
+ session.offset,
|
||||
Rect::new(r.x1 - 1., r.y1 - 1., r.x2 + 1., r.y2 + 1.) + session.offset,
|
||||
Renderer::UI_LAYER,
|
||||
Rotation::ZERO,
|
||||
Stroke::new(1.0, border_color),
|
||||
@ -1023,10 +955,8 @@ impl Renderer {
|
||||
if session.width >= 400. {
|
||||
// Fg color
|
||||
canvas.add(Shape::Rectangle(
|
||||
Rect::origin(11., 11.).with_origin(
|
||||
session.width * 0.4,
|
||||
self::LINE_HEIGHT + self::MARGIN + 2.,
|
||||
),
|
||||
Rect::origin(11., 11.)
|
||||
.with_origin(session.width * 0.4, self::LINE_HEIGHT + self::MARGIN + 2.),
|
||||
Renderer::UI_LAYER,
|
||||
Rotation::ZERO,
|
||||
Stroke::new(1.0, Rgba::WHITE),
|
||||
@ -1050,9 +980,7 @@ impl Renderer {
|
||||
if session.mode == Mode::Command {
|
||||
let s = format!("{}", &session.cmdline.input());
|
||||
text.add(&s, MARGIN, MARGIN, Renderer::TEXT_LAYER, Rgba8::WHITE);
|
||||
} else if !session.message.is_replay()
|
||||
&& session.settings["ui/message"].is_set()
|
||||
{
|
||||
} else if !session.message.is_replay() && session.settings["ui/message"].is_set() {
|
||||
let s = format!("{}", &session.message);
|
||||
text.add(
|
||||
&s,
|
||||
@ -1148,12 +1076,7 @@ impl Renderer {
|
||||
}
|
||||
|
||||
batch.add(Shape::Rectangle(
|
||||
Rect::new(
|
||||
p.x + x,
|
||||
p.y + y,
|
||||
p.x + x + p.cellsize,
|
||||
p.y + y + p.cellsize,
|
||||
),
|
||||
Rect::new(p.x + x, p.y + y, p.x + x + p.cellsize, p.y + y + p.cellsize),
|
||||
Renderer::PALETTE_LAYER,
|
||||
Rotation::ZERO,
|
||||
stroke,
|
||||
@ -1193,9 +1116,7 @@ impl Renderer {
|
||||
let c = session.cursor;
|
||||
let v = session.active_view();
|
||||
if v.contains(c - session.offset) {
|
||||
if session
|
||||
.is_selected(session.view_coords(v.id, c).into())
|
||||
{
|
||||
if session.is_selected(session.view_coords(v.id, c).into()) {
|
||||
if let Some(rect) = Cursors::rect(&Tool::Move) {
|
||||
let offset = Cursors::offset(&Tool::Move);
|
||||
batch.add(
|
||||
@ -1280,8 +1201,7 @@ impl Renderer {
|
||||
(Stroke::NONE, Fill::Solid(session.fg.into()))
|
||||
};
|
||||
|
||||
let view_coords =
|
||||
session.active_view_coords(session.cursor);
|
||||
let view_coords = session.active_view_coords(session.cursor);
|
||||
for p in brush.expand(view_coords.into(), v.extent()) {
|
||||
shapes.add(brush.shape(
|
||||
*session.session_coords(v.id, p.into()),
|
||||
@ -1314,22 +1234,11 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_paste(
|
||||
session: &Session,
|
||||
paste: &Paste,
|
||||
batch: &mut sprite2d::Batch,
|
||||
) {
|
||||
if let (Mode::Visual(VisualState::Pasting), Some(s)) =
|
||||
(session.mode, session.selection)
|
||||
{
|
||||
fn draw_paste(session: &Session, paste: &Paste, batch: &mut sprite2d::Batch) {
|
||||
if let (Mode::Visual(VisualState::Pasting), Some(s)) = (session.mode, session.selection) {
|
||||
batch.add(
|
||||
paste.texture.rect(),
|
||||
Rect::new(
|
||||
s.x1 as f32,
|
||||
s.y1 as f32,
|
||||
s.x2 as f32 + 1.,
|
||||
s.y2 as f32 + 1.,
|
||||
),
|
||||
Rect::new(s.x1 as f32, s.y1 as f32, s.x2 as f32 + 1., s.y2 as f32 + 1.),
|
||||
ZDepth::default(),
|
||||
Rgba::TRANSPARENT,
|
||||
0.9,
|
||||
@ -1379,17 +1288,12 @@ impl Renderer {
|
||||
p.draw_buffer(&paint_buf);
|
||||
}
|
||||
|
||||
pub fn handle_resized(
|
||||
&mut self,
|
||||
size: platform::LogicalSize,
|
||||
r: &core::Renderer,
|
||||
) {
|
||||
pub fn handle_resized(&mut self, size: platform::LogicalSize, r: &core::Renderer) {
|
||||
let (w, h) = (size.width as u32, size.height as u32);
|
||||
|
||||
self.window = size;
|
||||
self.screen_fb = r.framebuffer(w, h);
|
||||
self.screen_binding =
|
||||
self.screen2d.binding(r, &self.screen_fb, &self.sampler);
|
||||
self.screen_binding = self.screen2d.binding(r, &self.screen_fb, &self.sampler);
|
||||
}
|
||||
|
||||
fn update_view_transforms<'a, I>(
|
||||
@ -1404,9 +1308,8 @@ impl Renderer {
|
||||
self.view_transforms.clear();
|
||||
for v in views {
|
||||
self.view_transforms.push(
|
||||
Matrix4::from_translation(
|
||||
(offset + v.offset).extend(*Renderer::VIEW_LAYER),
|
||||
) * Matrix4::from_nonuniform_scale(v.zoom, v.zoom, 1.0),
|
||||
Matrix4::from_translation((offset + v.offset).extend(*Renderer::VIEW_LAYER))
|
||||
* Matrix4::from_nonuniform_scale(v.zoom, v.zoom, 1.0),
|
||||
);
|
||||
}
|
||||
self.view_transforms_buf
|
||||
@ -1427,8 +1330,7 @@ impl Renderer {
|
||||
v.width(),
|
||||
v.height(),
|
||||
v.animation.val(),
|
||||
Rect::new(-(v.fw as f32), 0., 0., v.fh as f32) * v.zoom
|
||||
+ (s.offset + v.offset),
|
||||
Rect::new(-(v.fw as f32), 0., 0., v.fh as f32) * v.zoom + (s.offset + v.offset),
|
||||
Renderer::VIEW_LAYER,
|
||||
Rgba::TRANSPARENT,
|
||||
1.,
|
||||
|
@ -45,10 +45,7 @@ impl Resources {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_snapshot_mut(
|
||||
&mut self,
|
||||
id: ViewId,
|
||||
) -> (&mut Snapshot, &[Bgra8]) {
|
||||
pub fn get_snapshot_mut(&mut self, id: ViewId) -> (&mut Snapshot, &[Bgra8]) {
|
||||
self.data
|
||||
.get_mut(&id)
|
||||
.map(|r| r.current_snapshot_mut())
|
||||
@ -95,9 +92,7 @@ impl ResourceManager {
|
||||
self.add_view(id, w, h, &pixels);
|
||||
}
|
||||
|
||||
pub fn load_image<P: AsRef<Path>>(
|
||||
path: P,
|
||||
) -> io::Result<(u32, u32, Vec<u8>)> {
|
||||
pub fn load_image<P: AsRef<Path>>(path: P) -> io::Result<(u32, u32, Vec<u8>)> {
|
||||
let (buffer, width, height) = image::load(path)?;
|
||||
|
||||
// Convert pixels to BGRA, since they are going to be loaded into
|
||||
@ -158,8 +153,7 @@ impl ResourceManager {
|
||||
let frame_delay = frame_delay.as_millis() / 10;
|
||||
// If the passed in delay is larger than a `u16` can hold,
|
||||
// we ensure it doesn't overflow.
|
||||
let frame_delay =
|
||||
u128::min(frame_delay, u16::max_value() as u128) as u16;
|
||||
let frame_delay = u128::min(frame_delay, u16::max_value() as u128) as u16;
|
||||
|
||||
let mut resources = self.lock_mut();
|
||||
let (snapshot, pixels) = resources.get_snapshot_mut(id);
|
||||
@ -275,11 +269,8 @@ impl ViewResources {
|
||||
self.snapshot += 1;
|
||||
self.pixels = Bgra8::align(&pixels).into();
|
||||
|
||||
self.snapshots.push(Snapshot::new(
|
||||
SnapshotId(self.snapshot),
|
||||
pixels,
|
||||
extent,
|
||||
));
|
||||
self.snapshots
|
||||
.push(Snapshot::new(SnapshotId(self.snapshot), pixels, extent));
|
||||
}
|
||||
|
||||
pub fn prev_snapshot(&mut self) -> Option<&Snapshot> {
|
||||
@ -335,14 +326,11 @@ pub struct Snapshot {
|
||||
impl Snapshot {
|
||||
pub fn new(id: SnapshotId, pixels: &[u8], extent: ViewExtent) -> Self {
|
||||
let size = pixels.len();
|
||||
let pixels = Compressed::from(pixels)
|
||||
.expect("compressing snapshot shouldn't result in an error");
|
||||
let pixels =
|
||||
Compressed::from(pixels).expect("compressing snapshot shouldn't result in an error");
|
||||
|
||||
debug_assert!(
|
||||
(extent.fw * extent.fh) as usize
|
||||
* extent.nframes
|
||||
* mem::size_of::<Rgba8>()
|
||||
== size,
|
||||
(extent.fw * extent.fh) as usize * extent.nframes * mem::size_of::<Rgba8>() == size,
|
||||
"the pixel buffer has the expected size"
|
||||
);
|
||||
|
||||
|
@ -48,10 +48,9 @@ impl Pipeline {
|
||||
framebuffer: &Framebuffer,
|
||||
sampler: &Sampler,
|
||||
) -> core::BindingGroup {
|
||||
renderer.device.create_binding_group(
|
||||
&self.pipeline.layout.sets[0],
|
||||
&[framebuffer, sampler],
|
||||
)
|
||||
renderer
|
||||
.device
|
||||
.create_binding_group(&self.pipeline.layout.sets[0], &[framebuffer, sampler])
|
||||
}
|
||||
|
||||
pub fn vertex_buffer(r: &Renderer) -> VertexBuffer {
|
||||
|
352
src/session.rs
352
src/session.rs
@ -7,9 +7,7 @@ use crate::event::{Event, TimedEvent};
|
||||
use crate::execution::{DigestMode, DigestState, Execution};
|
||||
use crate::hashmap;
|
||||
use crate::palette::*;
|
||||
use crate::platform::{
|
||||
self, InputState, KeyboardInput, LogicalSize, ModifiersState,
|
||||
};
|
||||
use crate::platform::{self, InputState, KeyboardInput, LogicalSize, ModifiersState};
|
||||
use crate::resources::ResourceManager;
|
||||
use crate::view::{FileStatus, View, ViewCoords, ViewId, ViewManager};
|
||||
|
||||
@ -162,9 +160,7 @@ impl fmt::Display for Mode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Normal => "normal".fmt(f),
|
||||
Self::Visual(VisualState::Selecting { dragging: true }) => {
|
||||
"visual (dragging)".fmt(f)
|
||||
}
|
||||
Self::Visual(VisualState::Selecting { dragging: true }) => "visual (dragging)".fmt(f),
|
||||
Self::Visual(VisualState::Selecting { .. }) => "visual".fmt(f),
|
||||
Self::Visual(VisualState::Pasting) => "visual (pasting)".fmt(f),
|
||||
Self::Command => "command".fmt(f),
|
||||
@ -534,8 +530,7 @@ impl KeyBindings {
|
||||
) -> Option<KeyBinding> {
|
||||
self.elems.iter().cloned().find(|kb| {
|
||||
kb.key == key
|
||||
&& (kb.modifiers == ModifiersState::default()
|
||||
|| kb.modifiers == modifiers)
|
||||
&& (kb.modifiers == ModifiersState::default() || kb.modifiers == modifiers)
|
||||
&& kb.state == state
|
||||
&& kb.modes.contains(&mode)
|
||||
})
|
||||
@ -822,10 +817,7 @@ impl Session {
|
||||
// The special source '-' is used to skip initialization.
|
||||
if init.as_os_str() != "-" {
|
||||
self.source_path(&init).map_err(|e| {
|
||||
io::Error::new(
|
||||
e.kind(),
|
||||
format!("error sourcing {:?}: {}", init, e),
|
||||
)
|
||||
io::Error::new(e.kind(), format!("error sourcing {:?}: {}", init, e))
|
||||
})?
|
||||
}
|
||||
} else {
|
||||
@ -835,8 +827,7 @@ impl Session {
|
||||
if cfg.exists() {
|
||||
self.source_path(cfg)?;
|
||||
} else {
|
||||
if let Err(e) = fs::create_dir_all(dir)
|
||||
.and_then(|_| fs::write(&cfg, data::CONFIG))
|
||||
if let Err(e) = fs::create_dir_all(dir).and_then(|_| fs::write(&cfg, data::CONFIG))
|
||||
{
|
||||
warn!(
|
||||
"Warning: couldn't create configuration file {:?}: {}",
|
||||
@ -924,9 +915,7 @@ impl Session {
|
||||
.into_iter()
|
||||
.for_each(|t| self.handle_event(t.event, exec));
|
||||
|
||||
let verify_ended = mode == DigestMode::Verify
|
||||
&& result.is_done()
|
||||
&& end.is_none();
|
||||
let verify_ended = mode == DigestMode::Verify && result.is_done() && end.is_none();
|
||||
let replay_ended = mode != DigestMode::Verify && end.is_none();
|
||||
let verify_failed = result.is_err();
|
||||
|
||||
@ -949,16 +938,10 @@ impl Session {
|
||||
}
|
||||
DigestMode::Record => match exec.finalize_replaying() {
|
||||
Ok(path) => {
|
||||
info!(
|
||||
"replaying: digest saved to `{}`",
|
||||
path.display()
|
||||
);
|
||||
info!("replaying: digest saved to `{}`", path.display());
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"replaying: error saving recording: {}",
|
||||
e
|
||||
);
|
||||
error!("replaying: error saving recording: {}", e);
|
||||
}
|
||||
},
|
||||
DigestMode::Ignore => {}
|
||||
@ -1088,13 +1071,7 @@ impl Session {
|
||||
/// Snap the given session coordinates to the pixel grid.
|
||||
/// This only has an effect at zoom levels greater than `1.0`.
|
||||
#[allow(dead_code)]
|
||||
pub fn snap(
|
||||
&self,
|
||||
p: SessionCoords,
|
||||
offx: f32,
|
||||
offy: f32,
|
||||
zoom: f32,
|
||||
) -> SessionCoords {
|
||||
pub fn snap(&self, p: SessionCoords, offx: f32, offy: f32, zoom: f32) -> SessionCoords {
|
||||
SessionCoords::new(
|
||||
p.x - ((p.x - offx - self.offset.x) % zoom),
|
||||
p.y - ((p.y - offy - self.offset.y) % zoom),
|
||||
@ -1210,13 +1187,9 @@ impl Session {
|
||||
self.cmdline_handle_input(':');
|
||||
}
|
||||
Mode::Visual(_) => {
|
||||
if self.selection.is_none()
|
||||
&& self.hover_view == Some(self.views.active_id)
|
||||
{
|
||||
let p =
|
||||
self.active_view_coords(self.cursor).map(|n| n as i32);
|
||||
self.selection =
|
||||
Some(Selection::new(p.x, p.y, p.x + 1, p.y + 1));
|
||||
if self.selection.is_none() && self.hover_view == Some(self.views.active_id) {
|
||||
let p = self.active_view_coords(self.cursor).map(|n| n as i32);
|
||||
self.selection = Some(Selection::new(p.x, p.y, p.x + 1, p.y + 1));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -1229,8 +1202,7 @@ impl Session {
|
||||
|
||||
/// Release all keys and mouse buttons.
|
||||
fn release_inputs(&mut self) {
|
||||
let pressed: Vec<platform::Key> =
|
||||
self.keys_pressed.iter().cloned().collect();
|
||||
let pressed: Vec<platform::Key> = self.keys_pressed.iter().cloned().collect();
|
||||
for k in pressed {
|
||||
self.handle_keyboard_input(
|
||||
platform::KeyboardInput {
|
||||
@ -1242,10 +1214,7 @@ impl Session {
|
||||
);
|
||||
}
|
||||
if self.mouse_state == InputState::Pressed {
|
||||
self.handle_mouse_input(
|
||||
platform::MouseButton::Left,
|
||||
InputState::Released,
|
||||
);
|
||||
self.handle_mouse_input(platform::MouseButton::Left, InputState::Released);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1353,10 +1322,7 @@ impl Session {
|
||||
}
|
||||
|
||||
/// Convert "logical" window coordinates to session coordinates.
|
||||
pub fn window_to_session_coords(
|
||||
&self,
|
||||
position: platform::LogicalPosition,
|
||||
) -> SessionCoords {
|
||||
pub fn window_to_session_coords(&self, position: platform::LogicalPosition) -> SessionCoords {
|
||||
let (x, y) = (position.x, position.y);
|
||||
let scale: f64 = self.settings["scale"].float64();
|
||||
SessionCoords::new(
|
||||
@ -1384,11 +1350,7 @@ impl Session {
|
||||
}
|
||||
|
||||
/// Convert view coordinates to session coordinates.
|
||||
pub fn session_coords(
|
||||
&self,
|
||||
v: ViewId,
|
||||
p: ViewCoords<f32>,
|
||||
) -> SessionCoords {
|
||||
pub fn session_coords(&self, v: ViewId, p: ViewCoords<f32>) -> SessionCoords {
|
||||
let v = self.view(v);
|
||||
|
||||
let p = Point2::new(p.x * v.zoom, p.y * v.zoom);
|
||||
@ -1477,11 +1439,7 @@ impl Session {
|
||||
|
||||
/// Save a view with the given file name. Returns an error if
|
||||
/// the format is not supported.
|
||||
pub fn save_view_as<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
id: ViewId,
|
||||
path: P,
|
||||
) -> io::Result<()> {
|
||||
pub fn save_view_as<P: AsRef<Path>>(&mut self, id: ViewId, path: P) -> io::Result<()> {
|
||||
let ext = path.as_ref().extension().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
@ -1489,10 +1447,7 @@ impl Session {
|
||||
)
|
||||
})?;
|
||||
let ext = ext.to_str().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"file extension is not valid unicode",
|
||||
)
|
||||
io::Error::new(io::ErrorKind::Other, "file extension is not valid unicode")
|
||||
})?;
|
||||
|
||||
if !Self::SUPPORTED_FORMATS.contains(&ext) {
|
||||
@ -1523,11 +1478,7 @@ impl Session {
|
||||
self.view_mut(id).save_as(s_id, path.as_ref().into());
|
||||
|
||||
self.message(
|
||||
format!(
|
||||
"\"{}\" {} pixels written",
|
||||
path.as_ref().display(),
|
||||
npixels,
|
||||
),
|
||||
format!("\"{}\" {} pixels written", path.as_ref().display(), npixels,),
|
||||
MessageType::Info,
|
||||
);
|
||||
Ok(())
|
||||
@ -1578,11 +1529,9 @@ impl Session {
|
||||
}
|
||||
|
||||
let (width, height, pixels) = ResourceManager::load_image(&path)?;
|
||||
let id = self.views.add(
|
||||
FileStatus::Saved(path.into()),
|
||||
width as u32,
|
||||
height as u32,
|
||||
);
|
||||
let id = self
|
||||
.views
|
||||
.add(FileStatus::Saved(path.into()), width as u32, height as u32);
|
||||
|
||||
self.effects.push(Effect::ViewAdded(id));
|
||||
self.resources.add_view(id, width, height, &pixels);
|
||||
@ -1619,29 +1568,21 @@ impl Session {
|
||||
match &v.file_status {
|
||||
FileStatus::Modified(_) | FileStatus::New(_) => {
|
||||
self.message(
|
||||
"Error: no write since last change (enter `:q!` to quit without saving)",
|
||||
MessageType::Error,
|
||||
);
|
||||
"Error: no write since last change (enter `:q!` to quit without saving)",
|
||||
MessageType::Error,
|
||||
);
|
||||
}
|
||||
_ => self.quit_view(id),
|
||||
}
|
||||
}
|
||||
|
||||
/// Save a view as a gif animation.
|
||||
fn save_view_gif<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
id: ViewId,
|
||||
path: P,
|
||||
) -> io::Result<()> {
|
||||
fn save_view_gif<P: AsRef<Path>>(&mut self, id: ViewId, path: P) -> io::Result<()> {
|
||||
let delay = self.view(id).animation.delay;
|
||||
let npixels = self.resources.save_view_gif(id, &path, delay)?;
|
||||
|
||||
self.message(
|
||||
format!(
|
||||
"\"{}\" {} pixels written",
|
||||
path.as_ref().display(),
|
||||
npixels,
|
||||
),
|
||||
format!("\"{}\" {} pixels written", path.as_ref().display(), npixels,),
|
||||
MessageType::Info,
|
||||
);
|
||||
Ok(())
|
||||
@ -1688,8 +1629,7 @@ impl Session {
|
||||
|
||||
/// Yank the selection.
|
||||
fn yank(&mut self) -> Option<Rect<i32>> {
|
||||
if let (Mode::Visual(VisualState::Selecting { .. }), Some(s)) =
|
||||
(self.mode, self.selection)
|
||||
if let (Mode::Visual(VisualState::Selecting { .. }), Some(s)) = (self.mode, self.selection)
|
||||
{
|
||||
let v = self.active_view_mut();
|
||||
let s = s.abs().bounds();
|
||||
@ -1789,9 +1729,7 @@ impl Session {
|
||||
self.handle_cursor_moved(coords);
|
||||
}
|
||||
}
|
||||
Event::KeyboardInput(input) => {
|
||||
self.handle_keyboard_input(input, exec)
|
||||
}
|
||||
Event::KeyboardInput(input) => self.handle_keyboard_input(input, exec),
|
||||
Event::ReceivedCharacter(c) => self.handle_received_character(c),
|
||||
}
|
||||
}
|
||||
@ -1807,11 +1745,7 @@ impl Session {
|
||||
self.effects.push(Effect::SessionResized(size));
|
||||
}
|
||||
|
||||
fn handle_mouse_input(
|
||||
&mut self,
|
||||
button: platform::MouseButton,
|
||||
state: platform::InputState,
|
||||
) {
|
||||
fn handle_mouse_input(&mut self, button: platform::MouseButton, state: platform::InputState) {
|
||||
if button != platform::MouseButton::Left {
|
||||
return;
|
||||
}
|
||||
@ -1860,17 +1794,12 @@ impl Session {
|
||||
match self.mode {
|
||||
Mode::Normal => match self.tool {
|
||||
Tool::Brush(ref mut brush) => {
|
||||
let color =
|
||||
if brush.is_set(BrushMode::Erase) {
|
||||
Rgba8::TRANSPARENT
|
||||
} else {
|
||||
self.fg
|
||||
};
|
||||
brush.start_drawing(
|
||||
p.into(),
|
||||
color,
|
||||
extent,
|
||||
);
|
||||
let color = if brush.is_set(BrushMode::Erase) {
|
||||
Rgba8::TRANSPARENT
|
||||
} else {
|
||||
self.fg
|
||||
};
|
||||
brush.start_drawing(p.into(), color, extent);
|
||||
}
|
||||
Tool::Sampler => {
|
||||
self.sample_color();
|
||||
@ -1881,12 +1810,9 @@ impl Session {
|
||||
Mode::Command => {
|
||||
// TODO
|
||||
}
|
||||
Mode::Visual(VisualState::Selecting {
|
||||
ref mut dragging,
|
||||
}) => {
|
||||
Mode::Visual(VisualState::Selecting { ref mut dragging }) => {
|
||||
let p = p.map(|n| n as i32);
|
||||
let unit =
|
||||
Selection::new(p.x, p.y, p.x + 1, p.y + 1);
|
||||
let unit = Selection::new(p.x, p.y, p.x + 1, p.y + 1);
|
||||
|
||||
if let Some(s) = &mut self.selection {
|
||||
if s.abs().bounds().contains(p) {
|
||||
@ -1909,9 +1835,7 @@ impl Session {
|
||||
} else {
|
||||
// Clicking outside a view...
|
||||
match self.mode {
|
||||
Mode::Visual(VisualState::Selecting {
|
||||
ref mut dragging,
|
||||
}) => {
|
||||
Mode::Visual(VisualState::Selecting { ref mut dragging }) => {
|
||||
self.selection = None;
|
||||
*dragging = false;
|
||||
}
|
||||
@ -1926,8 +1850,7 @@ impl Session {
|
||||
Mode::Normal => {
|
||||
if let Tool::Brush(ref mut brush) = self.tool {
|
||||
match brush.state {
|
||||
BrushState::Drawing { .. }
|
||||
| BrushState::DrawStarted { .. } => {
|
||||
BrushState::Drawing { .. } | BrushState::DrawStarted { .. } => {
|
||||
brush.stop_drawing();
|
||||
self.active_view_mut().touch();
|
||||
}
|
||||
@ -1960,31 +1883,25 @@ impl Session {
|
||||
|
||||
match self.mode {
|
||||
Mode::Normal => match self.tool {
|
||||
Tool::Brush(ref mut brush) if p != prev_p => {
|
||||
match brush.state {
|
||||
BrushState::DrawStarted { .. }
|
||||
| BrushState::Drawing { .. } => {
|
||||
let mut p: ViewCoords<i32> = p.into();
|
||||
if brush.is_set(BrushMode::Multi) {
|
||||
p.clamp(Rect::new(
|
||||
(brush.size / 2) as i32,
|
||||
(brush.size / 2) as i32,
|
||||
vw as i32 - (brush.size / 2) as i32 - 1,
|
||||
vh as i32 - (brush.size / 2) as i32 - 1,
|
||||
));
|
||||
brush.draw(p);
|
||||
} else {
|
||||
brush.draw(p);
|
||||
}
|
||||
Tool::Brush(ref mut brush) if p != prev_p => match brush.state {
|
||||
BrushState::DrawStarted { .. } | BrushState::Drawing { .. } => {
|
||||
let mut p: ViewCoords<i32> = p.into();
|
||||
if brush.is_set(BrushMode::Multi) {
|
||||
p.clamp(Rect::new(
|
||||
(brush.size / 2) as i32,
|
||||
(brush.size / 2) as i32,
|
||||
vw as i32 - (brush.size / 2) as i32 - 1,
|
||||
vh as i32 - (brush.size / 2) as i32 - 1,
|
||||
));
|
||||
brush.draw(p);
|
||||
} else {
|
||||
brush.draw(p);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Tool::Pan(PanState::Panning) => {
|
||||
self.pan(
|
||||
cursor.x - self.cursor.x,
|
||||
cursor.y - self.cursor.y,
|
||||
);
|
||||
self.pan(cursor.x - self.cursor.x, cursor.y - self.cursor.y);
|
||||
}
|
||||
Tool::Sampler if self.mouse_state == InputState::Pressed => {
|
||||
self.sample_color();
|
||||
@ -1994,12 +1911,7 @@ impl Session {
|
||||
Mode::Visual(VisualState::Selecting { dragging: false }) => {
|
||||
if self.mouse_state == InputState::Pressed {
|
||||
if let Some(ref mut s) = self.selection {
|
||||
*s = Selection::new(
|
||||
s.x1,
|
||||
s.y1,
|
||||
p.x as i32 + 1,
|
||||
p.y as i32 + 1,
|
||||
);
|
||||
*s = Selection::new(s.x1, s.y1, p.x as i32 + 1, p.y as i32 + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2010,8 +1922,7 @@ impl Session {
|
||||
if let Some(ref mut s) = self.selection {
|
||||
// TODO: (rgx) Better API.
|
||||
let delta = *p - Vector2::new(prev_p.x, prev_p.y);
|
||||
let delta =
|
||||
Vector2::new(delta.x as i32, delta.y as i32);
|
||||
let delta = Vector2::new(delta.x as i32, delta.y as i32);
|
||||
let t = Selection::from(s.bounds() + delta);
|
||||
|
||||
if view.intersects(t.abs().bounds()) {
|
||||
@ -2049,11 +1960,7 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_keyboard_input(
|
||||
&mut self,
|
||||
input: platform::KeyboardInput,
|
||||
exec: &mut Execution,
|
||||
) {
|
||||
fn handle_keyboard_input(&mut self, input: platform::KeyboardInput, exec: &mut Execution) {
|
||||
let KeyboardInput {
|
||||
state,
|
||||
modifiers,
|
||||
@ -2083,17 +1990,13 @@ impl Session {
|
||||
|
||||
match self.mode {
|
||||
Mode::Visual(VisualState::Selecting { .. }) => {
|
||||
if key == platform::Key::Escape
|
||||
&& state == InputState::Pressed
|
||||
{
|
||||
if key == platform::Key::Escape && state == InputState::Pressed {
|
||||
self.switch_mode(Mode::Normal);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Mode::Visual(VisualState::Pasting) => {
|
||||
if key == platform::Key::Escape
|
||||
&& state == InputState::Pressed
|
||||
{
|
||||
if key == platform::Key::Escape && state == InputState::Pressed {
|
||||
self.switch_mode(Mode::Visual(VisualState::default()));
|
||||
return;
|
||||
}
|
||||
@ -2116,9 +2019,7 @@ impl Session {
|
||||
return;
|
||||
}
|
||||
Mode::Help => {
|
||||
if state == InputState::Pressed
|
||||
&& key == platform::Key::Escape
|
||||
{
|
||||
if state == InputState::Pressed && key == platform::Key::Escape {
|
||||
self.switch_mode(Mode::Normal);
|
||||
return;
|
||||
}
|
||||
@ -2126,12 +2027,10 @@ impl Session {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(kb) = self.key_bindings.find(
|
||||
Key::Virtual(key),
|
||||
modifiers,
|
||||
state,
|
||||
self.mode,
|
||||
) {
|
||||
if let Some(kb) = self
|
||||
.key_bindings
|
||||
.find(Key::Virtual(key), modifiers, state, self.mode)
|
||||
{
|
||||
// For toggle-like key bindings, we don't want to run the command
|
||||
// on key repeats. For regular key bindings, we run the command
|
||||
// either way.
|
||||
@ -2148,16 +2047,10 @@ impl Session {
|
||||
match exec.stop_recording() {
|
||||
Ok(path) => {
|
||||
self.message(
|
||||
format!(
|
||||
"Recording saved to `{}`",
|
||||
path.display()
|
||||
),
|
||||
format!("Recording saved to `{}`", path.display()),
|
||||
MessageType::Replay,
|
||||
);
|
||||
info!(
|
||||
"recording: events saved to `{}`",
|
||||
path.display()
|
||||
);
|
||||
info!("recording: events saved to `{}`", path.display());
|
||||
self.quit(ExitReason::Normal);
|
||||
}
|
||||
Err(e) => {
|
||||
@ -2179,8 +2072,8 @@ impl Session {
|
||||
let path = path.as_ref();
|
||||
debug!("source: {}", path.display());
|
||||
|
||||
let f = File::open(&path)
|
||||
.or_else(|_| File::open(self.base_dirs.config_dir().join(path)))?;
|
||||
let f =
|
||||
File::open(&path).or_else(|_| File::open(self.base_dirs.config_dir().join(path)))?;
|
||||
|
||||
self.source_reader(io::BufReader::new(f), path)
|
||||
}
|
||||
@ -2192,11 +2085,7 @@ impl Session {
|
||||
}
|
||||
|
||||
/// Source a script from an [`io::BufRead`].
|
||||
fn source_reader<P: AsRef<Path>, R: io::BufRead>(
|
||||
&mut self,
|
||||
r: R,
|
||||
_path: P,
|
||||
) -> io::Result<()> {
|
||||
fn source_reader<P: AsRef<Path>, R: io::BufRead>(&mut self, r: R, _path: P) -> io::Result<()> {
|
||||
for line in r.lines() {
|
||||
let line = line?;
|
||||
|
||||
@ -2227,18 +2116,14 @@ impl Session {
|
||||
/// Vertically the active view in the workspace.
|
||||
fn center_active_view_v(&mut self) {
|
||||
let v = self.active_view();
|
||||
self.offset.y =
|
||||
(self.height / 2. - v.height() as f32 / 2. * v.zoom - v.offset.y)
|
||||
.floor();
|
||||
self.offset.y = (self.height / 2. - v.height() as f32 / 2. * v.zoom - v.offset.y).floor();
|
||||
self.cursor_dirty();
|
||||
}
|
||||
|
||||
/// Horizontally center the active view in the workspace.
|
||||
fn center_active_view_h(&mut self) {
|
||||
let v = self.active_view();
|
||||
self.offset.x =
|
||||
(self.width / 2. - v.width() as f32 * v.zoom / 2. - v.offset.x)
|
||||
.floor();
|
||||
self.offset.x = (self.width / 2. - v.width() as f32 * v.zoom / 2. - v.offset.x).floor();
|
||||
self.cursor_dirty();
|
||||
}
|
||||
|
||||
@ -2262,10 +2147,7 @@ impl Session {
|
||||
if let Some(z) = lvls.get(i + 1) {
|
||||
self.zoom(*z, center);
|
||||
} else {
|
||||
self.message(
|
||||
"Maximum zoom level reached",
|
||||
MessageType::Hint,
|
||||
);
|
||||
self.message("Maximum zoom level reached", MessageType::Hint);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -2280,10 +2162,7 @@ impl Session {
|
||||
for (i, zoom) in lvls.iter().enumerate() {
|
||||
if view.zoom <= *zoom {
|
||||
if i == 0 {
|
||||
self.message(
|
||||
"Minimum zoom level reached",
|
||||
MessageType::Hint,
|
||||
);
|
||||
self.message("Minimum zoom level reached", MessageType::Hint);
|
||||
} else if let Some(z) = lvls.get(i - 1) {
|
||||
self.zoom(*z, center);
|
||||
} else {
|
||||
@ -2444,26 +2323,16 @@ impl Session {
|
||||
"{}",
|
||||
self.base_dirs.config_dir().display()
|
||||
))),
|
||||
"s/hidpi" => {
|
||||
Ok(Value::Str(format!("{:.1}", self.hidpi_factor)))
|
||||
}
|
||||
"s/offset" => {
|
||||
Ok(Value::Float2(self.offset.x, self.offset.y))
|
||||
}
|
||||
"s/hidpi" => Ok(Value::Str(format!("{:.1}", self.hidpi_factor))),
|
||||
"s/offset" => Ok(Value::Float2(self.offset.x, self.offset.y)),
|
||||
"v/offset" => {
|
||||
let v = self.active_view();
|
||||
Ok(Value::Float2(v.offset.x, v.offset.y))
|
||||
}
|
||||
"v/zoom" => {
|
||||
Ok(Value::Float(self.active_view().zoom as f64))
|
||||
}
|
||||
"v/zoom" => Ok(Value::Float(self.active_view().zoom as f64)),
|
||||
_ => match self.settings.get(s) {
|
||||
None => Err(format!("Error: {} is undefined", s)),
|
||||
Some(result) => Ok(Value::Str(format!(
|
||||
"{} = {}",
|
||||
v.clone(),
|
||||
result
|
||||
))),
|
||||
Some(result) => Ok(Value::Str(format!("{} = {}", v.clone(), result))),
|
||||
},
|
||||
},
|
||||
_ => Err(format!("Error: argument cannot be echoed")),
|
||||
@ -2503,10 +2372,7 @@ impl Session {
|
||||
}
|
||||
Op::Set(z) => {
|
||||
if z < 1. || z > Self::MAX_ZOOM {
|
||||
self.message(
|
||||
"Error: invalid zoom level",
|
||||
MessageType::Error,
|
||||
);
|
||||
self.message("Error: invalid zoom level", MessageType::Error);
|
||||
} else {
|
||||
self.zoom(z, center);
|
||||
}
|
||||
@ -2524,18 +2390,14 @@ impl Session {
|
||||
}
|
||||
Command::ViewNext => {
|
||||
let id = self.views.active_id;
|
||||
if let Some(id) =
|
||||
self.views.range(id..).nth(1).map(|(id, _)| *id)
|
||||
{
|
||||
if let Some(id) = self.views.range(id..).nth(1).map(|(id, _)| *id) {
|
||||
self.activate(id);
|
||||
self.center_active_view_v();
|
||||
}
|
||||
}
|
||||
Command::ViewPrev => {
|
||||
let id = self.views.active_id;
|
||||
if let Some(id) =
|
||||
self.views.range(..id).next_back().map(|(id, _)| *id)
|
||||
{
|
||||
if let Some(id) = self.views.range(..id).next_back().map(|(id, _)| *id) {
|
||||
self.activate(id);
|
||||
self.center_active_view_v();
|
||||
}
|
||||
@ -2553,11 +2415,7 @@ impl Session {
|
||||
v.extend_clone(n);
|
||||
} else {
|
||||
self.message(
|
||||
format!(
|
||||
"Error: clone index must be in the range {}..{}",
|
||||
0,
|
||||
l - 1
|
||||
),
|
||||
format!("Error: clone index must be in the range {}..{}", 0, l - 1),
|
||||
MessageType::Error,
|
||||
);
|
||||
}
|
||||
@ -2578,10 +2436,7 @@ impl Session {
|
||||
let v = self.active_view_mut();
|
||||
if !v.slice(nframes) {
|
||||
self.message(
|
||||
format!(
|
||||
"Error: slice: view width is not divisible by {}",
|
||||
nframes
|
||||
),
|
||||
format!("Error: slice: view width is not divisible by {}", nframes),
|
||||
MessageType::Error,
|
||||
);
|
||||
} else {
|
||||
@ -2594,20 +2449,14 @@ impl Session {
|
||||
Command::Set(ref k, ref v) => {
|
||||
if Settings::DEPRECATED.contains(&k.as_str()) {
|
||||
self.message(
|
||||
format!(
|
||||
"Warning: the setting `{}` has been deprecated",
|
||||
k
|
||||
),
|
||||
format!("Warning: the setting `{}` has been deprecated", k),
|
||||
MessageType::Warning,
|
||||
);
|
||||
return;
|
||||
}
|
||||
match self.settings.set(k, v.clone()) {
|
||||
Err(e) => {
|
||||
self.message(
|
||||
format!("Error: {}", e),
|
||||
MessageType::Error,
|
||||
);
|
||||
self.message(format!("Error: {}", e), MessageType::Error);
|
||||
}
|
||||
Ok(ref old) => {
|
||||
if old != v {
|
||||
@ -2618,14 +2467,9 @@ impl Session {
|
||||
}
|
||||
#[allow(mutable_borrow_reservation_conflict)]
|
||||
Command::Toggle(ref k) => match self.settings.get(k) {
|
||||
Some(Value::Bool(b)) => {
|
||||
self.command(Command::Set(k.clone(), Value::Bool(!b)))
|
||||
}
|
||||
Some(Value::Bool(b)) => self.command(Command::Set(k.clone(), Value::Bool(!b))),
|
||||
Some(_) => {
|
||||
self.message(
|
||||
format!("Error: can't toggle `{}`", k),
|
||||
MessageType::Error,
|
||||
);
|
||||
self.message(format!("Error: can't toggle `{}`", k), MessageType::Error);
|
||||
}
|
||||
None => {
|
||||
self.message(
|
||||
@ -2649,10 +2493,7 @@ impl Session {
|
||||
if paths.is_empty() {
|
||||
self.unimplemented();
|
||||
} else if let Err(e) = self.edit(paths) {
|
||||
self.message(
|
||||
format!("Error loading path(s): {}", e),
|
||||
MessageType::Error,
|
||||
);
|
||||
self.message(format!("Error loading path(s): {}", e), MessageType::Error);
|
||||
}
|
||||
}
|
||||
Command::Write(None) => {
|
||||
@ -2746,9 +2587,7 @@ impl Session {
|
||||
let x2 = max.x + (fw - max.x % fw);
|
||||
let y2 = fh;
|
||||
|
||||
*selection = Selection::from(
|
||||
Rect::new(x1, 0, x2, y2).intersection(r),
|
||||
);
|
||||
*selection = Selection::from(Rect::new(x1, 0, x2, y2).intersection(r));
|
||||
}
|
||||
} else {
|
||||
self.selection = Some(Selection::new(0, 0, fw, fh));
|
||||
@ -2780,9 +2619,7 @@ impl Session {
|
||||
}
|
||||
}
|
||||
Command::SelectionPaste => {
|
||||
if let (Mode::Visual(VisualState::Pasting), Some(s)) =
|
||||
(self.mode, self.selection)
|
||||
{
|
||||
if let (Mode::Visual(VisualState::Pasting), Some(s)) = (self.mode, self.selection) {
|
||||
self.active_view_mut().paste(s.abs().bounds());
|
||||
} else {
|
||||
// TODO: Enter paste mode?
|
||||
@ -2810,15 +2647,14 @@ impl Session {
|
||||
}
|
||||
Command::SelectionFill(color) => {
|
||||
if let Some(s) = self.selection {
|
||||
self.effects.push(Effect::ViewPaintFinal(vec![
|
||||
Shape::Rectangle(
|
||||
self.effects
|
||||
.push(Effect::ViewPaintFinal(vec![Shape::Rectangle(
|
||||
s.abs().bounds().map(|n| n as f32),
|
||||
ZDepth::default(),
|
||||
Rotation::ZERO,
|
||||
Stroke::NONE,
|
||||
Fill::Solid(color.unwrap_or(self.fg).into()),
|
||||
),
|
||||
]));
|
||||
)]));
|
||||
self.active_view_mut().touch();
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,7 @@ impl FrameTimer {
|
||||
self.timings.truncate(Self::WINDOW - 1);
|
||||
self.timings.push_front(elapsed.as_micros());
|
||||
|
||||
let avg =
|
||||
self.timings.iter().sum::<u128>() / self.timings.len() as u128;
|
||||
let avg = self.timings.iter().sum::<u128>() / self.timings.len() as u128;
|
||||
self.avg = time::Duration::from_micros(avg as u64);
|
||||
|
||||
result
|
||||
|
@ -220,8 +220,7 @@ impl View {
|
||||
/// the given path.
|
||||
pub fn save_as(&mut self, id: SnapshotId, path: PathBuf) {
|
||||
match self.file_status {
|
||||
FileStatus::Modified(ref curr_path)
|
||||
| FileStatus::New(ref curr_path) => {
|
||||
FileStatus::Modified(ref curr_path) | FileStatus::New(ref curr_path) => {
|
||||
if curr_path == &path {
|
||||
self.saved(id, path);
|
||||
}
|
||||
@ -441,9 +440,7 @@ impl ToString for FileStatus {
|
||||
FileStatus::NoFile => String::new(),
|
||||
FileStatus::Saved(ref path) => format!("{}", path.display()),
|
||||
FileStatus::New(ref path) => format!("{} [new]", path.display()),
|
||||
FileStatus::Modified(ref path) => {
|
||||
format!("{} [modified]", path.display())
|
||||
}
|
||||
FileStatus::Modified(ref path) => format!("{} [modified]", path.display()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,9 +107,8 @@ fn run(name: &str) -> io::Result<()> {
|
||||
.join(name);
|
||||
let cfg: Config = {
|
||||
let path = path.join(name).with_extension("toml");
|
||||
let cfg = fs::read_to_string(&path).map_err(|e| {
|
||||
io::Error::new(e.kind(), format!("{}: {}", path.display(), e))
|
||||
})?;
|
||||
let cfg = fs::read_to_string(&path)
|
||||
.map_err(|e| io::Error::new(e.kind(), format!("{}: {}", path.display(), e)))?;
|
||||
toml::from_str(&cfg)?
|
||||
};
|
||||
let exec = Execution::replaying(path.clone(), DigestMode::Verify)?;
|
||||
|
Loading…
Reference in New Issue
Block a user