1use crate::conv::{from_imgui_cursor, to_imgui_button, to_imgui_key};
2use cgmath::Matrix3;
3use easy_imgui::{self as imgui, Vector2, cgmath, mint};
4use easy_imgui_renderer::Renderer;
5use easy_imgui_sys::*;
6use glutin::{
7 context::PossiblyCurrentContext,
8 prelude::*,
9 surface::{Surface, WindowSurface},
10};
11use std::num::NonZeroU32;
12use std::time::{Duration, Instant};
13use winit::{
14 dpi::{LogicalSize, PhysicalSize},
15 event::Ime::Commit,
16 keyboard::PhysicalKey,
17 window::{CursorIcon, Window},
18};
19
20#[allow(unused_imports)]
22use winit::dpi::{LogicalPosition, PhysicalPosition, Pixel};
23
24#[derive(Debug, Clone)]
26pub struct MainWindowStatus {
27 last_frame: Instant,
28 current_cursor: Option<CursorIcon>,
29}
30
31impl Default for MainWindowStatus {
32 fn default() -> MainWindowStatus {
33 let now = Instant::now();
34 MainWindowStatus {
35 last_frame: now,
36 current_cursor: Some(CursorIcon::Default),
37 }
38 }
39}
40
41pub struct MainWindowIdler {
43 idle_time: Duration,
44 idle_frame_count: u32,
45 last_input_time: Instant,
46 last_input_frame: u32,
47}
48
49impl Default for MainWindowIdler {
50 fn default() -> MainWindowIdler {
51 let now = Instant::now();
52 MainWindowIdler {
53 idle_time: Duration::from_secs(1),
54 idle_frame_count: 60,
55 last_input_time: now,
56 last_input_frame: 0,
57 }
58 }
59}
60
61impl MainWindowIdler {
62 pub fn set_idle_time(&mut self, time: Duration) {
64 self.idle_time = time;
65 }
66 pub fn set_idle_frame_count(&mut self, frame_count: u32) {
68 self.idle_frame_count = frame_count;
69 }
70 pub fn incr_frame(&mut self) {
72 self.last_input_frame = self.last_input_frame.saturating_add(1);
75 }
76 pub fn has_to_render(&self) -> bool {
78 self.last_input_frame < self.idle_frame_count
79 || Instant::now().duration_since(self.last_input_time) < self.idle_time
80 }
81 pub fn ping_user_input(&mut self) {
83 self.last_input_time = Instant::now();
84 self.last_input_frame = 0;
85 }
86}
87
88pub trait MainWindowRef {
93 fn window(&self) -> &Window;
95 fn pre_render(&mut self) {}
100 fn post_render(&mut self) {}
104 fn ping_user_input(&mut self) {}
106 fn about_to_wait(&mut self, _pinged: bool) {}
108 fn transform_position(&self, pos: Vector2) -> Vector2 {
110 pos / self.scale_factor()
111 }
112 fn scale_factor(&self) -> f32 {
114 self.window().scale_factor() as f32
115 }
116 fn set_scale_factor(&self, scale: f32) -> f32 {
123 scale
124 }
125 fn resize(&mut self, size: PhysicalSize<u32>) -> LogicalSize<f32> {
129 let scale = self.scale_factor();
130 size.to_logical(scale as f64)
131 }
132 fn set_cursor(&mut self, cursor: Option<CursorIcon>) {
134 let w = self.window();
135 match cursor {
136 None => w.set_cursor_visible(false),
137 Some(c) => {
138 w.set_cursor(c);
139 w.set_cursor_visible(true);
140 }
141 }
142 }
143}
144
145fn transform_position_with_optional_matrix(
146 w: &impl MainWindowRef,
147 pos: Vector2,
148 mx: &Option<Matrix3<f32>>,
149) -> Vector2 {
150 use cgmath::{EuclideanSpace as _, Transform};
151 match mx {
152 Some(mx) => mx.transform_point(cgmath::Point2::from_vec(pos)).to_vec(),
153 None => pos / w.scale_factor(),
154 }
155}
156
157bitflags::bitflags! {
158 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
160 pub struct EventFlags: u32 {
161 const DoNotRender = 1;
163 const DoNotResize = 4;
165 const DoNotMouse = 8;
167 }
168}
169
170pub struct MainWindowPieces<'a> {
172 window: &'a Window,
173 surface: &'a Surface<WindowSurface>,
174 gl_context: &'a PossiblyCurrentContext,
175 matrix: Option<Matrix3<f32>>,
176}
177
178impl<'a> MainWindowPieces<'a> {
179 pub fn new(
181 window: &'a Window,
182 surface: &'a Surface<WindowSurface>,
183 gl_context: &'a PossiblyCurrentContext,
184 ) -> Self {
185 MainWindowPieces {
186 window,
187 surface,
188 gl_context,
189 matrix: None,
190 }
191 }
192 pub fn set_matrix(&mut self, matrix: Option<Matrix3<f32>>) {
196 self.matrix = matrix;
197 }
198}
199
200impl MainWindowRef for MainWindowPieces<'_> {
202 fn window(&self) -> &Window {
203 self.window
204 }
205 fn pre_render(&mut self) {
206 let _ = self
207 .gl_context
208 .make_current(self.surface)
209 .inspect_err(|e| log::error!("{e}"));
210 }
211 fn post_render(&mut self) {
212 self.window.pre_present_notify();
213 let _ = self
214 .surface
215 .swap_buffers(self.gl_context)
216 .inspect_err(|e| log::error!("{e}"));
217 }
218 fn resize(&mut self, size: PhysicalSize<u32>) -> LogicalSize<f32> {
219 let width = NonZeroU32::new(size.width.max(1)).unwrap();
220 let height = NonZeroU32::new(size.height.max(1)).unwrap();
221 self.surface.resize(self.gl_context, width, height);
222 let scale = self.scale_factor();
223 size.to_logical(scale as f64)
224 }
225 fn transform_position(&self, pos: Vector2) -> Vector2 {
226 transform_position_with_optional_matrix(self, pos, &self.matrix)
227 }
228}
229
230impl MainWindowRef for &Window {
232 fn window(&self) -> &Window {
233 self
234 }
235}
236
237pub struct NoScale<'a>(pub &'a Window);
239
240impl MainWindowRef for NoScale<'_> {
241 fn window(&self) -> &Window {
242 self.0
243 }
244 fn scale_factor(&self) -> f32 {
245 1.0
246 }
247 fn set_scale_factor(&self, _scale: f32) -> f32 {
248 1.0
249 }
250}
251
252#[derive(Debug, Default, Clone)]
254pub struct EventResult {
255 pub window_closed: bool,
257 pub want_capture_mouse: bool,
259 pub want_capture_keyboard: bool,
261 pub want_text_input: bool,
263}
264
265pub fn new_events(renderer: &mut Renderer, status: &mut MainWindowStatus) {
267 let now = Instant::now();
268 unsafe {
269 renderer
270 .imgui()
271 .io_mut()
272 .inner()
273 .set_delta_time(now.duration_since(status.last_frame));
274 }
275 status.last_frame = now;
276}
277
278pub fn about_to_wait(main_window: &mut impl MainWindowRef, renderer: &mut Renderer) {
280 let imgui = unsafe { renderer.imgui().set_current() };
281 let io = imgui.io();
282 if io.WantSetMousePos {
283 let pos = io.MousePos;
284 let pos = winit::dpi::LogicalPosition { x: pos.x, y: pos.y };
285 let _ = main_window.window().set_cursor_position(pos);
286 }
287 let mouse = unsafe { ImGui_IsAnyMouseDown() };
289 main_window.about_to_wait(mouse);
290}
291
292pub fn window_event(
294 main_window: &mut impl MainWindowRef,
295 renderer: &mut Renderer,
296 status: &mut MainWindowStatus,
297 app: &mut impl imgui::UiBuilder,
298 event: &winit::event::WindowEvent,
299 flags: EventFlags,
300) -> EventResult {
301 use winit::event::WindowEvent::*;
302 let mut window_closed = false;
303 match event {
304 CloseRequested => {
305 window_closed = true;
306 }
307 RedrawRequested => unsafe {
308 let imgui = renderer.imgui().set_current();
309 let io = imgui.io();
310 let config_flags = imgui::ConfigFlags::from_bits_truncate(io.ConfigFlags);
311 if !config_flags.contains(imgui::ConfigFlags::NoMouseCursorChange) {
312 let cursor = if io.MouseDrawCursor {
313 None
314 } else {
315 let cursor = imgui::MouseCursor::from_bits(ImGui_GetMouseCursor())
316 .unwrap_or(imgui::MouseCursor::Arrow);
317 from_imgui_cursor(cursor)
318 };
319 if cursor != status.current_cursor {
320 main_window.set_cursor(cursor);
321 status.current_cursor = cursor;
322 }
323 }
324 if !flags.contains(EventFlags::DoNotRender) {
325 main_window.pre_render();
326 renderer.do_frame(app);
327 main_window.post_render();
328 }
329 },
330 Resized(size) => {
331 let size = main_window.resize(*size);
334 if !flags.contains(EventFlags::DoNotResize) {
335 main_window.ping_user_input();
336 let size = Vector2::from(mint::Vector2::from(size));
338 unsafe {
339 renderer.imgui().io_mut().inner().DisplaySize = imgui::v2_to_im(size);
340 }
341 }
342 }
343 ScaleFactorChanged { scale_factor, .. } => {
344 if !flags.contains(EventFlags::DoNotResize) {
345 main_window.ping_user_input();
346 let scale_factor = main_window.set_scale_factor(*scale_factor as f32);
347 unsafe {
348 let io = renderer.imgui().io_mut().inner();
349 let old_scale_factor = io.DisplayFramebufferScale.x;
352 if io.MousePos.x.is_finite() && io.MousePos.y.is_finite() {
353 io.MousePos.x *= scale_factor / old_scale_factor;
354 io.MousePos.y *= scale_factor / old_scale_factor;
355 }
356 }
357 let size = renderer.size();
358 renderer.set_size(size, scale_factor);
359 }
360 }
361 ModifiersChanged(mods) => {
362 main_window.ping_user_input();
363 unsafe {
364 let io = renderer.imgui().io_mut().inner();
365 io.AddKeyEvent(imgui::Key::ModCtrl.bits(), mods.state().control_key());
366 io.AddKeyEvent(imgui::Key::ModShift.bits(), mods.state().shift_key());
367 io.AddKeyEvent(imgui::Key::ModAlt.bits(), mods.state().alt_key());
368 io.AddKeyEvent(imgui::Key::ModSuper.bits(), mods.state().super_key());
369 }
370 }
371 KeyboardInput {
372 event:
373 winit::event::KeyEvent {
374 physical_key,
375 text,
376 state,
377 ..
378 },
379 is_synthetic: false,
380 ..
381 } => {
382 main_window.ping_user_input();
383 let pressed = *state == winit::event::ElementState::Pressed;
384 if let Some(key) = to_imgui_key(*physical_key) {
385 unsafe {
386 let io = renderer.imgui().io_mut().inner();
387 io.AddKeyEvent(key.bits(), pressed);
388
389 use winit::keyboard::KeyCode::*;
390 if let PhysicalKey::Code(keycode) = physical_key {
391 let kmod = match keycode {
392 ControlLeft | ControlRight => Some(imgui::Key::ModCtrl),
393 ShiftLeft | ShiftRight => Some(imgui::Key::ModShift),
394 AltLeft | AltRight => Some(imgui::Key::ModAlt),
395 SuperLeft | SuperRight => Some(imgui::Key::ModSuper),
396 _ => None,
397 };
398 if let Some(kmod) = kmod {
399 io.AddKeyEvent(kmod.bits(), pressed);
400 }
401 }
402 }
403 }
404 if pressed && let Some(text) = text {
405 unsafe {
406 let io = renderer.imgui().io_mut().inner();
407 for c in text.chars() {
408 io.AddInputCharacter(c as u32);
409 }
410 }
411 }
412 }
413 Ime(Commit(text)) => {
414 main_window.ping_user_input();
415 unsafe {
416 let io = renderer.imgui().io_mut().inner();
417 for c in text.chars() {
418 io.AddInputCharacter(c as u32);
419 }
420 }
421 }
422 CursorMoved { position, .. } => {
423 main_window.ping_user_input();
424 unsafe {
425 let io = renderer.imgui().io_mut().inner();
426 let position = main_window
427 .transform_position(Vector2::new(position.x as f32, position.y as f32));
428 io.AddMousePosEvent(position.x, position.y);
429 }
430 }
431 MouseWheel {
432 delta,
433 phase: winit::event::TouchPhase::Moved,
434 ..
435 } => {
436 main_window.ping_user_input();
437 let mut imgui = unsafe { renderer.imgui().set_current() };
438 unsafe {
439 let io = imgui.io_mut().inner();
440 let (h, v) = match delta {
441 winit::event::MouseScrollDelta::LineDelta(h, v) => (*h, *v),
442 winit::event::MouseScrollDelta::PixelDelta(d) => {
443 let scale = io.DisplayFramebufferScale.x;
444 let f_scale = ImGui_GetFontSize();
445 let scale = scale * f_scale;
446 (d.x as f32 / scale, d.y as f32 / scale)
447 }
448 };
449 io.AddMouseWheelEvent(h, v);
450 }
451 }
452 MouseInput { state, button, .. } => {
453 main_window.ping_user_input();
454 unsafe {
455 let io = renderer.imgui().io_mut().inner();
456 if let Some(btn) = to_imgui_button(*button) {
457 let pressed = *state == winit::event::ElementState::Pressed;
458 io.AddMouseButtonEvent(btn.bits(), pressed);
459 }
460 }
461 }
462 CursorLeft { .. } => {
463 main_window.ping_user_input();
464 unsafe {
465 let io = renderer.imgui().io_mut().inner();
466 io.AddMousePosEvent(f32::MAX, f32::MAX);
467 }
468 }
469 Focused(focused) => {
470 main_window.ping_user_input();
471 unsafe {
472 let io = renderer.imgui().io_mut().inner();
473 io.AddFocusEvent(*focused);
474 }
475 }
476 _ => {}
477 }
478 let imgui = renderer.imgui();
479 EventResult {
480 window_closed,
481 want_capture_mouse: imgui.io().want_capture_mouse(),
482 want_capture_keyboard: imgui.io().want_capture_keyboard(),
483 want_text_input: imgui.io().want_text_input(),
484 }
485}
486
487#[cfg(feature = "clipboard")]
488pub mod clipboard {
493 use easy_imgui::{self as imgui};
494 use std::ffi::{CStr, CString, c_char, c_void};
495
496 pub fn setup(imgui: &mut imgui::Context) {
498 if let Ok(ctx) = arboard::Clipboard::new() {
499 let clip = MyClipboard {
500 ctx,
501 text: CString::default(),
502 };
503 unsafe {
504 let pio = imgui.platform_io_mut();
505 pio.Platform_ClipboardUserData = Box::into_raw(Box::new(clip)) as *mut c_void;
506 pio.Platform_SetClipboardTextFn = Some(set_clipboard_text);
507 pio.Platform_GetClipboardTextFn = Some(get_clipboard_text);
508 }
509 }
510 }
511 unsafe extern "C" fn set_clipboard_text(
512 imgui: *mut easy_imgui_sys::ImGuiContext,
513 text: *const c_char,
514 ) {
515 unsafe {
516 let user = (*imgui).PlatformIO.Platform_ClipboardUserData;
517 let clip = &mut *(user as *mut MyClipboard);
518 if text.is_null() {
519 let _ = clip.ctx.clear();
520 } else {
521 let cstr = CStr::from_ptr(text);
522 let str = String::from_utf8_lossy(cstr.to_bytes()).to_string();
523 let _ = clip.ctx.set_text(str);
524 }
525 }
526 }
527
528 unsafe extern "C" fn get_clipboard_text(
530 imgui: *mut easy_imgui_sys::ImGuiContext,
531 ) -> *const c_char {
532 unsafe {
533 let user = (*imgui).PlatformIO.Platform_ClipboardUserData;
534 let clip = &mut *(user as *mut MyClipboard);
535 let Ok(text) = clip.ctx.get_text() else {
536 return std::ptr::null();
537 };
538 let Ok(text) = CString::new(text) else {
539 return std::ptr::null();
540 };
541 clip.text = text;
542 clip.text.as_ptr()
543 }
544 }
545
546 struct MyClipboard {
547 ctx: arboard::Clipboard,
548 text: CString,
549 }
550}
551
552#[cfg(feature = "main-window")]
553mod main_window {
554 use super::*;
555 use std::future::Future;
556 mod fut;
557 use anyhow::{Result, anyhow};
558 use easy_imgui_renderer::glow;
559 pub use fut::{FutureBackCaller, FutureHandle, FutureHandleGuard};
560 use glutin::{
561 config::{Config, ConfigTemplateBuilder},
562 context::{ContextApi, ContextAttributesBuilder},
563 display::GetGlDisplay,
564 surface::SurfaceAttributesBuilder,
565 };
566 use glutin_winit::DisplayBuilder;
567 use raw_window_handle::HasWindowHandle;
568 use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
569 use winit::window::WindowAttributes;
570
571 pub struct MainWindow {
573 gl_context: PossiblyCurrentContext,
574 surface: Surface<WindowSurface>,
576 window: Window,
577 matrix: Option<Matrix3<f32>>,
578 idler: MainWindowIdler,
579 }
580
581 pub struct MainWindowWithRenderer {
584 main_window: MainWindow,
585 renderer: Renderer,
586 status: MainWindowStatus,
587 }
588
589 impl MainWindow {
590 pub fn new(event_loop: &ActiveEventLoop, wattr: WindowAttributes) -> Result<MainWindow> {
592 let score = |c: &Config| (c.num_samples(), c.depth_size(), c.stencil_size());
594 Self::with_gl_chooser(event_loop, wattr, |cfg1, cfg2| {
595 if score(&cfg2) < score(&cfg1) {
596 cfg2
597 } else {
598 cfg1
599 }
600 })
601 }
602 pub fn with_gl_chooser(
607 event_loop: &ActiveEventLoop,
608 wattr: WindowAttributes,
609 f_choose_cfg: impl FnMut(Config, Config) -> Config,
610 ) -> Result<MainWindow> {
611 let template = ConfigTemplateBuilder::new()
612 .prefer_hardware_accelerated(Some(true))
613 .with_depth_size(0)
614 .with_stencil_size(0);
615
616 let display_builder = DisplayBuilder::new().with_window_attributes(Some(wattr));
617 let (window, gl_config) = display_builder
618 .build(event_loop, template, |configs| {
619 configs.reduce(f_choose_cfg).unwrap()
620 })
621 .map_err(|e| anyhow!("{:#?}", e))?;
622 let window = window.unwrap();
623 window.set_ime_allowed(true);
624 let raw_window_handle = Some(window.window_handle().unwrap().as_raw());
625 let gl_display = gl_config.display();
626 let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);
627 let fallback_context_attributes = ContextAttributesBuilder::new()
628 .with_context_api(ContextApi::Gles(None))
629 .build(raw_window_handle);
630
631 let mut not_current_gl_context = Some(unsafe {
632 gl_display
633 .create_context(&gl_config, &context_attributes)
634 .or_else(|_| {
635 gl_display.create_context(&gl_config, &fallback_context_attributes)
636 })?
637 });
638
639 let size = window.inner_size();
640
641 let (width, height): (u32, u32) = size.into();
642 let raw_window_handle = window.window_handle().unwrap().as_raw();
643 let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
644 raw_window_handle,
645 NonZeroU32::new(width).unwrap(),
646 NonZeroU32::new(height).unwrap(),
647 );
648
649 let surface = unsafe {
650 gl_config
651 .display()
652 .create_window_surface(&gl_config, &attrs)?
653 };
654 let gl_context = not_current_gl_context
655 .take()
656 .unwrap()
657 .make_current(&surface)?;
658
659 let _ = surface.set_swap_interval(
661 &gl_context,
662 glutin::surface::SwapInterval::Wait(NonZeroU32::new(1).unwrap()),
663 );
664
665 Ok(MainWindow {
666 gl_context,
667 window,
668 surface,
669 matrix: None,
670 idler: MainWindowIdler::default(),
671 })
672 }
673 pub fn set_matrix(&mut self, matrix: Option<Matrix3<f32>>) {
675 self.matrix = matrix;
676 }
677
678 pub unsafe fn into_pieces(
683 self,
684 ) -> (PossiblyCurrentContext, Surface<WindowSurface>, Window) {
685 (self.gl_context, self.surface, self.window)
686 }
687 pub fn glutin_context(&self) -> &PossiblyCurrentContext {
689 &self.gl_context
690 }
691 pub fn create_gl_context(&self) -> glow::Context {
693 let dsp = self.gl_context.display();
694 unsafe { glow::Context::from_loader_function_cstr(|s| dsp.get_proc_address(s)) }
695 }
696 pub fn window(&self) -> &Window {
698 &self.window
699 }
700 pub fn surface(&self) -> &Surface<WindowSurface> {
702 &self.surface
703 }
704 pub fn to_logical_size<X: Pixel, Y: Pixel>(&self, size: PhysicalSize<X>) -> LogicalSize<Y> {
706 let scale = self.window.scale_factor();
707 size.to_logical(scale)
708 }
709 pub fn to_physical_size<X: Pixel, Y: Pixel>(
711 &self,
712 size: LogicalSize<X>,
713 ) -> PhysicalSize<Y> {
714 let scale = self.window.scale_factor();
715 size.to_physical(scale)
716 }
717 pub fn to_logical_pos<X: Pixel, Y: Pixel>(
719 &self,
720 pos: PhysicalPosition<X>,
721 ) -> LogicalPosition<Y> {
722 let scale = self.window.scale_factor();
723 pos.to_logical(scale)
724 }
725 pub fn to_physical_pos<X: Pixel, Y: Pixel>(
727 &self,
728 pos: LogicalPosition<X>,
729 ) -> PhysicalPosition<Y> {
730 let scale = self.window.scale_factor();
731 pos.to_physical(scale)
732 }
733 }
734
735 impl MainWindowWithRenderer {
736 pub fn new(main_window: MainWindow) -> Self {
738 Self::with_builder(main_window, &imgui::ContextBuilder::new())
739 }
740 pub fn with_builder(main_window: MainWindow, builder: &imgui::ContextBuilder) -> Self {
744 let gl = main_window.create_gl_context();
745 let renderer = Renderer::with_builder(std::rc::Rc::new(gl), builder).unwrap();
746 Self::new_with_renderer(main_window, renderer)
747 }
748 pub fn set_idle_time(&mut self, time: Duration) {
750 self.main_window.idler.set_idle_time(time);
751 }
752 pub fn set_idle_frame_count(&mut self, frame_count: u32) {
756 self.main_window.idler.set_idle_frame_count(frame_count);
757 }
758 pub fn ping_user_input(&mut self) {
763 self.main_window.idler.ping_user_input();
764 }
765 pub fn renderer(&mut self) -> &mut Renderer {
767 &mut self.renderer
768 }
769 pub fn imgui(&mut self) -> &mut imgui::Context {
773 self.renderer.imgui()
774 }
775 pub fn main_window(&mut self) -> &mut MainWindow {
777 &mut self.main_window
778 }
779 pub fn new_with_renderer(main_window: MainWindow, mut renderer: Renderer) -> Self {
781 let w = main_window.window();
782 let size = w.inner_size();
783 let scale = w.scale_factor();
784 let size = size.to_logical::<f32>(scale);
785 renderer.set_size(Vector2::from(mint::Vector2::from(size)), scale as f32);
786
787 #[cfg(feature = "clipboard")]
788 clipboard::setup(renderer.imgui());
789
790 MainWindowWithRenderer {
791 main_window,
792 renderer,
793 status: MainWindowStatus::default(),
794 }
795 }
796 pub fn window_event(
801 &mut self,
802 app: &mut impl imgui::UiBuilder,
803 event: &winit::event::WindowEvent,
804 flags: EventFlags,
805 ) -> EventResult {
806 window_event(
807 &mut self.main_window,
808 &mut self.renderer,
809 &mut self.status,
810 app,
811 event,
812 flags,
813 )
814 }
815
816 pub fn new_events(&mut self) {
818 new_events(&mut self.renderer, &mut self.status);
819 }
820 pub fn about_to_wait(&mut self) {
822 about_to_wait(&mut self.main_window, &mut self.renderer);
823 }
824 }
825
826 impl MainWindowRef for MainWindow {
828 fn window(&self) -> &Window {
829 &self.window
830 }
831 fn pre_render(&mut self) {
832 self.idler.incr_frame();
833 let _ = self
834 .gl_context
835 .make_current(&self.surface)
836 .inspect_err(|e| log::error!("{e}"));
837 }
838 fn post_render(&mut self) {
839 self.window.pre_present_notify();
840 let _ = self
841 .surface
842 .swap_buffers(&self.gl_context)
843 .inspect_err(|e| log::error!("{e}"));
844 }
845 fn resize(&mut self, size: PhysicalSize<u32>) -> LogicalSize<f32> {
846 let width = NonZeroU32::new(size.width.max(1)).unwrap();
847 let height = NonZeroU32::new(size.height.max(1)).unwrap();
848 self.surface.resize(&self.gl_context, width, height);
849 self.to_logical_size::<_, f32>(size)
850 }
851 fn ping_user_input(&mut self) {
852 self.idler.ping_user_input();
853 }
854 fn about_to_wait(&mut self, pinged: bool) {
855 if pinged || self.idler.has_to_render() {
856 self.window.request_redraw();
859 }
860 }
861 fn transform_position(&self, pos: Vector2) -> Vector2 {
862 transform_position_with_optional_matrix(self, pos, &self.matrix)
863 }
864 }
865
866 #[non_exhaustive]
874 pub struct Args<'a, A: Application> {
875 pub window: &'a mut MainWindowWithRenderer,
877 pub event_loop: &'a ActiveEventLoop,
879 pub event_proxy: &'a EventLoopProxy<AppEvent<A>>,
881 pub data: &'a mut A::Data,
883 }
884
885 pub struct LocalProxy<A: Application> {
889 event_proxy: EventLoopProxy<AppEvent<A>>,
890 pd: std::marker::PhantomData<*const ()>,
892 }
893
894 impl<A: Application> Clone for LocalProxy<A> {
895 fn clone(&self) -> Self {
896 LocalProxy {
897 event_proxy: self.event_proxy.clone(),
898 pd: std::marker::PhantomData,
899 }
900 }
901 }
902
903 macro_rules! local_proxy_impl {
904 () => {
905 pub fn spawn_idle<T: 'static, F: Future<Output = T> + 'static>(
907 &self,
908 f: F,
909 ) -> crate::FutureHandle<T> {
910 unsafe { fut::spawn_idle(&self.event_proxy, f) }
911 }
912 pub fn run_idle<F: FnOnce(&mut A, Args<'_, A>) + 'static>(
914 &self,
915 f: F,
916 ) -> Result<(), winit::event_loop::EventLoopClosed<()>> {
917 let f = send_wrapper::SendWrapper::new(f);
920 self.event_proxy
923 .run_idle(move |app, args| (f.take())(app, args))
924 .map_err(|_| winit::event_loop::EventLoopClosed(()))
925 }
926 pub fn future_back(&self) -> crate::FutureBackCaller<A> {
928 fut::future_back_caller_new()
929 }
930 };
931 }
932
933 impl<A: Application> Args<'_, A> {
934 pub fn local_proxy(&self) -> LocalProxy<A> {
936 LocalProxy {
937 event_proxy: self.event_proxy.clone(),
938 pd: std::marker::PhantomData,
939 }
940 }
941 pub fn ping_user_input(&mut self) {
943 self.window.ping_user_input();
944 }
945 local_proxy_impl! {}
946 }
947
948 impl<A: Application> LocalProxy<A> {
949 pub fn event_proxy(&self) -> &EventLoopProxy<AppEvent<A>> {
951 &self.event_proxy
952 }
953 local_proxy_impl! {}
954 }
955
956 pub trait Application: imgui::UiBuilder + Sized + 'static {
960 type UserEvent: Send + 'static;
962 type Data;
964
965 const EVENT_FLAGS: EventFlags = EventFlags::empty();
967
968 fn new(args: Args<'_, Self>) -> Self;
970
971 fn window_event(
977 &mut self,
978 args: Args<'_, Self>,
979 _event: winit::event::WindowEvent,
980 res: EventResult,
981 ) {
982 if res.window_closed {
983 args.event_loop.exit();
984 }
985 }
986
987 fn window_event_full(&mut self, args: Args<'_, Self>, event: winit::event::WindowEvent) {
991 let res = args.window.window_event(self, &event, Self::EVENT_FLAGS);
992 self.window_event(args, event, res);
993 }
994
995 fn device_event(
999 &mut self,
1000 _args: Args<'_, Self>,
1001 _device_id: winit::event::DeviceId,
1002 _event: winit::event::DeviceEvent,
1003 ) {
1004 }
1005
1006 fn user_event(&mut self, _args: Args<'_, Self>, _event: Self::UserEvent) {}
1008
1009 fn suspended(&mut self, _args: Args<'_, Self>) {}
1011
1012 fn resumed(&mut self, _args: Args<'_, Self>) {}
1014 }
1015
1016 #[non_exhaustive]
1020 pub enum AppEvent<A: Application> {
1021 PingUserInput,
1023 #[allow(clippy::type_complexity)]
1025 RunIdle(Box<dyn FnOnce(&mut A, Args<'_, A>) + Send + Sync>),
1026 RunIdleSimple(Box<dyn FnOnce() + Send + Sync>),
1028 User(A::UserEvent),
1030 }
1031
1032 impl<A: Application> std::fmt::Debug for AppEvent<A> {
1033 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1034 write!(fmt, "<AppEvent>")
1035 }
1036 }
1037
1038 pub trait EventLoopExt<A: Application> {
1040 fn send_user(
1042 &self,
1043 u: A::UserEvent,
1044 ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>>;
1045 fn ping_user_input(&self) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>>;
1047 fn run_idle<F: FnOnce(&mut A, Args<'_, A>) + Send + Sync + 'static>(
1049 &self,
1050 f: F,
1051 ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>>;
1052 }
1053
1054 impl<A: Application> EventLoopExt<A> for EventLoopProxy<AppEvent<A>> {
1055 fn send_user(
1056 &self,
1057 u: A::UserEvent,
1058 ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>> {
1059 self.send_event(AppEvent::User(u))
1060 }
1061 fn ping_user_input(&self) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>> {
1062 self.send_event(AppEvent::PingUserInput)
1063 }
1064 fn run_idle<F: FnOnce(&mut A, Args<'_, A>) + Send + Sync + 'static>(
1065 &self,
1066 f: F,
1067 ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>> {
1068 self.send_event(AppEvent::RunIdle(Box::new(f)))
1069 }
1070 }
1071
1072 pub struct AppHandler<A: Application> {
1087 builder: imgui::ContextBuilder,
1088 wattrs: WindowAttributes,
1089 event_proxy: EventLoopProxy<AppEvent<A>>,
1090 window: Option<MainWindowWithRenderer>,
1091 app: Option<A>,
1092 app_data: A::Data,
1093 }
1094
1095 impl<A: Application> AppHandler<A> {
1096 pub fn new(event_loop: &EventLoop<AppEvent<A>>, app_data: A::Data) -> Self {
1100 AppHandler {
1101 builder: imgui::ContextBuilder::new(),
1102 wattrs: Window::default_attributes(),
1103 event_proxy: event_loop.create_proxy(),
1104 window: None,
1105 app: None,
1106 app_data,
1107 }
1108 }
1109 pub fn imgui_builder(&mut self) -> &mut imgui::ContextBuilder {
1113 &mut self.builder
1114 }
1115 pub fn set_attributes(&mut self, wattrs: WindowAttributes) {
1117 self.wattrs = wattrs;
1118 }
1119 pub fn attributes(&mut self) -> &mut WindowAttributes {
1124 &mut self.wattrs
1125 }
1126 pub fn data(&self) -> &A::Data {
1128 &self.app_data
1129 }
1130 pub fn data_mut(&mut self) -> &mut A::Data {
1132 &mut self.app_data
1133 }
1134 pub fn app(&self) -> Option<&A> {
1136 self.app.as_ref()
1137 }
1138 pub fn app_mut(&mut self) -> Option<&mut A> {
1140 self.app.as_mut()
1141 }
1142 pub fn into_inner(self) -> (Option<A>, A::Data) {
1147 (self.app, self.app_data)
1148 }
1149
1150 pub fn event_proxy(&self) -> &EventLoopProxy<AppEvent<A>> {
1152 &self.event_proxy
1153 }
1154 }
1155
1156 impl<A> winit::application::ApplicationHandler<AppEvent<A>> for AppHandler<A>
1157 where
1158 A: Application,
1159 {
1160 fn suspended(&mut self, event_loop: &ActiveEventLoop) {
1161 let Some(window) = self.window.as_mut() else {
1162 return;
1163 };
1164 if let Some(app) = &mut self.app {
1165 let args = Args {
1166 window,
1167 event_loop,
1168 event_proxy: &self.event_proxy,
1169 data: &mut self.app_data,
1170 };
1171 app.suspended(args);
1172 }
1173 self.window = None;
1174 }
1175 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
1176 let main_window = MainWindow::new(event_loop, self.wattrs.clone()).unwrap();
1177 let mut window = MainWindowWithRenderer::with_builder(main_window, &self.builder);
1178
1179 let args = Args {
1180 window: &mut window,
1181 event_loop,
1182 event_proxy: &self.event_proxy,
1183 data: &mut self.app_data,
1184 };
1185 match &mut self.app {
1186 None => self.app = Some(A::new(args)),
1187 Some(app) => app.resumed(args),
1188 }
1189 self.window = Some(window);
1190 }
1191 fn window_event(
1192 &mut self,
1193 event_loop: &ActiveEventLoop,
1194 window_id: winit::window::WindowId,
1195 event: winit::event::WindowEvent,
1196 ) {
1197 let (Some(window), Some(app)) = (self.window.as_mut(), self.app.as_mut()) else {
1198 return;
1199 };
1200 let w = window.main_window();
1201 if w.window().id() != window_id {
1202 return;
1203 }
1204
1205 let args = Args {
1206 window,
1207 event_loop,
1208 event_proxy: &self.event_proxy,
1209 data: &mut self.app_data,
1210 };
1211 app.window_event_full(args, event);
1212 }
1213 fn device_event(
1214 &mut self,
1215 event_loop: &ActiveEventLoop,
1216 device_id: winit::event::DeviceId,
1217 event: winit::event::DeviceEvent,
1218 ) {
1219 let (Some(window), Some(app)) = (self.window.as_mut(), self.app.as_mut()) else {
1220 return;
1221 };
1222 let args = Args {
1223 window,
1224 event_loop,
1225 event_proxy: &self.event_proxy,
1226 data: &mut self.app_data,
1227 };
1228 app.device_event(args, device_id, event);
1229 }
1230 fn user_event(&mut self, event_loop: &ActiveEventLoop, event: AppEvent<A>) {
1231 let (Some(window), Some(app)) = (self.window.as_mut(), self.app.as_mut()) else {
1232 return;
1233 };
1234 let args = Args {
1235 window,
1236 event_loop,
1237 event_proxy: &self.event_proxy,
1238 data: &mut self.app_data,
1239 };
1240
1241 match event {
1242 AppEvent::PingUserInput => window.ping_user_input(),
1243 AppEvent::RunIdle(f) => f(app, args),
1244 AppEvent::RunIdleSimple(f) => fut::future_back_caller_prepare((app, args), f),
1245 AppEvent::User(uevent) => app.user_event(args, uevent),
1246 }
1247 }
1248 fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: winit::event::StartCause) {
1249 let Some(window) = self.window.as_mut() else {
1250 return;
1251 };
1252 window.new_events();
1253 }
1254 fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
1255 let Some(window) = self.window.as_mut() else {
1256 return;
1257 };
1258 window.about_to_wait();
1259 }
1260 }
1261}
1262
1263#[cfg(feature = "main-window")]
1264pub use main_window::*;