Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Image Loading Asynchronous #12

Merged
merged 1 commit into from
Mar 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ fn muff() -> Result<(), String> {
let mut pb = ProgressBar::new(image_size);
pb.message("Reading image: ");
pb.set_units(Units::Bytes);

let data = image
.read(|total| {
let mut data = Vec::new();
image
.read(&mut data, |total| {
pb.set(total);
})
.map_err(|err| format!("image error with image at '{}': {}", image_path, err))?;
Expand Down
52 changes: 34 additions & 18 deletions gtk/src/image.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,45 @@
use super::ui::State;
use gtk;
use gtk::prelude::*;
use super::ui::BufferingData;
use muff::Image;
use std::path::Path;
use std::sync::Arc;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::atomic::Ordering;
use std::sync::mpsc::Receiver;

pub fn load_image<P: AsRef<Path>>(path: P, state: &State, label: &gtk::Label, next: &gtk::Button) {
pub fn image_load_event_loop(path_receiver: Receiver<PathBuf>, buffer: &BufferingData) {
while let Ok(path) = path_receiver.recv() {
buffer.state.store(0b1, Ordering::SeqCst);
let (ref mut name, ref mut data) = *buffer.data.lock().unwrap();
match load_image(&path, data) {
Ok(_) => {
*name = path;
buffer.state.store(0b10, Ordering::SeqCst);
}
Err(why) => {
eprintln!("muff-gtk: image loading error: {}", why);
buffer.state.store(0b100, Ordering::SeqCst);
}
}
}
}

pub fn load_image<P: AsRef<Path>>(path: P, data: &mut Vec<u8>) -> io::Result<()> {
let path = path.as_ref();
let mut new_image = match Image::new(path) {
Ok(image) => image,
Err(why) => {
eprintln!("muff: unable to open image: {}", why);
return;
return Err(io::Error::new(
io::ErrorKind::Other,
format!("unable to open image: {}", why),
));
}
};

let new_data = match new_image.read(|_| ()) {
Ok(data) => data,
Err(why) => {
eprintln!("muff: unable to read image: {}", why);
return;
}
};
if let Err(why) = new_image.read(data, |_| ()) {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("unable to open image: {}", why),
));
}

*state.image_data.borrow_mut() = Some(Arc::new(new_data));
label.set_label(&path.file_name().unwrap().to_string_lossy());
next.set_sensitive(true);
Ok(())
}
15 changes: 11 additions & 4 deletions gtk/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,22 @@ mod image;
mod ui;

use std::env;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::thread;
use ui::{App, Connect};

fn main() {
let app = App::new();
let (sender, receiver) = channel::<PathBuf>();
let app = App::new(sender);

{
let buffer = app.state.buffer.clone();
thread::spawn(move || image::image_load_event_loop(receiver, &buffer));
}

if let Some(iso_argument) = env::args().skip(1).next() {
let label = &app.content.image_view.image_path;
let next = &app.header.next;
image::load_image(&iso_argument, &app.state, label, next);
let _ = app.state.image_sender.send(PathBuf::from(iso_argument));
}

app.connect_events().then_execute()
Expand Down
34 changes: 26 additions & 8 deletions gtk/src/ui/content/images.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use gtk::*;
use pango::EllipsizeMode;

pub struct ImageView {
pub container: Box,
pub chooser: Button,
pub image_path: Label,
pub hash: ComboBoxText,
pub hash_label: Entry,
pub container: Box,
pub chooser_container: Stack,
pub chooser: Button,
pub image_path: Label,
pub hash: ComboBoxText,
pub hash_label: Entry,
}

impl ImageView {
Expand Down Expand Up @@ -34,6 +35,21 @@ impl ImageView {
image_path.set_ellipsize(EllipsizeMode::End);
image_path.get_style_context().map(|c| c.add_class("bold"));

let button_box = Box::new(Orientation::Vertical, 0);
button_box.pack_start(&chooser, false, false, 0);
button_box.pack_start(&image_path, false, false, 0);

let spinner = Spinner::new();
spinner.start();
let spinner_label = Label::new("Loading Image");
spinner_label
.get_style_context()
.map(|c| c.add_class("bold"));

let spinner_box = Box::new(Orientation::Vertical, 0);
spinner_box.pack_start(&spinner, false, false, 0);
spinner_box.pack_start(&spinner_label, false, false, 0);

let hash = ComboBoxText::new();
hash.append_text("Type");
hash.append_text("SHA256");
Expand All @@ -50,9 +66,10 @@ impl ImageView {
hash_container.pack_start(&hash, false, false, 0);
hash_container.pack_start(&hash_label, true, true, 0);

let chooser_container = Box::new(Orientation::Vertical, 5);
chooser_container.pack_start(&chooser, false, false, 0);
chooser_container.pack_start(&image_path, false, false, 0);
let chooser_container = Stack::new();
chooser_container.add_named(&button_box, "chooser");
chooser_container.add_named(&spinner_box, "loader");
chooser_container.set_visible_child_name("chooser");

let left_panel = Box::new(Orientation::Vertical, 0);
left_panel
Expand All @@ -75,6 +92,7 @@ impl ImageView {

ImageView {
container,
chooser_container,
chooser,
image_path,
hash,
Expand Down
48 changes: 19 additions & 29 deletions gtk/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ mod header;
use self::content::Content;
pub use self::dialogs::OpenDialog;
use self::header::Header;
pub use self::state::Connect;
pub use self::state::{BufferingData, Connect};

// TODO: Use AtomicU64 / Bool when https://github.com/rust-lang/rust/issues/32976 is stable.

use std::cell::RefCell;
use std::cell::{Cell, RefCell};
use std::mem;
use std::path::PathBuf;
use std::process;
use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicUsize;
use std::sync::mpsc::Sender;
use std::thread::JoinHandle;
use std::time::Instant;

Expand All @@ -32,50 +34,38 @@ pub struct App {
pub state: Arc<State>,
}

// TODO:
// pub struct BufferingData {
// data: Mutex<Vec<u8>>,
// state: AtomicUsize,
// }

// impl BufferingData {
// fn new() -> BufferingData {
// BufferingData { data: Vec::new().into(), state: 0.into() }
// }
// }

/// Contains all of the state that needs to be shared across the program's lifetime.
pub struct State {
/// Contains all of the progress bars in the flash view.
pub bars: RefCell<Vec<(ProgressBar, Label)>>,
/// Contains a list of devices detected, and their check buttons.
pub devices: Mutex<Vec<(String, CheckButton)>>,
/// Contains a buffered vector of the ISO data, to be shared across threads.
pub image_data: RefCell<Option<Arc<Vec<u8>>>>,
/// Holds the task threads that write the image to each device.
/// The handles may contain errors when joined, for printing on the summary page.
pub task_handles: Mutex<Vec<JoinHandle<Result<(), DiskError>>>>,
/// Contains progress data regarding each active flash task -- namely the progress.
pub tasks: Mutex<Vec<FlashTask>>,
/// Stores an integer which defines the currently-active view.
pub view: RefCell<u8>,
pub view: Cell<u8>,
/// Stores the time when the flashing process began.
pub start: RefCell<Instant>,
/* TODO:
* buffer: BufferingData */
pub buffer: Arc<BufferingData>,
pub image_sender: Sender<PathBuf>,
pub image_length: Cell<usize>,
}

impl State {
fn new() -> State {
fn new(image_sender: Sender<PathBuf>) -> State {
State {
bars: RefCell::new(Vec::new()),
devices: Mutex::new(Vec::new()),
image_data: RefCell::new(None),
bars: RefCell::new(Vec::new()),
devices: Mutex::new(Vec::new()),
task_handles: Mutex::new(Vec::new()),
tasks: Mutex::new(Vec::new()),
view: RefCell::new(0),
start: RefCell::new(unsafe { mem::uninitialized() }),
// buffer: BufferingData::new(),
tasks: Mutex::new(Vec::new()),
view: Cell::new(0),
start: RefCell::new(unsafe { mem::uninitialized() }),
buffer: Arc::new(BufferingData::new()),
image_sender,
image_length: Cell::new(0),
}
}
}
Expand All @@ -87,7 +77,7 @@ pub struct FlashTask {
}

impl App {
pub fn new() -> App {
pub fn new(sender: Sender<PathBuf>) -> App {
// Initialize GTK before proceeding.
if gtk::init().is_err() {
eprintln!("failed to initialize GTK Application");
Expand Down Expand Up @@ -130,7 +120,7 @@ impl App {
window,
header,
content,
state: Arc::new(State::new()),
state: Arc::new(State::new(sender)),
}
}
}
Loading