dear_imgui_rs/context/
suspended.rs1use std::panic::{self, AssertUnwindSafe};
2use std::ptr;
3
4use crate::clipboard::ClipboardContext;
5use crate::fonts::SharedFontAtlas;
6use crate::sys;
7
8use super::Context;
9use super::attachment::AttachmentRegistry;
10use super::binding::{
11 CTX_MUTEX, ContextBinding, ContextId, ContextState, clear_current_context, no_current_context,
12 set_current_context,
13};
14use super::frame::FrameLifecycleState;
15use super::snapshot_hub::SnapshotHub;
16use super::texture_registry::ManagedTextureRegistry;
17
18impl Context {
19 pub fn suspend(self) -> SuspendedContext {
21 let _guard = CTX_MUTEX.lock();
22 assert!(
23 self.is_current_context(),
24 "context to be suspended is not the active context"
25 );
26 assert_ne!(
27 self.frame_lifecycle_state_unlocked(),
28 FrameLifecycleState::InFrame,
29 "cannot suspend a context while a Dear ImGui frame is open"
30 );
31 clear_current_context();
32 SuspendedContext(self)
33 }
34}
35
36#[derive(Debug)]
40pub struct SuspendedContext(pub(super) Context);
41
42impl SuspendedContext {
43 pub fn id(&self) -> ContextId {
45 self.0.id()
46 }
47
48 pub fn try_with_active<T, E>(
58 &mut self,
59 f: impl FnOnce(&mut Context) -> Result<T, E>,
60 ) -> Result<T, E> {
61 let binding = self.0.binding();
62 binding.with_bound_context(|| {
63 let result = panic::catch_unwind(AssertUnwindSafe(|| f(&mut self.0)));
64
65 match result {
66 Ok(Ok(value)) => {
67 if self.0.end_frame_for_teardown_unlocked() {
68 panic!(
69 "SuspendedContext::try_with_active(): closure returned Ok while a Dear ImGui frame was still open"
70 );
71 }
72 Ok(value)
73 }
74 Ok(Err(error)) => {
75 self.0.end_frame_for_teardown_unlocked();
76 Err(error)
77 }
78 Err(payload) => {
79 let _ = panic::catch_unwind(AssertUnwindSafe(|| {
81 self.0.end_frame_for_teardown_unlocked();
82 }));
83 panic::resume_unwind(payload)
84 }
85 }
86 })
87 }
88
89 pub fn try_create() -> crate::error::ImGuiResult<Self> {
91 Self::try_create_internal(None)
92 }
93
94 pub fn try_create_with_shared_font_atlas(
102 shared_font_atlas: SharedFontAtlas,
103 ) -> crate::error::ImGuiResult<Self> {
104 Self::try_create_internal(Some(shared_font_atlas))
105 }
106
107 pub fn create() -> Self {
109 Self::try_create().expect("Failed to create Dear ImGui context")
110 }
111
112 pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Self {
118 Self::try_create_with_shared_font_atlas(shared_font_atlas)
119 .expect("Failed to create Dear ImGui context")
120 }
121
122 fn try_create_internal(
125 shared_font_atlas: Option<SharedFontAtlas>,
126 ) -> crate::error::ImGuiResult<Self> {
127 let _guard = CTX_MUTEX.lock();
128 let previous_context = unsafe { sys::igGetCurrentContext() };
129
130 let shared_font_atlas_ptr = match &shared_font_atlas {
131 Some(atlas) => atlas.as_ptr(),
132 None => ptr::null_mut(),
133 };
134 crate::fonts::validate_font_atlas_context_registration(shared_font_atlas_ptr)?;
135
136 let id =
137 ContextId::allocate().ok_or_else(|| crate::error::ImGuiError::ContextCreation {
138 reason: "process Context identity space is exhausted".to_string(),
139 })?;
140
141 let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
142 if raw.is_null() {
143 set_current_context(previous_context);
144 return Err(crate::error::ImGuiError::ContextCreation {
145 reason: "ImGui_CreateContext returned null".to_string(),
146 });
147 }
148
149 unsafe {
150 let io = sys::igGetIO_ContextPtr(raw);
151 assert!(
152 !io.is_null(),
153 "new ImGui context returned a null IO pointer"
154 );
155 crate::fonts::register_font_atlas_context((*io).Fonts, raw);
156 }
157
158 let state = ContextState::new(id, raw);
159 let texture_registry = ManagedTextureRegistry::new(id);
160 let ui = crate::ui::Ui::new(raw, ContextBinding::new(&state), texture_registry.clone());
161
162 let ctx = Context {
163 raw,
164 state,
165 attachments: AttachmentRegistry::default(),
166 snapshot_hub: SnapshotHub::new(id),
167 texture_registry,
168 shared_font_atlas,
169 ini_filename: None,
170 log_filename: None,
171 platform_name: None,
172 renderer_name: None,
173 clipboard_ctx: Box::new(ClipboardContext::dummy()),
174 ui,
175 };
176
177 if previous_context.is_null() {
178 clear_current_context();
179 } else {
180 set_current_context(previous_context);
181 }
182
183 Ok(SuspendedContext(ctx))
184 }
185
186 pub fn activate(self) -> Result<Context, SuspendedContext> {
191 let _guard = CTX_MUTEX.lock();
192 if no_current_context() {
193 set_current_context(self.0.raw);
194 Ok(self.0)
195 } else {
196 Err(self)
197 }
198 }
199}