use crate::{Assets, WindowGUIThreadData, WindowId, WindowVMThreadData};
use anyhow::Result;
use fui_drawing::{DrawingContextGl, DrawingFonts, DrawingTexture, Fonts};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Weak;
use std::sync::{Arc, Mutex};
use std::thread::JoinHandle;
use tokio::select;
use tokio::sync::{mpsc, oneshot};
thread_local! {
pub static APPLICATION_GUI_CONTEXT: RefCell<Option<ApplicationGuiContext >> = RefCell::new(None);
pub static APPLICATION_VM_CONTEXT: RefCell<Option<ApplicationVmContext >> = RefCell::new(None);
}
pub struct ApplicationGuiContext {
pub drawing_context_gl: Option<Arc<Mutex<DrawingContextGl>>>,
pub background_texture: Option<DrawingTexture>,
pub next_window_id: WindowId,
pub windows: HashMap<WindowId, WindowGUIThreadData>,
pub func_gui2vm_thread_tx: mpsc::UnboundedSender<Box<dyn 'static + Send + FnOnce()>>,
}
pub struct ApplicationVmContext {
pub windows: HashMap<WindowId, Weak<WindowVMThreadData>>,
pub fonts: DrawingFonts,
}
pub struct Application {
gui_thread_join_handle: Option<JoinHandle<()>>,
message_loop_handle: tokio::task::JoinHandle<()>,
}
impl Application {
pub async fn new(title: &'static str) -> Result<Self> {
let (func_gui2vm_thread_tx, mut func_gui2vm_thread_rx) = mpsc::unbounded_channel();
let (gui_thread_init_tx, gui_thread_init_rx) = oneshot::channel();
let (gui_thread_exit_tx, mut gui_thread_exit_rx) = oneshot::channel();
let gui_thread_join_handle = std::thread::Builder::new()
.name("GUI".to_string())
.spawn(move || {
let app = windowing_qt::Application::new(
windowing_qt::ApplicationOptions::new()
.with_title(title)
.with_opengl_share_contexts(true)
.with_opengl_stencil_bits(8) .with_opengl_depth_bits(16), )
.unwrap();
APPLICATION_GUI_CONTEXT.with(move |context| {
*context.borrow_mut() = Some(ApplicationGuiContext {
drawing_context_gl: None,
background_texture: None,
next_window_id: 1,
windows: HashMap::new(),
func_gui2vm_thread_tx,
})
});
gui_thread_init_tx.send(()).unwrap();
app.message_loop();
APPLICATION_GUI_CONTEXT.with(move |context| {
*context.borrow_mut() = None;
});
gui_thread_exit_tx.send(()).unwrap();
})
.unwrap();
gui_thread_init_rx.await?;
APPLICATION_VM_CONTEXT.with(move |context| {
let fonts = create_fonts();
*context.borrow_mut() = Some(ApplicationVmContext {
windows: HashMap::new(),
fonts,
})
});
let message_loop_handle = tokio::task::spawn_local(async move {
let mut exit = false;
while !exit {
select! {
f = func_gui2vm_thread_rx.recv() => if let Some(f) = f { f() },
_res = &mut gui_thread_exit_rx => exit = true,
}
}
});
Ok(Self {
gui_thread_join_handle: Some(gui_thread_join_handle),
message_loop_handle,
})
}
pub fn exit() {
windowing_qt::Application::exit(0);
}
pub async fn run(mut self) -> Result<()> {
self.message_loop_handle.await?;
if let Some(handle) = self.gui_thread_join_handle.take() {
handle.join().unwrap();
}
Ok(())
}
}
fn create_fonts() -> DrawingFonts {
let mut fonts = DrawingFonts::default();
let font_definitions = [
("sans-serif", "Rajdhani-Medium.ttf"),
("sans-serif bold", "Rajdhani-Bold.ttf"),
("monospace", "RajdhaniMono-Medium.ttf"),
("monospace bold", "RajdhaniMono-Bold.ttf"),
];
for (name, asset_path) in font_definitions {
if let Some(asset) = Assets::get(asset_path) {
fonts.register_font(asset.data, Some(name)).unwrap();
}
}
fonts
}