oxygengine_backend_desktop/
app.rs

1use crate::resource::DesktopAppEvents;
2use core::app::{App, AppLifeCycle, AppParams, BackendAppRunner};
3use glutin::{
4    dpi::LogicalSize,
5    event::{Event, WindowEvent},
6    event_loop::{ControlFlow, EventLoop},
7    platform::run_return::EventLoopExtRunReturn,
8    window::{Fullscreen, Window, WindowBuilder},
9    ContextBuilder, ContextWrapper, PossiblyCurrent,
10};
11use std::{
12    cell::RefCell,
13    collections::HashMap,
14    env::{current_exe, set_current_dir, var},
15    rc::Rc,
16    sync::Arc,
17};
18
19pub type DesktopContextWrapper = ContextWrapper<PossiblyCurrent, Window>;
20
21pub fn desktop_app_params() -> AppParams {
22    let mut result = HashMap::default();
23    let mut key = None;
24    for arg in std::env::args() {
25        if let Some(value) = arg.strip_prefix("--") {
26            key = Some(value.to_owned());
27            result.insert(value.to_owned(), Default::default());
28        } else if let Some(value) = arg.strip_prefix('-') {
29            key = Some(value.to_owned());
30            result.insert(value.to_owned(), Default::default());
31        } else if let Some(value) = arg.strip_prefix('/') {
32            key = Some(value.to_owned());
33            result.insert(value.to_owned(), Default::default());
34        } else if let Some(key) = key.take() {
35            result.insert(key, arg.to_owned());
36        }
37    }
38    AppParams::new(result)
39}
40
41pub struct DesktopAppConfig {
42    pub title: String,
43    pub width: u32,
44    pub height: u32,
45    pub fullscreen: bool,
46    pub vsync: bool,
47    pub depth: bool,
48    pub stencil: bool,
49}
50
51impl Default for DesktopAppConfig {
52    fn default() -> Self {
53        Self {
54            title: "Oxygengine Game".to_owned(),
55            width: 1024,
56            height: 576,
57            fullscreen: false,
58            vsync: false,
59            depth: false,
60            stencil: false,
61        }
62    }
63}
64
65pub struct DesktopAppRunner {
66    event_loop: EventLoop<()>,
67    context_wrapper: Arc<DesktopContextWrapper>,
68}
69
70impl DesktopAppRunner {
71    pub fn new(config: DesktopAppConfig) -> Self {
72        if let Ok(path) = var("OXY_ROOT_PATH") {
73            let _ = set_current_dir(path);
74        } else if let Ok(mut dir) = current_exe() {
75            dir.pop();
76            let _ = set_current_dir(dir);
77        }
78        let DesktopAppConfig {
79            title,
80            width,
81            height,
82            fullscreen,
83            vsync,
84            depth,
85            stencil,
86        } = config;
87        let fullscreen = if fullscreen {
88            Some(Fullscreen::Borderless(None))
89        } else {
90            None
91        };
92        let event_loop = EventLoop::new();
93        let window_builder = WindowBuilder::new()
94            .with_title(title.as_str())
95            .with_inner_size(LogicalSize::new(width, height))
96            .with_fullscreen(fullscreen);
97        let context_wrapper = Arc::new(unsafe {
98            let mut builder = ContextBuilder::new()
99                .with_vsync(vsync)
100                .with_double_buffer(Some(true))
101                .with_hardware_acceleration(Some(true));
102            if depth {
103                builder = builder.with_depth_buffer(24);
104            }
105            if stencil {
106                builder = builder.with_stencil_buffer(8);
107            }
108            builder
109                .build_windowed(window_builder, &event_loop)
110                .expect("Could not build windowed context wrapper!")
111                .make_current()
112                .expect("Could not make windowed context wrapper a current one!")
113        });
114        Self {
115            event_loop,
116            context_wrapper,
117        }
118    }
119
120    pub fn context_wrapper(&self) -> Arc<DesktopContextWrapper> {
121        self.context_wrapper.clone()
122    }
123}
124
125impl BackendAppRunner<()> for DesktopAppRunner {
126    fn run(&mut self, app: Rc<RefCell<App>>) -> Result<(), ()> {
127        let mut running = true;
128        while running {
129            self.event_loop.run_return(|event, _, control_flow| {
130                *control_flow = ControlFlow::Poll;
131                match event {
132                    Event::MainEventsCleared => {
133                        let mut app = app.borrow_mut();
134                        app.process();
135                        app.multiverse
136                            .default_universe_mut()
137                            .unwrap()
138                            .expect_resource_mut::<DesktopAppEvents>()
139                            .clear();
140                        let _ = self.context_wrapper.swap_buffers();
141                        if !app.multiverse.is_running() {
142                            running = false;
143                        }
144                        *control_flow = ControlFlow::Exit;
145                    }
146                    Event::WindowEvent { event, .. } => match event {
147                        WindowEvent::Resized(physical_size) => {
148                            self.context_wrapper.resize(physical_size);
149                            app.borrow_mut()
150                                .multiverse
151                                .default_universe_mut()
152                                .unwrap()
153                                .expect_resource_mut::<DesktopAppEvents>()
154                                .push(event.to_static().unwrap());
155                        }
156                        WindowEvent::CloseRequested => {
157                            for universe in app.borrow_mut().multiverse.universes_mut() {
158                                universe.expect_resource_mut::<AppLifeCycle>().running = false;
159                            }
160                        }
161                        event => {
162                            if let Some(event) = event.to_static() {
163                                app.borrow_mut()
164                                    .multiverse
165                                    .default_universe_mut()
166                                    .unwrap()
167                                    .expect_resource_mut::<DesktopAppEvents>()
168                                    .push(event);
169                            }
170                        }
171                    },
172                    _ => {}
173                }
174            });
175        }
176        Ok(())
177    }
178}