use std::{ sync::{ atomic::{AtomicBool, Ordering::SeqCst}, Mutex, }, thread, }; use gtk::{prelude::*, PolicyType, TextView}; use gtk::{Application, ApplicationWindow}; use gtk::{Button, Entry, Orientation, ScrolledWindow}; use glib; mod port; use port::*; static CONNECTED: AtomicBool = AtomicBool::new(false); static PORT: Mutex> = Mutex::new(None); fn main() { let app = Application::builder() .application_id("org.example.HelloWorld") .build(); app.connect_activate(move |app| { // We create the main window. let window = ApplicationWindow::builder() .application(app) .default_width(320) .default_height(200) .title("Hello, World!") .build(); let master_box = gtk::Box::new(Orientation::Vertical, 5); let top_bar_box = gtk::Box::new(Orientation::Horizontal, 5); let pid = Entry::builder().text("0x27dd").editable(true).build(); let vid = Entry::builder().text("0x16c0").editable(true).build(); let connect_button = Button::with_label("Connect"); let reset_button = Button::with_label("Reset"); let serial_info = TextView::new(); serial_info.set_editable(false); let scrolled_window = ScrolledWindow::builder() .vscrollbar_policy(PolicyType::Automatic) .child(&serial_info) .build(); top_bar_box.pack_end(&connect_button, true, true, 3); top_bar_box.pack_end(&reset_button, true, true, 3); top_bar_box.pack_end(&vid, true, true, 3); top_bar_box.pack_end(&pid, true, true, 3); master_box.pack_end(&scrolled_window, true, true, 3); master_box.pack_end(&top_bar_box, false, true, 3); let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); std::thread::spawn(move || { // loop forever loop { let mut port_lock = PORT.lock().unwrap(); if let Some(port) = &mut *port_lock { // handle incoming message match port.read().unwrap() { SerialReadResult::Message(msg) => { if !msg.is_empty() { tx.send(msg).unwrap(); } } SerialReadResult::UtfConversion(err) => println!("{:?}", err), SerialReadResult::Timeout => (), } } drop(port_lock); thread::sleep(Duration::from_millis(10)); } }); connect_button.connect_clicked(move |_| { if !CONNECTED.load(SeqCst) { let usb_id = UsbId { vendor_id: u16::from_str_radix( vid.buffer().text().trim_start_matches("0x"), 16, ) .unwrap(), product_id: u16::from_str_radix( pid.buffer().text().trim_start_matches("0x"), 16, ) .unwrap(), }; let serialport_settings = SerialPortSettings { baud_rate: 9600, data_bits: DataBits::Eight, parity: Parity::None, stop_bits: StopBits::One, flow_control: FlowControl::None, timeout: Duration::from_millis(2500), }; if let Ok(port) = Port::open(usb_id, serialport_settings) { CONNECTED.store(true, SeqCst); *PORT.lock().unwrap() = Some(port); } } }); reset_button.connect_clicked(move |_| { if CONNECTED.load(SeqCst) { CONNECTED.store(false, SeqCst); let mut port_lock = PORT.lock().unwrap(); if let Some(port) = &mut *port_lock { port.write("reset").unwrap(); } *port_lock = None; } }); let buffer = serial_info.buffer().unwrap(); rx.attach(None, move |message| { if !message.is_empty() { buffer.insert(&mut buffer.end_iter(), &format!("{}\n", message)); serial_info.scroll_to_iter(&mut buffer.end_iter(), 0.0, true, 0.0, 0.0); } glib::Continue(true) }); window.add(&master_box); // Don't forget to make all widgets visible. window.show_all(); }); app.run(); }