dear_imgui_rs/
context.rs

1//! ImGui context lifecycle
2//!
3//! Creates, manages and destroys the single active Dear ImGui context used by
4//! the crate. Obtain a `Ui` each frame via `Context::frame()` and render using
5//! your chosen backend. See struct-level docs for details and caveats about one
6//! active context at a time.
7//!
8use parking_lot::ReentrantMutex;
9use std::cell::UnsafeCell;
10use std::ffi::CString;
11use std::ops::Drop;
12use std::path::PathBuf;
13use std::ptr;
14
15use crate::clipboard::{ClipboardBackend, ClipboardContext};
16use crate::fonts::{Font, FontAtlas, SharedFontAtlas};
17use crate::io::Io;
18
19use crate::sys;
20
21/// An imgui context.
22///
23/// A context needs to be created to access most library functions. Due to current Dear ImGui
24/// design choices, at most one active Context can exist at any time. This limitation will likely
25/// be removed in a future Dear ImGui version.
26///
27/// If you need more than one context, you can use suspended contexts. As long as only one context
28/// is active at a time, it's possible to have multiple independent contexts.
29///
30/// # Examples
31///
32/// Creating a new active context:
33/// ```
34/// let ctx = dear_imgui_rs::Context::create();
35/// // ctx is dropped naturally when it goes out of scope, which deactivates and destroys the
36/// // context
37/// ```
38///
39/// Never try to create an active context when another one is active:
40///
41/// ```should_panic
42/// let ctx1 = dear_imgui_rs::Context::create();
43///
44/// let ctx2 = dear_imgui_rs::Context::create(); // PANIC
45/// ```
46#[derive(Debug)]
47pub struct Context {
48    raw: *mut sys::ImGuiContext,
49    shared_font_atlas: Option<SharedFontAtlas>,
50    ini_filename: Option<CString>,
51    log_filename: Option<CString>,
52    platform_name: Option<CString>,
53    renderer_name: Option<CString>,
54    // We need to box this because we hand imgui a pointer to it,
55    // and we don't want to deal with finding `clipboard_ctx`.
56    // We also put it in an UnsafeCell since we're going to give
57    // imgui a mutable pointer to it.
58    clipboard_ctx: Box<UnsafeCell<ClipboardContext>>,
59    ui: crate::ui::Ui,
60}
61
62// This mutex needs to be used to guard all public functions that can affect the underlying
63// Dear ImGui active context
64static CTX_MUTEX: ReentrantMutex<()> = parking_lot::const_reentrant_mutex(());
65
66fn clear_current_context() {
67    unsafe {
68        sys::igSetCurrentContext(ptr::null_mut());
69    }
70}
71
72fn no_current_context() -> bool {
73    let ctx = unsafe { sys::igGetCurrentContext() };
74    ctx.is_null()
75}
76
77impl Context {
78    /// Tries to create a new active Dear ImGui context.
79    ///
80    /// Returns an error if another context is already active or creation fails.
81    pub fn try_create() -> crate::error::ImGuiResult<Context> {
82        Self::try_create_internal(None)
83    }
84
85    /// Tries to create a new active Dear ImGui context with a shared font atlas.
86    pub fn try_create_with_shared_font_atlas(
87        shared_font_atlas: SharedFontAtlas,
88    ) -> crate::error::ImGuiResult<Context> {
89        Self::try_create_internal(Some(shared_font_atlas))
90    }
91
92    /// Creates a new active Dear ImGui context (panics on error).
93    ///
94    /// This aligns with imgui-rs behavior. For fallible creation use `try_create()`.
95    pub fn create() -> Context {
96        Self::try_create().expect("Failed to create Dear ImGui context")
97    }
98
99    /// Creates a new active Dear ImGui context with a shared font atlas (panics on error).
100    pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Context {
101        Self::try_create_with_shared_font_atlas(shared_font_atlas)
102            .expect("Failed to create Dear ImGui context")
103    }
104
105    // removed legacy create_or_panic variants (use create()/try_create())
106
107    fn try_create_internal(
108        mut shared_font_atlas: Option<SharedFontAtlas>,
109    ) -> crate::error::ImGuiResult<Context> {
110        let _guard = CTX_MUTEX.lock();
111
112        if !no_current_context() {
113            return Err(crate::error::ImGuiError::ContextAlreadyActive);
114        }
115
116        let shared_font_atlas_ptr = match &mut shared_font_atlas {
117            Some(atlas) => atlas.as_ptr_mut(),
118            None => ptr::null_mut(),
119        };
120
121        // Create the actual ImGui context
122        let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
123        if raw.is_null() {
124            return Err(crate::error::ImGuiError::ContextCreation {
125                reason: "ImGui_CreateContext returned null".to_string(),
126            });
127        }
128
129        // Set it as the current context
130        unsafe {
131            sys::igSetCurrentContext(raw);
132        }
133
134        Ok(Context {
135            raw,
136            shared_font_atlas,
137            ini_filename: None,
138            log_filename: None,
139            platform_name: None,
140            renderer_name: None,
141            clipboard_ctx: Box::new(UnsafeCell::new(ClipboardContext::dummy())),
142            ui: crate::ui::Ui::new(),
143        })
144    }
145
146    /// Returns a mutable reference to the active context's IO object
147    pub fn io_mut(&mut self) -> &mut Io {
148        let _guard = CTX_MUTEX.lock();
149        unsafe {
150            // Bindings provide igGetIO_Nil; use it to access current IO
151            let io_ptr = sys::igGetIO_Nil();
152            if io_ptr.is_null() {
153                panic!("Context::io_mut() requires an active ImGui context");
154            }
155            &mut *(io_ptr as *mut Io)
156        }
157    }
158
159    /// Get access to the IO structure
160    pub fn io(&self) -> &crate::io::Io {
161        let _guard = CTX_MUTEX.lock();
162        unsafe {
163            // Bindings provide igGetIO_Nil; use it to access current IO
164            let io_ptr = sys::igGetIO_Nil();
165            if io_ptr.is_null() {
166                panic!("Context::io() requires an active ImGui context");
167            }
168            &*(io_ptr as *const crate::io::Io)
169        }
170    }
171
172    /// Get access to the Style structure
173    pub fn style(&self) -> &crate::style::Style {
174        let _guard = CTX_MUTEX.lock();
175        unsafe {
176            let style_ptr = sys::igGetStyle();
177            if style_ptr.is_null() {
178                panic!("Context::style() requires an active ImGui context");
179            }
180            &*(style_ptr as *const crate::style::Style)
181        }
182    }
183
184    /// Get mutable access to the Style structure
185    pub fn style_mut(&mut self) -> &mut crate::style::Style {
186        let _guard = CTX_MUTEX.lock();
187        unsafe {
188            let style_ptr = sys::igGetStyle();
189            if style_ptr.is_null() {
190                panic!("Context::style_mut() requires an active ImGui context");
191            }
192            &mut *(style_ptr as *mut crate::style::Style)
193        }
194    }
195
196    /// Creates a new frame and returns a Ui object for building the interface
197    pub fn frame(&mut self) -> &mut crate::ui::Ui {
198        let _guard = CTX_MUTEX.lock();
199
200        unsafe {
201            sys::igNewFrame();
202        }
203        &mut self.ui
204    }
205
206    /// Create a new frame with a callback
207    pub fn frame_with<F, R>(&mut self, f: F) -> R
208    where
209        F: FnOnce(&crate::ui::Ui) -> R,
210    {
211        let ui = self.frame();
212        f(ui)
213    }
214
215    /// Renders the frame and returns a reference to the resulting draw data
216    ///
217    /// This finalizes the Dear ImGui frame and prepares all draw data for rendering.
218    /// The returned draw data contains all the information needed to render the frame.
219    pub fn render(&mut self) -> &crate::render::DrawData {
220        let _guard = CTX_MUTEX.lock();
221        unsafe {
222            sys::igRender();
223            let dd = sys::igGetDrawData();
224            if dd.is_null() {
225                panic!("Context::render() returned null draw data");
226            }
227            &*(dd as *const crate::render::DrawData)
228        }
229    }
230
231    /// Gets the draw data for the current frame
232    ///
233    /// This returns the draw data without calling render. Only valid after
234    /// `render()` has been called and before the next `new_frame()`.
235    pub fn draw_data(&self) -> Option<&crate::render::DrawData> {
236        let _guard = CTX_MUTEX.lock();
237        unsafe {
238            let draw_data = sys::igGetDrawData();
239            if draw_data.is_null() {
240                None
241            } else {
242                let data = &*(draw_data as *const crate::render::DrawData);
243                if data.valid() { Some(data) } else { None }
244            }
245        }
246    }
247
248    /// Sets the INI filename for settings persistence
249    ///
250    /// # Errors
251    ///
252    /// Returns an error if the filename contains null bytes
253    pub fn set_ini_filename<P: Into<PathBuf>>(
254        &mut self,
255        filename: Option<P>,
256    ) -> crate::error::ImGuiResult<()> {
257        use crate::error::SafeStringConversion;
258        let _guard = CTX_MUTEX.lock();
259
260        self.ini_filename = match filename {
261            Some(f) => Some(f.into().to_string_lossy().to_cstring_safe()?),
262            None => None,
263        };
264
265        unsafe {
266            let io = sys::igGetIO_Nil();
267            let ptr = self
268                .ini_filename
269                .as_ref()
270                .map(|s| s.as_ptr())
271                .unwrap_or(ptr::null());
272            (*io).IniFilename = ptr;
273        }
274        Ok(())
275    }
276
277    // removed legacy set_ini_filename_or_panic (use set_ini_filename())
278
279    /// Sets the log filename
280    ///
281    /// # Errors
282    ///
283    /// Returns an error if the filename contains null bytes
284    pub fn set_log_filename<P: Into<PathBuf>>(
285        &mut self,
286        filename: Option<P>,
287    ) -> crate::error::ImGuiResult<()> {
288        use crate::error::SafeStringConversion;
289        let _guard = CTX_MUTEX.lock();
290
291        self.log_filename = match filename {
292            Some(f) => Some(f.into().to_string_lossy().to_cstring_safe()?),
293            None => None,
294        };
295
296        unsafe {
297            let io = sys::igGetIO_Nil();
298            let ptr = self
299                .log_filename
300                .as_ref()
301                .map(|s| s.as_ptr())
302                .unwrap_or(ptr::null());
303            (*io).LogFilename = ptr;
304        }
305        Ok(())
306    }
307
308    // removed legacy set_log_filename_or_panic (use set_log_filename())
309
310    /// Sets the platform name
311    ///
312    /// # Errors
313    ///
314    /// Returns an error if the name contains null bytes
315    pub fn set_platform_name<S: Into<String>>(
316        &mut self,
317        name: Option<S>,
318    ) -> crate::error::ImGuiResult<()> {
319        use crate::error::SafeStringConversion;
320        let _guard = CTX_MUTEX.lock();
321
322        self.platform_name = match name {
323            Some(n) => Some(n.into().to_cstring_safe()?),
324            None => None,
325        };
326
327        unsafe {
328            let io = sys::igGetIO_Nil();
329            let ptr = self
330                .platform_name
331                .as_ref()
332                .map(|s| s.as_ptr())
333                .unwrap_or(ptr::null());
334            (*io).BackendPlatformName = ptr;
335        }
336        Ok(())
337    }
338
339    // removed legacy set_platform_name_or_panic (use set_platform_name())
340
341    /// Sets the renderer name
342    ///
343    /// # Errors
344    ///
345    /// Returns an error if the name contains null bytes
346    pub fn set_renderer_name<S: Into<String>>(
347        &mut self,
348        name: Option<S>,
349    ) -> crate::error::ImGuiResult<()> {
350        use crate::error::SafeStringConversion;
351        let _guard = CTX_MUTEX.lock();
352
353        self.renderer_name = match name {
354            Some(n) => Some(n.into().to_cstring_safe()?),
355            None => None,
356        };
357
358        unsafe {
359            let io = sys::igGetIO_Nil();
360            if io.is_null() {
361                panic!("igGetIO_Nil() returned null");
362            }
363            let ptr = self
364                .renderer_name
365                .as_ref()
366                .map(|s| s.as_ptr())
367                .unwrap_or(ptr::null());
368            (*io).BackendRendererName = ptr;
369        }
370        Ok(())
371    }
372
373    // removed legacy set_renderer_name_or_panic (use set_renderer_name())
374
375    /// Get mutable access to the platform IO
376    #[cfg(feature = "multi-viewport")]
377    pub fn platform_io_mut(&mut self) -> &mut crate::platform_io::PlatformIo {
378        let _guard = CTX_MUTEX.lock();
379        unsafe {
380            let pio = sys::igGetPlatformIO_Nil();
381            if pio.is_null() {
382                panic!("igGetPlatformIO_Nil() returned null");
383            }
384            crate::platform_io::PlatformIo::from_raw_mut(pio)
385        }
386    }
387
388    /// Returns a reference to the main Dear ImGui viewport.
389    ///
390    /// The returned reference is owned by the currently active ImGui context and
391    /// must not be used after the context is destroyed.
392    #[doc(alias = "GetMainViewport")]
393    pub fn main_viewport(&mut self) -> &crate::platform_io::Viewport {
394        let _guard = CTX_MUTEX.lock();
395        unsafe {
396            let ptr = sys::igGetMainViewport();
397            if ptr.is_null() {
398                panic!("Context::main_viewport() requires an active ImGui context");
399            }
400            crate::platform_io::Viewport::from_raw(ptr as *const sys::ImGuiViewport)
401        }
402    }
403
404    /// Enable multi-viewport support flags
405    #[cfg(feature = "multi-viewport")]
406    pub fn enable_multi_viewport(&mut self) {
407        // Enable viewport flags
408        crate::viewport_backend::utils::enable_viewport_flags(self.io_mut());
409    }
410
411    /// Update platform windows
412    ///
413    /// This function should be called every frame when multi-viewport is enabled.
414    /// It updates all platform windows and handles viewport management.
415    #[cfg(feature = "multi-viewport")]
416    pub fn update_platform_windows(&mut self) {
417        let _guard = CTX_MUTEX.lock();
418        unsafe {
419            // Ensure main viewport is properly set up before updating platform windows
420            let main_viewport = sys::igGetMainViewport();
421            if !main_viewport.is_null() && (*main_viewport).PlatformHandle.is_null() {
422                eprintln!("update_platform_windows: main viewport not set up, setting it up now");
423                // The main viewport needs to be set up - this should be done by the backend
424                // For now, we'll just log this and continue
425            }
426
427            sys::igUpdatePlatformWindows();
428        }
429    }
430
431    /// Render platform windows with default implementation
432    ///
433    /// This function renders all platform windows using the default implementation.
434    /// It calls the platform and renderer backends to render each viewport.
435    #[cfg(feature = "multi-viewport")]
436    pub fn render_platform_windows_default(&mut self) {
437        let _guard = CTX_MUTEX.lock();
438        unsafe {
439            sys::igRenderPlatformWindowsDefault(std::ptr::null_mut(), std::ptr::null_mut());
440        }
441    }
442
443    /// Destroy all platform windows
444    ///
445    /// This function should be called during shutdown to properly clean up
446    /// all platform windows and their associated resources.
447    #[cfg(feature = "multi-viewport")]
448    pub fn destroy_platform_windows(&mut self) {
449        let _guard = CTX_MUTEX.lock();
450        unsafe {
451            sys::igDestroyPlatformWindows();
452        }
453    }
454
455    /// Suspends this context so another context can be the active context
456    pub fn suspend(self) -> SuspendedContext {
457        let _guard = CTX_MUTEX.lock();
458        assert!(
459            self.is_current_context(),
460            "context to be suspended is not the active context"
461        );
462        clear_current_context();
463        SuspendedContext(self)
464    }
465
466    fn is_current_context(&self) -> bool {
467        let ctx = unsafe { sys::igGetCurrentContext() };
468        self.raw == ctx
469    }
470
471    /// Push a font onto the font stack
472    pub fn push_font(&mut self, font: &Font) {
473        let _guard = CTX_MUTEX.lock();
474        unsafe {
475            sys::igPushFont(font.raw(), 0.0);
476        }
477    }
478
479    /// Pop a font from the font stack
480    ///
481    /// This restores the previous font. Must be paired with a call to `push_font()`.
482    #[doc(alias = "PopFont")]
483    pub fn pop_font(&mut self) {
484        let _guard = CTX_MUTEX.lock();
485        unsafe {
486            sys::igPopFont();
487        }
488    }
489
490    /// Get the current font
491    #[doc(alias = "GetFont")]
492    pub fn current_font(&self) -> &Font {
493        let _guard = CTX_MUTEX.lock();
494        unsafe { Font::from_raw(sys::igGetFont() as *const _) }
495    }
496
497    /// Get the current font size
498    #[doc(alias = "GetFontSize")]
499    pub fn current_font_size(&self) -> f32 {
500        let _guard = CTX_MUTEX.lock();
501        unsafe { sys::igGetFontSize() }
502    }
503
504    /// Get the font atlas from the IO structure
505    pub fn font_atlas(&self) -> FontAtlas {
506        let _guard = CTX_MUTEX.lock();
507
508        // wasm32 import-style builds keep Dear ImGui state in a separate module
509        // and share linear memory. When the experimental font-atlas feature is
510        // enabled, we allow direct access to the atlas pointer, assuming the
511        // provider has been correctly configured via xtask.
512        #[cfg(all(target_arch = "wasm32", feature = "wasm-font-atlas-experimental"))]
513        unsafe {
514            let io = sys::igGetIO_Nil();
515            let atlas_ptr = (*io).Fonts;
516            assert!(
517                !atlas_ptr.is_null(),
518                "ImGui IO Fonts pointer is null on wasm; provider not initialized?"
519            );
520            FontAtlas::from_raw(atlas_ptr)
521        }
522
523        // Default wasm path: keep this API disabled to avoid accidental UB.
524        #[cfg(all(target_arch = "wasm32", not(feature = "wasm-font-atlas-experimental")))]
525        {
526            panic!(
527                "font_atlas() is not supported on wasm32 targets without \
528                 `wasm-font-atlas-experimental` feature; \
529                 see docs/WASM.md for current limitations."
530            );
531        }
532
533        #[cfg(not(target_arch = "wasm32"))]
534        unsafe {
535            let io = sys::igGetIO_Nil();
536            let atlas_ptr = (*io).Fonts;
537            FontAtlas::from_raw(atlas_ptr)
538        }
539    }
540
541    /// Get a mutable reference to the font atlas from the IO structure
542    pub fn font_atlas_mut(&mut self) -> FontAtlas {
543        let _guard = CTX_MUTEX.lock();
544
545        // wasm32 import-style builds keep Dear ImGui state in a separate module
546        // and share linear memory. When the experimental font-atlas feature is
547        // enabled, we allow direct access to the atlas pointer, assuming the
548        // provider has been correctly configured via xtask.
549        #[cfg(all(target_arch = "wasm32", feature = "wasm-font-atlas-experimental"))]
550        unsafe {
551            let io = sys::igGetIO_Nil();
552            let atlas_ptr = (*io).Fonts;
553            assert!(
554                !atlas_ptr.is_null(),
555                "ImGui IO Fonts pointer is null on wasm; provider not initialized?"
556            );
557            return FontAtlas::from_raw(atlas_ptr);
558        }
559
560        // Default wasm path: keep this API disabled to avoid accidental UB.
561        #[cfg(all(target_arch = "wasm32", not(feature = "wasm-font-atlas-experimental")))]
562        {
563            panic!(
564                "font_atlas_mut()/fonts() are not supported on wasm32 targets yet; \
565                 enable `wasm-font-atlas-experimental` to opt-in for experiments."
566            );
567        }
568
569        #[cfg(not(target_arch = "wasm32"))]
570        unsafe {
571            let io = sys::igGetIO_Nil();
572            let atlas_ptr = (*io).Fonts;
573            FontAtlas::from_raw(atlas_ptr)
574        }
575    }
576
577    /// Returns the font atlas (alias for font_atlas_mut)
578    ///
579    /// This provides compatibility with imgui-rs naming convention
580    pub fn fonts(&mut self) -> FontAtlas {
581        self.font_atlas_mut()
582    }
583
584    /// Attempts to clone the interior shared font atlas **if it exists**.
585    pub fn clone_shared_font_atlas(&mut self) -> Option<SharedFontAtlas> {
586        self.shared_font_atlas.clone()
587    }
588
589    /// Loads settings from a string slice containing settings in .Ini file format
590    #[doc(alias = "LoadIniSettingsFromMemory")]
591    pub fn load_ini_settings(&mut self, data: &str) {
592        let _guard = CTX_MUTEX.lock();
593        unsafe {
594            sys::igLoadIniSettingsFromMemory(data.as_ptr() as *const _, data.len());
595        }
596    }
597
598    /// Saves settings to a mutable string buffer in .Ini file format
599    #[doc(alias = "SaveIniSettingsToMemory")]
600    pub fn save_ini_settings(&mut self, buf: &mut String) {
601        let _guard = CTX_MUTEX.lock();
602        unsafe {
603            let mut out_ini_size: usize = 0;
604            let data_ptr = sys::igSaveIniSettingsToMemory(&mut out_ini_size as *mut usize);
605            if data_ptr.is_null() || out_ini_size == 0 {
606                return;
607            }
608
609            let mut bytes =
610                std::slice::from_raw_parts(data_ptr as *const u8, out_ini_size as usize);
611            if bytes.last() == Some(&0) {
612                bytes = &bytes[..bytes.len().saturating_sub(1)];
613            }
614            buf.push_str(&String::from_utf8_lossy(bytes));
615        }
616    }
617
618    /// Sets the clipboard backend used for clipboard operations
619    pub fn set_clipboard_backend<T: ClipboardBackend>(&mut self, backend: T) {
620        let _guard = CTX_MUTEX.lock();
621
622        let clipboard_ctx: Box<UnsafeCell<_>> =
623            Box::new(UnsafeCell::new(ClipboardContext::new(backend)));
624
625        // On native/desktop targets, register clipboard callbacks in ImGui PlatformIO
626        // so ImGui can call back into Rust for copy/paste.
627        //
628        // On wasm32 (import-style build), function pointers cannot safely cross the
629        // module boundary between the Rust main module and the cimgui provider. We
630        // therefore keep the backend alive on the Rust side but do not hook it into
631        // ImGui's PlatformIO yet; clipboard integration for web will need a dedicated
632        // design using JS bindings.
633        #[cfg(not(target_arch = "wasm32"))]
634        unsafe {
635            let platform_io = sys::igGetPlatformIO_Nil();
636            if platform_io.is_null() {
637                panic!("Context::set_clipboard_backend() requires an active ImGui context");
638            }
639            (*platform_io).Platform_SetClipboardTextFn = Some(crate::clipboard::set_clipboard_text);
640            (*platform_io).Platform_GetClipboardTextFn = Some(crate::clipboard::get_clipboard_text);
641            (*platform_io).Platform_ClipboardUserData = clipboard_ctx.get() as *mut _;
642        }
643
644        self.clipboard_ctx = clipboard_ctx;
645    }
646}
647
648impl Drop for Context {
649    fn drop(&mut self) {
650        let _guard = CTX_MUTEX.lock();
651        unsafe {
652            if !self.raw.is_null() {
653                if sys::igGetCurrentContext() == self.raw {
654                    clear_current_context();
655                }
656                sys::igDestroyContext(self.raw);
657            }
658        }
659    }
660}
661
662/// A suspended Dear ImGui context
663///
664/// A suspended context retains its state, but is not usable without activating it first.
665#[derive(Debug)]
666pub struct SuspendedContext(Context);
667
668impl SuspendedContext {
669    /// Tries to create a new suspended Dear ImGui context
670    pub fn try_create() -> crate::error::ImGuiResult<Self> {
671        Self::try_create_internal(None)
672    }
673
674    /// Tries to create a new suspended Dear ImGui context with a shared font atlas
675    pub fn try_create_with_shared_font_atlas(
676        shared_font_atlas: SharedFontAtlas,
677    ) -> crate::error::ImGuiResult<Self> {
678        Self::try_create_internal(Some(shared_font_atlas))
679    }
680
681    /// Creates a new suspended Dear ImGui context (panics on error)
682    pub fn create() -> Self {
683        Self::try_create().expect("Failed to create Dear ImGui context")
684    }
685
686    /// Creates a new suspended Dear ImGui context with a shared font atlas (panics on error)
687    pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Self {
688        Self::try_create_with_shared_font_atlas(shared_font_atlas)
689            .expect("Failed to create Dear ImGui context")
690    }
691
692    // removed legacy create_or_panic variants (use create()/try_create())
693
694    fn try_create_internal(
695        mut shared_font_atlas: Option<SharedFontAtlas>,
696    ) -> crate::error::ImGuiResult<Self> {
697        let _guard = CTX_MUTEX.lock();
698
699        let shared_font_atlas_ptr = match &mut shared_font_atlas {
700            Some(atlas) => atlas.as_ptr_mut(),
701            None => ptr::null_mut(),
702        };
703
704        let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
705        if raw.is_null() {
706            return Err(crate::error::ImGuiError::ContextCreation {
707                reason: "ImGui_CreateContext returned null".to_string(),
708            });
709        }
710
711        let ctx = Context {
712            raw,
713            shared_font_atlas,
714            ini_filename: None,
715            log_filename: None,
716            platform_name: None,
717            renderer_name: None,
718            clipboard_ctx: Box::new(UnsafeCell::new(ClipboardContext::dummy())),
719            ui: crate::ui::Ui::new(),
720        };
721
722        // If the context was activated during creation, deactivate it
723        if ctx.is_current_context() {
724            clear_current_context();
725        }
726
727        Ok(SuspendedContext(ctx))
728    }
729
730    /// Attempts to activate this suspended context
731    ///
732    /// If there is no active context, this suspended context is activated and `Ok` is returned.
733    /// If there is already an active context, nothing happens and `Err` is returned.
734    pub fn activate(self) -> Result<Context, SuspendedContext> {
735        let _guard = CTX_MUTEX.lock();
736        if no_current_context() {
737            unsafe {
738                sys::igSetCurrentContext(self.0.raw);
739            }
740            Ok(self.0)
741        } else {
742            Err(self)
743        }
744    }
745}
746
747// Dear ImGui is not thread-safe. The Context must not be sent or shared across
748// threads. If you need multi-threaded rendering, capture render data via
749// OwnedDrawData and move that to another thread for rendering.