use std::ffi::CString;
use std::ptr;
use std::rc::Rc;
use crate::clipboard::ClipboardContext;
use crate::fonts::SharedFontAtlas;
use crate::io::Io;
use crate::sys;
use super::attachment::{
AttachmentRegistry, ContextAttachment, ContextAttachmentError, ContextAttachmentHandle,
ContextAttachmentLease, ContextAttachmentPhase, ContextAttachmentRole,
ContextPlatformAttachmentRelease, ContextPlatformAttachmentReleaseError, run_post_destroy,
run_pre_destroy_phase,
};
use super::binding::{
CTX_MUTEX, ContextAliveToken, ContextBinding, ContextId, ContextState, ContextThreadLease,
RawBoundContextGuard, bound_context_scope_active, no_current_context, set_current_context,
with_bound_context,
};
use super::snapshot_hub::SnapshotHub;
use super::texture_registry::{ManagedTextureRegistry, SharedTextureRegistry};
#[doc(
alias = "CreateContext",
alias = "DestroyContext",
alias = "GetCurrentContext",
alias = "SetCurrentContext"
)]
#[derive(Debug)]
pub struct Context {
pub(super) raw: *mut sys::ImGuiContext,
pub(super) state: Rc<ContextState>,
pub(super) attachments: AttachmentRegistry,
pub(super) snapshot_hub: SnapshotHub,
pub(crate) texture_registry: SharedTextureRegistry,
pub(in crate::context) shared_font_atlas: Option<SharedFontAtlas>,
pub(in crate::context) ini_filename: Option<CString>,
pub(in crate::context) log_filename: Option<CString>,
pub(in crate::context) platform_name: Option<CString>,
pub(in crate::context) renderer_name: Option<CString>,
pub(in crate::context) clipboard_ctx: Box<ClipboardContext>,
pub(in crate::context) ui: crate::ui::Ui,
pub(super) _thread_lease: ContextThreadLease,
}
impl Context {
pub fn try_create() -> crate::error::ImGuiResult<Context> {
Self::try_create_internal(None)
}
pub fn try_create_with_shared_font_atlas(
shared_font_atlas: SharedFontAtlas,
) -> crate::error::ImGuiResult<Context> {
Self::try_create_internal(Some(shared_font_atlas))
}
pub fn create() -> Context {
Self::try_create().expect("Failed to create Dear ImGui context")
}
pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Context {
Self::try_create_with_shared_font_atlas(shared_font_atlas)
.expect("Failed to create Dear ImGui context")
}
pub fn as_raw(&self) -> *mut sys::ImGuiContext {
self.raw
}
pub fn id(&self) -> ContextId {
self.state.id()
}
pub fn binding(&self) -> ContextBinding {
ContextBinding::new(&self.state)
}
pub fn alive_token(&self) -> ContextAliveToken {
ContextAliveToken::from_binding(self.binding())
}
pub fn register_attachment<Marker: 'static>(
&mut self,
role: ContextAttachmentRole,
attachment: Rc<dyn ContextAttachment>,
) -> Result<ContextAttachmentLease, ContextAttachmentError> {
self.attachments
.register::<Marker>(self.state.lifecycle(), role, attachment)
}
pub fn preflight_attachment_registration<Marker: 'static>(
&self,
role: ContextAttachmentRole,
) -> Result<(), ContextAttachmentError> {
self.attachments
.preflight_register::<Marker>(self.state.lifecycle(), role)
}
pub fn prepare_platform_attachment_release(
&mut self,
handle: &ContextAttachmentHandle,
) -> Result<ContextPlatformAttachmentRelease<'_>, ContextPlatformAttachmentReleaseError> {
let control = self.attachments.prepare_platform_release(handle)?;
Ok(ContextPlatformAttachmentRelease::new(self, control))
}
pub(super) fn io_ptr(&self, caller: &str) -> *mut sys::ImGuiIO {
let io = unsafe { sys::igGetIO_ContextPtr(self.raw) };
if io.is_null() {
panic!("{caller} requires a valid ImGui context");
}
io
}
pub(super) fn platform_io_ptr(&self, caller: &str) -> *mut sys::ImGuiPlatformIO {
let pio = unsafe { sys::igGetPlatformIO_ContextPtr(self.raw) };
if pio.is_null() {
panic!("{caller} requires a valid ImGui context");
}
pio
}
pub(super) fn assert_current_context(&self, caller: &str) {
assert!(
self.is_current_context(),
"{caller} requires this context to be current"
);
}
fn try_create_internal(
shared_font_atlas: Option<SharedFontAtlas>,
) -> crate::error::ImGuiResult<Context> {
if bound_context_scope_active() {
return Err(crate::error::ImGuiError::ContextBindingScopeActive);
}
let thread_lease = ContextThreadLease::acquire()?;
let _guard = CTX_MUTEX.lock();
if !no_current_context() {
return Err(crate::error::ImGuiError::ContextAlreadyActive);
}
let shared_font_atlas_ptr = match &shared_font_atlas {
Some(atlas) => atlas.as_ptr(),
None => ptr::null_mut(),
};
crate::fonts::validate_font_atlas_context_registration(shared_font_atlas_ptr)?;
let id =
ContextId::allocate().ok_or_else(|| crate::error::ImGuiError::ContextCreation {
reason: "process Context identity space is exhausted".to_string(),
})?;
let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
if raw.is_null() {
return Err(crate::error::ImGuiError::ContextCreation {
reason: "ImGui_CreateContext returned null".to_string(),
});
}
set_current_context(raw);
unsafe {
let io = sys::igGetIO_ContextPtr(raw);
assert!(
!io.is_null(),
"new ImGui context returned a null IO pointer"
);
crate::fonts::register_font_atlas_context((*io).Fonts, raw);
}
let state = ContextState::new(id, raw);
let texture_registry = ManagedTextureRegistry::new(id);
let ui = crate::ui::Ui::new(raw, ContextBinding::new(&state), texture_registry.clone());
Ok(Context {
raw,
state,
_thread_lease: thread_lease,
attachments: AttachmentRegistry::default(),
snapshot_hub: SnapshotHub::new(id),
texture_registry,
shared_font_atlas,
ini_filename: None,
log_filename: None,
platform_name: None,
renderer_name: None,
clipboard_ctx: Box::new(ClipboardContext::dummy()),
ui,
})
}
pub fn io_mut(&mut self) -> &mut Io {
let _guard = CTX_MUTEX.lock();
unsafe {
let io_ptr = self.io_ptr("Context::io_mut()");
&mut *(io_ptr as *mut Io)
}
}
pub fn io(&self) -> &crate::io::Io {
let _guard = CTX_MUTEX.lock();
unsafe {
let io_ptr = self.io_ptr("Context::io()");
&*(io_ptr as *const crate::io::Io)
}
}
pub fn style(&self) -> &crate::style::Style {
let _guard = CTX_MUTEX.lock();
unsafe {
with_bound_context(self.raw, || {
let style_ptr = sys::igGetStyle();
if style_ptr.is_null() {
panic!("Context::style() requires a valid ImGui context");
}
&*(style_ptr as *const crate::style::Style)
})
}
}
pub fn style_mut(&mut self) -> &mut crate::style::Style {
let _guard = CTX_MUTEX.lock();
unsafe {
with_bound_context(self.raw, || {
let style_ptr = sys::igGetStyle();
if style_ptr.is_null() {
panic!("Context::style_mut() requires a valid ImGui context");
}
&mut *(style_ptr as *mut crate::style::Style)
})
}
}
pub(super) fn is_current_context(&self) -> bool {
let ctx = unsafe { sys::igGetCurrentContext() };
self.raw == ctx
}
}
impl Drop for Context {
fn drop(&mut self) {
let _lock = CTX_MUTEX.lock();
if self.raw.is_null() {
self.state.mark_native_destroyed();
return;
}
self.state.begin_drop();
let attachment_controls = self.attachments.begin_teardown();
let context_id = self.state.id();
let raw = self.raw;
let _bound = RawBoundContextGuard::bind(raw);
self.end_frame_for_teardown_unlocked();
for phase in [
ContextAttachmentPhase::Quiesce,
ContextAttachmentPhase::RendererResources,
ContextAttachmentPhase::PlatformWindows,
] {
if !run_pre_destroy_phase(&attachment_controls, self, phase) {
std::process::abort();
}
if phase == ContextAttachmentPhase::RendererResources {
self.snapshot_hub.close();
}
}
unsafe {
let io = sys::igGetIO_ContextPtr(raw);
let font_atlas = if io.is_null() {
std::ptr::null_mut()
} else {
(*io).Fonts
};
let owned_font_atlas = if self.shared_font_atlas.is_none() {
font_atlas
} else {
std::ptr::null_mut()
};
self.texture_registry.borrow_mut().prepare_teardown();
with_bound_context(raw, || {
crate::platform_io::clear_aggregate_callbacks_for_current_context();
});
#[cfg(feature = "stack-layout")]
sys::ImGuiStack_DestroyContextState(raw);
crate::fonts::unregister_font_atlas_context(font_atlas, raw, context_id);
if let Some(shared_font_atlas) = &self.shared_font_atlas {
with_bound_context(raw, || {
shared_font_atlas.unregister_from_current_context();
});
}
sys::igDestroyContext(raw);
self.texture_registry
.borrow_mut()
.release_after_native_destroy();
crate::platform_io::clear_typed_callbacks_for_context(raw);
crate::fonts::forget_font_atlas_generation(owned_font_atlas);
}
self.raw = ptr::null_mut();
self.state.mark_native_destroyed();
if !run_post_destroy(attachment_controls, context_id) {
std::process::abort();
}
}
}