1use crate::graphics::{
2 Graphics,
3 GraphicsSettings,
4 two_dimensions::Graphics2D,
5};
6
7#[cfg(feature="3D")]
8use crate::graphics::three_dimensions::Graphics3D;
9
10#[cfg(feature="fps_counter")]
11use super::fps;
12
13#[cfg(feature="ups_counter")]
14use super::ups;
15
16use super::{
17 window_width,
19 window_height,
20 window_center,
21 InnerWindowEvent,
23 GeneralSettings,
25};
26
27use glium::{
28 Display,
29 Surface,
30 Frame,
31 Version,
32 draw_parameters::{
33 DrawParameters,
34 Blend,
35 BlendingFunction,
36 LinearBlendingFactor,
37 BackfaceCullingMode,
38 },
39 texture::RawImage2d,
40 backend::glutin::DisplayCreationError,
41 SwapBuffersError,
42};
43
44use glium::glutin::{
45 ContextBuilder,
46 NotCurrent,
47 event_loop::{EventLoop,EventLoopClosed,EventLoopProxy},
48 window::WindowBuilder,
49};
50
51use image::{
52 ImageFormat,
53 ImageBuffer,
54 DynamicImage
55};
56
57use std::{
58 path::Path,
59 time::{Instant,Duration}
60};
61
62pub struct WindowBase{
65 pub display:Display,
67
68 pub event_loop:EventLoop<InnerWindowEvent>,
70
71 pub event_loop_proxy:EventLoopProxy<InnerWindowEvent>,
73
74 #[cfg(not(feature="lazy"))]
76 pub update_interval:Duration,
77 #[cfg(not(feature="lazy"))]
79 pub next_update:Instant,
80
81 #[cfg(feature="fps_counter")]
83 pub frames_passed:u32,
84 #[cfg(feature="ups_counter")]
86 pub updates_passed:u32,
87 #[cfg(any(feature="fps_counter",feature="ups_counter"))]
89 pub time:Instant,
90}
91
92impl WindowBase{
93 pub fn raw(
94 window_builder:WindowBuilder,
95 context_builder:ContextBuilder<NotCurrent>,
96 graphics_settings:GraphicsSettings,
97 event_loop:EventLoop<InnerWindowEvent>,
98 general_settings:GeneralSettings,
99 )->Result<(WindowBase,Graphics2D),DisplayCreationError>{
100 let display=Display::new(window_builder,context_builder,&event_loop)?;
102
103 let size=display.gl_window().window().inner_size();
104 unsafe{
105 window_width=size.width as f32;
106 window_height=size.height as f32;
107 window_center=[window_width/2f32,window_height/2f32];
108 }
109
110 let Version(..,m,l)=display.get_supported_glsl_version();
112 let glsl=match m{
113 1 if l<3 =>{
114 120
115 }
116 _=>{
117 140
118 }
119 };
120
121 if let Some([r,g,b,a])=general_settings.initial_colour{
122 let mut frame=display.draw(); frame.clear_color(r,g,b,a); frame.finish().unwrap(); }
126
127 let graphics2d=Graphics2D::new(&display,graphics_settings,glsl);
128
129 let proxy=event_loop.create_proxy();
130
131 #[cfg(not(feature="lazy"))]
132 let update_interval=Duration::from_secs(1).checked_div(general_settings.updates_per_second).expect("UPD = 0");
133 Ok((
137 Self{
138 display,
139
140 event_loop,
141 event_loop_proxy:proxy,
142
143 #[cfg(not(feature="lazy"))]
144 update_interval,
145 #[cfg(not(feature="lazy"))]
146 next_update:Instant::now(),
147
148 #[cfg(feature="fps_counter")]
149 frames_passed:0u32,
150 #[cfg(feature="ups_counter")]
151 updates_passed:0u32,
152 #[cfg(any(feature="fps_counter",feature="ups_counter"))]
153 time:Instant::now(),
154 },
155 graphics2d,
156 ))
157 }
158
159 #[inline(always)]
169 pub fn request_event_loop_close(&self)->Result<(),EventLoopClosed<InnerWindowEvent>>{
170 self.event_loop_proxy.send_event(InnerWindowEvent::EventLoopCloseRequested)
171 }
172}
173
174impl WindowBase{
176 pub fn draw<F:FnOnce(&mut Graphics<Frame>)>(&self,graphics_base:&Graphics2D,f:F)->Result<(),SwapBuffersError>{
180 let mut draw_parameters=default_draw_parameters();
181
182 let mut frame=self.display.draw();
183
184 let mut g=Graphics::new(
185 graphics_base,
186 draw_parameters,
187 &mut frame
188 );
189
190 f(&mut g);
191
192 frame.finish()
193 }
194}
195
196impl WindowBase{
198 pub fn screenshot(&self)->Option<DynamicImage>{
202 let image:RawImage2d<u8>=match self.display.read_front_buffer(){
204 Ok(t)=>t,
205 Err(_)=>return Option::None
206 };
207 let image=match ImageBuffer::from_raw(image.width,image.height,image.data.into_owned()){
209 Option::Some(i)=>i,
210 Option::None=>return Option::None
211 };
212 Some(DynamicImage::ImageRgba8(image).flipv())
214 }
215
216 pub fn save_screenshot<P:AsRef<Path>>(&self,path:P){
220 let image:RawImage2d<u8>=match self.display.read_front_buffer(){
222 Ok(t)=>t,
223 Err(_)=>return
224 };
225 let image=match ImageBuffer::from_raw(image.width,image.height,image.data.into_owned()){
227 Option::Some(i)=>i,
228 Option::None=>return
229 };
230 let image=DynamicImage::ImageRgba8(image).flipv();
232 if let Err(_)=image.save_with_format(path,ImageFormat::Png){
234 return
235 }
236 }
237}
238
239
240impl WindowBase{
244 #[cfg(feature="fps_counter")]
245 pub (crate) fn count_fps(&mut self){
246 self.frames_passed+=1;
247 }
248
249 #[cfg(feature="ups_counter")]
250 pub (crate) fn count_ups(&mut self){
251 self.updates_passed+=1;
252 }
253
254 #[cfg(not(feature="lazy"))]
255 pub (crate) fn update_check(&mut self){
256 let now=Instant::now();
257 if self.next_update<=now{
258 self.event_loop_proxy
259 .send_event(InnerWindowEvent::Update)
260 .expect("Dead event loop");
261
262 self.next_update+=self.update_interval;
263 }
264 }
265
266 #[cfg(any(feature="fps_counter",feature="ups_counter"))]
267 pub fn check_counters(&mut self){
268 let current_time=Instant::now();
269 let time_passed=current_time.duration_since(self.time);
270
271 if Duration::from_secs(1)<time_passed{
272 #[cfg(feature="fps_counter")]
273 unsafe{
274 fps=self.frames_passed;
275 self.frames_passed=0;
276 }
277 #[cfg(feature="ups_counter")]
278 unsafe{
279 ups=self.updates_passed;
280 self.updates_passed=0;
281 }
282
283 self.time=current_time;
284 }
285 }
286}
287
288
289pub fn default_draw_parameters<'a>()->DrawParameters<'a>{
290 DrawParameters{
291 blend:Blend{
292 color:BlendingFunction::Addition{
293 source:LinearBlendingFactor::SourceAlpha,
294 destination:LinearBlendingFactor::OneMinusSourceAlpha,
295 },
296 alpha:BlendingFunction::Addition{
297 source:LinearBlendingFactor::One,
298 destination:LinearBlendingFactor::One,
299 },
300 constant_value:(1f32,1f32,1f32,1f32),
301 },
302
303 backface_culling:BackfaceCullingMode::CullingDisabled,
304
305 ..DrawParameters::default()
306 }
307}