Skip to main content

dear_imgui_rs/context/
core.rs

1use std::ffi::CString;
2use std::ptr;
3use std::rc::{Rc, Weak};
4
5use crate::clipboard::ClipboardContext;
6use crate::fonts::SharedFontAtlas;
7use crate::io::Io;
8use crate::sys;
9
10use super::binding::{CTX_MUTEX, clear_current_context, no_current_context, with_bound_context};
11use super::texture_registry::unregister_user_textures_for_context;
12
13/// An imgui context.
14///
15/// A context needs to be created to access most library functions. Due to current Dear ImGui
16/// design choices, at most one active Context can exist at any time. This limitation will likely
17/// be removed in a future Dear ImGui version.
18///
19/// If you need more than one context, you can use suspended contexts. As long as only one context
20/// is active at a time, it's possible to have multiple independent contexts.
21///
22/// # Examples
23///
24/// Creating a new active context:
25/// ```
26/// let ctx = dear_imgui_rs::Context::create();
27/// // ctx is dropped naturally when it goes out of scope, which deactivates and destroys the
28/// // context
29/// ```
30///
31/// Never try to create an active context when another one is active:
32///
33/// ```should_panic
34/// let ctx1 = dear_imgui_rs::Context::create();
35///
36/// let ctx2 = dear_imgui_rs::Context::create(); // PANIC
37/// ```
38#[derive(Debug)]
39pub struct Context {
40    pub(super) raw: *mut sys::ImGuiContext,
41    pub(super) alive: Rc<()>,
42    pub(in crate::context) shared_font_atlas: Option<SharedFontAtlas>,
43    pub(in crate::context) ini_filename: Option<CString>,
44    pub(in crate::context) log_filename: Option<CString>,
45    pub(in crate::context) platform_name: Option<CString>,
46    pub(in crate::context) renderer_name: Option<CString>,
47    // Boxed so the raw PlatformIO user-data pointer remains stable.
48    // Interior mutability and reentrancy guarding live inside ClipboardContext.
49    pub(in crate::context) clipboard_ctx: Box<ClipboardContext>,
50    pub(in crate::context) ui: crate::ui::Ui,
51}
52
53/// A weak token that indicates whether a `Context` is still alive.
54#[derive(Clone, Debug)]
55pub struct ContextAliveToken(Weak<()>);
56
57impl ContextAliveToken {
58    /// Returns true if the originating `Context` has not been dropped.
59    pub fn is_alive(&self) -> bool {
60        self.0.upgrade().is_some()
61    }
62}
63
64impl Context {
65    /// Tries to create a new active Dear ImGui context.
66    ///
67    /// Returns an error if another context is already active or creation fails.
68    pub fn try_create() -> crate::error::ImGuiResult<Context> {
69        Self::try_create_internal(None)
70    }
71
72    /// Tries to create a new active Dear ImGui context with a shared font atlas.
73    pub fn try_create_with_shared_font_atlas(
74        shared_font_atlas: SharedFontAtlas,
75    ) -> crate::error::ImGuiResult<Context> {
76        Self::try_create_internal(Some(shared_font_atlas))
77    }
78
79    /// Creates a new active Dear ImGui context (panics on error).
80    ///
81    /// This aligns with imgui-rs behavior. For fallible creation use `try_create()`.
82    pub fn create() -> Context {
83        Self::try_create().expect("Failed to create Dear ImGui context")
84    }
85
86    /// Creates a new active Dear ImGui context with a shared font atlas (panics on error).
87    pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Context {
88        Self::try_create_with_shared_font_atlas(shared_font_atlas)
89            .expect("Failed to create Dear ImGui context")
90    }
91
92    /// Returns the raw `ImGuiContext*` for FFI integrations.
93    pub fn as_raw(&self) -> *mut sys::ImGuiContext {
94        self.raw
95    }
96
97    /// Returns a token that can be used to check whether this context is still alive.
98    ///
99    /// Useful for extension crates that store raw pointers and need to avoid calling into FFI
100    /// after the owning `Context` has been dropped.
101    pub fn alive_token(&self) -> ContextAliveToken {
102        ContextAliveToken(Rc::downgrade(&self.alive))
103    }
104
105    // removed legacy create_or_panic variants (use create()/try_create())
106
107    pub(super) fn io_ptr(&self, caller: &str) -> *mut sys::ImGuiIO {
108        let io = unsafe { sys::igGetIO_ContextPtr(self.raw) };
109        if io.is_null() {
110            panic!("{caller} requires a valid ImGui context");
111        }
112        io
113    }
114
115    pub(super) fn platform_io_ptr(&self, caller: &str) -> *mut sys::ImGuiPlatformIO {
116        let pio = unsafe { sys::igGetPlatformIO_ContextPtr(self.raw) };
117        if pio.is_null() {
118            panic!("{caller} requires a valid ImGui context");
119        }
120        pio
121    }
122
123    pub(super) fn assert_current_context(&self, caller: &str) {
124        assert!(
125            self.is_current_context(),
126            "{caller} requires this context to be current"
127        );
128    }
129
130    fn try_create_internal(
131        mut shared_font_atlas: Option<SharedFontAtlas>,
132    ) -> crate::error::ImGuiResult<Context> {
133        let _guard = CTX_MUTEX.lock();
134
135        if !no_current_context() {
136            return Err(crate::error::ImGuiError::ContextAlreadyActive);
137        }
138
139        let shared_font_atlas_ptr = match &mut shared_font_atlas {
140            Some(atlas) => atlas.as_ptr_mut(),
141            None => ptr::null_mut(),
142        };
143
144        // Create the actual ImGui context
145        let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
146        if raw.is_null() {
147            return Err(crate::error::ImGuiError::ContextCreation {
148                reason: "ImGui_CreateContext returned null".to_string(),
149            });
150        }
151
152        // Set it as the current context
153        unsafe {
154            sys::igSetCurrentContext(raw);
155        }
156
157        Ok(Context {
158            raw,
159            alive: Rc::new(()),
160            shared_font_atlas,
161            ini_filename: None,
162            log_filename: None,
163            platform_name: None,
164            renderer_name: None,
165            clipboard_ctx: Box::new(ClipboardContext::dummy()),
166            ui: crate::ui::Ui::new(),
167        })
168    }
169
170    /// Returns a mutable reference to this context's IO object.
171    pub fn io_mut(&mut self) -> &mut Io {
172        let _guard = CTX_MUTEX.lock();
173        unsafe {
174            let io_ptr = self.io_ptr("Context::io_mut()");
175            &mut *(io_ptr as *mut Io)
176        }
177    }
178
179    /// Get shared access to this context's IO object.
180    pub fn io(&self) -> &crate::io::Io {
181        let _guard = CTX_MUTEX.lock();
182        unsafe {
183            let io_ptr = self.io_ptr("Context::io()");
184            &*(io_ptr as *const crate::io::Io)
185        }
186    }
187
188    /// Get access to the Style structure
189    pub fn style(&self) -> &crate::style::Style {
190        let _guard = CTX_MUTEX.lock();
191        unsafe {
192            with_bound_context(self.raw, || {
193                let style_ptr = sys::igGetStyle();
194                if style_ptr.is_null() {
195                    panic!("Context::style() requires a valid ImGui context");
196                }
197                &*(style_ptr as *const crate::style::Style)
198            })
199        }
200    }
201
202    /// Get mutable access to the Style structure
203    pub fn style_mut(&mut self) -> &mut crate::style::Style {
204        let _guard = CTX_MUTEX.lock();
205        unsafe {
206            with_bound_context(self.raw, || {
207                let style_ptr = sys::igGetStyle();
208                if style_ptr.is_null() {
209                    panic!("Context::style_mut() requires a valid ImGui context");
210                }
211                &mut *(style_ptr as *mut crate::style::Style)
212            })
213        }
214    }
215
216    pub(super) fn is_current_context(&self) -> bool {
217        let ctx = unsafe { sys::igGetCurrentContext() };
218        self.raw == ctx
219    }
220}
221
222impl Drop for Context {
223    fn drop(&mut self) {
224        let _guard = CTX_MUTEX.lock();
225        unsafe {
226            if !self.raw.is_null() {
227                unregister_user_textures_for_context(self.raw);
228                if self.shared_font_atlas.is_none() {
229                    let io = sys::igGetIO_ContextPtr(self.raw);
230                    if !io.is_null() {
231                        crate::fonts::forget_font_atlas_generation((*io).Fonts);
232                    }
233                }
234                crate::platform_io::clear_typed_callbacks_for_context(self.raw);
235                with_bound_context(self.raw, || {
236                    crate::platform_io::clear_out_param_callbacks_for_current_context();
237                });
238                if sys::igGetCurrentContext() == self.raw {
239                    clear_current_context();
240                }
241                sys::igDestroyContext(self.raw);
242            }
243        }
244    }
245}