Skip to main content

cotis_raylib/
renderer.rs

1//! Raylib window renderer and handle types.
2//!
3//! [`RaylibRender`] owns the raylib window, drawing thread, font table, and image texture cache.
4//! It implements [`CotisRenderer`](cotis::renders::CotisRenderer) in [`render_trait`] for
5//! [`crate::drawables::RaylibDrawable`] command streams.
6//!
7//! Application code usually constructs a [`RaylibRender`] via [`RaylibRender::auto_start`] or
8//! [`RaylibRender::new`], then passes it to [`CotisApp`](cotis::cotis_app::CotisApp).
9
10use crate::fonts::ScalableFont;
11use cotis::renders::RenderCompatibleWith;
12use raylib::color::Color;
13use raylib::consts::TextureFilter;
14use raylib::error::RaylibError;
15use raylib::prelude::Texture2D;
16use raylib::{RaylibBuilder, RaylibHandle, RaylibThread};
17use std::collections::HashMap;
18use std::ops::{Deref, DerefMut};
19use std::sync::{Arc, Mutex, MutexGuard};
20
21pub mod render_trait;
22
23/// Shared, mutex-protected raylib handle used across threads for input polling.
24pub type SharedRaylibHandle = Arc<Mutex<RayRenderHandle>>;
25
26/// Mutex-guarded wrapper around [`RaylibHandle`].
27///
28/// Implements [`Deref`] and [`DerefMut`] to the inner handle so raylib methods can be called
29/// directly on `&RayRenderHandle`. Input traits from `cotis-utils` are implemented on this type in
30/// [`crate::interactivity`].
31pub struct RayRenderHandle(pub(crate) RaylibHandle);
32
33impl Deref for RayRenderHandle {
34    type Target = RaylibHandle;
35    fn deref(&self) -> &Self::Target {
36        &self.0
37    }
38}
39impl DerefMut for RayRenderHandle {
40    fn deref_mut(&mut self) -> &mut Self::Target {
41        &mut self.0
42    }
43}
44
45/// Primary raylib-backed renderer for Cotis applications.
46///
47/// Owns the window handle (behind a mutex), the raylib drawing thread, loaded fonts, and a
48/// path-keyed texture cache. Implements [`CotisRenderer`](cotis::renders::CotisRenderer) for
49/// [`crate::drawables::RaylibDrawable`] streams and Cotis context traits in [`crate::cotis`].
50///
51/// # Construction
52///
53/// - [`RaylibRender::auto_start`] — gray transparent background, 1000×1000 resizable window.
54/// - [`RaylibRender::new`] — custom clear color, same window defaults.
55/// - [`RaylibRender::new_custom`] — full control via [`RaylibBuilder`].
56pub struct RaylibRender {
57    rl: Arc<Mutex<RayRenderHandle>>,
58    thread: RaylibThread,
59    fonts: Arc<Mutex<HashMap<usize, ScalableFont>>>,
60    font_max_id: usize,
61    image_cache: HashMap<String, Arc<Texture2D>>,
62    /// Clear color used at the start of each frame.
63    pub background_color: Color,
64}
65
66impl<Manager> RenderCompatibleWith<Manager> for RaylibRender {
67    fn init(&mut self, _layout_manager: &mut Manager) {}
68}
69
70impl RaylibRender {
71    /// Creates a renderer with a gray transparent background and a default 1000×1000 resizable window.
72    pub fn auto_start() -> Self {
73        Self::new(Color::new(100, 100, 100, 0))
74    }
75
76    /// Creates a renderer with the given background clear color and a default 1000×1000 resizable window.
77    pub fn new(background_color: Color) -> Self {
78        let (rl, thread) = raylib::init()
79            .resizable()
80            .size(1000, 1000)
81            .title("Raylib")
82            .build();
83        Self {
84            rl: Arc::new(Mutex::new(RayRenderHandle(rl))),
85            thread,
86            fonts: Default::default(),
87            font_max_id: 0,
88            image_cache: Default::default(),
89            background_color,
90        }
91    }
92
93    /// Creates a renderer from a custom [`RaylibBuilder`].
94    pub fn new_custom(raylib_builder: RaylibBuilder, background_color: Color) -> Self {
95        let (rl, thread) = raylib_builder.build();
96        Self {
97            rl: Arc::new(Mutex::new(RayRenderHandle(rl))),
98            thread,
99            fonts: Default::default(),
100            font_max_id: 0,
101            image_cache: Default::default(),
102            background_color,
103        }
104    }
105
106    /// Locks and returns the raylib handle.
107    ///
108    /// # Panics
109    ///
110    /// Panics if the mutex is poisoned.
111    pub fn get_handle(&mut self) -> MutexGuard<'_, RayRenderHandle> {
112        self.rl.lock().unwrap()
113    }
114
115    /// Locks and returns the raylib handle (immutable receiver).
116    ///
117    /// # Panics
118    ///
119    /// Panics if the mutex is poisoned.
120    pub fn lock_handle(&self) -> MutexGuard<'_, RayRenderHandle> {
121        self.rl.lock().unwrap()
122    }
123
124    /// Returns the raylib drawing thread.
125    ///
126    /// Must be passed to drawing calls on the thread that created the window.
127    pub fn get_thread(&self) -> &RaylibThread {
128        &self.thread
129    }
130
131    /// Returns the raylib drawing thread.
132    ///
133    /// Alias for [`Self::get_thread`].
134    pub fn thread(&self) -> &RaylibThread {
135        &self.thread
136    }
137
138    /// Returns the locked handle and drawing thread together for low-level drawing.
139    ///
140    /// # Panics
141    ///
142    /// Panics if the mutex is poisoned.
143    pub fn get_raylib(&mut self) -> (MutexGuard<'_, RayRenderHandle>, &mut RaylibThread) {
144        (self.rl.lock().unwrap(), &mut self.thread)
145    }
146
147    /// Returns whether the window close flag is set.
148    ///
149    /// # Panics
150    ///
151    /// Panics if the mutex is poisoned.
152    pub fn window_should_close(&self) -> bool {
153        self.rl.lock().unwrap().window_should_close()
154    }
155
156    /// Loads a font at size 16 and registers it in the font table.
157    ///
158    /// Returns a font ID to store in [`TextConfig::font_id`](cotis_defaults::element_configs::text_config::TextConfig::font_id).
159    /// IDs are assigned in load order starting at `0` and are never reused.
160    ///
161    /// # Errors
162    ///
163    /// Returns [`RaylibError`] if raylib cannot load the font file.
164    pub fn load_font_and_keep(&mut self, file_name: &str) -> Result<usize, RaylibError> {
165        self.load_font_ex_and_keep(file_name, 16, None, None)
166    }
167
168    pub(crate) fn load_new_font_sizes(&mut self) {
169        let mut fonts = self.fonts.lock().unwrap();
170        for font in fonts.values_mut() {
171            font.load_queued_fonts(&mut self.rl.lock().unwrap(), &self.thread)
172                .unwrap();
173        }
174    }
175
176    /// Loads a font with custom size, optional character subset, and texture filter.
177    ///
178    /// Returns a font ID (0-based, monotonic) for use in [`TextConfig::font_id`](cotis_defaults::element_configs::text_config::TextConfig::font_id).
179    /// Additional pixel sizes requested during drawing are queued and loaded at the start of the
180    /// next frame.
181    ///
182    /// # Errors
183    ///
184    /// Returns [`RaylibError`] if raylib cannot load the font file.
185    pub fn load_font_ex_and_keep(
186        &mut self,
187        file_name: &str,
188        font_size: i32,
189        chars: Option<&str>,
190        filter: Option<TextureFilter>,
191    ) -> Result<usize, RaylibError> {
192        let mut rl = self.rl.lock().unwrap();
193        self.fonts.lock().unwrap().insert(
194            self.font_max_id,
195            ScalableFont::new(
196                file_name.to_string(),
197                chars.map(|s| s.to_string()),
198                filter,
199                &mut rl,
200                &self.thread,
201                font_size as usize,
202            )?,
203        );
204        self.font_max_id += 1;
205        Ok(self.font_max_id - 1)
206    }
207
208    /// Returns the elapsed time between the previous and current frame, in seconds.
209    ///
210    /// # Panics
211    ///
212    /// Panics if the mutex is poisoned.
213    pub fn delta_time(&self) -> f32 {
214        self.rl.lock().unwrap().get_frame_time()
215    }
216
217    /// Returns a clone of the shared raylib handle for cross-thread input polling.
218    ///
219    /// Pair with [`Self::thread`] when calling raylib drawing APIs from the owning thread.
220    pub fn get_shared_handle(&self) -> SharedRaylibHandle {
221        self.rl.clone()
222    }
223
224    /// Returns a clone of the shared raylib handle.
225    ///
226    /// Alias for [`Self::get_shared_handle`].
227    pub fn shared_handle(&self) -> SharedRaylibHandle {
228        self.get_shared_handle()
229    }
230
231    /// Returns the path-keyed texture cache populated by [`translate_generic_image`](RaylibRender::translate_generic_image).
232    pub fn image_cache(&self) -> &HashMap<String, Arc<Texture2D>> {
233        &self.image_cache
234    }
235
236    /// Returns a mutable reference to the texture cache for manual preload or inspection.
237    pub fn image_cache_mut(&mut self) -> &mut HashMap<String, Arc<Texture2D>> {
238        &mut self.image_cache
239    }
240
241    /// Returns the shared font table used during drawing and text measurement.
242    pub fn fonts(&self) -> Arc<Mutex<HashMap<usize, ScalableFont>>> {
243        self.fonts.clone()
244    }
245}