Skip to main content

macroquad_ply/
window.rs

1//! Window and associated to window rendering context related functions.
2
3use crate::{get_context, get_quad_context};
4
5use crate::color::Color;
6
7// miniquad is re-exported for the use in combination with `get_internal_gl`
8pub use miniquad;
9
10pub use miniquad::conf::Conf;
11
12/// Block execution until the next frame.
13#[must_use = "use `next_frame().await` to advance to the next frame"]
14pub fn next_frame() -> crate::exec::FrameFuture {
15    crate::thread_assert::same_thread();
16    crate::exec::FrameFuture::default()
17}
18
19/// Fill window background with solid color.
20/// Note: even when "clear_background" is not called explicitly,
21/// the screen will be cleared at the beginning of the frame.
22pub fn clear_background(color: Color) {
23    let context = get_context();
24
25    context.gl.clear(get_quad_context(), color);
26}
27
28#[doc(hidden)]
29pub fn gl_set_drawcall_buffer_capacity(max_vertices: usize, max_indices: usize) {
30    let context = get_context();
31    context
32        .gl
33        .update_drawcall_capacity(get_quad_context(), max_vertices, max_indices);
34}
35
36pub struct InternalGlContext<'a> {
37    pub quad_context: &'a mut dyn miniquad::RenderingBackend,
38    pub quad_gl: &'a mut crate::quad_gl::QuadGl,
39}
40
41impl<'a> InternalGlContext<'a> {
42    /// Draw all the batched stuff and reset the internal state cache.
43    /// May be helpful for combining macroquad's drawing with raw miniquad/opengl calls.
44    pub fn flush(&mut self) {
45        get_context().perform_render_passes();
46    }
47}
48
49pub unsafe fn get_internal_gl<'a>() -> InternalGlContext<'a> {
50    let context = get_context();
51
52    InternalGlContext {
53        quad_context: get_quad_context(),
54        quad_gl: &mut context.gl,
55    }
56}
57
58pub fn screen_width() -> f32 {
59    let context = get_context();
60    context.screen_width / miniquad::window::dpi_scale()
61}
62
63pub fn screen_height() -> f32 {
64    let context = get_context();
65
66    context.screen_height / miniquad::window::dpi_scale()
67}
68
69pub fn screen_dpi_scale() -> f32 {
70    miniquad::window::dpi_scale()
71}
72
73/// Request the window size to be the given value. This takes DPI into account.
74///
75/// Note that the OS might decide to give a different size. Additionally, the size in macroquad won't be updated until the next `next_frame().await`.
76pub fn request_new_screen_size(width: f32, height: f32) {
77    miniquad::window::set_window_size(
78        (width * miniquad::window::dpi_scale()) as u32,
79        (height * miniquad::window::dpi_scale()) as u32,
80    );
81    // We do not set the context.screen_width and context.screen_height here.
82    // After `set_window_size` is called, EventHandlerFree::resize will be invoked, setting the size correctly.
83    // Because the OS might decide to give a different screen dimension, setting the context.screen_* here would be confusing.
84}
85
86/// Toggle whether the window is fullscreen.
87pub fn set_fullscreen(fullscreen: bool) {
88    miniquad::window::set_fullscreen(fullscreen);
89}
90
91/// With `set_panic_handler` set to a handler code, macroquad will use
92/// `std::panic::catch_unwind` on user code to catch some panics.
93///
94/// Sometimes it is nice to let player send a bug report with a screenshot of an
95/// error. It is way easier to ask for a screenshot than ask to connect to a phone
96/// with adb and post a log.
97///
98/// For this very case "set_panic_handler" exists.
99/// ```ignore
100/// set_panic_handler(|msg, backtrace| async move {
101///     loop {
102///         clear_background(RED);
103///         ui::root_ui().label(None, &msg);
104///         for line in backtrace.split('\n') {
105///             root_ui().label(None, line);
106///         }
107///         next_frame().await;
108///      }
109/// });
110/// ```
111///
112/// `set_panic_handler` acts as a second app entry-point, that will be used
113/// after a panic in user code will happen. Macroquad will also try to catch some OS
114/// panics, but not all of them - some compatibility bugs may end up crashing the app.
115///
116/// Without `set_panic_handler` macroquad will not use `catch_unwind` at all,
117/// therefore `panic_handler` is completely optional.
118/// NOTE: only with "backtrace" macroquad feature `backtrace` string will contain an
119/// actual backtrace. Otherwise only panic location and message will be available.
120/// NOTE: on android, even with "backtrace" nice backtrace is available only if the game is compiled with sdk >= 21.
121/// To use sdk >= 21 add "min_sdk_version = 21" to Cargo.toml
122pub fn set_panic_handler<T, F>(future: F)
123where
124    T: std::future::Future<Output = ()> + 'static,
125    F: Fn(String, String) -> T + Send + Sync + 'static,
126{
127    std::panic::set_hook(Box::new(move |info| {
128        let message = format!("{info:?}");
129        #[cfg(feature = "backtrace")]
130        let backtrace_string = format!("{:?}", backtrace::Backtrace::new());
131        #[cfg(not(feature = "backtrace"))]
132        let backtrace_string = format!("Macroquad compiled without \"backtrace\" feature");
133        crate::logging::error!("{}", message);
134        crate::logging::error!("{}", backtrace_string);
135
136        crate::get_context().recovery_future = Some(Box::pin(future(message, backtrace_string)));
137    }));
138
139    crate::get_context().unwind = true;
140}