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(
248 raw,
249 ContextBinding::new(&state),
250 texture_registry.clone(),
251 state.ime.clone(),
252 );
253
254 Ok(Context {
255 raw,
256 state,
257 _thread_lease: thread_lease,
258 attachments: AttachmentRegistry::default(),
259 snapshot_hub: SnapshotHub::new(id),
260 texture_registry,
261 shared_font_atlas,
262 ini_filename: None,
263 log_filename: None,
264 platform_name: None,
265 renderer_name: None,
266 clipboard_ctx: Box::new(ClipboardContext::dummy()),
267 ui,
268 })
269 }
270
271 pub fn io_mut(&mut self) -> &mut Io {
273 let _guard = CTX_MUTEX.lock();
274 unsafe {
275 let io_ptr = self.io_ptr("Context::io_mut()");
276 &mut *(io_ptr as *mut Io)
277 }
278 }
279
280 pub fn io(&self) -> &crate::io::Io {
282 let _guard = CTX_MUTEX.lock();
283 unsafe {
284 let io_ptr = self.io_ptr("Context::io()");
285 &*(io_ptr as *const crate::io::Io)
286 }
287 }
288
289 pub fn style(&self) -> &crate::style::Style {
291 let _guard = CTX_MUTEX.lock();
292 unsafe {
293 with_bound_context(self.raw, || {
294 let style_ptr = sys::igGetStyle();
295 if style_ptr.is_null() {
296 panic!("Context::style() requires a valid ImGui context");
297 }
298 &*(style_ptr as *const crate::style::Style)
299 })
300 }
301 }
302
303 pub fn style_mut(&mut self) -> &mut crate::style::Style {
305 let _guard = CTX_MUTEX.lock();
306 unsafe {
307 with_bound_context(self.raw, || {
308 let style_ptr = sys::igGetStyle();
309 if style_ptr.is_null() {
310 panic!("Context::style_mut() requires a valid ImGui context");
311 }
312 &mut *(style_ptr as *mut crate::style::Style)
313 })
314 }
315 }
316
317 pub(super) fn is_current_context(&self) -> bool {
318 let ctx = unsafe { sys::igGetCurrentContext() };
319 self.raw == ctx
320 }
321}
322
323impl Drop for Context {
324 fn drop(&mut self) {
325 let _lock = CTX_MUTEX.lock();
326 if self.raw.is_null() {
327 self.state.mark_native_destroyed();
328 return;
329 }
330
331 self.state.begin_drop();
332 let attachment_controls = self.attachments.begin_teardown();
333 let context_id = self.state.id();
334 let raw = self.raw;
335 let _bound = RawBoundContextGuard::bind(raw);
336
337 self.end_frame_for_teardown_unlocked();
341
342 for phase in [
343 ContextAttachmentPhase::Quiesce,
344 ContextAttachmentPhase::RendererResources,
345 ContextAttachmentPhase::PlatformWindows,
346 ] {
347 if !run_pre_destroy_phase(&attachment_controls, self, phase) {
348 std::process::abort();
351 }
352 if phase == ContextAttachmentPhase::RendererResources {
353 self.snapshot_hub.close();
358 }
359 }
360
361 unsafe {
362 let io = sys::igGetIO_ContextPtr(raw);
363 let font_atlas = if io.is_null() {
364 std::ptr::null_mut()
365 } else {
366 (*io).Fonts
367 };
368 let owned_font_atlas = if self.shared_font_atlas.is_none() {
369 font_atlas
370 } else {
371 std::ptr::null_mut()
372 };
373 self.texture_registry.borrow_mut().prepare_teardown();
374 with_bound_context(raw, || {
375 crate::platform_io::clear_aggregate_callbacks_for_current_context();
376 });
377 #[cfg(feature = "stack-layout")]
378 sys::ImGuiStack_DestroyContextState(raw);
379 crate::fonts::unregister_font_atlas_context(font_atlas, raw, context_id);
380 if let Some(shared_font_atlas) = &self.shared_font_atlas {
381 with_bound_context(raw, || {
382 shared_font_atlas.unregister_from_current_context();
383 });
384 }
385 sys::igDestroyContext(raw);
386 self.texture_registry
387 .borrow_mut()
388 .release_after_native_destroy();
389 crate::platform_io::clear_typed_callbacks_for_context(raw);
392 crate::fonts::forget_font_atlas_generation(owned_font_atlas);
393 }
394
395 self.raw = ptr::null_mut();
396 self.state.mark_native_destroyed();
397 if !run_post_destroy(attachment_controls, context_id) {
398 std::process::abort();
399 }
400 }
401}