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, ContextThreadLease,
18 RawBoundContextGuard, bound_context_scope_active, no_current_context, set_current_context,
19 with_bound_context,
20};
21use super::snapshot_hub::SnapshotHub;
22use super::texture_registry::{ManagedTextureRegistry, SharedTextureRegistry};
23
24#[doc(
50 alias = "CreateContext",
51 alias = "DestroyContext",
52 alias = "GetCurrentContext",
53 alias = "SetCurrentContext"
54)]
55#[derive(Debug)]
56pub struct Context {
57 pub(super) raw: *mut sys::ImGuiContext,
58 pub(super) state: Rc<ContextState>,
59 pub(super) attachments: AttachmentRegistry,
60 pub(super) snapshot_hub: SnapshotHub,
61 pub(crate) texture_registry: SharedTextureRegistry,
62 pub(in crate::context) shared_font_atlas: Option<SharedFontAtlas>,
63 pub(in crate::context) ini_filename: Option<CString>,
64 pub(in crate::context) log_filename: Option<CString>,
65 pub(in crate::context) platform_name: Option<CString>,
66 pub(in crate::context) renderer_name: Option<CString>,
67 pub(in crate::context) clipboard_ctx: Box<ClipboardContext>,
70 pub(in crate::context) ui: crate::ui::Ui,
71 pub(super) _thread_lease: ContextThreadLease,
73}
74
75impl Context {
76 pub fn try_create() -> crate::error::ImGuiResult<Context> {
80 Self::try_create_internal(None)
81 }
82
83 pub fn try_create_with_shared_font_atlas(
91 shared_font_atlas: SharedFontAtlas,
92 ) -> crate::error::ImGuiResult<Context> {
93 Self::try_create_internal(Some(shared_font_atlas))
94 }
95
96 pub fn create() -> Context {
100 Self::try_create().expect("Failed to create Dear ImGui context")
101 }
102
103 pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Context {
109 Self::try_create_with_shared_font_atlas(shared_font_atlas)
110 .expect("Failed to create Dear ImGui context")
111 }
112
113 pub fn as_raw(&self) -> *mut sys::ImGuiContext {
115 self.raw
116 }
117
118 pub fn id(&self) -> ContextId {
120 self.state.id()
121 }
122
123 pub fn binding(&self) -> ContextBinding {
125 ContextBinding::new(&self.state)
126 }
127
128 pub fn alive_token(&self) -> ContextAliveToken {
133 ContextAliveToken::from_binding(self.binding())
134 }
135
136 pub fn register_attachment<Marker: 'static>(
141 &mut self,
142 role: ContextAttachmentRole,
143 attachment: Rc<dyn ContextAttachment>,
144 ) -> Result<ContextAttachmentLease, ContextAttachmentError> {
145 self.attachments
146 .register::<Marker>(self.state.lifecycle(), role, attachment)
147 }
148
149 pub fn preflight_attachment_registration<Marker: 'static>(
155 &self,
156 role: ContextAttachmentRole,
157 ) -> Result<(), ContextAttachmentError> {
158 self.attachments
159 .preflight_register::<Marker>(self.state.lifecycle(), role)
160 }
161
162 pub fn prepare_platform_attachment_release(
169 &mut self,
170 handle: &ContextAttachmentHandle,
171 ) -> Result<ContextPlatformAttachmentRelease<'_>, ContextPlatformAttachmentReleaseError> {
172 let control = self.attachments.prepare_platform_release(handle)?;
173 Ok(ContextPlatformAttachmentRelease::new(self, control))
174 }
175
176 pub(super) fn io_ptr(&self, caller: &str) -> *mut sys::ImGuiIO {
179 let io = unsafe { sys::igGetIO_ContextPtr(self.raw) };
180 if io.is_null() {
181 panic!("{caller} requires a valid ImGui context");
182 }
183 io
184 }
185
186 pub(super) fn platform_io_ptr(&self, caller: &str) -> *mut sys::ImGuiPlatformIO {
187 let pio = unsafe { sys::igGetPlatformIO_ContextPtr(self.raw) };
188 if pio.is_null() {
189 panic!("{caller} requires a valid ImGui context");
190 }
191 pio
192 }
193
194 pub(super) fn assert_current_context(&self, caller: &str) {
195 assert!(
196 self.is_current_context(),
197 "{caller} requires this context to be current"
198 );
199 }
200
201 fn try_create_internal(
202 shared_font_atlas: Option<SharedFontAtlas>,
203 ) -> crate::error::ImGuiResult<Context> {
204 if bound_context_scope_active() {
205 return Err(crate::error::ImGuiError::ContextBindingScopeActive);
206 }
207 let thread_lease = ContextThreadLease::acquire()?;
208 let _guard = CTX_MUTEX.lock();
209
210 if !no_current_context() {
211 return Err(crate::error::ImGuiError::ContextAlreadyActive);
212 }
213
214 let shared_font_atlas_ptr = match &shared_font_atlas {
215 Some(atlas) => atlas.as_ptr(),
216 None => ptr::null_mut(),
217 };
218 crate::fonts::validate_font_atlas_context_registration(shared_font_atlas_ptr)?;
219
220 let id =
221 ContextId::allocate().ok_or_else(|| crate::error::ImGuiError::ContextCreation {
222 reason: "process Context identity space is exhausted".to_string(),
223 })?;
224
225 let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
227 if raw.is_null() {
228 return Err(crate::error::ImGuiError::ContextCreation {
229 reason: "ImGui_CreateContext returned null".to_string(),
230 });
231 }
232
233 set_current_context(raw);
235
236 unsafe {
237 let io = sys::igGetIO_ContextPtr(raw);
238 assert!(
239 !io.is_null(),
240 "new ImGui context returned a null IO pointer"
241 );
242 crate::fonts::register_font_atlas_context((*io).Fonts, raw);
243 }
244
245 let state = ContextState::new(id, raw);
246 let texture_registry = ManagedTextureRegistry::new(id);
247 let ui = crate::ui::Ui::new(raw, ContextBinding::new(&state), texture_registry.clone());
248
249 Ok(Context {
250 raw,
251 state,
252 _thread_lease: thread_lease,
253 attachments: AttachmentRegistry::default(),
254 snapshot_hub: SnapshotHub::new(id),
255 texture_registry,
256 shared_font_atlas,
257 ini_filename: None,
258 log_filename: None,
259 platform_name: None,
260 renderer_name: None,
261 clipboard_ctx: Box::new(ClipboardContext::dummy()),
262 ui,
263 })
264 }
265
266 pub fn io_mut(&mut self) -> &mut Io {
268 let _guard = CTX_MUTEX.lock();
269 unsafe {
270 let io_ptr = self.io_ptr("Context::io_mut()");
271 &mut *(io_ptr as *mut Io)
272 }
273 }
274
275 pub fn io(&self) -> &crate::io::Io {
277 let _guard = CTX_MUTEX.lock();
278 unsafe {
279 let io_ptr = self.io_ptr("Context::io()");
280 &*(io_ptr as *const crate::io::Io)
281 }
282 }
283
284 pub fn style(&self) -> &crate::style::Style {
286 let _guard = CTX_MUTEX.lock();
287 unsafe {
288 with_bound_context(self.raw, || {
289 let style_ptr = sys::igGetStyle();
290 if style_ptr.is_null() {
291 panic!("Context::style() requires a valid ImGui context");
292 }
293 &*(style_ptr as *const crate::style::Style)
294 })
295 }
296 }
297
298 pub fn style_mut(&mut self) -> &mut crate::style::Style {
300 let _guard = CTX_MUTEX.lock();
301 unsafe {
302 with_bound_context(self.raw, || {
303 let style_ptr = sys::igGetStyle();
304 if style_ptr.is_null() {
305 panic!("Context::style_mut() requires a valid ImGui context");
306 }
307 &mut *(style_ptr as *mut crate::style::Style)
308 })
309 }
310 }
311
312 pub(super) fn is_current_context(&self) -> bool {
313 let ctx = unsafe { sys::igGetCurrentContext() };
314 self.raw == ctx
315 }
316}
317
318impl Drop for Context {
319 fn drop(&mut self) {
320 let _lock = CTX_MUTEX.lock();
321 if self.raw.is_null() {
322 self.state.mark_native_destroyed();
323 return;
324 }
325
326 self.state.begin_drop();
327 let attachment_controls = self.attachments.begin_teardown();
328 let context_id = self.state.id();
329 let raw = self.raw;
330 let _bound = RawBoundContextGuard::bind(raw);
331
332 self.end_frame_for_teardown_unlocked();
336
337 for phase in [
338 ContextAttachmentPhase::Quiesce,
339 ContextAttachmentPhase::RendererResources,
340 ContextAttachmentPhase::PlatformWindows,
341 ] {
342 if !run_pre_destroy_phase(&attachment_controls, self, phase) {
343 std::process::abort();
346 }
347 if phase == ContextAttachmentPhase::RendererResources {
348 self.snapshot_hub.close();
353 }
354 }
355
356 unsafe {
357 let io = sys::igGetIO_ContextPtr(raw);
358 let font_atlas = if io.is_null() {
359 std::ptr::null_mut()
360 } else {
361 (*io).Fonts
362 };
363 let owned_font_atlas = if self.shared_font_atlas.is_none() {
364 font_atlas
365 } else {
366 std::ptr::null_mut()
367 };
368 self.texture_registry.borrow_mut().prepare_teardown();
369 with_bound_context(raw, || {
370 crate::platform_io::clear_aggregate_callbacks_for_current_context();
371 });
372 #[cfg(feature = "stack-layout")]
373 sys::ImGuiStack_DestroyContextState(raw);
374 crate::fonts::unregister_font_atlas_context(font_atlas, raw, context_id);
375 if let Some(shared_font_atlas) = &self.shared_font_atlas {
376 with_bound_context(raw, || {
377 shared_font_atlas.unregister_from_current_context();
378 });
379 }
380 sys::igDestroyContext(raw);
381 self.texture_registry
382 .borrow_mut()
383 .release_after_native_destroy();
384 crate::platform_io::clear_typed_callbacks_for_context(raw);
387 crate::fonts::forget_font_atlas_generation(owned_font_atlas);
388 }
389
390 self.raw = ptr::null_mut();
391 self.state.mark_native_destroyed();
392 if !run_post_destroy(attachment_controls, context_id) {
393 std::process::abort();
394 }
395 }
396}