Skip to content

Commit

Permalink
refactor(glutin_examples): lib: migrate from EventLoop::run to `E…
Browse files Browse the repository at this point in the history
…ventLoop::run_app`
  • Loading branch information
ErichDonGubler committed May 14, 2024
1 parent e88044f commit 678a3a1
Showing 1 changed file with 144 additions and 136 deletions.
280 changes: 144 additions & 136 deletions glutin_examples/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::ops::Deref;

use gl::types::GLfloat;
use raw_window_handle::HasRawWindowHandle;
use winit::event::{Event, KeyEvent, WindowEvent};
use winit::application::ApplicationHandler;
use winit::event::{KeyEvent, WindowEvent};
use winit::keyboard::{Key, NamedKey};
use winit::window::Window;

Expand Down Expand Up @@ -44,151 +45,157 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box<dyn

let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes));

let mut app = App::new(display_builder);
event_loop.run(|event, event_loop| {
match event {
Event::Resumed => {
let (mut window, gl_config) = match app.display_builder.clone().build(
event_loop,
template.clone(),
gl_config_picker,
) {
Ok(ok) => ok,
Err(e) => {
app.exit_state = Err(e);
event_loop.exit();
return;
},
};

println!("Picked a config with {} samples", gl_config.num_samples());

let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle());

// XXX The display could be obtained from any object created by it, so we can
// query it from the config.
let gl_display = gl_config.display();

// The context creation part.
let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);

// Since glutin by default tries to create OpenGL core context, which may not be
// present we should try gles.
let fallback_context_attributes = ContextAttributesBuilder::new()
.with_context_api(ContextApi::Gles(None))
.build(raw_window_handle);

// There are also some old devices that support neither modern OpenGL nor GLES.
// To support these we can try and create a 2.1 context.
let legacy_context_attributes = ContextAttributesBuilder::new()
.with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1))))
.build(raw_window_handle);

app.not_current_gl_context.replace(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")
})
},
)
});

#[cfg(android_platform)]
println!("Android window available");

let window = window.take().unwrap_or_else(|| {
let window_attributes = Window::default_attributes()
.with_transparent(true)
.with_title("Glutin triangle gradient example (press Escape to exit)");
glutin_winit::finalize_window(event_loop, window_attributes, &gl_config)
.unwrap()
});

let attrs = window.build_surface_attributes(Default::default());
let gl_surface = unsafe {
gl_config.display().create_window_surface(&gl_config, &attrs).unwrap()
};

// Make it current.
let gl_context =
app.not_current_gl_context.take().unwrap().make_current(&gl_surface).unwrap();

// The context needs to be current for the Renderer to set up shaders and
// buffers. It also performs function loading, which needs a current context on
// WGL.
app.renderer.get_or_insert_with(|| Renderer::new(&gl_display));

// Try setting vsync.
if let Err(res) = gl_surface
.set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
{
eprintln!("Error setting vsync: {res:?}");
}
let mut app = App::new(template, display_builder);
event_loop.run_app(&mut app)?;

assert!(app.state.replace(AppState { gl_context, gl_surface, window }).is_none());
},
Event::Suspended => {
// This event is only raised on Android, where the backing NativeWindow for a GL
// Surface can appear and disappear at any moment.
println!("Android window removed");

// Destroy the GL Surface and un-current the GL Context before ndk-glue releases
// the window back to the system.
let gl_context = app.state.take().unwrap().gl_context;
assert!(app
.not_current_gl_context
.replace(gl_context.make_not_current().unwrap())
.is_none());
},
Event::WindowEvent { event, .. } => match event {
WindowEvent::Resized(size) => {
if size.width != 0 && size.height != 0 {
// Some platforms like EGL require resizing GL surface to update the size
// Notable platforms here are Wayland and macOS, other don't require it
// and the function is no-op, but it's wise to resize it for portability
// reasons.
if let Some(AppState { gl_context, gl_surface, window: _ }) =
app.state.as_ref()
{
gl_surface.resize(
gl_context,
NonZeroU32::new(size.width).unwrap(),
NonZeroU32::new(size.height).unwrap(),
);
let renderer = app.renderer.as_ref().unwrap();
renderer.resize(size.width as i32, size.height as i32);
}
}
},
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event: KeyEvent { logical_key: Key::Named(NamedKey::Escape), .. },
..
} => event_loop.exit(),
_ => (),
app.exit_state
}

impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
let (mut window, gl_config) = match self.display_builder.clone().build(
event_loop,
self.template.clone(),
gl_config_picker,
) {
Ok(ok) => ok,
Err(e) => {
self.exit_state = Err(e);
event_loop.exit();
return;
},
Event::AboutToWait => {
if let Some(AppState { gl_context, gl_surface, window }) = app.state.as_ref() {
let renderer = app.renderer.as_ref().unwrap();
renderer.draw();
window.request_redraw();
};

println!("Picked a config with {} samples", gl_config.num_samples());

let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle());

// XXX The display could be obtained from any object created by it, so we can
// query it from the config.
let gl_display = gl_config.display();

// The context creation part.
let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);

// Since glutin by default tries to create OpenGL core context, which may not be
// present we should try gles.
let fallback_context_attributes = ContextAttributesBuilder::new()
.with_context_api(ContextApi::Gles(None))
.build(raw_window_handle);

// There are also some old devices that support neither modern OpenGL nor GLES.
// To support these we can try and create a 2.1 context.
let legacy_context_attributes = ContextAttributesBuilder::new()
.with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1))))
.build(raw_window_handle);

self.not_current_gl_context.replace(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")
},
)
})
});

#[cfg(android_platform)]
println!("Android window available");

let window = window.take().unwrap_or_else(|| {
let window_attributes = Window::default_attributes()
.with_transparent(true)
.with_title("Glutin triangle gradient example (press Escape to exit)");
glutin_winit::finalize_window(event_loop, window_attributes, &gl_config).unwrap()
});

let attrs = window.build_surface_attributes(Default::default());
let gl_surface =
unsafe { gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() };

// Make it current.
let gl_context =
self.not_current_gl_context.take().unwrap().make_current(&gl_surface).unwrap();

// The context needs to be current for the Renderer to set up shaders and
// buffers. It also performs function loading, which needs a current context on
// WGL.
self.renderer.get_or_insert_with(|| Renderer::new(&gl_display));

// Try setting vsync.
if let Err(res) = gl_surface
.set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
{
eprintln!("Error setting vsync: {res:?}");
}

gl_surface.swap_buffers(gl_context).unwrap();
assert!(self.state.replace(AppState { gl_context, gl_surface, window }).is_none());
}

fn suspended(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
// This event is only raised on Android, where the backing NativeWindow for a GL
// Surface can appear and disappear at any moment.
println!("Android window removed");

// Destroy the GL Surface and un-current the GL Context before ndk-glue releases
// the window back to the system.
let gl_context = self.state.take().unwrap().gl_context;
assert!(self
.not_current_gl_context
.replace(gl_context.make_not_current().unwrap())
.is_none());
}

fn window_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
_window_id: winit::window::WindowId,
event: WindowEvent,
) {
match event {
WindowEvent::Resized(size) => {
if size.width != 0 && size.height != 0 {
// Some platforms like EGL require resizing GL surface to update the size
// Notable platforms here are Wayland and macOS, other don't require it
// and the function is no-op, but it's wise to resize it for portability
// reasons.
if let Some(AppState { gl_context, gl_surface, window: _ }) =
self.state.as_ref()
{
gl_surface.resize(
gl_context,
NonZeroU32::new(size.width).unwrap(),
NonZeroU32::new(size.height).unwrap(),
);
let renderer = self.renderer.as_ref().unwrap();
renderer.resize(size.width as i32, size.height as i32);
}
}
},
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event: KeyEvent { logical_key: Key::Named(NamedKey::Escape), .. },
..
} => event_loop.exit(),
_ => (),
}
})?;
}

app.exit_state
fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
if let Some(AppState { gl_context, gl_surface, window }) = self.state.as_ref() {
let renderer = self.renderer.as_ref().unwrap();
renderer.draw();
window.request_redraw();

gl_surface.swap_buffers(gl_context).unwrap();
}
}
}

struct App {
template: ConfigTemplateBuilder,
display_builder: DisplayBuilder,
exit_state: Result<(), Box<dyn Error>>,
not_current_gl_context: Option<NotCurrentContext>,
Expand All @@ -197,8 +204,9 @@ struct App {
}

impl App {
fn new(display_builder: DisplayBuilder) -> Self {
fn new(template: ConfigTemplateBuilder, display_builder: DisplayBuilder) -> Self {
Self {
template,
display_builder,
exit_state: Ok(()),
not_current_gl_context: None,
Expand Down

0 comments on commit 678a3a1

Please sign in to comment.