use std::{ cell::RefCell, collections::{HashMap, VecDeque}, num::NonZeroU32, ptr::null_mut, rc::Rc, slice, }; use glow::HasContext; use glutin::{ config::ConfigTemplateBuilder, context::{ContextAttributesBuilder, NotCurrentContext}, display::GetGlDisplay, prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext}, surface::{GlSurface, Surface, SurfaceAttributesBuilder, WindowSurface}, }; use glutin_winit::DisplayBuilder; use imgui::{BackendFlags, ConfigFlags, Id, Key, MouseButton, ViewportFlags}; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use thiserror::Error; use winit::{ dpi::{PhysicalPosition, PhysicalSize}, event::{DeviceEvent, ElementState, KeyboardInput, TouchPhase, VirtualKeyCode}, event_loop::EventLoopWindowTarget, window::{CursorIcon, Window, WindowBuilder}, }; const VERTEX_SHADER: &str = include_str!("vertex_shader.glsl"); const FRAGMENT_SHADER: &str = include_str!("fragment_shader.glsl"); #[derive(Debug, Error)] pub enum RendererError { #[error("OpenGL shader creation failed: {0}")] GlShaderCreationFailed(String), #[error("OpenGL program creation failed: {0}")] GlProgramCreationFailed(String), #[error("OpenGL texture creation failed: {0}")] GlTextureCreationFailed(String), #[error("OpenGL buffer creation failed: {0}")] GlBufferCreationFailed(String), #[error("OpenGL vertex array creation failed: {0}")] GlVertexArrayCreationFailed(String), #[error("Failed to create viewport window")] WindowCreationFailed, #[error("Failed to create viewport window context")] WindowContextCreationFailed, #[error("Failed to create viewport window surface")] WindowSurfaceCreationFailed, #[error("Failed to make viewport context current")] MakeCurrentFailed, #[error("Failed to make swap buffers on surface")] SwapBuffersFailed, } #[derive(Debug)] enum ViewportEvent { Create(Id), Destroy(Id), SetPos(Id, [f32; 2]), SetSize(Id, [f32; 2]), SetVisible(Id), SetFocus(Id), SetTitle(Id, String), } #[derive(Debug)] pub struct Renderer { gl_objects: GlObjects, glutin_config: Option, /// The tuple members have to stay in exactly this order /// to ensure that surface, context and window are dropped in this order extra_windows: HashMap< Id, ( GlObjects, Surface, Option, Window, ), >, event_queue: Rc>>, font_width: u32, font_height: u32, font_pixels: Vec, last_cursor: CursorIcon, } #[derive(Debug)] struct GlObjects { program: glow::Program, font_texture: glow::Texture, vao: glow::VertexArray, vbo: glow::Buffer, ibo: glow::Buffer, } impl GlObjects { pub fn new( font_width: u32, font_height: u32, font_pixels: &[u8], glow: &glow::Context, ) -> Result { let program = unsafe { let vertex_shader = glow .create_shader(glow::VERTEX_SHADER) .map_err(RendererError::GlShaderCreationFailed)?; glow.shader_source(vertex_shader, VERTEX_SHADER); glow.compile_shader(vertex_shader); assert!( glow.get_shader_compile_status(vertex_shader), "Vertex Shader contains error" ); let fragment_shader = glow .create_shader(glow::FRAGMENT_SHADER) .map_err(RendererError::GlShaderCreationFailed)?; glow.shader_source(fragment_shader, FRAGMENT_SHADER); glow.compile_shader(fragment_shader); assert!( glow.get_shader_compile_status(fragment_shader), "Fragment Shader contains error" ); let program = glow .create_program() .map_err(RendererError::GlProgramCreationFailed)?; glow.attach_shader(program, vertex_shader); glow.attach_shader(program, fragment_shader); glow.link_program(program); assert!( glow.get_program_link_status(program), "Program contains error" ); glow.delete_shader(vertex_shader); glow.delete_shader(fragment_shader); program }; let font_texture = unsafe { let tex = glow .create_texture() .map_err(RendererError::GlTextureCreationFailed)?; glow.bind_texture(glow::TEXTURE_2D, Some(tex)); glow.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::LINEAR as i32, ); glow.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR as i32, ); glow.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as i32, ); glow.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as i32, ); glow.tex_image_2d( glow::TEXTURE_2D, 0, glow::RGBA as i32, font_width as i32, font_height as i32, 0, glow::RGBA, glow::UNSIGNED_BYTE, Some(font_pixels), ); tex }; let vbo = unsafe { glow.create_buffer() .map_err(RendererError::GlBufferCreationFailed)? }; let ibo = unsafe { glow.create_buffer() .map_err(RendererError::GlBufferCreationFailed)? }; let vao = unsafe { let vao = glow .create_vertex_array() .map_err(RendererError::GlVertexArrayCreationFailed)?; glow.bind_vertex_array(Some(vao)); glow.bind_buffer(glow::ARRAY_BUFFER, Some(vbo)); glow.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ibo)); glow.enable_vertex_attrib_array(0); glow.enable_vertex_attrib_array(1); glow.enable_vertex_attrib_array(2); glow.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, 20, 0); glow.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, 20, 8); glow.vertex_attrib_pointer_f32(2, 4, glow::UNSIGNED_BYTE, true, 20, 16); glow.bind_vertex_array(None); vao }; Ok(Self { program, font_texture, vao, vbo, ibo, }) } } #[derive(Debug)] struct GlStateBackup { viewport: [i32; 4], blend_enabled: bool, blend_func_src: i32, blend_func_dst: i32, scissor_enabled: bool, scissor: [i32; 4], vao: i32, vbo: i32, ibo: i32, active_texture: i32, texture: i32, program: i32, } impl GlStateBackup { fn backup(context: &glow::Context) -> Self { unsafe { let mut viewport = [0; 4]; context.get_parameter_i32_slice(glow::VIEWPORT, &mut viewport); let blend_enabled = context.is_enabled(glow::BLEND); let blend_func_src = context.get_parameter_i32(glow::BLEND_SRC); let blend_func_dst = context.get_parameter_i32(glow::BLEND_DST); let scissor_enabled = context.is_enabled(glow::SCISSOR_TEST); let mut scissor = [0; 4]; context.get_parameter_i32_slice(glow::SCISSOR_BOX, &mut scissor); let vao = context.get_parameter_i32(glow::VERTEX_ARRAY_BINDING); let vbo = context.get_parameter_i32(glow::ARRAY_BUFFER_BINDING); let ibo = context.get_parameter_i32(glow::ELEMENT_ARRAY_BUFFER_BINDING); let active_texture = context.get_parameter_i32(glow::ACTIVE_TEXTURE); context.active_texture(0); let texture = context.get_parameter_i32(glow::TEXTURE_BINDING_2D); let program = context.get_parameter_i32(glow::CURRENT_PROGRAM); Self { viewport, blend_enabled, blend_func_src, blend_func_dst, scissor_enabled, scissor, vao, vbo, ibo, active_texture, texture, program, } } } fn restore(&self, context: &glow::Context) { unsafe { context.viewport( self.viewport[0], self.viewport[1], self.viewport[2], self.viewport[3], ); Self::enable(context, glow::BLEND, self.blend_enabled); context.blend_func(self.blend_func_src as _, self.blend_func_dst as _); Self::enable(context, glow::SCISSOR_TEST, self.scissor_enabled); context.scissor( self.scissor[0], self.scissor[1], self.scissor[2], self.scissor[3], ); if self.vao != 0 { let vao = std::mem::transmute(self.vao); context.bind_vertex_array(Some(vao)); } else { context.bind_vertex_array(None); } if self.vbo != 0 { let vbo = std::mem::transmute(self.vbo); context.bind_buffer(glow::ARRAY_BUFFER, Some(vbo)); } else { context.bind_buffer(glow::ARRAY_BUFFER, None); } if self.ibo != 0 { let ibo = std::mem::transmute(self.ibo); context.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ibo)); } else { context.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); } if self.texture != 0 { let texture = std::mem::transmute(self.texture); context.bind_texture(glow::TEXTURE_2D, Some(texture)); } else { context.bind_texture(glow::TEXTURE_2D, None); } context.active_texture(self.active_texture as _); if self.program != 0 { let program = std::mem::transmute(self.program); context.use_program(Some(program)); } else { context.use_program(None); } } } fn enable(context: &glow::Context, feature: u32, value: bool) { unsafe { if value { context.enable(feature); } else { context.disable(feature); } } } } impl Renderer { pub fn new( imgui: &mut imgui::Context, main_window: &Window, gl_context: &glow::Context, ) -> Result { let io = imgui.io_mut(); // there is no good way to handle viewports on wayland, // so we disable them match main_window.raw_window_handle() { RawWindowHandle::Wayland(_) => {} _ => { io.backend_flags .insert(BackendFlags::PLATFORM_HAS_VIEWPORTS); io.backend_flags .insert(BackendFlags::RENDERER_HAS_VIEWPORTS); } } io.backend_flags.insert(BackendFlags::HAS_MOUSE_CURSORS); io.backend_flags.insert(BackendFlags::HAS_SET_MOUSE_POS); io.backend_flags .insert(BackendFlags::RENDERER_HAS_VTX_OFFSET); let window_size = main_window.inner_size().cast::(); io.display_size = [window_size.width, window_size.height]; io.display_framebuffer_scale = [1.0, 1.0]; let viewport = imgui.main_viewport_mut(); let main_pos = main_window .inner_position() .unwrap_or_default() .cast::(); viewport.pos = [main_pos.x, main_pos.y]; viewport.work_pos = viewport.pos; viewport.size = [window_size.width, window_size.height]; viewport.work_size = viewport.size; viewport.dpi_scale = 1.0; viewport.platform_user_data = Box::into_raw(Box::new(ViewportData { pos: [main_pos.x, main_pos.y], size: [window_size.width, window_size.height], focus: true, minimized: false, })) .cast(); let mut monitors = Vec::new(); for monitor in main_window.available_monitors() { monitors.push(imgui::PlatformMonitor { main_pos: [monitor.position().x as f32, monitor.position().y as f32], main_size: [monitor.size().width as f32, monitor.size().height as f32], work_pos: [monitor.position().x as f32, monitor.position().y as f32], work_size: [monitor.size().width as f32, monitor.size().height as f32], dpi_scale: 1.0, }); } imgui .platform_io_mut() .monitors .replace_from_slice(&monitors); imgui.set_platform_name(Some(format!( "imgui-winit-glow-renderer-viewports {}", env!("CARGO_PKG_VERSION") ))); imgui.set_renderer_name(Some(format!( "imgui-winit-glow-renderer-viewports {}", env!("CARGO_PKG_VERSION") ))); let event_queue = Rc::new(RefCell::new(VecDeque::new())); imgui.set_platform_backend(PlatformBackend { event_queue: event_queue.clone(), }); imgui.set_renderer_backend(RendererBackend {}); let font_tex = imgui.fonts().build_rgba32_texture(); let gl_objects = GlObjects::new(font_tex.width, font_tex.height, font_tex.data, gl_context)?; Ok(Self { gl_objects, glutin_config: None, extra_windows: HashMap::new(), event_queue, font_width: font_tex.width, font_height: font_tex.height, font_pixels: font_tex.data.to_vec(), last_cursor: CursorIcon::Default, }) } pub fn handle_event( &mut self, imgui: &mut imgui::Context, main_window: &Window, event: &winit::event::Event, ) { match *event { winit::event::Event::WindowEvent { window_id, ref event, } => { let (window, viewport) = if window_id == main_window.id() { (main_window, imgui.main_viewport_mut()) } else if let Some((id, wnd)) = self.extra_windows.iter().find_map(|(id, (_, _, _, wnd))| { if wnd.id() == window_id { Some((*id, wnd)) } else { None } }) { if let Some(viewport) = imgui.viewport_by_id_mut(id) { (wnd, viewport) } else { return; } } else { return; }; match *event { winit::event::WindowEvent::Resized(new_size) => { unsafe { (*(viewport.platform_user_data.cast::())).size = [new_size.width as f32, new_size.height as f32]; } viewport.platform_request_resize = true; if window_id == main_window.id() { imgui.io_mut().display_size = [new_size.width as f32, new_size.height as f32]; } } winit::event::WindowEvent::Moved(_) => unsafe { let new_pos = window.inner_position().unwrap().cast::(); (*(viewport.platform_user_data.cast::())).pos = [new_pos.x as f32, new_pos.y as f32]; viewport.platform_request_move = true; }, winit::event::WindowEvent::CloseRequested if window_id != main_window.id() => { viewport.platform_request_close = true; } winit::event::WindowEvent::ReceivedCharacter(c) => { imgui.io_mut().add_input_character(c); } winit::event::WindowEvent::Focused(f) => unsafe { (*(viewport.platform_user_data.cast::())).focus = f; }, winit::event::WindowEvent::KeyboardInput { input: KeyboardInput { virtual_keycode: Some(key), state: ElementState::Pressed, .. }, .. } => { if let Some(key) = to_imgui_key(key) { imgui.io_mut().add_key_event(key, true); } } winit::event::WindowEvent::ModifiersChanged(modifiers) => { imgui .io_mut() .add_key_event(Key::ModShift, modifiers.shift()); imgui.io_mut().add_key_event(Key::ModCtrl, modifiers.ctrl()); imgui.io_mut().add_key_event(Key::ModAlt, modifiers.alt()); imgui .io_mut() .add_key_event(Key::ModSuper, modifiers.logo()); } winit::event::WindowEvent::CursorMoved { position, .. } => { if imgui .io() .config_flags .contains(ConfigFlags::VIEWPORTS_ENABLE) { let window_pos = window.inner_position().unwrap_or_default().cast::(); imgui.io_mut().add_mouse_pos_event([ position.x as f32 + window_pos.x, position.y as f32 + window_pos.y, ]); } else { imgui .io_mut() .add_mouse_pos_event([position.x as f32, position.y as f32]); } } winit::event::WindowEvent::MouseWheel { delta, phase: TouchPhase::Moved, .. } => match delta { winit::event::MouseScrollDelta::LineDelta(h, v) => { imgui.io_mut().add_mouse_wheel_event([h, v]); } winit::event::MouseScrollDelta::PixelDelta(pos) => { let h = if pos.x > 0.0 { 1.0 } else if pos.x < 0.0 { -1.0 } else { 0.0 }; let v = if pos.y > 0.0 { 1.0 } else if pos.y < 0.0 { -1.0 } else { 0.0 }; imgui.io_mut().add_mouse_wheel_event([h, v]); } }, winit::event::WindowEvent::MouseInput { state, button, .. } => { let state = state == ElementState::Pressed; if let Some(button) = to_imgui_mouse_button(button) { imgui.io_mut().add_mouse_button_event(button, state); } } _ => {} } } winit::event::Event::DeviceEvent { event: DeviceEvent::Key(KeyboardInput { virtual_keycode: Some(key), state: ElementState::Released, .. }), .. } => { if let Some(key) = to_imgui_key(key) { imgui.io_mut().add_key_event(key, false); } } _ => {} } } pub fn update_viewports( &mut self, imgui: &mut imgui::Context, window_target: &EventLoopWindowTarget, glow: &glow::Context, ) -> Result<(), RendererError> { loop { let event = self.event_queue.borrow_mut().pop_front(); let event = if let Some(event) = event { event } else { break; }; match event { ViewportEvent::Create(id) => { if let Some(viewport) = imgui.viewport_by_id_mut(id) { let extra_window = self.create_extra_window(viewport, window_target, glow)?; self.extra_windows.insert(id, extra_window); } } ViewportEvent::Destroy(id) => { self.extra_windows.remove(&id); } ViewportEvent::SetPos(id, pos) => { if let Some((_, _, _, wnd)) = self.extra_windows.get(&id) { wnd.set_outer_position(PhysicalPosition::new(pos[0], pos[1])); } } ViewportEvent::SetSize(id, size) => { if let Some((_, _, _, wnd)) = self.extra_windows.get(&id) { wnd.set_inner_size(PhysicalSize::new(size[0], size[1])); } } ViewportEvent::SetVisible(id) => { if let Some((_, _, _, wnd)) = self.extra_windows.get(&id) { wnd.set_visible(true); } } ViewportEvent::SetFocus(id) => { if let Some((_, _, _, wnd)) = self.extra_windows.get(&id) { wnd.focus_window(); } } ViewportEvent::SetTitle(id, title) => { if let Some((_, _, _, wnd)) = self.extra_windows.get(&id) { wnd.set_title(&title); } } } } Ok(()) } fn to_winit_cursor(cursor: imgui::MouseCursor) -> winit::window::CursorIcon { match cursor { imgui::MouseCursor::Arrow => winit::window::CursorIcon::Default, imgui::MouseCursor::TextInput => winit::window::CursorIcon::Text, imgui::MouseCursor::ResizeAll => winit::window::CursorIcon::Move, imgui::MouseCursor::ResizeNS => winit::window::CursorIcon::NsResize, imgui::MouseCursor::ResizeEW => winit::window::CursorIcon::EwResize, imgui::MouseCursor::ResizeNESW => winit::window::CursorIcon::NeswResize, imgui::MouseCursor::ResizeNWSE => winit::window::CursorIcon::NwseResize, imgui::MouseCursor::Hand => winit::window::CursorIcon::Hand, imgui::MouseCursor::NotAllowed => winit::window::CursorIcon::NotAllowed, } } pub fn prepare_render(&mut self, imgui: &mut imgui::Context, main_window: &Window) { if let Some(cursor) = imgui.mouse_cursor() { let cursor = Self::to_winit_cursor(cursor); if self.last_cursor != cursor { main_window.set_cursor_icon(cursor); for (_, _, _, wnd) in self.extra_windows.values() { wnd.set_cursor_icon(cursor); } self.last_cursor = cursor; } } } fn create_extra_window( &mut self, viewport: &mut imgui::Viewport, window_target: &EventLoopWindowTarget, glow: &glow::Context, ) -> Result< ( GlObjects, Surface, Option, Window, ), RendererError, > { let window_builder = WindowBuilder::new() .with_position(PhysicalPosition::new(viewport.pos[0], viewport.pos[1])) .with_inner_size(PhysicalSize::new(viewport.size[0], viewport.size[1])) .with_visible(false) .with_resizable(true) .with_decorations(!viewport.flags.contains(ViewportFlags::NO_DECORATION)); let window = if let Some(glutin_config) = &self.glutin_config { glutin_winit::finalize_window(window_target, window_builder, glutin_config) .map_err(|_| RendererError::WindowCreationFailed)? } else { let template_builder = ConfigTemplateBuilder::new(); let (window, cfg) = DisplayBuilder::new() .with_window_builder(Some(window_builder)) .build(window_target, template_builder, |mut configs| { configs.next().unwrap() }) .map_err(|_| RendererError::WindowCreationFailed)?; self.glutin_config = Some(cfg); window.unwrap() }; let glutin_config = self.glutin_config.as_ref().unwrap(); let context_attribs = ContextAttributesBuilder::new().build(Some(window.raw_window_handle())); let context = unsafe { glutin_config .display() .create_context(glutin_config, &context_attribs) .map_err(|_| RendererError::WindowContextCreationFailed)? }; let surface_attribs = SurfaceAttributesBuilder::::new().build( window.raw_window_handle(), NonZeroU32::new(viewport.size[0] as u32).unwrap(), NonZeroU32::new(viewport.size[1] as u32).unwrap(), ); let surface = unsafe { glutin_config .display() .create_window_surface(glutin_config, &surface_attribs) .map_err(|_| RendererError::WindowSurfaceCreationFailed)? }; let context = context .make_current(&surface) .map_err(|_| RendererError::MakeCurrentFailed)?; let gl_objects = GlObjects::new(self.font_width, self.font_height, &self.font_pixels, glow)?; Ok(( gl_objects, surface, Some(context.make_not_current().unwrap()), window, )) } pub fn render( &mut self, main_window: &Window, glow: &glow::Context, draw_data: &imgui::DrawData, ) -> Result<(), RendererError> { let backup = GlStateBackup::backup(glow); let res = Self::render_window(main_window, glow, draw_data, &self.gl_objects); backup.restore(glow); res } pub fn render_viewports( &mut self, glow: &glow::Context, imgui: &mut imgui::Context, ) -> Result<(), RendererError> { for (id, (gl_objects, surface, context, wnd)) in &mut self.extra_windows { if let Some(viewport) = imgui.viewport_by_id(*id) { let current_context = context .take() .unwrap() .make_current(surface) .map_err(|_| RendererError::MakeCurrentFailed)?; unsafe { glow.disable(glow::SCISSOR_TEST); glow.clear(glow::COLOR_BUFFER_BIT); } Self::render_window(wnd, glow, viewport.draw_data(), gl_objects)?; surface .swap_buffers(¤t_context) .map_err(|_| RendererError::SwapBuffersFailed)?; *context = Some(current_context.make_not_current().unwrap()); } } Ok(()) } fn render_window( window: &Window, glow: &glow::Context, draw_data: &imgui::DrawData, gl_objects: &GlObjects, ) -> Result<(), RendererError> { unsafe { let window_size = window.inner_size(); glow.viewport(0, 0, window_size.width as i32, window_size.height as i32); glow.enable(glow::BLEND); glow.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA); glow.enable(glow::SCISSOR_TEST); glow.bind_vertex_array(Some(gl_objects.vao)); glow.bind_buffer(glow::ARRAY_BUFFER, Some(gl_objects.vbo)); glow.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(gl_objects.ibo)); glow.active_texture(glow::TEXTURE0); glow.bind_texture(glow::TEXTURE_2D, Some(gl_objects.font_texture)); glow.use_program(Some(gl_objects.program)); let left = draw_data.display_pos[0]; let right = draw_data.display_pos[0] + draw_data.display_size[0]; let top = draw_data.display_pos[1]; let bottom = draw_data.display_pos[1] + draw_data.display_size[1]; let matrix = [ 2.0 / (right - left), 0.0, 0.0, 0.0, 0.0, (2.0 / (top - bottom)), 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, (right + left) / (left - right), (top + bottom) / (bottom - top), 0.0, 1.0, ]; let loc = glow .get_uniform_location(gl_objects.program, "u_Matrix") .unwrap(); glow.uniform_matrix_4_f32_slice(Some(&loc), false, &matrix); for list in draw_data.draw_lists() { glow.buffer_data_u8_slice( glow::ARRAY_BUFFER, slice::from_raw_parts( list.vtx_buffer().as_ptr().cast(), list.vtx_buffer().len() * 20, ), glow::STREAM_DRAW, ); glow.buffer_data_u8_slice( glow::ELEMENT_ARRAY_BUFFER, slice::from_raw_parts( list.idx_buffer().as_ptr().cast(), list.idx_buffer().len() * 2, ), glow::STREAM_DRAW, ); for cmd in list.commands() { if let imgui::DrawCmd::Elements { count, cmd_params } = cmd { let clip_x1 = (cmd_params.clip_rect[0] - draw_data.display_pos[0]) as i32; let clip_y1 = (cmd_params.clip_rect[1] - draw_data.display_pos[1]) as i32; let clip_x2 = (cmd_params.clip_rect[2] - draw_data.display_pos[0]) as i32; let clip_y2 = (cmd_params.clip_rect[3] - draw_data.display_pos[1]) as i32; glow.scissor( clip_x1, window_size.height as i32 - clip_y2, clip_x2 - clip_x1, clip_y2 - clip_y1, ); glow.draw_elements_base_vertex( glow::TRIANGLES, count as i32, glow::UNSIGNED_SHORT, (cmd_params.idx_offset * 2) as i32, cmd_params.vtx_offset as i32, ); } } } } Ok(()) } } struct ViewportData { pos: [f32; 2], size: [f32; 2], focus: bool, minimized: bool, } struct PlatformBackend { event_queue: Rc>>, } impl imgui::PlatformViewportBackend for PlatformBackend { fn create_window(&mut self, viewport: &mut imgui::Viewport) { viewport.platform_user_data = Box::into_raw(Box::new(ViewportData { pos: viewport.pos, size: viewport.size, focus: false, minimized: false, })) .cast(); self.event_queue .borrow_mut() .push_back(ViewportEvent::Create(viewport.id)); } fn destroy_window(&mut self, viewport: &mut imgui::Viewport) { unsafe { drop(Box::from_raw( viewport.platform_user_data.cast::(), )); } viewport.platform_user_data = null_mut(); self.event_queue .borrow_mut() .push_back(ViewportEvent::Destroy(viewport.id)); } fn show_window(&mut self, viewport: &mut imgui::Viewport) { self.event_queue .borrow_mut() .push_back(ViewportEvent::SetVisible(viewport.id)); } fn set_window_pos(&mut self, viewport: &mut imgui::Viewport, pos: [f32; 2]) { self.event_queue .borrow_mut() .push_back(ViewportEvent::SetPos(viewport.id, pos)); } fn get_window_pos(&mut self, viewport: &mut imgui::Viewport) -> [f32; 2] { unsafe { (*(viewport.platform_user_data.cast::())).pos } } fn set_window_size(&mut self, viewport: &mut imgui::Viewport, size: [f32; 2]) { self.event_queue .borrow_mut() .push_back(ViewportEvent::SetSize(viewport.id, size)); } fn get_window_size(&mut self, viewport: &mut imgui::Viewport) -> [f32; 2] { unsafe { (*(viewport.platform_user_data.cast::())).size } } fn set_window_focus(&mut self, viewport: &mut imgui::Viewport) { self.event_queue .borrow_mut() .push_back(ViewportEvent::SetFocus(viewport.id)); } fn get_window_focus(&mut self, viewport: &mut imgui::Viewport) -> bool { unsafe { (*(viewport.platform_user_data.cast::())).focus } } fn get_window_minimized(&mut self, viewport: &mut imgui::Viewport) -> bool { unsafe { (*(viewport.platform_user_data.cast::())).minimized } } fn set_window_title(&mut self, viewport: &mut imgui::Viewport, title: &str) { self.event_queue .borrow_mut() .push_back(ViewportEvent::SetTitle(viewport.id, title.to_owned())); } fn set_window_alpha(&mut self, _viewport: &mut imgui::Viewport, _alpha: f32) {} fn update_window(&mut self, _viewport: &mut imgui::Viewport) {} fn render_window(&mut self, _viewport: &mut imgui::Viewport) {} fn swap_buffers(&mut self, _viewport: &mut imgui::Viewport) {} fn create_vk_surface( &mut self, _viewport: &mut imgui::Viewport, _instance: u64, _out_surface: &mut u64, ) -> i32 { 0 } } struct RendererBackend {} impl imgui::RendererViewportBackend for RendererBackend { fn create_window(&mut self, _viewport: &mut imgui::Viewport) {} fn destroy_window(&mut self, _viewport: &mut imgui::Viewport) {} fn set_window_size(&mut self, _viewport: &mut imgui::Viewport, _size: [f32; 2]) {} fn render_window(&mut self, _viewport: &mut imgui::Viewport) {} fn swap_buffers(&mut self, _viewport: &mut imgui::Viewport) {} } fn to_imgui_key(keycode: VirtualKeyCode) -> Option { match keycode { VirtualKeyCode::Tab => Some(Key::Tab), VirtualKeyCode::Left => Some(Key::LeftArrow), VirtualKeyCode::Right => Some(Key::RightArrow), VirtualKeyCode::Up => Some(Key::UpArrow), VirtualKeyCode::Down => Some(Key::DownArrow), VirtualKeyCode::PageUp => Some(Key::PageUp), VirtualKeyCode::PageDown => Some(Key::PageDown), VirtualKeyCode::Home => Some(Key::Home), VirtualKeyCode::End => Some(Key::End), VirtualKeyCode::Insert => Some(Key::Insert), VirtualKeyCode::Delete => Some(Key::Delete), VirtualKeyCode::Back => Some(Key::Backspace), VirtualKeyCode::Space => Some(Key::Space), VirtualKeyCode::Return => Some(Key::Enter), VirtualKeyCode::Escape => Some(Key::Escape), VirtualKeyCode::LControl => Some(Key::LeftCtrl), VirtualKeyCode::LShift => Some(Key::LeftShift), VirtualKeyCode::LAlt => Some(Key::LeftAlt), VirtualKeyCode::LWin => Some(Key::LeftSuper), VirtualKeyCode::RControl => Some(Key::RightCtrl), VirtualKeyCode::RShift => Some(Key::RightShift), VirtualKeyCode::RAlt => Some(Key::RightAlt), VirtualKeyCode::RWin => Some(Key::RightSuper), //VirtualKeyCode::Menu => Some(Key::Menu), // TODO: find out if there is a Menu key in winit VirtualKeyCode::Key0 => Some(Key::Alpha0), VirtualKeyCode::Key1 => Some(Key::Alpha1), VirtualKeyCode::Key2 => Some(Key::Alpha2), VirtualKeyCode::Key3 => Some(Key::Alpha3), VirtualKeyCode::Key4 => Some(Key::Alpha4), VirtualKeyCode::Key5 => Some(Key::Alpha5), VirtualKeyCode::Key6 => Some(Key::Alpha6), VirtualKeyCode::Key7 => Some(Key::Alpha7), VirtualKeyCode::Key8 => Some(Key::Alpha8), VirtualKeyCode::Key9 => Some(Key::Alpha9), VirtualKeyCode::A => Some(Key::A), VirtualKeyCode::B => Some(Key::B), VirtualKeyCode::C => Some(Key::C), VirtualKeyCode::D => Some(Key::D), VirtualKeyCode::E => Some(Key::E), VirtualKeyCode::F => Some(Key::F), VirtualKeyCode::G => Some(Key::G), VirtualKeyCode::H => Some(Key::H), VirtualKeyCode::I => Some(Key::I), VirtualKeyCode::J => Some(Key::J), VirtualKeyCode::K => Some(Key::K), VirtualKeyCode::L => Some(Key::L), VirtualKeyCode::M => Some(Key::M), VirtualKeyCode::N => Some(Key::N), VirtualKeyCode::O => Some(Key::O), VirtualKeyCode::P => Some(Key::P), VirtualKeyCode::Q => Some(Key::Q), VirtualKeyCode::R => Some(Key::R), VirtualKeyCode::S => Some(Key::S), VirtualKeyCode::T => Some(Key::T), VirtualKeyCode::U => Some(Key::U), VirtualKeyCode::V => Some(Key::V), VirtualKeyCode::W => Some(Key::W), VirtualKeyCode::X => Some(Key::X), VirtualKeyCode::Y => Some(Key::Y), VirtualKeyCode::Z => Some(Key::Z), VirtualKeyCode::F1 => Some(Key::F1), VirtualKeyCode::F2 => Some(Key::F2), VirtualKeyCode::F3 => Some(Key::F3), VirtualKeyCode::F4 => Some(Key::F4), VirtualKeyCode::F5 => Some(Key::F5), VirtualKeyCode::F6 => Some(Key::F6), VirtualKeyCode::F7 => Some(Key::F7), VirtualKeyCode::F8 => Some(Key::F8), VirtualKeyCode::F9 => Some(Key::F9), VirtualKeyCode::F10 => Some(Key::F10), VirtualKeyCode::F11 => Some(Key::F11), VirtualKeyCode::F12 => Some(Key::F12), VirtualKeyCode::Apostrophe => Some(Key::Apostrophe), VirtualKeyCode::Comma => Some(Key::Comma), VirtualKeyCode::Minus => Some(Key::Minus), VirtualKeyCode::Period => Some(Key::Period), VirtualKeyCode::Slash => Some(Key::Slash), VirtualKeyCode::Semicolon => Some(Key::Semicolon), VirtualKeyCode::Equals => Some(Key::Equal), VirtualKeyCode::LBracket => Some(Key::LeftBracket), VirtualKeyCode::Backslash => Some(Key::Backslash), VirtualKeyCode::RBracket => Some(Key::RightBracket), VirtualKeyCode::Grave => Some(Key::GraveAccent), VirtualKeyCode::Capital => Some(Key::CapsLock), VirtualKeyCode::Scroll => Some(Key::ScrollLock), VirtualKeyCode::Numlock => Some(Key::NumLock), VirtualKeyCode::Snapshot => Some(Key::PrintScreen), VirtualKeyCode::Pause => Some(Key::Pause), VirtualKeyCode::Numpad0 => Some(Key::Keypad0), VirtualKeyCode::Numpad1 => Some(Key::Keypad1), VirtualKeyCode::Numpad2 => Some(Key::Keypad2), VirtualKeyCode::Numpad3 => Some(Key::Keypad3), VirtualKeyCode::Numpad4 => Some(Key::Keypad4), VirtualKeyCode::Numpad5 => Some(Key::Keypad5), VirtualKeyCode::Numpad6 => Some(Key::Keypad6), VirtualKeyCode::Numpad7 => Some(Key::Keypad7), VirtualKeyCode::Numpad8 => Some(Key::Keypad8), VirtualKeyCode::Numpad9 => Some(Key::Keypad9), VirtualKeyCode::NumpadDecimal => Some(Key::KeypadDecimal), VirtualKeyCode::NumpadDivide => Some(Key::KeypadDivide), VirtualKeyCode::NumpadMultiply => Some(Key::KeypadMultiply), VirtualKeyCode::NumpadSubtract => Some(Key::KeypadSubtract), VirtualKeyCode::NumpadAdd => Some(Key::KeypadAdd), VirtualKeyCode::NumpadEnter => Some(Key::KeypadEnter), VirtualKeyCode::NumpadEquals => Some(Key::KeypadEqual), _ => None, } } fn to_imgui_mouse_button(button: winit::event::MouseButton) -> Option { match button { winit::event::MouseButton::Left | winit::event::MouseButton::Other(0) => { Some(imgui::MouseButton::Left) } winit::event::MouseButton::Right | winit::event::MouseButton::Other(1) => { Some(imgui::MouseButton::Right) } winit::event::MouseButton::Middle | winit::event::MouseButton::Other(2) => { Some(imgui::MouseButton::Middle) } winit::event::MouseButton::Other(3) => Some(imgui::MouseButton::Extra1), winit::event::MouseButton::Other(4) => Some(imgui::MouseButton::Extra2), _ => None, } }