use super::{ImGuiDrawData, ImguiFontAtlasData};
use imgui::sys as imgui_sys;
use std::sync::Arc;
use std::sync::Mutex;
struct ImguiManagerInner {
context: imgui::Context,
font_atlas_texture: *mut imgui::FontAtlasTexture<'static>,
ui: Option<*mut imgui::Ui<'static>>,
want_capture_keyboard: bool,
want_capture_mouse: bool,
want_set_mouse_pos: bool,
want_text_input: bool,
}
unsafe impl Send for ImguiManagerInner {}
impl Drop for ImguiManagerInner {
fn drop(&mut self) {
let mut ui = None;
std::mem::swap(&mut self.ui, &mut ui);
if let Some(ui) = ui {
let _ui = unsafe { Box::from_raw(ui) };
}
unsafe { Box::from_raw(self.font_atlas_texture) };
}
}
#[derive(Clone)]
pub struct ImguiManager {
inner: Arc<Mutex<ImguiManagerInner>>,
}
impl ImguiManager {
pub fn new(mut imgui_context: imgui::Context) -> Self {
let font_atlas_texture = {
let mut fonts = imgui_context.fonts();
let font_atlas_texture = Box::new(fonts.build_rgba32_texture());
log::info!("Building ImGui font atlas");
let font_atlas_texture: *mut imgui::FontAtlasTexture =
Box::into_raw(font_atlas_texture);
let font_atlas_texture: *mut imgui::FontAtlasTexture<'static> =
unsafe { std::mem::transmute(font_atlas_texture) };
font_atlas_texture
};
ImguiManager {
inner: Arc::new(Mutex::new(ImguiManagerInner {
context: imgui_context,
font_atlas_texture,
ui: None,
want_capture_keyboard: false,
want_capture_mouse: false,
want_set_mouse_pos: false,
want_text_input: false,
})),
}
}
#[profiling::function]
pub fn begin_frame(&self) {
let mut inner_mutex_guard = self.inner.lock().unwrap();
let inner = &mut *inner_mutex_guard;
if inner.ui.is_some() {
log::warn!("a frame is already in progress, starting a new one");
Self::take_ui(&mut *inner);
}
let ui = Box::new(inner.context.frame());
inner.want_capture_keyboard = ui.io().want_capture_keyboard;
inner.want_capture_mouse = ui.io().want_capture_mouse;
inner.want_set_mouse_pos = ui.io().want_set_mouse_pos;
inner.want_text_input = ui.io().want_text_input;
let ui_ptr: *mut imgui::Ui = Box::into_raw(ui);
let ui_ptr: *mut imgui::Ui<'static> = unsafe { std::mem::transmute(ui_ptr) };
inner.ui = Some(ui_ptr);
}
fn take_ui(inner: &mut ImguiManagerInner) -> Option<Box<imgui::Ui<'static>>> {
let mut ui = None;
std::mem::swap(&mut inner.ui, &mut ui);
if let Some(ui) = ui {
return Some(unsafe { Box::from_raw(ui) });
}
None
}
#[profiling::function]
pub fn render(&self) {
let mut inner = self.inner.lock().unwrap();
if inner.ui.is_none() {
log::warn!("render() was called but a frame was not started");
return;
}
let ui = ImguiManager::take_ui(&mut inner);
if let Some(ui) = ui {
ui.render();
} else {
log::warn!("ui did not exist");
}
}
pub fn with_context<F>(
&self,
f: F,
) where
F: FnOnce(&mut imgui::Context),
{
let mut inner = self.inner.lock().unwrap();
(f)(&mut inner.context);
}
pub fn with_ui<F>(
&self,
f: F,
) where
F: FnOnce(&mut imgui::Ui),
{
let inner = self.inner.lock().unwrap();
if inner.ui.is_none() {
log::warn!("Tried to use imgui ui when a frame was not started");
return;
}
if let Some(ui) = inner.ui {
unsafe {
(f)(&mut *ui);
}
}
}
#[profiling::function]
pub fn copy_font_atlas_texture(&self) -> Option<ImguiFontAtlasData> {
let inner = self.inner.lock().unwrap();
if inner.font_atlas_texture.is_null() {
None
} else {
unsafe { Some(ImguiFontAtlasData::new(&*inner.font_atlas_texture)) }
}
}
#[allow(unused_unsafe)]
pub unsafe fn sys_font_atlas_texture(&self) -> Option<&imgui::FontAtlasTexture> {
let inner = self.inner.lock().unwrap();
if inner.font_atlas_texture.is_null() {
None
} else {
unsafe { Some(&*inner.font_atlas_texture) }
}
}
pub fn is_frame_started(&self) -> bool {
let inner = self.inner.lock().unwrap();
inner.ui.is_some()
}
#[profiling::function]
pub fn copy_draw_data(&self) -> Option<ImGuiDrawData> {
let inner = self.inner.lock().unwrap();
if inner.ui.is_some() {
log::warn!("get_draw_data() was called but a frame is in progress");
return None;
}
let draw_data = unsafe { imgui_sys::igGetDrawData() };
if draw_data.is_null() {
log::warn!("no draw data available");
return None;
}
let draw_data = unsafe { &*(draw_data as *mut imgui::DrawData) };
Some(ImGuiDrawData::new(draw_data))
}
#[allow(unused_unsafe)]
pub unsafe fn sys_draw_data(&self) -> Option<&imgui::DrawData> {
let inner = self.inner.lock().unwrap();
if inner.ui.is_some() {
log::warn!("get_draw_data() was called but a frame is in progress");
return None;
}
let draw_data = unsafe { imgui_sys::igGetDrawData() };
if draw_data.is_null() {
log::warn!("no draw data available");
return None;
}
let draw_data = unsafe { &*(draw_data as *mut imgui::DrawData) };
Some(draw_data)
}
pub fn want_capture_keyboard(&self) -> bool {
self.inner.lock().unwrap().want_capture_keyboard
}
pub fn want_capture_mouse(&self) -> bool {
self.inner.lock().unwrap().want_capture_mouse
}
pub fn want_set_mouse_pos(&self) -> bool {
self.inner.lock().unwrap().want_set_mouse_pos
}
pub fn want_text_input(&self) -> bool {
self.inner.lock().unwrap().want_text_input
}
}