use std::{
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
time::Duration,
};
#[cfg(feature = "glow")]
use egui_glow::glow;
#[cfg(feature = "wgpu")]
use egui_wgpu::wgpu;
use egui_winit::winit::{
self,
application::ApplicationHandler,
event::{StartCause, WindowEvent},
event_loop::{ActiveEventLoop, EventLoopProxy},
};
#[cfg(feature = "glow")]
pub(crate) mod _glow;
#[cfg(feature = "wgpu")]
pub(crate) mod _wgpu;
#[derive(Debug, Clone, Copy)]
pub enum Renderer {
Glow,
Wgpu,
}
pub struct Frame {
handle: Handle,
#[cfg(feature = "glow")]
gl: Option<Arc<glow::Context>>,
#[cfg(feature = "wgpu")]
render_state: Option<egui_wgpu::RenderState>,
window: Arc<winit::window::Window>,
}
impl Frame {
#[cfg(feature = "glow")]
pub fn gl(&self) -> Option<&Arc<glow::Context>> {
self.gl.as_ref()
}
#[cfg(feature = "wgpu")]
pub fn render_state(&self) -> Option<&egui_wgpu::RenderState> {
self.render_state.as_ref()
}
#[cfg(feature = "wgpu")]
pub fn device(&self) -> Option<&wgpu::Device> {
self.render_state.as_ref().map(|x| &x.device)
}
#[cfg(feature = "wgpu")]
pub fn queue(&self) -> Option<&wgpu::Queue> {
self.render_state.as_ref().map(|x| &x.queue)
}
pub fn winit_window(&self) -> &Arc<winit::window::Window> {
&self.window
}
pub fn handle(&self) -> Handle {
self.handle.clone()
}
}
impl std::ops::Deref for Frame {
type Target = Handle;
fn deref(&self) -> &Self::Target {
&self.handle
}
}
impl std::ops::DerefMut for Frame {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.handle
}
}
#[derive(Debug, Clone, Default)]
pub struct Handle {
proxy: Arc<std::sync::OnceLock<EventLoopProxy<UserEvent>>>,
visible: Arc<AtomicBool>,
}
impl Handle {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn init(&self, proxy: EventLoopProxy<UserEvent>) {
self.proxy.set(proxy).unwrap();
}
pub fn request_repaint(&self) {
if let Some(proxy) = self.proxy.get() {
_ = proxy.send_event(UserEvent::MessageReady);
}
}
#[cfg(not(target_os = "android"))]
pub fn hide(&self) {
if let Some(proxy) = self.proxy.get() {
_ = proxy.send_event(UserEvent::Hide);
}
}
#[cfg(not(target_os = "android"))]
pub fn show(&self) {
if let Some(proxy) = self.proxy.get() {
_ = proxy.send_event(UserEvent::Show);
}
}
pub fn is_visible(&self) -> bool {
self.visible.load(Ordering::Relaxed)
}
pub fn exit(&self) {
if let Some(proxy) = self.proxy.get() {
_ = proxy.send_event(UserEvent::Exit);
}
}
}
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum UserEvent {
Show,
Hide,
Exit,
RequestRepaint(Duration),
MessageReady,
}
pub(crate) trait Runner {
fn has_window(&self) -> bool;
fn ensure_window(&mut self, event_loop: &ActiveEventLoop);
fn destroy_window(&mut self);
fn update(&mut self);
fn request_redraw(&self);
fn set_repaint_delay(&mut self, delay: Duration);
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: winit::window::WindowId, event: WindowEvent);
}
impl ApplicationHandler<UserEvent> for dyn Runner {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if !self.has_window() {
self.ensure_window(event_loop);
}
}
fn suspended(&mut self, _event_loop: &ActiveEventLoop) {
self.destroy_window();
}
fn new_events(&mut self, _event_loop: &ActiveEventLoop, cause: StartCause) {
if matches!(cause, StartCause::ResumeTimeReached { .. }) {
self.request_redraw();
}
}
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
match event {
UserEvent::Show => self.ensure_window(event_loop),
UserEvent::Hide => self.destroy_window(),
UserEvent::Exit => event_loop.exit(),
UserEvent::RequestRepaint(delay) => self.set_repaint_delay(delay),
UserEvent::MessageReady => self.update(),
}
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: winit::window::WindowId, event: WindowEvent) {
Runner::window_event(self, event_loop, window_id, event);
}
fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
self.destroy_window();
}
}