1use std::ffi::CString;
2use std::ptr;
3use std::rc::Rc;
4
5use crate::clipboard::ClipboardContext;
6use crate::fonts::SharedFontAtlas;
7use crate::io::Io;
8use crate::sys;
9
10use super::attachment::{
11 AttachmentRegistry, ContextAttachment, ContextAttachmentError, ContextAttachmentHandle,
12 ContextAttachmentLease, ContextAttachmentPhase, ContextAttachmentRole,
13 ContextPlatformAttachmentRelease, ContextPlatformAttachmentReleaseError, run_post_destroy,
14 run_pre_destroy_phase,
15};
16use super::binding::{
17 CTX_MUTEX, ContextAliveToken, ContextBinding, ContextId, ContextState, RawBoundContextGuard,
18 no_current_context, set_current_context, with_bound_context,
19};
20use super::snapshot_hub::SnapshotHub;
21use super::texture_registry::{ManagedTextureRegistry, SharedTextureRegistry};
22
23#[doc(
49 alias = "CreateContext",
50 alias = "DestroyContext",
51 alias = "GetCurrentContext",
52 alias = "SetCurrentContext"
53)]
54#[derive(Debug)]
55pub struct Context {
56 pub(super) raw: *mut sys::ImGuiContext,
57 pub(super) state: Rc<ContextState>,
58 pub(super) attachments: AttachmentRegistry,
59 pub(super) snapshot_hub: SnapshotHub,
60 pub(crate) texture_registry: SharedTextureRegistry,
61 pub(in crate::context) shared_font_atlas: Option<SharedFontAtlas>,
62 pub(in crate::context) ini_filename: Option<CString>,
63 pub(in crate::context) log_filename: Option<CString>,
64 pub(in crate::context) platform_name: Option<CString>,
65 pub(in crate::context) renderer_name: Option<CString>,
66 pub(in crate::context) clipboard_ctx: Box<ClipboardContext>,
69 pub(in crate::context) ui: crate::ui::Ui,
70}
71
72impl Context {
73 pub fn try_create() -> crate::error::ImGuiResult<Context> {
77 Self::try_create_internal(None)
78 }
79
80 pub fn try_create_with_shared_font_atlas(
88 shared_font_atlas: SharedFontAtlas,
89 ) -> crate::error::ImGuiResult<Context> {
90 Self::try_create_internal(Some(shared_font_atlas))
91 }
92
93 pub fn create() -> Context {
97 Self::try_create().expect("Failed to create Dear ImGui context")
98 }
99
100 pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Context {
106 Self::try_create_with_shared_font_atlas(shared_font_atlas)
107 .expect("Failed to create Dear ImGui context")
108 }
109
110 pub fn as_raw(&self) -> *mut sys::ImGuiContext {
112 self.raw
113 }
114
115 pub fn id(&self) -> ContextId {
117 self.state.id()
118 }
119
120 pub fn binding(&self) -> ContextBinding {
122 ContextBinding::new(&self.state)
123 }
124
125 pub fn alive_token(&self) -> ContextAliveToken {
130 ContextAliveToken::from_binding(self.binding())
131 }
132
133 pub fn register_attachment<Marker: 'static>(
138 &mut self,
139 role: ContextAttachmentRole,
140 attachment: Rc<dyn ContextAttachment>,
141 ) -> Result<ContextAttachmentLease, ContextAttachmentError> {
142 self.attachments
143 .register::<Marker>(self.state.lifecycle(), role, attachment)
144 }
145
146 pub fn preflight_attachment_registration<Marker: 'static>(
152 &self,
153 role: ContextAttachmentRole,
154 ) -> Result<(), ContextAttachmentError> {
155 self.attachments
156 .preflight_register::<Marker>(self.state.lifecycle(), role)
157 }
158
159 pub fn prepare_platform_attachment_release(
166 &mut self,
167 handle: &ContextAttachmentHandle,
168 ) -> Result<ContextPlatformAttachmentRelease<'_>, ContextPlatformAttachmentReleaseError> {
169 let control = self.attachments.prepare_platform_release(handle)?;
170 Ok(ContextPlatformAttachmentRelease::new(self, control))
171 }
172
173 pub(super) fn io_ptr(&self, caller: &str) -> *mut sys::ImGuiIO {
176 let io = unsafe { sys::igGetIO_ContextPtr(self.raw) };
177 if io.is_null() {
178 panic!("{caller} requires a valid ImGui context");
179 }
180 io
181 }
182
183 pub(super) fn platform_io_ptr(&self, caller: &str) -> *mut sys::ImGuiPlatformIO {
184 let pio = unsafe { sys::igGetPlatformIO_ContextPtr(self.raw) };
185 if pio.is_null() {
186 panic!("{caller} requires a valid ImGui context");
187 }
188 pio
189 }
190
191 pub(super) fn assert_current_context(&self, caller: &str) {
192 assert!(
193 self.is_current_context(),
194 "{caller} requires this context to be current"
195 );
196 }
197
198 fn try_create_internal(
199 shared_font_atlas: Option<SharedFontAtlas>,
200 ) -> crate::error::ImGuiResult<Context> {
201 let _guard = CTX_MUTEX.lock();
202
203 if !no_current_context() {
204 return Err(crate::error::ImGuiError::ContextAlreadyActive);
205 }
206
207 let shared_font_atlas_ptr = match &shared_font_atlas {
208 Some(atlas) => atlas.as_ptr(),
209 None => ptr::null_mut(),
210 };
211 crate::fonts::validate_font_atlas_context_registration(shared_font_atlas_ptr)?;
212
213 let id =
214 ContextId::allocate().ok_or_else(|| crate::error::ImGuiError::ContextCreation {
215 reason: "process Context identity space is exhausted".to_string(),
216 })?;
217
218 let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
220 if raw.is_null() {
221 return Err(crate::error::ImGuiError::ContextCreation {
222 reason: "ImGui_CreateContext returned null".to_string(),
223 });
224 }
225
226 set_current_context(raw);
228
229 unsafe {
230 let io = sys::igGetIO_ContextPtr(raw);
231 assert!(
232 !io.is_null(),
233 "new ImGui context returned a null IO pointer"
234 );
235 crate::fonts::register_font_atlas_context((*io).Fonts, raw);
236 }
237
238 let state = ContextState::new(id, raw);
239 let texture_registry = ManagedTextureRegistry::new(id);
240 let ui = crate::ui::Ui::new(raw, ContextBinding::new(&state), texture_registry.clone());
241
242 Ok(Context {
243 raw,
244 state,
245 attachments: AttachmentRegistry::default(),
246 snapshot_hub: SnapshotHub::new(id),
247 texture_registry,
248 shared_font_atlas,
249 ini_filename: None,
250 log_filename: None,
251 platform_name: None,
252 renderer_name: None,
253 clipboard_ctx: Box::new(ClipboardContext::dummy()),
254 ui,
255 })
256 }
257
258 pub fn io_mut(&mut self) -> &mut Io {
260 let _guard = CTX_MUTEX.lock();
261 unsafe {
262 let io_ptr = self.io_ptr("Context::io_mut()");
263 &mut *(io_ptr as *mut Io)
264 }
265 }
266
267 pub fn io(&self) -> &crate::io::Io {
269 let _guard = CTX_MUTEX.lock();
270 unsafe {
271 let io_ptr = self.io_ptr("Context::io()");
272 &*(io_ptr as *const crate::io::Io)
273 }
274 }
275
276 pub fn style(&self) -> &crate::style::Style {
278 let _guard = CTX_MUTEX.lock();
279 unsafe {
280 with_bound_context(self.raw, || {
281 let style_ptr = sys::igGetStyle();
282 if style_ptr.is_null() {
283 panic!("Context::style() requires a valid ImGui context");
284 }
285 &*(style_ptr as *const crate::style::Style)
286 })
287 }
288 }
289
290 pub fn style_mut(&mut self) -> &mut crate::style::Style {
292 let _guard = CTX_MUTEX.lock();
293 unsafe {
294 with_bound_context(self.raw, || {
295 let style_ptr = sys::igGetStyle();
296 if style_ptr.is_null() {
297 panic!("Context::style_mut() requires a valid ImGui context");
298 }
299 &mut *(style_ptr as *mut crate::style::Style)
300 })
301 }
302 }
303
304 pub(super) fn is_current_context(&self) -> bool {
305 let ctx = unsafe { sys::igGetCurrentContext() };
306 self.raw == ctx
307 }
308}
309
310impl Drop for Context {
311 fn drop(&mut self) {
312 let _lock = CTX_MUTEX.lock();
313 if self.raw.is_null() {
314 self.state.mark_native_destroyed();
315 return;
316 }
317
318 self.state.begin_drop();
319 let attachment_controls = self.attachments.begin_teardown();
320 let context_id = self.state.id();
321 let raw = self.raw;
322 let _bound = RawBoundContextGuard::bind(raw);
323
324 self.end_frame_for_teardown_unlocked();
328
329 for phase in [
330 ContextAttachmentPhase::Quiesce,
331 ContextAttachmentPhase::RendererResources,
332 ContextAttachmentPhase::PlatformWindows,
333 ] {
334 if !run_pre_destroy_phase(&attachment_controls, self, phase) {
335 std::process::abort();
338 }
339 if phase == ContextAttachmentPhase::RendererResources {
340 self.snapshot_hub.close();
345 }
346 }
347
348 unsafe {
349 let io = sys::igGetIO_ContextPtr(raw);
350 let font_atlas = if io.is_null() {
351 std::ptr::null_mut()
352 } else {
353 (*io).Fonts
354 };
355 let owned_font_atlas = if self.shared_font_atlas.is_none() {
356 font_atlas
357 } else {
358 std::ptr::null_mut()
359 };
360 self.texture_registry.borrow_mut().prepare_teardown();
361 with_bound_context(raw, || {
362 crate::platform_io::clear_aggregate_callbacks_for_current_context();
363 });
364 #[cfg(feature = "stack-layout")]
365 sys::ImGuiStack_DestroyContextState(raw);
366 crate::fonts::unregister_font_atlas_context(font_atlas, raw, context_id);
367 if let Some(shared_font_atlas) = &self.shared_font_atlas {
368 with_bound_context(raw, || {
369 shared_font_atlas.unregister_from_current_context();
370 });
371 }
372 sys::igDestroyContext(raw);
373 self.texture_registry
374 .borrow_mut()
375 .release_after_native_destroy();
376 crate::platform_io::clear_typed_callbacks_for_context(raw);
379 crate::fonts::forget_font_atlas_generation(owned_font_atlas);
380 }
381
382 self.raw = ptr::null_mut();
383 self.state.mark_native_destroyed();
384 if !run_post_destroy(attachment_controls, context_id) {
385 std::process::abort();
386 }
387 }
388}