duat_core/context/mod.rs
1//! Access the state of Duat.
2use std::{
3 any::TypeId,
4 sync::{
5 atomic::{AtomicUsize, Ordering::Relaxed},
6 mpsc,
7 },
8 time::{Duration, Instant},
9};
10
11use crossterm::event::KeyEvent;
12
13pub use self::{global::*, handles::*, log::*};
14use crate::{
15 buffer::Buffer,
16 data::{Pass, RwData},
17 session::{DuatEvent, UiMouseEvent},
18 ui::{Area, Node, Widget},
19};
20
21pub mod cache;
22mod handles;
23mod log;
24
25mod global {
26 use std::{
27 path::{Path, PathBuf},
28 sync::{
29 LazyLock, Mutex, OnceLock,
30 atomic::{AtomicBool, AtomicUsize, Ordering},
31 mpsc,
32 },
33 };
34
35 use super::{CurWidgetNode, DynBuffer};
36 use crate::{
37 context::{DuatReceiver, DuatSender, Handle},
38 data::{Pass, RwData},
39 session::DuatEvent,
40 ui::{Widget, Window, Windows},
41 };
42
43 static WINDOWS: OnceLock<&Windows> = OnceLock::new();
44 static MODE_NAME: LazyLock<RwData<&str>> = LazyLock::new(RwData::default);
45 static CUR_DIR: LazyLock<Mutex<PathBuf>> =
46 LazyLock::new(|| Mutex::new(std::env::current_dir().unwrap()));
47 static NEW_EVENT_COUNT: AtomicUsize = AtomicUsize::new(0);
48 static WILL_UNLOAD: AtomicBool = AtomicBool::new(false);
49 static DUAT_CHANNEL: LazyLock<Mutex<(DuatSender, Option<DuatReceiver>)>> =
50 LazyLock::new(|| {
51 let (sender, receiver) = mpsc::channel();
52 let sender = DuatSender(sender, &NEW_EVENT_COUNT);
53 let receiver = DuatReceiver(receiver, &NEW_EVENT_COUNT);
54 Mutex::new((sender, Some(receiver)))
55 });
56
57 /// Queues a function to be done on the main thread with a
58 /// [`Pass`].
59 ///
60 /// You can use this whenever you don't have access to a `Pass`,
61 /// in order to execute an action on the main thread, gaining
62 /// access to Duat's global state within that function.
63 ///
64 /// Note that, since this can be called from any thread, it needs
65 /// to be [`Send`] and `'static`.
66 pub fn queue(f: impl FnOnce(&mut Pass) + Send + 'static) {
67 sender().send(DuatEvent::QueuedFunction(Box::new(f)));
68 }
69
70 ////////// Internal setters meant to be called internally
71
72 /// Attempts to set the current [`Handle`].
73 ///
74 /// Fails if said [`Handle`] was already deleted.
75 #[track_caller]
76 pub(crate) fn set_current_node(pa: &mut Pass, node: crate::ui::Node) {
77 if let Err(err) = windows().set_current_node(pa, node) {
78 super::warn!("{err}");
79 }
80 }
81
82 /// Sets the [`Window`]s for Duat.
83 pub(crate) fn set_windows(windows: Windows) {
84 if WINDOWS.set(Box::leak(Box::new(windows))).is_err() {
85 panic!("Setup ran twice");
86 }
87 }
88
89 /// Wether Duat has received new events that need to be handled.
90 ///
91 /// Events can be anything, from a [key press], a [refocus], or
92 /// even a [queued function].
93 ///
94 /// You can use this function to set up "timeouts". That is, if
95 /// the thing you're trying to do is on the main thread and is
96 /// taking way too long, you can check for this function and stop
97 /// what you're doing if there's any new events that Duat hasn't
98 /// handled.
99 ///
100 /// [key press]: crate::mode::KeyEvent
101 /// [refocus]: crate::hook::FocusedOnDuat
102 /// [queued function]: queue
103 pub fn has_unhandled_events() -> bool {
104 NEW_EVENT_COUNT.load(Ordering::SeqCst) > 0
105 }
106
107 /// The only receiver of [`DuatEvent`]s.
108 pub(crate) fn receiver() -> DuatReceiver {
109 DUAT_CHANNEL.lock().unwrap().1.take().unwrap()
110 }
111
112 ////////// Widget Handle getters
113
114 /// Returns a "fixed" [`Handle`] for the currently active
115 /// [`Buffer`].
116 ///
117 /// This `Handle` will always point to the same `Buffer`,
118 /// even when it is not active. If you want a `Handle` that
119 /// always points to the current Buffer, see [`dynamic_buffer`].
120 ///
121 /// [`Buffer`]: crate::buffer::Buffer
122 pub fn current_buffer(pa: &Pass) -> Handle {
123 windows().current_buffer(pa).read(pa).clone()
124 }
125
126 /// Returns a "dynamic" [`Handle`] for the active [`Buffer`].
127 ///
128 /// This `Handle` will change to point to the current `Buffer`,
129 /// whenever the user swicthes which `Buffer` is active. If you
130 /// want a `Handle` that will stay on the current `Buffer`, see
131 /// [`current_buffer`].
132 ///
133 /// [`Buffer`]: crate::buffer::Buffer
134 pub fn dynamic_buffer(pa: &Pass) -> DynBuffer {
135 let dyn_buffer = windows().current_buffer(pa).clone();
136 let cur_buffer = RwData::new(dyn_buffer.read(pa).clone());
137 DynBuffer {
138 cur_buffer: dyn_buffer,
139 saved_buffer: cur_buffer,
140 }
141 }
142
143 /// Returns a [`Handle`] for a [`Buffer`] with the given name.
144 ///
145 /// [`Buffer`]: crate::buffer::Buffer
146 pub fn get_buffer(pa: &Pass, name: impl ToString) -> Option<Handle> {
147 let (.., handle) = windows().named_buffer_entry(pa, &name.to_string())?;
148 Some(handle)
149 }
150
151 /// Returns a [`Handle`] for a [`Buffer`] with the given [`Path`].
152 ///
153 /// [`Buffer`]: crate::buffer::Buffer
154 pub fn get_buffer_by_path(pa: &Pass, path: &Path) -> Option<Handle> {
155 let (.., handle) = windows().path_buffer_entry(pa, path)?;
156 Some(handle)
157 }
158
159 /// Get the "most appropriate" [`Handle`] for a given [`Widget`].
160 ///
161 /// The "most appropriate" `Handle` is determined like this:
162 ///
163 /// 1. Try to look for those pushed around the current [`Buffer`].
164 /// 2. Try to look for those pushed around the current [`Window`].
165 /// 3. Try to look for those pushed around other [`Window`]s.
166 ///
167 /// [`Buffer`]: crate::buffer::Buffer
168 pub fn handle_of<W: Widget>(pa: &Pass) -> Option<Handle<W>> {
169 windows()
170 .node_of::<W>(pa)
171 .ok()
172 .and_then(|node| node.try_downcast())
173 }
174
175 /// Returns the current active [`Handle`].
176 ///
177 /// Unlike [`current_buffer`], this function will return a
178 /// [`Handle<dyn Widget>`], which means it could be any
179 /// [`Widget`], not just a [`Buffer`].
180 ///
181 /// [`Buffer`]: crate::buffer::Buffer
182 pub fn current_widget(pa: &Pass) -> &Handle<dyn Widget> {
183 windows().current_widget(pa).read(pa).handle()
184 }
185
186 /// The [`CurWidgetNode`]
187 pub(crate) fn current_widget_node(pa: &Pass) -> CurWidgetNode {
188 CurWidgetNode(windows().current_widget(pa).clone())
189 }
190
191 ////////// Other getters
192
193 /// The [`Window`]s of Duat.
194 ///
195 /// This struct gives you reading access to every `Window` in
196 /// Duat with [`Windows::get`], you also get access to every
197 /// [`Handle<dyn Widget>`], including every `Handle<Buffer>`,
198 /// through the [`Windows::handles`] and [`Window::buffers`]
199 /// function.
200 pub fn windows() -> &'static Windows {
201 WINDOWS.get().unwrap()
202 }
203
204 /// Try to get the [`Windows`].
205 pub(crate) fn get_windows() -> Option<&'static Windows> {
206 WINDOWS.get().cloned()
207 }
208
209 /// A list of all open [`Buffer`]'s [`Handle`]s.
210 ///
211 /// [`Buffer`]: crate::buffer::Buffer
212 pub fn buffers(pa: &Pass) -> Vec<Handle> {
213 windows().buffers(pa)
214 }
215
216 /// The current [`Window`].
217 ///
218 /// You can iterate through all [`Handle<Buffer>`]s and
219 /// `Handle<dyn Widget>` with [`Window::buffers`] and
220 /// [`Window::handles`] respectively.
221 ///
222 /// If you wish to access other `Window`s, you can use
223 /// `context::windows().get(pa, n)` to get the `n`th `Window`.
224 /// The current window number can be found with
225 /// [`context::current_win_index`]
226 ///
227 /// [`context::current_win_index`]: current_win_index
228 pub fn current_window(pa: &Pass) -> &Window {
229 let win = current_win_index(pa);
230 WINDOWS.get().unwrap().get(pa, win).unwrap()
231 }
232
233 /// The index of the currently active window.
234 pub fn current_win_index(pa: &Pass) -> usize {
235 windows().current_window(pa)
236 }
237
238 /// The name of the current [`Mode`].
239 ///
240 /// [`Mode`]: crate::mode::Mode
241 pub fn mode_name() -> RwData<&'static str> {
242 MODE_NAME.clone()
243 }
244
245 /// The current directory.
246 pub fn current_dir() -> PathBuf {
247 CUR_DIR.lock().unwrap().clone()
248 }
249
250 /// Wether Duat is in the process of unloading.
251 ///
252 /// You should make use of this function in order to halt spawned
253 /// threads, as Duat will stall untill all spawned threads are
254 /// dropped or joined.
255 ///
256 /// This function will be set to true right before the
257 /// [`ConfigUnloaded`] hook is triggered.
258 ///
259 /// [`ConfigUnloaded`]: crate::hook::ConfigUnloaded
260 pub fn will_unload() -> bool {
261 WILL_UNLOAD.load(Ordering::Relaxed)
262 }
263 /// Declares that Duat will reload or quit.
264 pub(crate) fn declare_will_unload() {
265 WILL_UNLOAD.store(true, Ordering::Relaxed)
266 }
267
268 /// A [`mpsc::Sender`] for [`DuatEvent`]s in the main loop.
269 pub(crate) fn sender() -> DuatSender {
270 DUAT_CHANNEL.lock().unwrap().0.clone()
271 }
272}
273
274/// A sender of [`DuatEvent`]s.
275#[doc(hidden)]
276#[derive(Clone, Debug)]
277pub struct DuatSender(mpsc::Sender<DuatEvent>, &'static AtomicUsize);
278
279impl DuatSender {
280 /// Sends a [`KeyEvent`].
281 pub fn send_key(&self, key: KeyEvent) {
282 self.1.fetch_add(1, Relaxed);
283 _ = self.0.send(DuatEvent::KeyEventSent(key));
284 }
285
286 /// Sends an [`UiMouseEvent`].
287 pub fn send_mouse(&self, mouse: UiMouseEvent) {
288 self.1.fetch_add(1, Relaxed);
289 _ = self.0.send(DuatEvent::MouseEventSent(mouse));
290 }
291
292 /// Sends a notice that the app has resized.
293 pub fn send_resize(&self) {
294 self.1.fetch_add(1, Relaxed);
295 _ = self.0.send(DuatEvent::Resized);
296 }
297
298 /// Triggers the [`FocusedOnDuat`] [`hook`].
299 ///
300 /// [`FocusedOnDuat`]: crate::hook::FocusedOnDuat
301 /// [`hook`]: crate::hook
302 pub fn send_focused(&self) {
303 self.1.fetch_add(1, Relaxed);
304 _ = self.0.send(DuatEvent::FocusedOnDuat);
305 }
306
307 /// Triggers the [`UnfocusedFromDuat`] [`hook`].
308 ///
309 /// [`UnfocusedFromDuat`]: crate::hook::UnfocusedFromDuat
310 /// [`hook`]: crate::hook
311 pub fn send_unfocused(&self) {
312 self.1.fetch_add(1, Relaxed);
313 _ = self.0.send(DuatEvent::UnfocusedFromDuat);
314 }
315
316 /// Sends any [`DuatEvent`].
317 #[track_caller]
318 pub(crate) fn send(&self, event: DuatEvent) {
319 self.1.fetch_add(1, Relaxed);
320 _ = self.0.send(event);
321 }
322}
323
324/// A receiver for [`DuatEvent`]s.
325#[doc(hidden)]
326pub struct DuatReceiver(mpsc::Receiver<DuatEvent>, &'static AtomicUsize);
327
328impl DuatReceiver {
329 /// Receive a [`DuatEvent`].
330 ///
331 /// If less than 5 milliseconds have passed since the [`Instant`]
332 /// for chained events, then it will attempt to receive until said
333 /// timeout.
334 ///
335 /// Otherwise, it will wait until an event shows up.
336 pub(crate) fn recv(&self, chain_instant: &mut Option<Instant>) -> Option<DuatEvent> {
337 if let Some(instant) = chain_instant {
338 if let Some(duration) = Duration::from_micros(500).checked_sub(instant.elapsed()) {
339 self.0
340 .recv_timeout(duration)
341 .inspect(|_| _ = self.1.fetch_sub(1, Relaxed))
342 .ok()
343 } else {
344 *chain_instant = None;
345 None
346 }
347 } else {
348 self.0
349 .recv()
350 .inspect(|_| _ = self.1.fetch_sub(1, Relaxed))
351 .ok()
352 }
353 }
354}
355
356/// A "dynamic" [`Handle`] wrapper for [`Buffer`]s.
357///
358/// This `Handle` wrapper will always point to the presently active
359/// `Buffer`. It can also detect when that `Buffer` has been changed
360/// or when another `Buffer` becomes the active `Buffer`.
361pub struct DynBuffer {
362 cur_buffer: RwData<Handle>,
363 saved_buffer: RwData<Handle>,
364}
365
366impl DynBuffer {
367 /// Wether the [`Buffer`] pointed to has changed or swapped with
368 /// another.
369 pub fn has_changed(&self, pa: &Pass) -> bool {
370 if self.cur_buffer.has_changed() {
371 true
372 } else {
373 self.saved_buffer.read(pa).has_changed(pa)
374 }
375 }
376
377 /// Swaps the [`DynBuffer`] to the currently active [`Buffer`].
378 pub fn swap_to_current(&mut self) {
379 // SAFETY: Since this struct uses deep Cloning, no mutable
380 // references to the RwData exist.
381 let pa = unsafe { &mut Pass::new() };
382 if self.cur_buffer.has_changed() {
383 *self.saved_buffer.write(pa) = self.cur_buffer.read(pa).clone();
384 }
385 }
386
387 /// Reads the presently active [`Buffer`].
388 pub fn read<'a>(&'a mut self, pa: &'a Pass) -> &'a Buffer {
389 self.saved_buffer.read(pa).read(pa)
390 }
391
392 /// The [`Handle<Buffer>`] currently being pointed to.
393 pub fn handle(&self) -> &Handle {
394 // SAFETY: Since this struct uses deep Cloning, no mutable
395 // references to the RwData exist.
396 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
397 self.saved_buffer.read(INTERNAL_PASS)
398 }
399
400 /// Simulates a [`read`] without actually reading.
401 ///
402 /// This is useful if you want to tell Duat that you don't want
403 /// [`has_changed`] to return `true`, but you don't have a
404 /// [`Pass`] available to [`read`] the value.
405 ///
406 /// This assumes that you don't care about the active [`Buffer`]
407 /// possibly being swapped.
408 ///
409 /// [`read`]: Self::read
410 /// [`has_changed`]: Self::has_changed
411 pub fn declare_as_read(&self) {
412 // SAFETY: Since this struct uses deep Cloning, no mutable
413 // references to the RwData exist.
414 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
415 self.cur_buffer.declare_as_read();
416 self.saved_buffer.read(INTERNAL_PASS).declare_as_read();
417 }
418
419 ////////// Writing functions
420
421 /// Reads the presently active [`Buffer`].
422 pub fn write<'a>(&'a self, pa: &'a mut Pass) -> &'a mut Buffer {
423 // SAFETY: Because I already got a &mut Pass, the RwData can't be
424 // accessed anyways.
425 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
426
427 self.saved_buffer.read(INTERNAL_PASS).write(pa)
428 }
429
430 /// Writes to the [`Buffer`] and [`Area`], making use of a
431 /// [`Pass`].
432 ///
433 /// [`Area`]: crate::ui::Area
434 pub fn write_with_area<'a>(&'a self, pa: &'a mut Pass) -> (&'a mut Buffer, &'a mut Area) {
435 // SAFETY: Because I already got a &mut Pass, the RwData can't be
436 // accessed anyways.
437 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
438
439 self.saved_buffer.read(INTERNAL_PASS).write_with_area(pa)
440 }
441
442 /// Simulates a [`write`] without actually writing.
443 ///
444 /// This is useful if you want to tell Duat that you want
445 /// [`has_changed`] to return `true`, but you don't have a
446 /// [`Pass`] available to `write` the value with.
447 ///
448 /// [`write`]: Self::write
449 /// [`has_changed`]: Self::has_changed
450 pub fn declare_written(&self) {
451 // SAFETY: Since this struct uses deep Cloning, no other references to
452 // the RwData exist.
453 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
454 self.cur_buffer.read(INTERNAL_PASS).declare_written();
455 }
456}
457
458impl Clone for DynBuffer {
459 /// Returns a _deep cloned_ duplicate of the value.
460 ///
461 /// In this case, what this means is that the clone and `self`
462 /// will have different internal pointers for the current
463 /// [`Buffer`]. So if, for example, you call
464 /// [`DynBuffer::swap_to_current`] on `self`, that will switch
465 /// `self` to point to the current `Buffer`, but the same will not
466 /// be done in the clone.
467 fn clone(&self) -> Self {
468 // SAFETY: Because I already got a &mut Pass, the RwData can't be
469 // accessed anyways.
470 static INTERNAL_PASS: &Pass = unsafe { &Pass::new() };
471
472 Self {
473 cur_buffer: RwData::new(self.cur_buffer.read(INTERNAL_PASS).clone()),
474 saved_buffer: self.saved_buffer.clone(),
475 }
476 }
477}
478
479/// The current [`Widget`].
480pub(crate) struct CurWidgetNode(RwData<Node>);
481
482impl CurWidgetNode {
483 /// The [`Widget`]'s [`TypeId`].
484 pub fn type_id(&self, pa: &Pass) -> TypeId {
485 self.0.read(pa).widget().type_id()
486 }
487
488 /// Reads the [`Widget`] and its [`Area`].
489 pub fn _read<R>(&self, pa: &Pass, f: impl FnOnce(&dyn Widget, &Area) -> R) -> R {
490 let node = self.0.read(pa);
491 f(node.handle().read(pa), node.area().read(pa))
492 }
493
494 /// Reads the [`Widget`] as `W` and its [`Area`].
495 pub fn _read_as<W: Widget, R>(&self, pa: &Pass, f: impl FnOnce(&W, &Area) -> R) -> Option<R> {
496 let node = self.0.read(pa);
497 Some(f(node.read_as(pa)?, node.area().read(pa)))
498 }
499
500 /// Mutates the [`RwData<dyn Widget>`], its [`Area`], and related
501 /// [`Widget`]s.
502 pub(crate) fn mutate_data<R>(&self, pa: &Pass, f: impl FnOnce(&Handle<dyn Widget>) -> R) -> R {
503 f(self.0.read(pa).handle())
504 }
505
506 /// The inner [`Node`].
507 pub(crate) fn node(&self, pa: &Pass) -> Node {
508 self.0.read(pa).clone()
509 }
510}