use std::ffi::{CString};
use std::num::NonZeroU32;
use winit::event::{Event, WindowEvent};
use winit::window::WindowBuilder;
use winit::event_loop::{EventLoopBuilder};
use raw_window_handle::HasRawWindowHandle;
use glutin::config::ConfigTemplateBuilder;
use glutin::context::{ContextApi, ContextAttributesBuilder, Version};
use glutin::display::GetGlDisplay;
use glutin::prelude::*;
use crate::AppWindowBuilder;
use glutin_winit::{self, DisplayBuilder, GlWindow};
#[cfg(target_os="android")]
use winit::platform::android::EventLoopBuilderExtAndroid;
#[cfg(target_os="android")]
pub use winit::platform::android::activity::AndroidApp;
use crate::{AppWindow, AppEvent, MouseKind, MouseButton, AppUnits};
pub struct AppWindowBuilderGlutin {
#[cfg(target_os="android")]
android_app: Option<winit::platform::android::activity::AndroidApp>,
title:String,
size: (f32,f32),
units: AppUnits,
}
impl AppWindowBuilder for AppWindowBuilderGlutin {
fn build(self:Box<Self>)->Box<dyn AppWindow> {
Box::new(AppWindowGlutin::new(&self))
}
fn title(&mut self, title:String) {
self.title=title;
}
fn size(&mut self, w:f32, h:f32) {
self.size=(w,h);
}
fn units(&mut self, units: AppUnits) {
self.units=units;
}
}
impl AppWindowBuilderGlutin {
pub fn new()->Box<Self> {
Box::new(Self {
title: "Unknown".to_string(),
size: (800.,600.),
units: AppUnits::HardwarePixels
})
}
#[cfg(target_os="android")]
pub fn with_android_app(mut self, android_app:winit::platform::android::activity::AndroidApp)
->Self {
self.android_app=Some(android_app);
self
}
pub fn build_event_loop(&self)->winit::event_loop::EventLoop<()> {
let mut event_loop_builder=EventLoopBuilder::new();
#[cfg(target_os="android")] {
event_loop_builder.with_android_app(self.android_app.take().unwrap());
}
event_loop_builder.build()
}
}
pub struct AppWindowGlutin {
gl_config: glutin::config::Config,
event_loop: Option<winit::event_loop::EventLoop<()>>,
window: Option<winit::window::Window>,
not_current_gl_context: Option<glutin::context::NotCurrentContext>,
gl_context: Option<glutin::context::PossiblyCurrentContext>,
gl_surface: Option<glutin::surface::Surface<glutin::surface::WindowSurface>>,
hw_size: (f32,f32),
mouse_position: winit::dpi::PhysicalPosition<f64>,
pixel_ratio: f32,
units: AppUnits,
}
impl AppWindow for AppWindowGlutin {
fn size(&self)->(f32,f32) {
if self.hw_size.0<=0. {
panic!("We have no window at this point");
}
(
self.units.hw_to_units(self.pixel_ratio,self.hw_size.0),
self.units.hw_to_units(self.pixel_ratio,self.hw_size.1)
)
}
fn pixel_ratio(&self)->f32 {
if self.pixel_ratio<=0. {
panic!("We have no window at this point");
}
self.pixel_ratio
}
fn post_redisplay(&mut self) {
self.window.as_ref().unwrap().request_redraw();
}
fn run(self: Box<Self>, handler:Box<dyn FnMut(&mut dyn AppWindow,AppEvent)>) {
self.run_impl(handler);
}
}
impl AppWindowGlutin {
pub fn new(builder:&AppWindowBuilderGlutin)->Self {
let event_loop=builder.build_event_loop();
let window_builder=
if cfg!(target_os = "android") {None}
else {Some(WindowBuilder::new())};
let template =
ConfigTemplateBuilder::new();
let display_builder = DisplayBuilder::new().with_window_builder(window_builder);
let (window, gl_config) = display_builder
.build(&event_loop, template, |configs| {
configs
.reduce(|accum, config| {
if config.num_samples() > accum.num_samples() {
config
} else {
accum
}
})
.unwrap()
})
.unwrap();
if window.is_some() {
window.as_ref().unwrap().set_title(&builder.title);
}
let pixel_ratio=if window.is_some() {
window.as_ref().unwrap().scale_factor() as f32
}
else {
-1.0
};
println!("Picked a config with {} samples", gl_config.num_samples());
let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle());
let gl_display = gl_config.display();
let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);
let fallback_context_attributes = ContextAttributesBuilder::new()
.with_context_api(ContextApi::Gles(None))
.build(raw_window_handle);
let legacy_context_attributes = ContextAttributesBuilder::new()
.with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1))))
.build(raw_window_handle);
let not_current_gl_context = Some(unsafe {
gl_display.create_context(&gl_config, &context_attributes).unwrap_or_else(|_| {
gl_display.create_context(&gl_config, &fallback_context_attributes).unwrap_or_else(
|_| {
gl_display
.create_context(&gl_config, &legacy_context_attributes)
.expect("failed to create context")
},
)
})
});
gl::load_with(|symbol| {
let symbol = CString::new(symbol).unwrap();
gl_display.get_proc_address(symbol.as_c_str()).cast()
});
AppWindowGlutin {
gl_config,
event_loop: Some(event_loop),
window,
not_current_gl_context,
gl_context: None,
gl_surface: None,
hw_size: (-1.,-1.),
mouse_position: winit::dpi::PhysicalPosition::<f64>{x:0.0, y:0.0},
pixel_ratio: pixel_ratio,
units: builder.units.clone()
}
}
fn run_impl(mut self, mut handler:Box<dyn FnMut(&mut dyn AppWindow,AppEvent)>) {
let event_loop=self.event_loop.take().unwrap();
event_loop.run(move |event, window_target, control_flow| {
control_flow.set_wait();
match event {
Event::Resumed => {
if self.window.is_none() {
let window_builder = WindowBuilder::new();
self.window=Some(glutin_winit::finalize_window(window_target, window_builder, &self.gl_config).unwrap());
}
let inner_size=self.window.as_ref().unwrap().inner_size();
self.hw_size=(
inner_size.width as f32,
inner_size.height as f32
);
self.pixel_ratio=self.window.as_ref().unwrap().scale_factor() as f32;
let attrs = self.window.as_ref().unwrap().build_surface_attributes(<_>::default());
self.gl_surface=Some(unsafe {
self.gl_config.display().create_window_surface(&self.gl_config, &attrs).unwrap()
});
self.gl_context =
Some(self.not_current_gl_context.take().unwrap().make_current(&self.gl_surface.as_ref().unwrap()).unwrap());
handler(&mut self,AppEvent::Show);
self.window.as_ref().unwrap().request_redraw();
},
Event::Suspended => {
println!("Android window removed");
let not_current=self.gl_context.take().unwrap().make_not_current();
self.not_current_gl_context=Some(not_current.unwrap());
self.gl_context=None;
self.gl_surface=None;
},
Event::RedrawRequested(_window_id)=>{
if self.gl_context.is_some() &&
self.gl_surface.is_some() {
unsafe {
gl::ClearColor(0.0,0.0,0.0,0.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
}
handler(&mut self,AppEvent::Render);
self.gl_surface.as_ref().unwrap().swap_buffers(&self.gl_context.as_ref().unwrap()).unwrap();
}
},
Event::WindowEvent { event, .. } => match event {
WindowEvent::Resized(size) => {
self.gl_surface.as_ref().unwrap().resize(
&self.gl_context.as_ref().unwrap(),
NonZeroU32::new(size.width).unwrap(),
NonZeroU32::new(size.height).unwrap(),
);
unsafe {
gl::Viewport(0, 0, size.width as i32, size.height as i32);
}
self.hw_size=(
size.width as f32,
size.height as f32
);
let e=AppEvent::Resize{
width: self.units.hw_to_units(self.pixel_ratio,self.hw_size.0),
height: self.units.hw_to_units(self.pixel_ratio,self.hw_size.1)
};
handler(&mut self,e);
self.window.as_ref().unwrap().request_redraw();
},
WindowEvent::ScaleFactorChanged{new_inner_size,..}=>{
let size=new_inner_size;
self.gl_surface.as_ref().unwrap().resize(
&self.gl_context.as_ref().unwrap(),
NonZeroU32::new(size.width).unwrap(),
NonZeroU32::new(size.height).unwrap(),
);
unsafe {
gl::Viewport(0, 0, size.width as i32, size.height as i32);
}
self.pixel_ratio=self.window.as_ref().unwrap().scale_factor() as f32;
self.hw_size=(
size.width as f32,
size.height as f32
);
let e=AppEvent::Resize{
width: self.units.hw_to_units(self.pixel_ratio,self.hw_size.0),
height: self.units.hw_to_units(self.pixel_ratio,self.hw_size.1)
};
handler(&mut self,e);
self.window.as_ref().unwrap().request_redraw();
},
WindowEvent::CloseRequested => {
control_flow.set_exit();
},
WindowEvent::CursorMoved{position,..}=>{
self.mouse_position=position;
let e=AppEvent::MouseMove{
x:self.units.hw_to_units(self.pixel_ratio,position.x as f32),
y:self.units.hw_to_units(self.pixel_ratio,position.y as f32),
kind:MouseKind::Mouse
};
handler(&mut self,e);
},
WindowEvent::MouseInput{state,button,..}=>{
let event_button=match button {
winit::event::MouseButton::Left=>MouseButton::Left,
winit::event::MouseButton::Right=>MouseButton::Right,
_=>MouseButton::Unknown
};
let event=match state {
winit::event::ElementState::Pressed=>{
AppEvent::MouseDown{
x: self.units.hw_to_units(self.pixel_ratio,self.mouse_position.x as f32),
y: self.units.hw_to_units(self.pixel_ratio,self.mouse_position.y as f32),
kind: MouseKind::Mouse,
button: event_button
}
},
winit::event::ElementState::Released=>{
AppEvent::MouseUp{
x: self.units.hw_to_units(self.pixel_ratio,self.mouse_position.x as f32),
y: self.units.hw_to_units(self.pixel_ratio,self.mouse_position.y as f32),
kind: MouseKind::Mouse,
button: event_button
}
}
};
handler(&mut self,event);
},
WindowEvent::Touch(winit::event::Touch{phase,location,..})=>{
let event=match phase {
winit::event::TouchPhase::Started=>{
AppEvent::MouseDown{
x: self.units.hw_to_units(self.pixel_ratio,location.x as f32),
y: self.units.hw_to_units(self.pixel_ratio,location.y as f32),
kind: MouseKind::Touch,
button: MouseButton::Unknown
}
},
winit::event::TouchPhase::Moved=>{
AppEvent::MouseMove{
x: self.units.hw_to_units(self.pixel_ratio,location.x as f32),
y: self.units.hw_to_units(self.pixel_ratio,location.y as f32),
kind: MouseKind::Touch,
}
},
winit::event::TouchPhase::Ended |
winit::event::TouchPhase::Cancelled=>{
AppEvent::MouseUp{
x: self.units.hw_to_units(self.pixel_ratio,location.x as f32),
y: self.units.hw_to_units(self.pixel_ratio,location.y as f32),
kind: MouseKind::Touch,
button: MouseButton::Unknown
}
},
};
handler(&mut self,event);
}
_ => (),
},
_ => (),
}
});
}
}