
This lifts the context handling from commit hash 8eb687c
as this does
all the required handling for us that the older glutin once did.
242 lines
8.7 KiB
Rust
242 lines
8.7 KiB
Rust
//! Example how to use pure `egui_glow`.
|
|
|
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
|
#![allow(unsafe_code)]
|
|
|
|
use egui_winit::winit;
|
|
|
|
/// The majority of `GlutinWindowContext` is taken from `eframe`
|
|
struct GlutinWindowContext {
|
|
window: winit::window::Window,
|
|
gl_context: glutin::context::PossiblyCurrentContext,
|
|
gl_display: glutin::display::Display,
|
|
gl_surface: glutin::surface::Surface<glutin::surface::WindowSurface>,
|
|
}
|
|
|
|
impl GlutinWindowContext {
|
|
// refactor this function to use `glutin-winit` crate eventually.
|
|
// preferably add android support at the same time.
|
|
#[allow(unsafe_code)]
|
|
unsafe fn new(winit_window: winit::window::Window) -> Self {
|
|
use glutin::prelude::*;
|
|
use raw_window_handle::*;
|
|
|
|
let raw_display_handle = winit_window.raw_display_handle();
|
|
let raw_window_handle = winit_window.raw_window_handle();
|
|
|
|
// EGL is crossplatform and the official khronos way
|
|
// but sometimes platforms/drivers may not have it, so we use back up options where possible.
|
|
|
|
// try egl and fallback to windows wgl. Windows is the only platform that *requires* window handle to create display.
|
|
#[cfg(target_os = "windows")]
|
|
let preference = glutin::display::DisplayApiPreference::EglThenWgl(Some(window_handle));
|
|
// try egl and fallback to x11 glx
|
|
#[cfg(target_os = "linux")]
|
|
let preference = glutin::display::DisplayApiPreference::EglThenGlx(Box::new(
|
|
winit::platform::unix::register_xlib_error_hook,
|
|
));
|
|
#[cfg(target_os = "macos")]
|
|
let preference = glutin::display::DisplayApiPreference::Cgl;
|
|
#[cfg(target_os = "android")]
|
|
let preference = glutin::display::DisplayApiPreference::Egl;
|
|
|
|
let gl_display = glutin::display::Display::new(raw_display_handle, preference).unwrap();
|
|
|
|
let config_template = glutin::config::ConfigTemplateBuilder::new()
|
|
.prefer_hardware_accelerated(None)
|
|
.with_depth_size(0)
|
|
.with_stencil_size(0)
|
|
.with_transparency(false)
|
|
.compatible_with_native_window(raw_window_handle)
|
|
.build();
|
|
|
|
let config = gl_display
|
|
.find_configs(config_template)
|
|
.unwrap()
|
|
.next()
|
|
.unwrap();
|
|
|
|
let context_attributes =
|
|
glutin::context::ContextAttributesBuilder::new().build(Some(raw_window_handle));
|
|
// for surface creation.
|
|
let (width, height): (u32, u32) = winit_window.inner_size().into();
|
|
let surface_attributes =
|
|
glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new()
|
|
.build(
|
|
raw_window_handle,
|
|
std::num::NonZeroU32::new(width).unwrap(),
|
|
std::num::NonZeroU32::new(height).unwrap(),
|
|
);
|
|
// start creating the gl objects
|
|
let gl_context = gl_display
|
|
.create_context(&config, &context_attributes)
|
|
.unwrap();
|
|
|
|
let gl_surface = gl_display
|
|
.create_window_surface(&config, &surface_attributes)
|
|
.unwrap();
|
|
|
|
let gl_context = gl_context.make_current(&gl_surface).unwrap();
|
|
|
|
gl_surface
|
|
.set_swap_interval(
|
|
&gl_context,
|
|
glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap()),
|
|
)
|
|
.unwrap();
|
|
|
|
GlutinWindowContext {
|
|
window: winit_window,
|
|
gl_context,
|
|
gl_display,
|
|
gl_surface,
|
|
}
|
|
}
|
|
|
|
fn window(&self) -> &winit::window::Window {
|
|
&self.window
|
|
}
|
|
|
|
fn resize(&self, physical_size: winit::dpi::PhysicalSize<u32>) {
|
|
use glutin::surface::GlSurface;
|
|
self.gl_surface.resize(
|
|
&self.gl_context,
|
|
physical_size.width.try_into().unwrap(),
|
|
physical_size.height.try_into().unwrap(),
|
|
);
|
|
}
|
|
|
|
fn swap_buffers(&self) -> glutin::error::Result<()> {
|
|
use glutin::surface::GlSurface;
|
|
self.gl_surface.swap_buffers(&self.gl_context)
|
|
}
|
|
|
|
fn get_proc_address(&self, addr: &std::ffi::CStr) -> *const std::ffi::c_void {
|
|
use glutin::display::GlDisplay;
|
|
self.gl_display.get_proc_address(addr)
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let mut clear_color = [0.1, 0.1, 0.1];
|
|
|
|
let event_loop = winit::event_loop::EventLoopBuilder::with_user_event().build();
|
|
let (gl_window, gl) = create_display(&event_loop);
|
|
let gl = std::sync::Arc::new(gl);
|
|
|
|
let mut egui_glow = egui_glow::EguiGlow::new(&event_loop, gl.clone(), None);
|
|
|
|
event_loop.run(move |event, _, control_flow| {
|
|
let mut redraw = || {
|
|
let mut quit = false;
|
|
|
|
let repaint_after = egui_glow.run(gl_window.window(), |egui_ctx| {
|
|
egui::SidePanel::left("my_side_panel").show(egui_ctx, |ui| {
|
|
ui.heading("Hello World!");
|
|
if ui.button("Quit").clicked() {
|
|
quit = true;
|
|
}
|
|
ui.color_edit_button_rgb(&mut clear_color);
|
|
});
|
|
});
|
|
|
|
*control_flow = if quit {
|
|
winit::event_loop::ControlFlow::Exit
|
|
} else if repaint_after.is_zero() {
|
|
gl_window.window().request_redraw();
|
|
winit::event_loop::ControlFlow::Poll
|
|
} else if let Some(repaint_after_instant) =
|
|
std::time::Instant::now().checked_add(repaint_after)
|
|
{
|
|
winit::event_loop::ControlFlow::WaitUntil(repaint_after_instant)
|
|
} else {
|
|
winit::event_loop::ControlFlow::Wait
|
|
};
|
|
|
|
{
|
|
unsafe {
|
|
use glow::HasContext as _;
|
|
gl.clear_color(clear_color[0], clear_color[1], clear_color[2], 1.0);
|
|
gl.clear(glow::COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
// draw things behind egui here
|
|
|
|
egui_glow.paint(gl_window.window());
|
|
|
|
// draw things on top of egui here
|
|
|
|
gl_window.swap_buffers().unwrap();
|
|
gl_window.window().set_visible(true);
|
|
}
|
|
};
|
|
|
|
match event {
|
|
// Platform-dependent event handlers to workaround a winit bug
|
|
// See: https://github.com/rust-windowing/winit/issues/987
|
|
// See: https://github.com/rust-windowing/winit/issues/1619
|
|
winit::event::Event::RedrawEventsCleared if cfg!(windows) => redraw(),
|
|
winit::event::Event::RedrawRequested(_) if !cfg!(windows) => redraw(),
|
|
|
|
winit::event::Event::WindowEvent { event, .. } => {
|
|
use winit::event::WindowEvent;
|
|
if matches!(event, WindowEvent::CloseRequested | WindowEvent::Destroyed) {
|
|
*control_flow = winit::event_loop::ControlFlow::Exit;
|
|
}
|
|
|
|
if let winit::event::WindowEvent::Resized(physical_size) = &event {
|
|
gl_window.resize(*physical_size);
|
|
} else if let winit::event::WindowEvent::ScaleFactorChanged {
|
|
new_inner_size, ..
|
|
} = &event
|
|
{
|
|
gl_window.resize(**new_inner_size);
|
|
}
|
|
|
|
let event_response = egui_glow.on_event(&event);
|
|
|
|
if event_response.repaint {
|
|
gl_window.window().request_redraw();
|
|
}
|
|
}
|
|
winit::event::Event::LoopDestroyed => {
|
|
egui_glow.destroy();
|
|
}
|
|
winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached {
|
|
..
|
|
}) => {
|
|
gl_window.window().request_redraw();
|
|
}
|
|
|
|
_ => (),
|
|
}
|
|
});
|
|
}
|
|
|
|
fn create_display(
|
|
event_loop: &winit::event_loop::EventLoopWindowTarget<()>,
|
|
) -> (GlutinWindowContext, glow::Context) {
|
|
let winit_window = winit::window::WindowBuilder::new()
|
|
.with_resizable(true)
|
|
.with_inner_size(winit::dpi::LogicalSize {
|
|
width: 800.0,
|
|
height: 600.0,
|
|
})
|
|
.with_title("egui_glow example")
|
|
.with_visible(false)
|
|
.build(event_loop)
|
|
.unwrap(); // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
|
|
|
|
// a lot of the code below has been lifted from glutin example in their repo.
|
|
let glutin_window_context = unsafe { GlutinWindowContext::new(winit_window) };
|
|
let gl = unsafe {
|
|
glow::Context::from_loader_function(|s| {
|
|
let s = std::ffi::CString::new(s)
|
|
.expect("failed to construct C string from string for gl proc address");
|
|
|
|
glutin_window_context.get_proc_address(&s)
|
|
})
|
|
};
|
|
|
|
(glutin_window_context, gl)
|
|
}
|