Skip to main content

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}