mod eval; mod lexer; mod parser; use gdk4::{Key, ModifierType}; use gtk4::glib::{clone, ExitCode}; use gtk4::prelude::{ ApplicationExt, ApplicationExtManual, EditableExt, GridExt, GtkWindowExt, WidgetExt, }; use gtk4::{glib, Application, ApplicationWindow, Entry, EventControllerKey, Grid}; use std::sync::{Arc, Mutex}; fn send_mail(m: &Mutex>, msg: T) { let mut guard = m.lock().unwrap(); *guard = Some(msg); } fn get_mail(m: &Mutex>) -> Option { let mut guard = m.lock().unwrap(); std::mem::take(&mut guard) } fn main() -> ExitCode { let app = Application::builder() .application_id("com.github.rhogenson.qc") .build(); app.connect_activate(|app| { let window = ApplicationWindow::builder() .application(app) .decorated(false) .resizable(false) .build(); let grid = Grid::new(); window.set_child(Some(&grid)); let input = Entry::new(); grid.attach(&input, 0, 0, 1, 1); let output = Entry::builder().editable(false).build(); grid.attach(&output, 0, 1, 1, 1); let (ping_send, ping_recv) = async_channel::bounded(1); let mailbox = Arc::new(Mutex::new(None)); let key_controller = EventControllerKey::new(); key_controller.connect_key_released( clone!(@weak window, @weak input, @strong mailbox => move |_, k, _, modifiers| { if k == Key::Escape || k == Key::Return || k == Key::bracketleft && modifiers.contains(ModifierType::CONTROL_MASK) { window.destroy(); } send_mail(mailbox.as_ref(), format!("{}", input.text())); let _ = ping_send.try_send(()); }), ); window.add_controller(key_controller); glib::spawn_future_local(async move { loop { if ping_recv.recv().await.is_err() { break; } let Some(input) = get_mail(mailbox.as_ref()) else { continue; }; let (output_send, output_recv) = async_channel::bounded(1); let thread_handle = gtk4::gio::spawn_blocking(move || { if let Some(result) = parser::parse(&input) { output_send.send_blocking(result).unwrap(); } }); let _ = thread_handle.await; let Ok(result) = output_recv.try_recv() else { continue; }; output.set_text(&format!("{result}")); } }); window.present(); }); app.run() }